{
    "schema": "https://saferpage.de/schemas/operator-delivery-credential-preflight.v1",
    "generated_at": "2026-06-09T21:06:18+00:00",
    "available": true,
    "status": "blocked",
    "health": "blocked",
    "summary": "Delivery-Credential-Preflight: 1 von 7 Zielsystemtypen voll konfiguriert, 0 teilweise, native Runner-Ziele 1, Versandfreigabe nicht aktiv.",
    "metrics": {
        "channel_count": 7,
        "ready_channel_count": 1,
        "partial_channel_count": 0,
        "native_ready_channel_count": 1,
        "configured_target_ref_count": 0,
        "gate_count": 9,
        "passed_gate_count": 7,
        "warning_gate_count": 1,
        "blocked_gate_count": 1,
        "latest_smoke_available": 1,
        "latest_smoke_ok": 1,
        "latest_smoke_target_count": 6,
        "latest_smoke_failed_check_count": 0,
        "latest_smoke_blocked_expected_count": 1,
        "latest_smoke_dry_run_sent_count": 0,
        "latest_smoke_runner_sent_count": 0,
        "dispatch_approved": false,
        "runner_state_available": true,
        "runner_sent_count": 0,
        "runner_deliverable_count": 16,
        "runner_error_count": 0,
        "runner_outbox_count": 64,
        "runner_channel_count": 4,
        "external_send_attempt_count": 0,
        "local_file_sink_attempt_count": 0,
        "dry_run_queued_count": 16,
        "credential_reference_count": 6,
        "configured_credential_reference_count": 0,
        "runtime_control_count": 8
    },
    "run_policy": {
        "base_url": "http://127.0.0.1",
        "recent_limit": 80,
        "max_hosts": 20,
        "timeout_seconds": 20,
        "execute_ready": true,
        "dispatch_approved": false,
        "external_delivery_only_if": "--execute-ready was passed, SAFERPAGE_ALERT_DISPATCH_APPROVED=yes and the channel target is configured",
        "external_send_attempt_count": 0,
        "local_file_sink_attempt_count": 0,
        "dry_run_queued_count": 16,
        "local_file_sink_policy": "Writes sanitized JSONL summary rows only when dispatch is approved and --execute-ready is passed.",
        "no_secret_export": true
    },
    "credential_manifest": {
        "reference_count": 6,
        "present_reference_count": 0,
        "external_native_target_ready": false,
        "dispatch_approved": false,
        "secret_policy": "Only env_ref and present flags are exported; never secret values, webhook URLs, recipients, target URLs or raw payloads.",
        "references": [
            {
                "id": "dispatch_approval",
                "env_ref": "SAFERPAGE_ALERT_DISPATCH_APPROVED",
                "present": false,
                "required_for_external_delivery": true,
                "purpose": "Explicit approval gate for productive alert delivery."
            },
            {
                "id": "generic_webhook_url",
                "env_ref": "SAFERPAGE_WEBHOOK_URL",
                "present": false,
                "required_for_external_delivery": false,
                "purpose": "Generic operator webhook target."
            },
            {
                "id": "generic_webhook_secret",
                "env_ref": "SAFERPAGE_WEBHOOK_SECRET",
                "present": false,
                "required_for_signed_webhook": true,
                "purpose": "HMAC-SHA256 signing secret for generic webhook deliveries."
            },
            {
                "id": "slack_webhook_url",
                "env_ref": "SAFERPAGE_SLACK_WEBHOOK_URL",
                "present": false,
                "required_for_external_delivery": false,
                "purpose": "Slack incoming webhook target."
            },
            {
                "id": "teams_webhook_url",
                "env_ref": "SAFERPAGE_TEAMS_WEBHOOK_URL",
                "present": false,
                "required_for_external_delivery": false,
                "purpose": "Microsoft Teams incoming webhook target."
            },
            {
                "id": "local_file_sink_path",
                "env_ref": "SAFERPAGE_LOCAL_FILE_SINK_PATH",
                "present": false,
                "required_for_external_delivery": false,
                "purpose": "Optional override for sanitized local JSONL audit sink; default path is used when absent."
            }
        ]
    },
    "receiver_contract": {
        "event": "monitoring.alert",
        "method": "POST",
        "content_type": "application/json; charset=utf-8",
        "required_headers": [
            "X-SaferPage-Event",
            "X-SaferPage-Domain",
            "X-SaferPage-Idempotency-Key"
        ],
        "optional_headers": [
            "X-SaferPage-Signature"
        ],
        "signature_algorithm": "HMAC-SHA256 over the compact JSON request body, formatted as sha256=<hex>.",
        "signature_header": "X-SaferPage-Signature",
        "idempotency_policy": "Receivers must deduplicate on X-SaferPage-Idempotency-Key and may compare body_sha256 for replay diagnostics.",
        "secret_policy": "Secrets, target URLs, recipients and raw payloads are never included in public runner state."
    },
    "runtime_controls": {
        "schema": "https://saferpage.de/schemas/delivery-runtime-controls.v1",
        "generated_at": "2026-06-09T10:44:00+00:00",
        "control_count": 8,
        "controls": {
            "credential_manifest_no_secret": true,
            "dispatch_approval_gate": true,
            "execute_ready_required": true,
            "hmac_signature_contract": true,
            "idempotency_contract": true,
            "local_file_sink_sanitized": true,
            "public_runner_state_sanitized": true,
            "dry_run_smoke_isolated_state": true
        },
        "url": "https://saferpage.de/evidence/delivery-runtime-controls.json"
    },
    "approval_contract": {
        "environment_file": "/etc/saferpage/alert-dispatch.env",
        "approval_env": "SAFERPAGE_ALERT_DISPATCH_APPROVED=yes",
        "execute_gate": "Der systemd-Service startet run-alert-dispatch.py mit --execute-ready; Versand erfolgt trotzdem nur bei SAFERPAGE_ALERT_DISPATCH_APPROVED=yes und konfiguriertem Ziel.",
        "native_runner_channels": [
            "local_file_sink",
            "generic_webhook",
            "slack",
            "teams"
        ],
        "payload_only_channels": [
            "jira",
            "email_smtp",
            "sendgrid"
        ],
        "does_not_send_from_preflight": true
    },
    "activation_package": {
        "schema": "https://saferpage.de/schemas/operator-delivery-activation-package.v1",
        "available": true,
        "environment_file": "/etc/saferpage/alert-dispatch.env",
        "template_url": "https://saferpage.de/integrationen/delivery-approval-template.env",
        "does_not_send": true,
        "does_not_include_secret_values": true,
        "env_template": [
            {
                "env_ref": "SAFERPAGE_ALERT_DISPATCH_APPROVED",
                "example_value": "no",
                "required_for_external_delivery": true,
                "public_export_allowed": false,
                "purpose": "Finales Betreiber-Go-live-Gate; erst nach Dry-run, Empfaenger- und Datenschutzfreigabe auf yes setzen."
            },
            {
                "env_ref": "SAFERPAGE_WEBHOOK_URL",
                "example_value": "__SET_IN_SECRET_MANAGER_OR_SECURE_SERVER_ENV__",
                "required_for_external_delivery": false,
                "public_export_allowed": false,
                "purpose": "Generisches Betreiber-Webhook-Ziel."
            },
            {
                "env_ref": "SAFERPAGE_WEBHOOK_SECRET",
                "example_value": "__SET_IN_SECRET_MANAGER_OR_SECURE_SERVER_ENV__",
                "required_for_external_delivery": false,
                "public_export_allowed": false,
                "purpose": "HMAC-SHA256-Secret fuer generische Webhook-Payloads."
            },
            {
                "env_ref": "SAFERPAGE_SLACK_WEBHOOK_URL",
                "example_value": "__SET_IN_SECRET_MANAGER_OR_SECURE_SERVER_ENV__",
                "required_for_external_delivery": false,
                "public_export_allowed": false,
                "purpose": "Slack Incoming Webhook fuer PrivacyOps-/Security-Alerts."
            },
            {
                "env_ref": "SAFERPAGE_TEAMS_WEBHOOK_URL",
                "example_value": "__SET_IN_SECRET_MANAGER_OR_SECURE_SERVER_ENV__",
                "required_for_external_delivery": false,
                "public_export_allowed": false,
                "purpose": "Microsoft Teams Incoming Webhook fuer Betreiber-Alerts."
            },
            {
                "env_ref": "SAFERPAGE_LOCAL_FILE_SINK_PATH",
                "example_value": "/var/www/saferpage.de/runtime/alert_delivery_sink.jsonl",
                "required_for_external_delivery": false,
                "public_export_allowed": false,
                "purpose": "Optionaler lokaler Audit-Sink fuer sanitisierte Zustellnachweise."
            }
        ],
        "approval_record_template": [
            "approver_role",
            "approved_at",
            "scope",
            "target_systems",
            "test_recipient_or_test_channel_ref",
            "preflight_urls",
            "dry_run_result_refs",
            "receiver_signature_verified",
            "idempotency_verified",
            "rollback_owner",
            "expires_or_review_at"
        ],
        "smoke_matrix": [
            {
                "id": "dry_run_no_send",
                "command": "scripts/run-alert-dispatch-dry-run-smoke.sh",
                "expected": "sent_count=0, external_send_attempt_count=0, local_file_sink_attempt_count=0."
            },
            {
                "id": "execute_ready_without_approval",
                "command": "python3 scripts/run-alert-dispatch.py anrufer.info --base-url http://127.0.0.1 --max 1 --timeout 15 --execute-ready",
                "expected": "Ohne SAFERPAGE_ALERT_DISPATCH_APPROVED=yes bleibt sent_count=0."
            },
            {
                "id": "receiver_payload_fixture",
                "command": "curl -fsS https://saferpage.de/alarme/anrufer.info/delivery-json",
                "expected": "Payload enthaelt Idempotency-Key, body_sha256 und HMAC-Receiver-Vertrag ohne Ziel-URL."
            },
            {
                "id": "public_runner_state",
                "command": "curl -fsS https://saferpage.de/alarme/dispatch-runner-json",
                "expected": "Public State enthaelt nur Env-Refs, Hashes und Status; keine Secrets, Ziel-URLs oder Empfaenger."
            }
        ],
        "acceptance_criteria": [
            "Mindestens ein externes natives Zielsystem ist serverseitig konfiguriert oder der lokale File-Sink ist fuer Audit-Dry-run bewusst als einziges Ziel zugelassen.",
            "SAFERPAGE_ALERT_DISPATCH_APPROVED=yes ist erst nach dokumentierter Betreiberfreigabe gesetzt.",
            "Receiver prueft X-SaferPage-Idempotency-Key und optional X-SaferPage-Signature.",
            "Dry-run-Smoke zeigt keine externen Sendungen und keine Secret-Leaks.",
            "Nach erstem produktiven Lauf sind sent_count, error_count und Zielsystem-Dedupe dokumentiert."
        ],
        "stop_conditions": [
            "Webhook-URL, Token, API-Key, E-Mail-Empfaenger oder private Payloads erscheinen in Public-State, Logs oder Exporten.",
            "sent_count steigt ohne Approval-Record oder ohne SAFERPAGE_ALERT_DISPATCH_APPROVED=yes.",
            "Zielsystem erzeugt Duplikate trotz Idempotency-Key.",
            "error_count groesser 0 oder wiederholte HTTP-/TLS-/Rate-Limit-Fehler."
        ],
        "no_secret_policy": "Dieses Paket exportiert nur Env-Referenzen, Placeholder, Smoke-Kommandos und Abnahmekriterien; keine echten URLs, Tokens, Empfaenger oder Payload-Rohdaten."
    },
    "latest_smoke_result": {
        "schema": "https://saferpage.de/schemas/alert-delivery-readiness-smoke.v1",
        "generated_at": "2026-06-09T17:46:10+00:00",
        "ok": true,
        "summary": "No-Secret-Smoke fuer Alert-Delivery-Preflight, Dispatch-Runner, Runtime-Kontrollen, Delivery-Fixture, Operator-Go-live und isolierten Dry-run.",
        "host": "anrufer.info",
        "source_preflight_schema": "https://saferpage.de/schemas/operator-delivery-credential-preflight.v1",
        "source_preflight_url": "https://saferpage.de/integrationen/delivery-credential-preflight-json",
        "metrics": {
            "target_count": 6,
            "http_passed_count": 6,
            "http_failed_count": 0,
            "check_count": 9,
            "failed_check_count": 0,
            "blocked_expected_count": 1,
            "dispatch_approved": 0,
            "native_ready_channel_count": 1,
            "preflight_blocked_gate_count": 1,
            "runner_sent_count": 0,
            "runner_external_send_attempt_count": 0,
            "runner_local_file_sink_attempt_count": 0,
            "dry_run_sent_count": 0,
            "dry_run_queued_count": 1
        },
        "checks": [
            {
                "id": "public_routes_http_200",
                "label": "Öffentliche Alert-Delivery-Readiness-Routen erreichbar",
                "status": "passed",
                "evidence": "6/6 Route(s) liefern HTTP 200.",
                "operator_action": "Weiter mit Delivery-Go-live-Gates."
            },
            {
                "id": "dry_run_blocks_external_delivery",
                "label": "Dry-run sendet keine externe Zustellung",
                "status": "passed",
                "evidence": "dry_run_ok=yes, sent_count=0, dry_run_queued_count=1.",
                "operator_action": "Dry-run isoliert halten; externe Zustellung nur mit Execute-Gate, Approval und konfiguriertem Ziel."
            },
            {
                "id": "runner_no_unapproved_delivery",
                "label": "Runner-State zeigt keine ungeprüften Sendungen",
                "status": "passed",
                "evidence": "sent_count=0, external_send_attempt_count=0, local_file_sink_attempt_count=0.",
                "operator_action": "Ungeplante Zustellung stoppen und Public-State/Journal prüfen."
            },
            {
                "id": "runtime_controls_documented",
                "label": "Delivery-Runtime-Kontrollen dokumentiert",
                "status": "passed",
                "evidence": "8 Runtime-Control-Einträge, Approval-/Execute-/HMAC-/Dry-run-Gates geprüft.",
                "operator_action": "Runtime-Controls bei Runner- oder Zielsystem-Änderungen erneut reviewen."
            },
            {
                "id": "delivery_payload_fixture_contract",
                "label": "Delivery-Payload-Fixture ist abnahmefähig",
                "status": "passed",
                "evidence": "receiver_signature=HMAC-SHA256 over the compact JSON request body, formatted as sha256=<hex>..",
                "operator_action": "Receiver muss Idempotency-Key und optional HMAC-Signatur prüfen."
            },
            {
                "id": "dispatch_approval_blocker_explicit",
                "label": "Produktivblocker Dispatch-Freigabe explizit",
                "status": "blocked_expected",
                "evidence": "dispatch_approved=no.",
                "operator_action": "SAFERPAGE_ALERT_DISPATCH_APPROVED erst nach Zielsystem-, Empfänger- und Datenschutzfreigabe setzen."
            },
            {
                "id": "target_channel_blocker_explicit",
                "label": "Produktivblocker Zielsystem explizit",
                "status": "passed",
                "evidence": "native_ready_channel_count=1, blocked_gate_count=1.",
                "operator_action": "Signatur und Dedupe im Zielsystem testen."
            },
            {
                "id": "operator_go_live_links_delivery_blocker",
                "label": "Operator-Go-live verlinkt Delivery-Blocker",
                "status": "passed",
                "evidence": "Go-live Delivery-Phase=yes, alert_runner_available=1, alert_sent_count=0.",
                "operator_action": "Go-live-Center nach Zielsystem-/Approval-Freigaben erneut prüfen."
            },
            {
                "id": "public_exports_no_secret_scan",
                "label": "Öffentliche Delivery-Exports enthalten keine offensichtlichen Secret-Werte",
                "status": "passed",
                "evidence": "Keine Forbidden-Pattern-Treffer.",
                "operator_action": "Exports können in die Abnahmeakte."
            }
        ],
        "failed_checks": [],
        "failed_targets": [],
        "targets": [
            {
                "id": "delivery_credential_preflight_json",
                "label": "Delivery Credential Preflight JSON",
                "url": "https://saferpage.de/integrationen/delivery-credential-preflight-json",
                "path": "/integrationen/delivery-credential-preflight-json",
                "expected_http_status": 200,
                "actual_http_status": 200,
                "ok": true,
                "started_at": "2026-06-09T17:46:09Z",
                "finished_at": "2026-06-09T17:46:09Z"
            },
            {
                "id": "alert_dispatch_runner_json",
                "label": "Alert Dispatch Runner JSON",
                "url": "https://saferpage.de/alarme/dispatch-runner-json",
                "path": "/alarme/dispatch-runner-json",
                "expected_http_status": 200,
                "actual_http_status": 200,
                "ok": true,
                "started_at": "2026-06-09T17:46:09Z",
                "finished_at": "2026-06-09T17:46:09Z"
            },
            {
                "id": "delivery_runtime_controls",
                "label": "Delivery Runtime Controls",
                "url": "https://saferpage.de/evidence/delivery-runtime-controls.json",
                "path": "/evidence/delivery-runtime-controls.json",
                "expected_http_status": 200,
                "actual_http_status": 200,
                "ok": true,
                "started_at": "2026-06-09T17:46:09Z",
                "finished_at": "2026-06-09T17:46:09Z"
            },
            {
                "id": "delivery_approval_template_env",
                "label": "Delivery Approval Env Template",
                "url": "https://saferpage.de/integrationen/delivery-approval-template.env",
                "path": "/integrationen/delivery-approval-template.env",
                "expected_http_status": 200,
                "actual_http_status": 200,
                "ok": true,
                "started_at": "2026-06-09T17:46:09Z",
                "finished_at": "2026-06-09T17:46:09Z"
            },
            {
                "id": "alert_delivery_package_json",
                "label": "Alert Delivery Package JSON",
                "url": "https://saferpage.de/alarme/anrufer.info/delivery-json",
                "path": "/alarme/anrufer.info/delivery-json",
                "expected_http_status": 200,
                "actual_http_status": 200,
                "ok": true,
                "started_at": "2026-06-09T17:46:09Z",
                "finished_at": "2026-06-09T17:46:09Z"
            },
            {
                "id": "operator_go_live_json",
                "label": "Operator Go-live JSON",
                "url": "https://saferpage.de/betreiber/go-live-json",
                "path": "/betreiber/go-live-json",
                "expected_http_status": 200,
                "actual_http_status": 200,
                "ok": true,
                "started_at": "2026-06-09T17:46:09Z",
                "finished_at": "2026-06-09T17:46:09Z"
            }
        ],
        "dry_run_smoke": {
            "ok": true,
            "exit_code": 0,
            "evaluated_count": 1,
            "sent_count": 0,
            "dry_run_queued_count": 1,
            "external_delivery_only_if": "--execute-ready was passed, SAFERPAGE_ALERT_DISPATCH_APPROVED=yes and the channel target is configured"
        },
        "no_secret_policy": {
            "contains_secrets": false,
            "contains_target_urls": false,
            "contains_recipients": false,
            "contains_raw_payloads": false,
            "contains_private_documents": false,
            "contains_visitor_logs": false,
            "public_export_secret_pattern_hits": []
        },
        "claim_boundary": "Dieser Smoke setzt keine Delivery-Secrets, konfiguriert keine Zielsysteme, versendet keine Alerts, schreibt keinen produktiven Sink und veröffentlicht keine Webhook-URLs, Tokens, Empfänger oder Rohpayloads.",
        "available": true,
        "status": "passed",
        "url": "https://saferpage.de/evidence/alert-delivery-readiness-smoke.json"
    },
    "channels": [
        {
            "id": "local_file_sink",
            "label": "Lokaler Audit-File-Sink",
            "purpose": "Interne, secretfreie Zustell-Evidence als JSONL schreiben, ohne externe Ziel-URL, Empfaenger oder Payload-Rohdaten zu veroeffentlichen.",
            "owner": "IT/Compliance",
            "events": [
                "monitoring.alert"
            ],
            "status": "ready_for_approval",
            "required_env_refs": [],
            "present_required_refs": [],
            "missing_required_refs": [],
            "optional_env_refs": [
                "SAFERPAGE_LOCAL_FILE_SINK_PATH"
            ],
            "present_optional_refs": [],
            "missing_optional_refs": [
                "SAFERPAGE_LOCAL_FILE_SINK_PATH"
            ],
            "runner_support": "native_alert_dispatch_runner",
            "secret_policy": "Nur Env-Referenzen und present/missing werden gezeigt; Werte, Ziel-URLs, Tokens und Empfaenger bleiben serverseitig."
        },
        {
            "id": "generic_webhook",
            "label": "Generischer Webhook",
            "purpose": "Alerts, Remediation-Tickets, Portfolio-Digests und Scan-Dispatches an eine eigene Orchestrierung senden.",
            "owner": "IT/Platform",
            "events": [
                "monitoring.alert",
                "operator.remediation.ticket_ready",
                "saferpage.portfolio.digest",
                "saferpage.portfolio.scan_dispatch"
            ],
            "status": "missing_configuration",
            "required_env_refs": [
                "SAFERPAGE_WEBHOOK_URL",
                "SAFERPAGE_WEBHOOK_SECRET"
            ],
            "present_required_refs": [],
            "missing_required_refs": [
                "SAFERPAGE_WEBHOOK_URL",
                "SAFERPAGE_WEBHOOK_SECRET"
            ],
            "optional_env_refs": [
                "SAFERPAGE_OPERATOR_WEBHOOK_SECRET"
            ],
            "present_optional_refs": [],
            "missing_optional_refs": [
                "SAFERPAGE_OPERATOR_WEBHOOK_SECRET"
            ],
            "runner_support": "native_alert_dispatch_runner",
            "secret_policy": "Nur Env-Referenzen und present/missing werden gezeigt; Werte, Ziel-URLs, Tokens und Empfaenger bleiben serverseitig."
        },
        {
            "id": "slack",
            "label": "Slack",
            "purpose": "PrivacyOps- und Security-Kanaele mit priorisierten Alerts, Ownern und SLA-Kontext informieren.",
            "owner": "PrivacyOps",
            "events": [
                "monitoring.alert",
                "operator.remediation.ticket_ready",
                "saferpage.portfolio.scan_dispatch"
            ],
            "status": "missing_configuration",
            "required_env_refs": [
                "SAFERPAGE_SLACK_WEBHOOK_URL"
            ],
            "present_required_refs": [],
            "missing_required_refs": [
                "SAFERPAGE_SLACK_WEBHOOK_URL"
            ],
            "optional_env_refs": [
                "SAFERPAGE_SLACK_CHANNEL"
            ],
            "present_optional_refs": [],
            "missing_optional_refs": [
                "SAFERPAGE_SLACK_CHANNEL"
            ],
            "runner_support": "native_alert_dispatch_runner",
            "secret_policy": "Nur Env-Referenzen und present/missing werden gezeigt; Werte, Ziel-URLs, Tokens und Empfaenger bleiben serverseitig."
        },
        {
            "id": "teams",
            "label": "Microsoft Teams",
            "purpose": "Adaptive Cards fuer Betreiber-, Datenschutz- oder Security-Teams vorbereiten.",
            "owner": "Website-Betrieb",
            "events": [
                "monitoring.alert",
                "operator.remediation.ticket_ready",
                "saferpage.portfolio.scan_dispatch"
            ],
            "status": "missing_configuration",
            "required_env_refs": [
                "SAFERPAGE_TEAMS_WEBHOOK_URL"
            ],
            "present_required_refs": [],
            "missing_required_refs": [
                "SAFERPAGE_TEAMS_WEBHOOK_URL"
            ],
            "optional_env_refs": [],
            "present_optional_refs": [],
            "missing_optional_refs": [],
            "runner_support": "native_alert_dispatch_runner",
            "secret_policy": "Nur Env-Referenzen und present/missing werden gezeigt; Werte, Ziel-URLs, Tokens und Empfaenger bleiben serverseitig."
        },
        {
            "id": "jira",
            "label": "Jira",
            "purpose": "Remediation- und Alert-Aufgaben mit External-ID, Labels, Prioritaet und Akzeptanzkriterien anlegen.",
            "owner": "Projekt-/Ticket-Owner",
            "events": [
                "operator.remediation.ticket_ready",
                "monitoring.alert"
            ],
            "status": "missing_configuration",
            "required_env_refs": [
                "SAFERPAGE_JIRA_BASE_URL",
                "SAFERPAGE_JIRA_EMAIL",
                "SAFERPAGE_JIRA_API_TOKEN"
            ],
            "present_required_refs": [],
            "missing_required_refs": [
                "SAFERPAGE_JIRA_BASE_URL",
                "SAFERPAGE_JIRA_EMAIL",
                "SAFERPAGE_JIRA_API_TOKEN"
            ],
            "optional_env_refs": [],
            "present_optional_refs": [],
            "missing_optional_refs": [],
            "runner_support": "delivery_payload_ready_in_exports",
            "secret_policy": "Nur Env-Referenzen und present/missing werden gezeigt; Werte, Ziel-URLs, Tokens und Empfaenger bleiben serverseitig."
        },
        {
            "id": "email_smtp",
            "label": "E-Mail/SMTP",
            "purpose": "Portfolio-Digests und Eskalationen an Management, Datenschutz und Webbetrieb zustellen.",
            "owner": "Programm-Owner",
            "events": [
                "saferpage.portfolio.digest",
                "monitoring.alert"
            ],
            "status": "missing_configuration",
            "required_env_refs": [
                "SAFERPAGE_MAIL_FROM",
                "SAFERPAGE_MAIL_TO"
            ],
            "present_required_refs": [],
            "missing_required_refs": [
                "SAFERPAGE_MAIL_FROM",
                "SAFERPAGE_MAIL_TO"
            ],
            "optional_env_refs": [
                "SAFERPAGE_SMTP_HOST",
                "SAFERPAGE_SMTP_USER",
                "SAFERPAGE_SMTP_PASSWORD"
            ],
            "present_optional_refs": [],
            "missing_optional_refs": [
                "SAFERPAGE_SMTP_HOST",
                "SAFERPAGE_SMTP_USER",
                "SAFERPAGE_SMTP_PASSWORD"
            ],
            "runner_support": "delivery_payload_ready_in_exports",
            "secret_policy": "Nur Env-Referenzen und present/missing werden gezeigt; Werte, Ziel-URLs, Tokens und Empfaenger bleiben serverseitig."
        },
        {
            "id": "sendgrid",
            "label": "SendGrid",
            "purpose": "Wiederkehrende Portfolio-Digests ueber einen Mail-Provider ausliefern.",
            "owner": "IT/Platform",
            "events": [
                "saferpage.portfolio.digest"
            ],
            "status": "missing_configuration",
            "required_env_refs": [
                "SENDGRID_API_KEY",
                "SAFERPAGE_MAIL_FROM",
                "SAFERPAGE_MAIL_TO"
            ],
            "present_required_refs": [],
            "missing_required_refs": [
                "SENDGRID_API_KEY",
                "SAFERPAGE_MAIL_FROM",
                "SAFERPAGE_MAIL_TO"
            ],
            "optional_env_refs": [],
            "present_optional_refs": [],
            "missing_optional_refs": [],
            "runner_support": "delivery_payload_ready_in_exports",
            "secret_policy": "Nur Env-Referenzen und present/missing werden gezeigt; Werte, Ziel-URLs, Tokens und Empfaenger bleiben serverseitig."
        }
    ],
    "readiness_gates": [
        {
            "id": "dispatch_approval",
            "label": "Produktive Alert-Zustellung freigegeben",
            "status": "blocked",
            "evidence": "SAFERPAGE_ALERT_DISPATCH_APPROVED ist nicht aktiv; Runner bleibt Dry-run/Outbox.",
            "action": "Freigabe erst nach Zielsystem-Dry-run, Empfaengerfreigabe, Datenschutz-/Security-Abnahme und Monitoring setzen."
        },
        {
            "id": "native_target_available",
            "label": "Mindestens ein nativ sendbares Zielsystem konfiguriert",
            "status": "passed",
            "evidence": "1 native(s) Zielsystem(e) sind voll konfiguriert: Webhook, Slack oder Teams.",
            "action": "Mindestens SAFERPAGE_WEBHOOK_URL plus Secret oder SAFERPAGE_SLACK_WEBHOOK_URL/SAFERPAGE_TEAMS_WEBHOOK_URL serverseitig setzen."
        },
        {
            "id": "signed_delivery",
            "label": "HMAC-Signatur fuer Betreiber-Webhook verfuegbar",
            "status": "warning",
            "evidence": "Kein HMAC-Secret gesetzt; generische Webhooks koennen nicht signiert werden.",
            "action": "HMAC-Secret im Secret Manager oder in /etc/saferpage/alert-dispatch.env setzen."
        },
        {
            "id": "dispatch_runner_state",
            "label": "Alert-Dispatch-Runner-State erreichbar",
            "status": "passed",
            "evidence": "State alert_dispatch_runner_state.json ist lesbar; sent=0, deliverable=16, errors=0.",
            "action": "Timer und Runner-State unter /alarme/dispatch-runner-json pruefen."
        },
        {
            "id": "dry_run_outbox",
            "label": "Dry-run/Outbox als Testnachweis vorhanden",
            "status": "passed",
            "evidence": "64 Outbox-Eintraege im letzten Runner-State.",
            "action": "Mit Beispiel-Domain einen Dry-run ausfuehren und Body-SHA-256, Idempotency-Key und Zielsystemstatus pruefen."
        },
        {
            "id": "no_secret_export",
            "label": "No-Secret-Export",
            "status": "passed",
            "evidence": "Dieser Preflight zeigt nur Env-Referenzen und present/missing-Status, keine Werte, Ziel-URLs, Empfaenger, Tokens oder API-Keys.",
            "action": "Neue Felder vor Veroeffentlichung auf Secrets, URLs, Empfaenger und Rohpayloads pruefen."
        },
        {
            "id": "runtime_controls_manifest",
            "label": "Runtime-Control-Manifest vorhanden",
            "status": "passed",
            "evidence": "8 Delivery-Runtime-Kontrolle(n) im Evidence-Manifest dokumentiert.",
            "action": "Manifest und Smoke nach Runner-Aenderungen erneut pruefen."
        },
        {
            "id": "idempotency_contract",
            "label": "Idempotency- und Retry-Vertrag vorhanden",
            "status": "passed",
            "evidence": "Delivery-Payloads und Runner-State nutzen Idempotency-Key, Body-SHA-256, Status und Stop-Bedingungen.",
            "action": "Zielsysteme muessen Idempotency-Key als externe ID oder Dedupe-Key verwenden."
        },
        {
            "id": "provider_matrix_complete",
            "label": "Zielsystem-Matrix abgedeckt",
            "status": "passed",
            "evidence": "1 ready, 0 teilweise, 7 Zielsystemtypen im Preflight.",
            "action": "Jira/E-Mail/SendGrid produktiv erst mit Connector-Code oder externem Orchestrator aktivieren."
        }
    ],
    "dispatch_runner_snapshot": {
        "available": true,
        "state_path": "alert_dispatch_runner_state.json",
        "started_at": "2026-06-09T08:46:39+00:00",
        "finished_at": "2026-06-09T08:46:40+00:00",
        "execute_ready": true,
        "dispatch_approved": false,
        "sent_count": 0,
        "deliverable_count": 16,
        "blocked_count": 52,
        "error_count": 0,
        "run_policy": {
            "external_send_attempt_count": 0,
            "local_file_sink_attempt_count": 0,
            "dry_run_queued_count": 16,
            "no_secret_export": true
        },
        "outbox_preview": [
            {
                "host": "parodontologie-darmstadt.de",
                "channel": "generic_webhook",
                "status": "blocked_missing_target",
                "configured": false,
                "sent": false,
                "target_ref": "SAFERPAGE_WEBHOOK_URL",
                "idempotency_key": "sp-alert-b2ee1e913fd19253e3afb760",
                "body_sha256": "48e7270f29215609eca12980cc44010142b90b39d554f89a5e663044b29e1eb4"
            },
            {
                "host": "parodontologie-darmstadt.de",
                "channel": "slack",
                "status": "blocked_missing_target",
                "configured": false,
                "sent": false,
                "target_ref": "SAFERPAGE_SLACK_WEBHOOK_URL",
                "idempotency_key": "sp-alert-b2ee1e913fd19253e3afb760",
                "body_sha256": "9ca8c93265891544dcbe140eaf79dc0778bff96ec0cb91e57e6f15ce0dcfec9a"
            },
            {
                "host": "parodontologie-darmstadt.de",
                "channel": "teams",
                "status": "blocked_missing_target",
                "configured": false,
                "sent": false,
                "target_ref": "SAFERPAGE_TEAMS_WEBHOOK_URL",
                "idempotency_key": "sp-alert-b2ee1e913fd19253e3afb760",
                "body_sha256": "c3c17a3d57c79f3985b87705dcbab3d2703f3cbfd5275ea8f7db1e2094e0f29b"
            },
            {
                "host": "parodontologie-darmstadt.de",
                "channel": "local_file_sink",
                "status": "queued_dry_run",
                "configured": true,
                "sent": false,
                "target_ref": "SAFERPAGE_LOCAL_FILE_SINK_PATH",
                "idempotency_key": "sp-alert-b2ee1e913fd19253e3afb760",
                "body_sha256": "48e7270f29215609eca12980cc44010142b90b39d554f89a5e663044b29e1eb4"
            },
            {
                "host": "dtb.de",
                "channel": "generic_webhook",
                "status": "blocked_missing_target",
                "configured": false,
                "sent": false,
                "target_ref": "SAFERPAGE_WEBHOOK_URL",
                "idempotency_key": "sp-alert-c52a43a27a8f05699e768ed0",
                "body_sha256": "660ad8e329750089362d5ed1351be87733367cb4c7fca05183451ec1acb8b8bd"
            },
            {
                "host": "dtb.de",
                "channel": "slack",
                "status": "blocked_missing_target",
                "configured": false,
                "sent": false,
                "target_ref": "SAFERPAGE_SLACK_WEBHOOK_URL",
                "idempotency_key": "sp-alert-c52a43a27a8f05699e768ed0",
                "body_sha256": "34c74317b8313123987a220e7e32335ede5ae5ffa940a02462cb4c23c5a13203"
            },
            {
                "host": "dtb.de",
                "channel": "teams",
                "status": "blocked_missing_target",
                "configured": false,
                "sent": false,
                "target_ref": "SAFERPAGE_TEAMS_WEBHOOK_URL",
                "idempotency_key": "sp-alert-c52a43a27a8f05699e768ed0",
                "body_sha256": "c8c16deb00e908094bb5babf51017d34b7b47e12d1f3ce49ee75b5c2b7a3ecc6"
            },
            {
                "host": "dtb.de",
                "channel": "local_file_sink",
                "status": "queued_dry_run",
                "configured": true,
                "sent": false,
                "target_ref": "SAFERPAGE_LOCAL_FILE_SINK_PATH",
                "idempotency_key": "sp-alert-c52a43a27a8f05699e768ed0",
                "body_sha256": "660ad8e329750089362d5ed1351be87733367cb4c7fca05183451ec1acb8b8bd"
            },
            {
                "host": "fahrrad-buecher-karten.de",
                "channel": "generic_webhook",
                "status": "blocked_missing_target",
                "configured": false,
                "sent": false,
                "target_ref": "SAFERPAGE_WEBHOOK_URL",
                "idempotency_key": "sp-alert-61dd5ea201fcbea5216fa9b7",
                "body_sha256": "d186f44c3b5f5326b5065d308496fc559179d93c30a135d64bcd27a37c5d954c"
            },
            {
                "host": "fahrrad-buecher-karten.de",
                "channel": "slack",
                "status": "blocked_missing_target",
                "configured": false,
                "sent": false,
                "target_ref": "SAFERPAGE_SLACK_WEBHOOK_URL",
                "idempotency_key": "sp-alert-61dd5ea201fcbea5216fa9b7",
                "body_sha256": "0cd0cc8e6af89f7d9ede8e5df263f6f63657fe1506b0e4557196d3c858d56f72"
            },
            {
                "host": "fahrrad-buecher-karten.de",
                "channel": "teams",
                "status": "blocked_missing_target",
                "configured": false,
                "sent": false,
                "target_ref": "SAFERPAGE_TEAMS_WEBHOOK_URL",
                "idempotency_key": "sp-alert-61dd5ea201fcbea5216fa9b7",
                "body_sha256": "5461399ad61fb5ec81bb1bf26f685bccee3f2d8de3eb525f8489d236f2e8e146"
            },
            {
                "host": "fahrrad-buecher-karten.de",
                "channel": "local_file_sink",
                "status": "queued_dry_run",
                "configured": true,
                "sent": false,
                "target_ref": "SAFERPAGE_LOCAL_FILE_SINK_PATH",
                "idempotency_key": "sp-alert-61dd5ea201fcbea5216fa9b7",
                "body_sha256": "d186f44c3b5f5326b5065d308496fc559179d93c30a135d64bcd27a37c5d954c"
            },
            {
                "host": "admin.dsv.de",
                "channel": "generic_webhook",
                "status": "blocked_missing_target",
                "configured": false,
                "sent": false,
                "target_ref": "SAFERPAGE_WEBHOOK_URL",
                "idempotency_key": "sp-alert-67887577cb3af2870d49e445",
                "body_sha256": "eb68fb7c19027756b28b7591fdfa6f2dbce43f511c905bd0257a9c8625bf90d6"
            },
            {
                "host": "admin.dsv.de",
                "channel": "slack",
                "status": "blocked_missing_target",
                "configured": false,
                "sent": false,
                "target_ref": "SAFERPAGE_SLACK_WEBHOOK_URL",
                "idempotency_key": "sp-alert-67887577cb3af2870d49e445",
                "body_sha256": "5fedddba7cec116a53a0d05c4c86dc28cad5b4342c50b156096858ad577ccd52"
            },
            {
                "host": "admin.dsv.de",
                "channel": "teams",
                "status": "blocked_missing_target",
                "configured": false,
                "sent": false,
                "target_ref": "SAFERPAGE_TEAMS_WEBHOOK_URL",
                "idempotency_key": "sp-alert-67887577cb3af2870d49e445",
                "body_sha256": "7830a821cb31279e7490205ff83df8b737b3dca1c8688a56b2704da7b02d8a6a"
            },
            {
                "host": "admin.dsv.de",
                "channel": "local_file_sink",
                "status": "queued_dry_run",
                "configured": true,
                "sent": false,
                "target_ref": "SAFERPAGE_LOCAL_FILE_SINK_PATH",
                "idempotency_key": "sp-alert-67887577cb3af2870d49e445",
                "body_sha256": "eb68fb7c19027756b28b7591fdfa6f2dbce43f511c905bd0257a9c8625bf90d6"
            },
            {
                "host": "dsv.de",
                "channel": "generic_webhook",
                "status": "blocked_missing_target",
                "configured": false,
                "sent": false,
                "target_ref": "SAFERPAGE_WEBHOOK_URL",
                "idempotency_key": "sp-alert-8a62d9cb14b71dbee1874335",
                "body_sha256": "5f87f7783356710238eeb4e667a6849b451327e68309e225fe78fd4e1ba80a85"
            },
            {
                "host": "dsv.de",
                "channel": "slack",
                "status": "blocked_missing_target",
                "configured": false,
                "sent": false,
                "target_ref": "SAFERPAGE_SLACK_WEBHOOK_URL",
                "idempotency_key": "sp-alert-8a62d9cb14b71dbee1874335",
                "body_sha256": "2a7294bf72b064a2340a0a3d1ac794fabb3a6d285cd623fd3b13223e250e1e74"
            },
            {
                "host": "dsv.de",
                "channel": "teams",
                "status": "blocked_missing_target",
                "configured": false,
                "sent": false,
                "target_ref": "SAFERPAGE_TEAMS_WEBHOOK_URL",
                "idempotency_key": "sp-alert-8a62d9cb14b71dbee1874335",
                "body_sha256": "bb2dd8c82fbd21d0194f7e5e874aa4981714cc6ab6eb104fc68a94343bf76e12"
            },
            {
                "host": "dsv.de",
                "channel": "local_file_sink",
                "status": "queued_dry_run",
                "configured": true,
                "sent": false,
                "target_ref": "SAFERPAGE_LOCAL_FILE_SINK_PATH",
                "idempotency_key": "sp-alert-8a62d9cb14b71dbee1874335",
                "body_sha256": "5f87f7783356710238eeb4e667a6849b451327e68309e225fe78fd4e1ba80a85"
            }
        ]
    },
    "operations_runbook": {
        "purpose": "Produktive Betreiber-Zielsysteme kontrolliert aktivieren, ohne Secret-Werte oder Empfaenger im oeffentlichen Nachweis offenzulegen.",
        "preflight_sequence": [
            "Zielsystem-Owner, Empfaenger, Kanaele und Zwecke festlegen.",
            "Secrets und Ziel-URLs in /etc/saferpage/alert-dispatch.env oder Secret Manager setzen.",
            "Ohne SAFERPAGE_ALERT_DISPATCH_APPROVED einen Dry-run ausfuehren und /alarme/dispatch-runner-json pruefen.",
            "HMAC-Signatur, Idempotency-Key, Body-SHA-256 und Zielsystem-Dedupe im Testempfaenger verifizieren.",
            "SAFERPAGE_ALERT_DISPATCH_APPROVED=yes erst nach dokumentierter Betreiberfreigabe setzen.",
            "Nach erstem produktiven Lauf sent_count, error_count und Zielsystem-Logs pruefen."
        ],
        "systemd_checks": [
            {
                "label": "Environment-Datei",
                "command": "systemctl cat saferpage-alert-dispatch.service --no-pager",
                "expected": "EnvironmentFile=-/etc/saferpage/alert-dispatch.env ist gesetzt; Secrets stehen nicht in der Unit."
            },
            {
                "label": "Timer",
                "command": "systemctl list-timers saferpage-alert-dispatch.timer --no-pager",
                "expected": "Taeglicher Timer ist sichtbar und Persistent=true."
            },
            {
                "label": "Runner-State",
                "command": "curl -fsS https://saferpage.de/alarme/dispatch-runner-json",
                "expected": "channels/outbox zeigen nur Env-Refs, Hashes und Status."
            },
            {
                "label": "Preflight",
                "command": "curl -fsS https://saferpage.de/integrationen/delivery-credential-preflight-json",
                "expected": "readiness_gates zeigen approval, target availability, signing und no-secret export."
            },
            {
                "label": "Isolierter Dry-run-Smoke",
                "command": "scripts/run-alert-dispatch-dry-run-smoke.sh",
                "expected": "Schreibt nur temporaere State-Dateien, sent_count=0 und external_send_attempt_count=0."
            }
        ],
        "stop_conditions": [
            "sent_count groesser 0 ohne SAFERPAGE_ALERT_DISPATCH_APPROVED=yes und dokumentierte Betreiberfreigabe.",
            "Oeffentliche Exports enthalten Ziel-URLs, API-Keys, Tokens, E-Mail-Empfaenger, Slack-/Teams-Webhook-URLs oder private Payloads.",
            "Zielsystem meldet Duplikate trotz Idempotency-Key.",
            "error_count groesser 0 oder wiederholte HTTP-/TLS-/Rate-Limit-Fehler.",
            "Empfaenger oder Kanalzweck wurden ohne Datenschutz-/Security-Abnahme geaendert."
        ],
        "public_contract": [
            "Preflight sendet nichts und testet keine externen Zielsysteme.",
            "Produktiver Versand braucht --execute-ready, SAFERPAGE_ALERT_DISPATCH_APPROVED=yes und mindestens ein konfiguriertes natives Ziel.",
            "Jira, E-Mail und SendGrid sind als Zielsystem-Gates sichtbar; produktive Zustellung braucht Connector oder externen Orchestrator.",
            "Alle oeffentlichen Nachweise zeigen nur Referenznamen, Status, Hashes und Idempotency-Keys."
        ]
    },
    "links": {
        "html": "https://saferpage.de/integrationen/delivery-credential-preflight",
        "json": "https://saferpage.de/integrationen/delivery-credential-preflight-json",
        "csv": "https://saferpage.de/integrationen/delivery-credential-preflight-csv",
        "markdown": "https://saferpage.de/integrationen/delivery-credential-preflight-md",
        "integration_setup": "https://saferpage.de/integrationen/setup-json",
        "dispatch_runner": "https://saferpage.de/alarme/dispatch-runner-json",
        "alert_delivery_example": "https://saferpage.de/alarme/anrufer.info/delivery-json",
        "comparison": "https://saferpage.de/vergleich/features-json",
        "security_feed_preflight": "https://saferpage.de/sicherheit/feed-credential-preflight-json",
        "runtime_controls": "https://saferpage.de/evidence/delivery-runtime-controls.json",
        "readiness_smoke": "https://saferpage.de/evidence/alert-delivery-readiness-smoke.json",
        "approval_template_env": "https://saferpage.de/integrationen/delivery-approval-template.env"
    },
    "disclaimer": "Dieser Preflight zeigt nur Credential-Referenzen, Gate-Status und Betriebsnachweise. Er veroeffentlicht keine Webhook-URLs, API-Keys, Tokens, E-Mail-Empfaenger, Slack-/Teams-Ziel-URLs oder privaten Rohpayloads."
}
