# KeyCape audit-core intake package Status: proposed, 2026-10-27. Answers audit-core AUDIT-WP-0017-T04 ("common intake package for every new sender") for KeyCape, and NetKingdom NK-WP-0055-T11. Implementation work: `workplans/KEY-WP-0044-audit-correlation-and-intake.md`. Unknowns are marked **UNKNOWN**. Authelia, LLDAP and privacyIDEA get their own packages and source names; this one covers KeyCape only. ## 1. What KeyCape emits today (from source, 2026-10-27) All events go through `telemetry.Emitter` (`src/internal/server/telemetry/`). In production `cmd/keycape/main.go` wires a `LogEmitter`: one zerolog JSON line on stdout per event. There is no webhook, receipt store or audit-core sender. Fields per line: `event_type`, `client_id`, `endpoint`, `result`, `environment`, `trace_id`, `timestamp`, and optionally `feature`, `error_type`, `scopes`, `uid`, `assurance`, `grant_type`. Events never carry tokens, codes, email or OTPs (comment in `events.go`). | event_type | Emitted at | |---|---| | `auth_start` | `/authorize` request accepted | | `auth_success` | `/authorize` completes (with `uid`, `assurance` aal1/aal2); also `/userinfo` | | `auth_failure` | `/authorize`: `auth_failed`, `stale_upstream_authentication`, `mfa_check_error`, `mfa_failed` | | `token_issued` | `/token` authorization_code/refresh and client_credentials success | | `unsupported_feature` | profile enforcement middleware | | `migration_event` | migration tools only (not the server) | Gaps against audit needs: - `trace_id` is never set (always empty) — fixed by KEY-WP-0044-T01. - No event on `/token` failures, client authentication failure, revoke or introspection; no explicit event for denied scope/tenant/principal-type preconditions — KEY-WP-0044-T02. - `token_issued` lacks subject and tenant; `environment` is unset on several emissions; no stable event id — KEY-WP-0044-T02. ## 2. Source registration | Item | Value | |---|---| | `source` | `netkingdom.keycape` (proposed) | | Owning repo | `key-cape` (runtime package owner: **UNKNOWN**, the Railiance/rapp package that deploys KeyCape) | | Namespace / pod selector | **UNKNOWN** — to be taken from the deploying package; KeyCape does not own its manifests | | Emitting identity | a dedicated KeyCape audit sender credential (section 6); not a KeyCape OIDC client | | Tenant policy | per-tenant: the event's `tenant` is the principal's tenant; platform-scoped events (client auth failure before a tenant is known, service clients without a tenant) use a platform tenant value — **UNKNOWN**, audit-core to name it | ## 3. Envelope mapping (audit-core `docs/event-envelope.md`) | Envelope field | KeyCape value | |---|---| | `id` (= `Idempotency-Key`) | `event_id`: UUIDv7 minted once when the event is created and reused on every retry (KEY-WP-0044-T02). Not derived from request content, so two identical logins stay distinct | | `type` | `keycape.` from section 4 | | `source` | `netkingdom.keycape` | | `subject` | the principal (`sub`/`uid`) for user events; `client:` for client-only events | | `tenant` | principal tenant, else the platform tenant value | | `correlation_id` | W3C trace-id from inbound `traceparent`, or a generated one (contract AC-1, AC-4) | | `occurred_at` | event timestamp, RFC 3339 with offset (UTC `Z`) | | `data` | `client_id`, `endpoint`, `result`, `error_type`, `grant_type`, `scopes`, `assurance`, `principal_type`, `environment`, `correlation_origin` (`propagated`/`generated`), optional `parent_event_id` | Never in `data`: access/ID/refresh tokens, authorization codes, client secrets, passwords, OTPs, email. `actor` is null at ingest per the envelope; the authenticated subject lives in `subject`/`data`. ## 4. Scope rows (action vocabulary) | `type` | Today | Meaning | |---|---|---| | `keycape.auth.start` | yes | authorize request accepted | | `keycape.login.success` | yes (`auth_success`) | user authenticated, with assurance | | `keycape.login.failure` | yes (`auth_failure`) | upstream auth failed or stale | | `keycape.mfa.success` | partly (aal2 on `auth_success`) | MFA/step-up satisfied | | `keycape.mfa.failure` | yes (`mfa_failed`, `mfa_check_error`) | MFA challenge failed or errored | | `keycape.token.issued` | yes | token issued (any grant) | | `keycape.token.denied` | no | `/token` refused: invalid_grant, invalid_scope, unauthorized_client | | `keycape.client.auth_failure` | no | client authentication failed at `/token`, revoke or introspect | | `keycape.scope.denied` | no | requested scope, tenant or principal-type precondition refused | | `keycape.token.revoked` | no | revocation accepted | | `keycape.token.introspected` | no | introspection served (volume to be judged; may be excluded) | | `keycape.userinfo.served` | yes (`auth_success` on `/userinfo`) | userinfo served | | `keycape.profile.unsupported_feature` | yes | request used a feature the profile refuses | | `keycape.heartbeat` | no | liveness, section 5 | `migration_event` is excluded: it comes from offline tools, not the server. ## 5. Volume, heartbeat, retention - Volume: **UNKNOWN** — no production measurement exists. Estimate for the current one-node estate with a handful of users and six flex-auth consumers: low hundreds to a few thousand events/day, peak under 5/s, dominated by `token.issued` (client_credentials) and `userinfo.served`. To be replaced by a measured count from the stdout stream before overlay sizing. - `emission_cadence`: event-driven plus `keycape.heartbeat` every 5 minutes; proposed silence threshold 15 minutes. - Retention: audit-core default 30-day query window plus archive. Proposed archive for login/MFA/token/denial records: 1 year (security-relevant identity evidence). Final class is a NetKingdom/audit-core decision — **UNKNOWN**. ## 6. Credential path The sender secret is minted through the attended OpenBao path that `warden route find "audit-core sender credential"` points to (railiance-platform OpenBao), delivered to the KeyCape pod as a Kubernetes Secret reference, and never in Git, State Hub, logs or this document. audit-core never holds or prints it; `may_read` stays false; `may_write` is limited to source `netkingdom.keycape`. The exact OpenBao path and delivery mechanism are **UNKNOWN** until railiance-platform names them. ## 7. Export mechanism Open choice, recorded in KEY-WP-0044-T03: (a) a collector tailing KeyCape's stdout JSON lines and posting envelopes, or (b) a KeyCape `AuditEmitter` posting directly with retry on the stable `event_id`. Preference: (b), because `event_id` and `correlation_origin` are known only inside KeyCape and a log tail cannot guarantee idempotent retry. **UNKNOWN** until audit-core states its preferred intake for application senders.