# API-Key-Readiness

API-Key-Readiness: Runtime-Kontrollen 7/7 implementiert; API-Access-Storage 2/2 Tabellen; produktive Freigaben 0/7 aktiv.

> Dieses Dossier erzeugt keine echten API-Keys, speichert keine Hashes und gibt keine Secret-Werte aus. Produktive Key-Ausstellung braucht Betreiber-Auth, Domain-Claim und Server-Gates.

## Letzter API-Key-Readiness-Smoke
- Status: ok
- Zeitpunkt: 2026-06-15T00:27:41+00:00
- Targets: 9
- Failed Checks: 0
- Erwartete Blocker: 0
- Ergebnis: https://saferpage.de/evidence/api-key-readiness-smoke.json
- **Öffentliche API-Key-Readiness-Routen erreichbar**: passed - 9/9 Route(s) liefern HTTP 200.
- **Migration-Preflight öffentlich und sanitisiert**: passed - missing_required_artifacts=0, admin_dsn_required=no.
- **API-Key-Store-Migration braucht DB-Admin-DSN der verantwortlichen Rolle statt Web-DB-User**: passed - required_artifacts=2, ready_artifacts=2, missing_artifacts=0, admin_dsn_required=no.
- **API-Runtime-Kontrollen dokumentiert**: passed - manifest_ok=yes, controls=8/8, missing=0, secrets=0, implementation=7/7.
- **Runtime-Gate-Probe beschreibt Public Reads, Protected Contracts, Deny-Fixtures und Write-HMAC**: passed - public_routes=4, protected_routes=7, denied_fixtures=4, live_denied=0, hmac_negative_tests=4, implementation=7/7, systemd=1.
- **Live-Deny-Smoke verweigert fehlende Authorization und schreibt sanitisiertes Audit**: passed - ok=yes, status_code=401, decision=deny, audit_storage=postgres, fallback_events_24h=0.
- **Developer-Quickstarts, Fehlervertrag, Rotation und Client-Abnahme sind no-secret belegbar**: passed - quickstarts=4, errors=6, rotation_steps=5, acceptance=6.
- **API-Key-Ausgabe hat Issuance-Receipt, Rate-Limits, Abuse-Stop und Nachlauf**: passed - issuance_items=6, rate_limit_tiers=5, abuse_steps=5, watch_signals=5.
- **API-Integration-Launch-Kit deckt OpenAPI, Postman, SDKs, Sandbox, Webhook-Abnahme und Support ab**: passed - openapi=6, postman=5, sdks=5, sandbox=6, webhook=5, slo=4.
- **API-Hauptseite zeigt Developer-Launch-Kit mit OpenAPI, Postman, SDK, Sandbox und Nicht-live-Grenzen**: passed - api_access_url=https://saferpage.de/api-zugriff, visible=yes.
- **API-Hauptseite liefert no-secret OpenAPI 3.1 Vertrag mit BearerAuth, Pfaden und Claim-Grenze**: passed - openapi=3.1.0, paths=6, bearer=yes, no_secret=yes.
- **API-Hauptseite liefert no-secret Postman/Smoke Collection mit Placeholder-Key**: passed - schema=yes, requests=7, bearer_placeholder=yes, no_secret=yes, forbidden_hits=0.

## API-Access-Storage
- Tabellen bereit: ja
- api_keys: vorhanden
- api_access_audit_log: vorhanden
- CREATE-Recht public: nein
- Migration SHA-256: 27d802d48887d084e9132c841b943681c70638bd432746a92cab7809be621213
- Preflight: `scripts/run-api-access-migration-preflight.sh`

## Migration-Artefakte
- Evidence: https://saferpage.de/evidence/api-access-migration-preflight.json
- Erforderlich: 2
- Bereit: 0
- Fehlend: 2
- **API-Key-Tabelle**: missing - infra/postgres/migrations/api-access.sql mit kurzlebigem Admin-DSN aus sicherer Shell anwenden.
- **API-Access-Auditlog**: missing - infra/postgres/migrations/api-access.sql mit kurzlebigem Admin-DSN aus sicherer Shell anwenden.
- **Scan-Results-Basistabelle**: optional_missing - Basisinstallation prüfen; Scan-Results-Tabelle sollte für Reports vorhanden sein.

## Runtime-Implementierung
- **Key-Store- und Hash-Vertrag im Backend vorhanden**: implemented - Migration deklariert api_keys mit Prefix/Hash/Scopes/Status; Storage liest Key-Records ohne Roh-Key-Export.
- **Scope-Enforcement im Operator-Probe vorhanden**: implemented - /api/operator/probe vergleicht angeforderten Scope mit dem Key-Record und auditiert Deny-Entscheidungen.
- **Access-Audit mit Fallback vorhanden**: implemented - Postgres-Audit und File-Fallback schreiben sanitisierte Events; Fallback-Events letzte 24h: 0.
- **Rate-Limit-Gate im Operator-Probe vorhanden**: implemented - Operator-Probe prüft ein serverseitiges Rate-Limit vor Auth-/Scope-Entscheidung und auditiert 429.
- **Revocation- und Expiry-Checks vorhanden**: implemented - Key-Status revoked/expired und expires_at werden vor Allow-Entscheidung geprüft.
- **Domain-Scope-Gate vorhanden**: implemented - Operator-Probe erzwingt Domain-Scope für Operator-Scopes und auditiert Domain-Scope-Mismatches nur mit Hash-Evidence.
- **Write-HMAC- und Idempotency-Gate vorhanden**: implemented - Write-Scopes verlangen X-SaferPage-Signature und X-SaferPage-Idempotency-Key; Secret bleibt Server-Env.

## Produktive Freigabe-Gates
- **Gehashter Key-Store produktiv**: required - Server-Freigabe SAFERPAGE_API_KEY_STORE_READY ist nicht aktiv.
  Aktion: Key-Store mit Hashing, Prefix und Revocation-Feldern bereitstellen.
- **Serverseitige Scope-Prüfung**: required - Server-Freigabe SAFERPAGE_API_SCOPE_ENFORCEMENT_READY ist nicht aktiv.
  Aktion: Middleware für Scope, Domain-Claim und Method-Policy aktivieren.
- **Access-Auditlog aktiv**: required - Server-Freigabe SAFERPAGE_API_ACCESS_AUDIT_READY ist nicht aktiv.
  Aktion: Append-only Auditlog mit Request-ID, Scope, Entscheidung und Statuscode anbinden.
- **Rate-Limits je Key und Scope**: required - Server-Freigabe SAFERPAGE_API_RATE_LIMIT_READY ist nicht aktiv.
  Aktion: Rate-Limit-Store und Stop-Condition für Schreibpfade aktivieren.
- **Sofortige Sperrung und Rotation**: required - Server-Freigabe SAFERPAGE_API_REVOCATION_READY ist nicht aktiv.
  Aktion: Revocation-Check vor Scope-Entscheidung ausführen und Rotation dokumentieren.
- **Domain-Claim vor Operator-Zugriff**: required - Server-Freigabe SAFERPAGE_API_DOMAIN_CLAIM_READY ist nicht aktiv.
  Aktion: Domain-Verifizierung und Rollenfreigabe vor Key-Ausstellung erzwingen.
- **HMAC für Schreib- und Webhook-Pfade**: required - Server-Freigabe SAFERPAGE_API_WRITE_HMAC_READY ist nicht aktiv.
  Aktion: X-SaferPage-Signature und X-SaferPage-Idempotency-Key für Write-Scopes erzwingen.

## Konsolidierungsstand und Grenzen
- **Runtime-Kontrollen vorbereitet** (erreicht): 7/7 Implementierungs-Gates sind durch Code- oder Control-Evidence belegt.
  Grenze: Das ersetzt keine produktive Key-Ausgabe und kein echtes Betreiber-Signoff.
  Nächster Schritt: Nach Storage-Migration Deny-, Allow-, Revocation-, Rate-Limit- und HMAC-Smokes erneut ausführen.
- **API-Access-Storage** (erreicht): api_keys/api_access_audit_log: 2/2 Tabellen vorhanden.
  Grenze: Tabellenstatus sagt noch nichts über echte Keys, Pepper, Rollenfreigabe oder Rotation.
  Nächster Schritt: Pepper, Domain-Claim und Smoke-Gates produktiv signieren.
- **Öffentliche Evidence und No-Secret-Exports** (erreicht): Readiness-Smoke: ok; Secret-Policy bleibt öffentlich prüfbar.
  Grenze: Öffentliche Evidence darf keine DSN, Roh-Keys, Hashes, Betreiberidentitäten oder private Zielsysteme enthalten.
  Nächster Schritt: Smokes nach jeder Änderung neu laufen lassen und erst dann Claims in Parity-/Go-live-Ansichten übernehmen.
- **Claims für Betreiber konsolidieren** (begrenzt): Dossier trennt technische Vorbereitung, Produktiv-Gates, Signoff, Rollback und Canary-Checks.
  Grenze: Nicht behaupten: echte API-Keys sind live, solange Storage, Secrets, Domain-Claim und Gates nicht zusammen grün sind.
  Nächster Schritt: Darstellung auf klare Betreiber-Entscheidungen ausrichten: was ist fertig, was ist blockiert, was ist der nächste sichere Schritt.

## API-Parity-Schließplan
- **Key-Store-Migration, Pepper und One-time Display** (waiting_for_secret_signoff): Schließt die Basislücke zu Plattformen mit API-Key-Lifecycle, Secret-Handling und Audit-Trail.
  Verantwortlich: DBA/IT-Security
  Grenze: Vor Secret-Signoff keine produktive Key-Ausgabe behaupten.
  Nicht behaupten: Produktive API-Keys sind verfügbar.
  Evidence: https://saferpage.de/evidence/api-access-migration-preflight.json
- **Betreiber-Auth, Domain-Claim und Rollenfreigabe** (waiting_for_operator_auth): Schließt die Trust-/Operator-Grenze gegen Plattformen mit Customer Portal und rollenbasiertem API-Zugang.
  Verantwortlich: Customer Success/Programm-Verantwortung
  Grenze: Public Evidence darf keine Betreiberidentitäten oder privaten Domain-Claims offenlegen.
  Nicht behaupten: Jeder Betreiber kann selbststaendig API-Keys ausstellen.
  Evidence: https://saferpage.de/betreiber/go-live-json
- **Scopes, Runtime-Gates, Rate-Limits und Revocation** (waiting_for_runtime_signoff): Schließt die Integrationsreife zu API-first Compliance- und Trust-Plattformen.
  Verantwortlich: Backend/Platform
  Grenze: Runtime-Verträge beweisen kein echtes Allow für Kunden, solange keine produktiven Keys ausgegeben wurden.
  Nicht behaupten: Operator-API ist voll produktiv nutzbar.
  Evidence: https://saferpage.de/api-zugriff/runtime-gate-probe-json
- **Write-HMAC, Idempotency und Webhook-Receiver-Abnahme** (waiting_for_hmac_secret): Schließt Write-/Webhook-Lücken gegen Plattformen mit Jira, Slack, Teams, Webhook und Ticket-Automation.
  Verantwortlich: IT-Security/Integration-Verantwortung
  Grenze: Fixture ist öffentlich und kein produktives Secret; echte Receiver bleiben privat.
  Nicht behaupten: Schreibende Kundenintegrationen sind produktiv abgenommen.
  Evidence: https://saferpage.de/api-zugriff/key-readiness-json
- **Developer-Onboarding, OpenAPI, Postman, SDKs und Sandbox** (contract_ready_waiting_for_live_keys): Schließt die Developer-Experience-Lücke zu API-first Wettbewerbern mit Docs, Collections und SDK-Pfaden.
  Verantwortlich: Developer Experience
  Grenze: Docs und Fixtures sind bereit; ohne Live-Key-Ausgabe bleibt es eine Integrationsvorbereitung.
  Nicht behaupten: Self-service API-Onboarding ist produktiv live.
  Evidence: https://saferpage.de/api-zugriff/key-readiness-md
- **Support-SLO, Abuse-Stop, Go-live-Signoff und Nachlauf** (waiting_for_operator_go_live_signoff): Schließt die Betriebslücke zu reifen Plattformen mit Support-Ziel, Incident-Prozess und Post-Go-live-Monitoring.
  Verantwortlich: Support/Security/Programm-Verantwortung
  Grenze: Ohne Betreiber-Signoff bleibt API-Reife ein vorbereitetes Dossier, kein Live-Service-Versprechen.
  Nicht behaupten: API-Betrieb ist mit produktiver Servicezusage und Support freigegeben.
  Evidence: https://saferpage.de/betreiber/go-live-json

## Claim-Grenzen
- **Keine produktive Key-Ausgabe behaupten**
  Erlaubt: API-Key-Prozess ist vorbereitet und no-secret dokumentiert.
  Nicht behaupten: Produktive API-Keys sind verfügbar.
  Grund: Storage-Tabellen, Pepper, Betreiberrolle, Domain-Claim und Runtime-Gates müssen gemeinsam freigegeben sein.
- **Migration ist Übergabepaket, kein Auto-Apply**
  Erlaubt: SQL, Hash und Preflight sind für die DB-Verantwortung prüfbar.
  Nicht behaupten: Die Migration wurde produktiv angewendet.
  Grund: Der aktuelle Web-DB-User hat kein CREATE-Recht; Admin-DSN darf nicht öffentlich gespeichert werden.
- **Smokes prüfen Grenzen, nicht private Zielsysteme**
  Erlaubt: Öffentliche Routen, No-Secret-Regeln und Deny-Verhalten sind prüfbar.
  Nicht behaupten: Alle Kundenintegrationen sind produktiv abgenommen.
  Grund: Private Empfänger, Betreiberidentitäten, echte Keys und Webhook-Secrets bleiben außerhalb öffentlicher Evidence.
- **Betreiber-Wording statt Technikbehauptung**
  Erlaubt: Betreiber sehen nächste sichere Schritte und klare Stop-Bedingungen.
  Nicht behaupten: Score oder API-Reife als pauschales Sicherheitsurteil darstellen.
  Grund: Automatisierte Checks können Kontext wie Rollen, Consent, Paywall oder manuelle Betreiberfreigaben nicht vollständig bewerten.

## Scope-Matrix
- `reports.public:read`: GET /{domain}, /{domain}/share-card-json, /badge/{domain} - ohne Key oder Public-Key mit Cache erlaubt
- `schemas:read`: GET /schemas, /schemas/{schema}.v1 - öffentlich cachebar
- `reports:read`: GET /{domain}/module-export, /report-pack/{domain}/export - Domain-Claim und Operator-Key erforderlich
- `portfolio:read`: GET /portfolio/export, /portfolio/audit-json, /portfolio/schedule-json - Portfolio-Zuordnung prüfen
- `evidence:read`: GET /nachweise/{domain}/export, /api/report/export - Sanitization, Domain-Claim und Auditlog erforderlich
- `nachweise:write`: POST Nachweispositions-Delivery - HMAC, Idempotency-Key und Zielsystem-Dry-Run erforderlich
- `dispatch:write`: POST Scan-Dispatch und Alert-Dispatch - Betreiberfreigabe, Rate-Limit und Stop-Conditions erforderlich
- `keys:rotate`: POST Key-Rotation und Revocation - OIDC/MFA, Vier-Augen-Gate und Auditlog erforderlich

## Write-HMAC-Test-Fixture
- Canonical SHA-256: `16e4a7e04cf94f5ad6bc986e4214f0e600c06100b9cef6b40fbee419f5382049`
- Erwartete Signatur: `sha256=aa2e6130e9eeb1bd9d3b92f4d2b883ece08b2b76389a01f61d99b55fed7a3b1d`
- Idempotency-Key: `sp-api-write-test-fixture`

## Developer Quickstarts
- **Read-Probe mit Bearer-Key** (`curl`, `reports:read`): Bearer-Key nur aus Secret Manager oder Server-Env laden; nie in Browser, Logs oder Public Config schreiben. Erwartet vor Go-live: 401 ohne Authorization oder 403 bei fehlendem Scope/Domain-Claim.
- **Node.js Write-HMAC** (`node`, `nachweise:write`): HMAC-Secret serverseitig halten; Idempotency-Key pro logischem Auftrag stabil setzen. Erwartet vor Go-live: 401/403/409 bei fehlender Signatur, falschem Scope oder wiederverwendetem Idempotency-Key.
- **PHP Server-Client** (`php`, `portfolio:read`): Key aus getenv lesen; Exception-Handler darf Authorization-Header nicht loggen. Erwartet vor Go-live: 401 bis Key-Store, Domain-Claim und Scope-Gate produktiv freigegeben sind.
- **CI-Dry-run ohne echten Key** (`ci`, `schemas:read`): CI nutzt nur Public Probe und Fixture-Signaturen; keine Live-Keys in Pull Requests. Erwartet vor Go-live: Protected Routen bleiben im CI ohne Key denied.

## Integration Launch Kit
### OpenAPI-Vertrag
- **Bearer-Auth als Security Scheme**: Authorization: Bearer ${SAFERPAGE_API_KEY}; Roh-Key nie in Beispiele oder Public-Exports schreiben. Status: documented_no_secret
- **Scopes als x-saferpage-scopes**: Jede geschützte Route nennt erlaubte Scopes, Domain-Scope und Risiko-Tier. Status: documented_no_secret
- **Sanitisierte Fehlerstruktur**: code, message, request_id, retry_after und decision; keine Secrets, Header oder Rohpayloads. Status: documented_no_secret
- **Rate-Limit-Header**: Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining und X-RateLimit-Reset als Client-Vertrag. Status: documented_no_secret
- **Write-HMAC Extension**: x-saferpage-hmac mit Canonical-String, Idempotency-Key und Negativtests aus Fixture. Status: documented_no_secret
- **Request-ID und Audit-Korrelation**: Clients senden oder übernehmen X-Request-ID; Audit exportiert nur Prefix, Scope, Entscheidung und Hashes. Status: documented_no_secret

### Postman/Smoke Collection
- `GET /api-zugriff/runtime-gate-probe-json`: Public Runtime-Vertrag und Deny-Fixtures abrufen. Secret-Policy: kein Key erforderlich
- `GET /api/operator/probe?scope=reports:read&domain=anrufer.info`: 401-Deny ohne Authorization reproduzieren. Secret-Policy: kein Key im Request
- `GET /api/operator/probe?scope=reports:read&domain=anrufer.info`: Read-Key nach Go-live prüfen. Secret-Policy: Bearer aus Environment-Variable
- `POST /api/operator/probe`: Write-HMAC und Idempotency testen. Secret-Policy: HMAC-Secret nur serverseitig
- `GET /api/operator/probe?scope=reports:read&domain=anrufer.info`: Widerrufener oder abgelaufener Key muss 403 liefern. Secret-Policy: nur Test-Key nach Freigabe

### SDK-Readiness
- **Node.js**: fetch oder undici, crypto.createHmac, AbortController Erster Test: Runtime-Gate-Probe und HMAC-Fixture.
- **PHP**: curl/ext-openssl, getenv, PSR-3 Redaction Erster Test: Read-Probe mit serverseitigem Bearer-Key.
- **Python**: requests/httpx, hmac, uuid, backoff Erster Test: 429/Retry-After und Request-ID prüfen.
- **Go**: net/http, crypto/hmac, context timeout Erster Test: Idempotency und Timeout-Handling.
- **CI/curl**: curl, jq, masked secrets Erster Test: Deny-Smoke ohne Key und Public Schema-Check.

### Sandbox-Onboarding
- 1. **Public Contract lesen**: Runtime-Gate-Probe, Scope-Matrix, Fehlervertrag und No-Secret-Grenzen prüfen. Exit: Client versteht 401/403/409/429 und Request-ID.
- 2. **Deny-Smoke ausführen**: Ohne Authorization 401 und sanitisiertes Audit erwarten. Exit: Kein Secret, kein Raw-Key, Audit-Storage sichtbar.
- 3. **Domain-Claim vorbereiten**: Betreiberrolle, Domain-Scope und Zweckbindung vor Key-Ausgabe dokumentieren. Exit: Domain-Claim-Receipt liegt vor.
- 4. **Read-Key testen**: Test-Key einmal anzeigen, in Secret Manager legen und Read-Probe ausführen. Exit: Nur erlaubte Domain/Scope-Kombination liefert allow.
- 5. **Write-HMAC testen**: Canonical-String, Signatur und Idempotency-Key gegen Fixture prüfen. Exit: Positive und negative HMAC-Tests dokumentiert.
- 6. **Rotation proben**: Dual-run, Client-Switch, Revocation und 24h-Nachlauf durchführen. Exit: Alter Prefix erzeugt nur Deny-Audit.

### Webhook-Receiver-Abnahme
- **Receiver validiert HMAC**: X-SaferPage-Signature wird mit konstantzeitlichem Vergleich geprüft. Status: required_before_write_live
- **Receiver speichert Idempotency-Key**: Doppelte Write-Requests erzeugen keine zweite externe Aktion. Status: required_before_write_live
- **Retry ist nebenwirkungsarm**: 409/429/5xx werden mit Backoff und Jitter behandelt. Status: required_before_write_live
- **Payload minimiert**: Keine Roh-Cookies, Authorization-Header, Besucherlogs oder private Dokument-URLs. Status: required_before_write_live
- **Receiver-Logs redacted**: Logs enthalten Request-ID, Prefix und Entscheidung, aber keine Secrets. Status: required_before_write_live

### Support-SLO
- **sev1_key_leak_suspected**: 15 Minuten, Aktion: Key pausieren, Prefix auditieren, Rotation erzwingen., Output: Sanitisierter Incident-Receipt ohne Roh-Key.
- **sev2_write_delivery_blocked**: 4 Stunden, Aktion: HMAC, Idempotency, 409/429 und Receiver-Antwort prüfen., Output: Request-ID-Liste und nächster Testschritt.
- **sev3_scope_or_domain_denied**: 1 Arbeitstag, Aktion: Scope-Matrix, Domain-Claim und Betreiberrolle prüfen., Output: Freigabe- oder Ablehnungsgrund.
- **sev4_docs_or_sdk_question**: 2 Arbeitstage, Aktion: Quickstart, OpenAPI/Postman oder SDK-Beispiel aktualisieren., Output: Dokumentationslink und Beispielrequest.

## Fehlervertrag
- `400 bad_request`: Pflichtparameter, Domain oder Scope fehlt. Aktion: Request lokal validieren; keine Wiederholung ohne Korrektur.
- `401 missing_or_invalid_authorization`: Bearer-Key fehlt, ist falsch formatiert oder kann nicht verifiziert werden. Aktion: Secret-Quelle prüfen; Key nicht in Logs ausgeben.
- `403 scope_or_domain_denied`: Key ist gültig, aber Scope, Domain-Claim oder Status erlaubt den Zugriff nicht. Aktion: Scope-Matrix und Domain-Freigabe prüfen.
- `409 idempotency_conflict`: Write-Request kollidiert mit vorhandenem Idempotency-Key oder Payload. Aktion: Duplikat behandeln; keinen neuen Key für denselben Auftrag erzeugen.
- `429 rate_limited`: Key-, Scope- oder IP-bezogenes Limit erreicht. Aktion: Retry-After beachten, Backoff mit Jitter verwenden.
- `500 server_error_sanitized`: Serverfehler ohne Secret- oder Rohpayload-Details. Aktion: Request-ID an Support geben; keine Secrets mitsenden.

## Rotation und Revocation
- 1. **Neuen Key vorbereiten**: Neuen Key mit identischen oder reduzierten Scopes und maximal 90 Tagen Laufzeit erstellen. Evidence: key_prefix, scopes, expires_at, reviewer
- 2. **Dual-run testen**: Read- und Write-Dry-runs mit neuem Key ausführen, ohne alten Key zu widerrufen. Evidence: Deny-/Allow-Smoke, HMAC-Fixture, Audit-Event
- 3. **Client umstellen**: Secret Manager aktualisieren und Dienste kontrolliert neu starten. Evidence: Deploy-ID, Key-Prefix, Zeitpunkt
- 4. **Alten Key widerrufen**: Alten Key auf revoked setzen und sicherstellen, dass der nächste Request denied wird. Evidence: revoked_at, deny_audit_event
- 5. **Nachlauf prüfen**: Auditlog auf Nutzung alter Prefixe, Rate-Limits und Scope-Mismatches prüfen. Evidence: 24h Audit-Auszug ohne Roh-Keys

## Client-Abnahme
- **Kein API-Key im Browser**: Keys werden nur serverseitig genutzt; Frontend, HTML, JS und Mobile-Bundles enthalten keine Secrets. Status: required_before_live
- **Least-Privilege-Scopes**: Jeder Client bekommt nur benötigte Scopes und Domain-/Portfolio-Grenzen. Status: required_before_live
- **Request-ID und Audit**: Jeder Client sendet oder akzeptiert Request-ID; Deny/Allow wird sanitisiert auditiert. Status: required_before_live
- **Retry/Backoff**: Clients behandeln 409/429 deterministisch und wiederholen nicht unkontrolliert. Status: required_before_live
- **Write-HMAC und Idempotency**: Schreibende Clients signieren Payload-Kontext und setzen Idempotency-Key. Status: required_before_write_live
- **Rotation geprobt**: Rotation und Revocation sind vor dem ersten Produktiv-Key mit Fixture-Key dokumentiert. Status: required_before_live

## Key-Issuance Receipt
- **Requester und Betreiberrolle verifiziert** (required_before_key): OIDC/MFA, Betreiberkonto und Domain-Claim sind vor Key-Erzeugung bestätigt. Verantwortlich: Customer Success/Security
- **Scopes und Domain-Grenzen freigegeben** (required_before_key): Scope-Matrix, Domain-Scope und Zweckbindung sind dokumentiert. Verantwortlich: Security/Programm-Verantwortung
- **Ablaufdatum gesetzt** (required_before_key): Produktive Keys maximal 90 Tage; Test-Keys kürzer. Verantwortlich: Integration-Verantwortung
- **One-time Display akzeptiert** (required_before_key): Raw-Key wird nur einmal angezeigt, nie gespeichert und nie exportiert. Verantwortlich: Requester
- **Deny-/Allow-Smoke geplant** (required_before_live): 401/403/429/Revocation-Fixtures und Request-ID werden vor Go-live geprüft. Verantwortlich: Platform
- **Rotationsverantwortung gesetzt** (required_before_live): Verantwortlichkeit, Rotationstermin, Revocation-Pfad und Notfallkontakt sind bekannt. Verantwortlich: Security/Integration-Verantwortung

## Rate-Limit-Tiers
- `public_read`: reports.public:read, schemas:read, Limit: cachebar, pro IP begrenzt, Abuse: 429 und Cache/Edge-Regel prüfen
- `operator_read`: reports:read, portfolio:read, Limit: pro Key und Domain moderat, Abuse: Key-Prefix, Domain-Hash und Request-ID auditieren
- `evidence_read`: evidence:read, Limit: streng pro Key und Report-Pack, Abuse: Sanitization prüfen und Key temporär pausieren
- `operator_write`: nachweise:write, dispatch:write, Limit: sehr streng mit Idempotency, Abuse: Write-HMAC, Idempotency und Zielsystem-Dry-run prüfen
- `admin`: keys:rotate, Limit: MFA/Vier-Augen, kein Burst, Abuse: Break-glass-Review und Audit-Export erzeugen

## Abuse-Response
- 1. Anomalie erkennen: Key-Prefix, Scope, Domain-Hash und Request-ID ohne Raw-Key prüfen. Trigger: 429-Spike, Scope-Mismatch, unbekannter User-Agent oder viele Deny-Events.
- 2. Key pausieren: status=revoked oder rotating setzen; keine Roh-Keys exportieren. Trigger: Verdacht auf Fehlkonfiguration oder Leak.
- 3. Client informieren: Sanitisierte Fehlermeldung mit Prefix, Zeitraum und Request-IDs senden. Trigger: Betreiberkontakt vorhanden.
- 4. Rotation erzwingen: Neuen Key mit reduzierten Scopes ausstellen und alten Key denied testen. Trigger: Client bestätigt Wechsel.
- 5. Nachlauf auditieren: Auditlog auf alte Prefixe, Rate-Limits und Scope-Mismatches prüfen. Trigger: 24 Stunden nach Pausierung.

## Post-Issuance-Watch
- **first_successful_request** (erste 30 Minuten): Genau erwartete Domain/Scope-Kombination, Request-ID vorhanden.
- **denied_request_ratio** (erste 24 Stunden): Deny-Anteil plausibel; keine wiederholten 401/403-Schleifen.
- **rate_limit_events** (erste 24 Stunden): 429 selten; Client beachtet Retry-After und Backoff.
- **write_idempotency_conflicts** (erste 24 Stunden): Keine unkontrollierten 409-Wiederholungen.
- **old_prefix_usage_after_rotation** (nach Rotation 24 Stunden): Alter Prefix erzeugt nur Deny-Audit, keine Allow-Events.

## DB-Verantwortungs-Migrationspaket
- Version: api-access-2026-06-09
- Rolle: PostgreSQL-Rolle mit Besitz- oder Adminrechten sowie CREATE TABLE, CREATE INDEX und CREATE TRIGGER im Schema public.
- SQL: https://saferpage.de/api-zugriff/access-migration.sql
- SHA-256: 27d802d48887d084e9132c841b943681c70638bd432746a92cab7809be621213
- Command: `SAFERPAGE_MIGRATION_DATABASE_URL='<admin-dsn-from-secure-shell>' scripts/run-api-access-migration.sh`
- Download + apply: `curl -fsS https://saferpage.de/api-zugriff/access-migration.sql -o /tmp/saferpage-api-access.sql && export SAFERPAGE_MIGRATION_DATABASE_URL='<admin-dsn-from-secure-shell>' && psql "$SAFERPAGE_MIGRATION_DATABASE_URL" -v ON_ERROR_STOP=1 -f /tmp/saferpage-api-access.sql`

### Preflight
- scripts/run-api-access-migration-preflight.sh
- psql -d saferpage -Atq -c "select current_database(), current_user;"
- psql -d saferpage -Atq -c "select coalesce(to_regclass('public.api_keys')::text,'missing'), coalesce(to_regclass('public.api_access_audit_log')::text,'missing');"

### Smoke Tests
- scripts/run-api-access-migration-preflight.sh
- curl -fsS https://saferpage.de/api-zugriff/key-readiness-json | python3 -m json.tool
- scripts/run-api-runtime-deny-smoke.sh
- scripts/run-api-service-smoke.sh

### Aktivierung
- DDL-Migration für api_keys und api_access_audit_log anwenden.
- API-Readiness erneut prüfen: api_access_storage_table_count muss 2 zeigen.
- API-Key-Pepper und Write-HMAC-Secret nur im Server-Environment oder Secret Manager setzen.
- Domain-Claim und Betreiberrolle verifizieren.
- Test-Key nur einmal anzeigen, danach Deny-/Allow-/Revocation-Smokes ausführen.
- Produktive Gates erst nach erfolgreicher Smoke-Evidence aktivieren.

### Pause/Rollback
- Sofortige Pause ohne DDL-Rollback: alle SAFERPAGE_API_*_READY Freigaben entfernen und API-Service neu starten.
- Keys bei Verdacht auf Fehlkonfiguration auf status=revoked setzen statt Rohdaten zu exportieren.
- DDL-Drop nur nach Backup-, Audit- und Retention-Freigabe ausführen.

### Abnahme
- api_keys und api_access_audit_log existieren.
- api_keys enthält nur Prefix/Hash/Scopes/Status und keinen Roh-Key.
- api_access_audit_log schreibt sanitisierte Deny-/Allow-Events ohne Authorization-Header.
- Runtime-Gate-Probe bestätigt 401/403/429/Revocation-Fixtures.
- Write-HMAC-Test-Fixture ist gegen Client/Receiver verifiziert.

### Signoff-Paket
- **DDL-Hash fixiert**: ready
  Aktion: Hash vor Apply mit Public-Export und lokaler Datei vergleichen.
- **Preflight vor/nach Migration**: ready
  Aktion: Preflight unmittelbar vor und nach Apply ausführen und Ergebnis publizieren.
- **Kurzlebiger Admin-DSN**: not_required_after_apply
  Aktion: DSN nur in sicherer Shell setzen und nach Apply entfernen.
- **DB-Verantwortungsfreigabe**: ready
  Aktion: Signoff außerhalb des Public-Exports dokumentieren; öffentlich nur Status/Hash zeigen.
- **API-Service Restart geplant**: ready
  Aktion: Restart-Fenster, Fallback und erneute Smokes festlegen.

### Rollback-Probe
- **Key-Ausgabe pausieren**: Protected Routen bleiben denied.
  Aktion: SAFERPAGE_API_*_READY Freigaben entfernen und API-Service neu starten.
- **Keine Rohdaten exportieren**: No-Secret-Policy im Smoke bleibt grün.
  Aktion: Bei Fehlern nur Prefix, Request-ID, Scope, Entscheidung und Hash-Evidence nutzen.
- **Test-Keys widerrufen**: Revocation-Smoke liefert Deny.
  Aktion: status=revoked setzen, bevor echte Betreiberzugriffe wieder erlaubt werden.
- **DDL nur nach Backup droppen**: DB-Verantwortungsentscheidung außerhalb Public-Export dokumentiert.
  Aktion: Tabellen-Drop nur mit Backup-, Audit- und Retention-Freigabe ausführen.

### Canary nach Migration
- **Preflight nach Apply**: api_access_storage_table_count=2 und missing_required_artifact_count=0.
  Aktion: scripts/run-api-access-migration-preflight.sh
- **Readiness JSON prüfen**: Storage grün, Produktiv-Gates nur bei echten Env-Freigaben grün.
  Aktion: curl -fsS https://saferpage.de/api-zugriff/key-readiness-json
- **Deny-Smoke ohne Key**: 401 deny und sanitisiertes Audit.
  Aktion: scripts/run-api-runtime-deny-smoke.sh
- **Runtime-Gate-Probe**: Public/Protected/HMAC/Revocation-Vertrag vollständig.
  Aktion: curl -fsS https://saferpage.de/api-zugriff/runtime-gate-probe-json
- **No-Secret-Smoke**: failed_check_count=0; blocked_expected nur für bewusst offene Gates.
  Aktion: SAFERPAGE_BASE_URL=https://saferpage.de scripts/run-api-key-readiness-smoke.sh
- **Public Evidence erneut deployen**: Öffentliche Evidence zeigt neue Smoke-Zeit und keine Secrets.
  Aktion: ./scripts/install-system-nginx.sh

## DB-Verantwortungs-Handoff
- Status: storage_ready_after_apply
- Zusammenfassung: API-Access-Tabellen sind vorhanden; DB-Verantwortungs-Handoff bleibt als Nachweis für Hash, Rollback und Canary erhalten.

### Übergabeschritte
- **SQL-Hash vergleichen** (DB-Verantwortung): Public SHA-256 mit der lokal angewendeten Datei vergleichen.
  Abnahme: Hash stimmt exakt mit dem Readiness-Export überein.
  Grenze: Hash beweist nur Dateigleichheit, nicht erfolgreiche Migration.
  Evidence: https://saferpage.de/api-zugriff/key-readiness-json
- **Apply-Fenster und Backup festlegen** (DB-Verantwortung/Platform): Zeitfenster, Backup-Stand und Pause-Entscheidung privat dokumentieren.
  Abnahme: Zeitfenster und Rollback-Verantwortung sind vor Apply bestätigt.
  Grenze: Private Backup-Details werden nicht öffentlich exportiert.
  Evidence: https://saferpage.de/api-zugriff/key-readiness-md
- **Kurzlebigen Admin-DSN nutzen** (DB-Verantwortung): Migration nur aus sicherer Shell mit kurzlebigem Admin-DSN ausführen und DSN danach entfernen.
  Abnahme: DSN taucht in keinem Public-Export, Repo, Log oder Smoke auf.
  Grenze: Ohne Admin-DSN bleibt der Web-DB-User erwartbar blockiert.
  Evidence: https://saferpage.de/evidence/api-access-migration-preflight.json
- **Preflight und Runtime-Smokes wiederholen** (Platform/Security): Preflight, Deny-Smoke, Runtime-Probe und API-Key-Readiness-Smoke nach Apply erneut ausführen.
  Abnahme: api_access_storage_table_count=2 und failed_check_count=0; offene Produktiv-Gates bleiben als Blocker sichtbar.
  Grenze: Smokes erzeugen keine produktiven API-Keys.
  Evidence: https://saferpage.de/evidence/api-key-readiness-smoke.json
- **Runtime-Gates erst nach Secret-Signoff aktivieren** (Security/Operator): API-Key-Pepper, Write-HMAC-Secret, Domain-Claim und Betreiberrolle privat abnehmen.
  Abnahme: Alle Produktiv-Gates sind mit privatem Signoff und öffentlicher No-Secret-Evidence belegt.
  Grenze: Storage allein erlaubt noch keine Key-Ausgabe.
  Evidence: https://saferpage.de/betreiber/go-live-json

### Private Inputs
- **Kurzlebiger Admin-DSN**: Nur die DB-Verantwortung kann Tabellen/Indizes/Trigger sicher anlegen. Public Handling: Nie öffentlich ausgeben.
- **DB-Verantwortungsfreigabe**: DDL-Änderung braucht Verantwortlichen und Zeitfenster. Public Handling: Nur Status, Zeitpunktklasse und SQL-Hash öffentlich zeigen.
- **Backup-/Pause-Verantwortung**: Rollback oder Pause muss vor Apply feststehen. Public Handling: Keine Backup-Pfade oder internen Hostnamen exportieren.
- **API-Key-Pepper-Secret-Referenz**: Hash-Verifikation braucht serverseitiges Secret. Public Handling: Nur Vorhandensein als Gate zeigen, nie Wert oder Secret-Manager-Pfad.
- **Write-HMAC-Secret-Referenz**: Schreibende Aufrufe brauchen Signaturpruefung. Public Handling: Nur Fixture und Gate-Status exportieren.

### Öffentlich erlaubte Outputs
- **Migration-SHA-256**: DB-Verantwortung kann Datei vor Apply prüfen.
- **Tabellen-Readiness 0/2 oder 2/2**: Öffentlicher Fortschritt ohne DSN/Tabellendaten.
- **Redaktierter DB-User-Hash**: Preflight kann Rollenwechsel erkennen, ohne Usernamen zu nennen.
- **Sichere Command-Namen**: Runbook bleibt reproduzierbar ohne private Parameter.
- **SaferPage-Evidence-URLs**: Abnahme ist verlinkbar und maschinenlesbar.
- **Smoke-Status und Zähler**: Produktreife ist prüfbar, ohne Secrets zu leaken.

### Verbotene Outputs
- Admin-DSN oder Passwort
- DB-Host, Port oder interner Username
- Roh-API-Key oder vollständiger Key-Prefix außer Testfixture
- Key-Hash, Pepper oder Secret-Manager-Pfad
- Authorization-Header oder Bearer-Token
- Write-HMAC-Secret oder echte Signatur für Live-Payloads
- Private Webhook-, Slack-, Teams- oder Kundenziel-URLs
- Besucherlogs, Request-Payloads oder personenbezogene Auditdetails

### Validierungsqueries
- `select coalesce(to_regclass('public.api_keys')::text, 'missing');`
- `select coalesce(to_regclass('public.api_access_audit_log')::text, 'missing');`
- `select count(*) from information_schema.columns where table_schema = 'public' and table_name = 'api_keys';`
- `select count(*) from information_schema.columns where table_schema = 'public' and table_name = 'api_access_audit_log';`
- `select tgname from pg_trigger where tgrelid = 'public.api_keys'::regclass and not tgisinternal;`

### Handoff-Abnahme
- DB-Verantwortung bestätigt SQL-Hash und Apply-Fenster privat.
- api_keys und api_access_audit_log existieren nach Apply.
- Public Evidence enthält keine DSN-, Passwort-, Key-, Hash-, Header- oder Ziel-URL-Werte.
- Deny-Smoke liefert 401/deny und schreibt nur sanitisiertes Audit.
- Produktive Key-Ausgabe bleibt blockiert, bis Pepper, HMAC, Domain-Claim und Betreiberrolle privat freigegeben sind.
- Go-live-Dossier nennt Rollback, Stop-Bedingungen und Nachlaufbeobachtung.
