From 9adfb0bf4d61addcf3e9929ee87e4882f23d5e26 Mon Sep 17 00:00:00 2001 From: tegwick Date: Tue, 27 Oct 2026 23:28:54 +0100 Subject: [PATCH] KEY-WP-0044: traceparent correlation task and audit-core intake package (NK-WP-0055-T11) Co-Authored-By: Claude Opus 5.5 Assistant: claude-code Assistant-Model: opus Assistant-Process: 4515@bnt-lap001 Assistant-Session: 0bf96127-6e04-41be-bfda-d11717c3f354 --- WORK-RECORDS.md | 4 + docs/audit-core-sender-package.md | 118 ++++++++++++++++++ ...EY-WP-0044-audit-correlation-and-intake.md | 79 ++++++++++++ 3 files changed, 201 insertions(+) create mode 100644 docs/audit-core-sender-package.md create mode 100644 workplans/KEY-WP-0044-audit-correlation-and-intake.md diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index 22b530f..35d9e54 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -54,6 +54,7 @@ | workplan | KEY-WP-0041 | finished | — | workplans/KEY-WP-0041-sign-in-diagnosis-page.md | | workplan | KEY-WP-0042 | proposed | — | workplans/KEY-WP-0042-online-access-token-status.md | | workplan | KEY-WP-0043 | proposed | — | workplans/KEY-WP-0043-per-run-agent-identity.md | +| workplan | KEY-WP-0044 | proposed | — | workplans/KEY-WP-0044-audit-correlation-and-intake.md | | task | ADHOC-2026-09-05-T01 | done | — | workplans/ADHOC-2026-09-05.md | | task | ADHOC-2026-09-07-T01 | done | — | workplans/ADHOC-2026-09-07.md | | task | ADHOC-2026-09-27-T01 | done | — | workplans/ADHOC-2026-09-27.md | @@ -231,4 +232,7 @@ | task | KEY-WP-0043-T02 | wait | — | workplans/KEY-WP-0043-per-run-agent-identity.md | | task | KEY-WP-0043-T03 | wait | — | workplans/KEY-WP-0043-per-run-agent-identity.md | | task | KEY-WP-0043-T04 | wait | — | workplans/KEY-WP-0043-per-run-agent-identity.md | +| task | KEY-WP-0044-T01 | todo | — | workplans/KEY-WP-0044-audit-correlation-and-intake.md | +| task | KEY-WP-0044-T02 | todo | — | workplans/KEY-WP-0044-audit-correlation-and-intake.md | +| task | KEY-WP-0044-T03 | wait | — | workplans/KEY-WP-0044-audit-correlation-and-intake.md | | intake | KEY-IN-0001 | closed | — | intakes/intakes.md | diff --git a/docs/audit-core-sender-package.md b/docs/audit-core-sender-package.md new file mode 100644 index 0000000..7dc9d27 --- /dev/null +++ b/docs/audit-core-sender-package.md @@ -0,0 +1,118 @@ +# 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. diff --git a/workplans/KEY-WP-0044-audit-correlation-and-intake.md b/workplans/KEY-WP-0044-audit-correlation-and-intake.md new file mode 100644 index 0000000..8bd896e --- /dev/null +++ b/workplans/KEY-WP-0044-audit-correlation-and-intake.md @@ -0,0 +1,79 @@ +--- +id: KEY-WP-0044 +type: workplan +title: "Audit correlation (traceparent) and audit-core intake for KeyCape" +domain: infotech +repo: key-cape +status: proposed +owner: claude-code +topic_slug: custodian +created: "2026-10-27" +updated: "2026-10-27" +flavor: extension +depends_on: [] +--- + +Accepted from net-kingdom NK-WP-0055-T11 under the NetKingdom Audit Correlation +Contract (`net-kingdom/canon/standards/audit-correlation_v0.1.md`, AC-1 to AC-7). +KeyCape's intake package for audit-core AUDIT-WP-0017-T04 is +`docs/audit-core-sender-package.md`. Nothing here registers a sender, mints a +credential or changes a live deployment; activation stays owner-run. + +## Read inbound traceparent and record correlation_id + +```task +id: KEY-WP-0044-T01 +status: todo +priority: high +``` + +On `/authorize` (including the login, MFA and handoff legs of the same flow) +and `/token` (all grants), parse the inbound W3C `traceparent` header. If it is +valid (version 00, 32 lowercase hex trace-id, not all zeros, valid parent-id), +record its trace-id as `correlation_id` with `correlation_origin: propagated`; +otherwise generate a fresh random trace-id and record `correlation_origin: +generated`. Carry the value through the request context into every +`telemetry.Event` (today `TraceID` is always empty) and add a +`CorrelationOrigin` field. For the authorize flow, keep the id from the first +`/authorize` request on the login session so the success/failure record for +that flow carries it; `/token` uses its own inbound header. + +Rules (AC-3, AC-5): the id is never placed in an access token, ID token, +refresh token, authorization code or userinfo response; it never influences an +authentication, authorization or token decision; a malformed header is ignored, +never an error. Outbound calls KeyCape makes while serving the request +(Authelia, LLDAP, privacyIDEA) send a `traceparent` with the same trace-id. + +Tests: propagated, absent, malformed and all-zero headers; a decoded token from +each grant carries no trace id; the token/authorize response is identical with +and without the header. + +## Emit audit-grade records for every issuance and refusal + +```task +id: KEY-WP-0044-T02 +status: todo +priority: high +``` + +Close the gaps the intake package lists: emit on `/token` failures +(`invalid_client`, `invalid_grant`, `unauthorized_client`, `invalid_scope`), +on revoke and introspection, and on denied scope/tenant/principal-type +preconditions; add `sub`/`uid`, `tenant` and `principal_type` to +`token_issued`; add a stable `event_id` (UUIDv7 minted once per event, reused on +retry); set `environment` on every event. No secret, token, code, password, +OTP or email on any event. + +## Forward to audit-core (after admission) + +```task +id: KEY-WP-0044-T03 +status: wait +priority: medium +``` + +Waits on audit-core admitting `netkingdom.keycape` (AUDIT-WP-0017-T04) and on +AUDIT-WP-0017-T05 confirming `correlation_origin`. Implement the envelope +mapping from the intake package (log-tail collector or direct POST, owner's +choice), heartbeat, and fixture tests against audit-core's receiver. Sender +credential is read from an OpenBao-delivered Secret; activation is owner-run.