feat(audit): record security events in the audit log #34

Merged
nalum merged 1 commit from feat/audit-security-events into main 2026-08-13 11:40:24 +00:00
Owner

Stacked on #32 (fix/login-timing-oracle).

Auth events existed only as slog lines + OTel counters — invisible to the admin AuditLog view. New audit.RecordSecurity seam rides the existing best-effort queue/sink (no proto change: the security.* action name travels in entity_type, so SearchAudit filters work as-is).

Recorded: failed logins (target = account uid; an attacker-submitted unknown email is NEVER stored — log injection into the admin UI), successful logins, password lock transitions (emitted after the commit, so the log never claims a rolled-back lock), PIN lock transitions, matrix denials (via a new auth.Config.OnDenied hook — audit imports auth, so auth cannot import audit back), and svc resource-guard denials.

Skipped, recorded in the report: session revocations (mid-transaction, no clean after-commit seam; 5 of 6 call sites are routine housekeeping) and per-service denial scatter sites (the shared guards cover the seam paths).

Admin UI renders the new rows via its existing raw-fallback path; full entry in the expanded JSON.

Fixes #16

🤖 Generated with Claude Code

Stacked on #32 (`fix/login-timing-oracle`). Auth events existed only as slog lines + OTel counters — invisible to the admin AuditLog view. New `audit.RecordSecurity` seam rides the existing best-effort queue/sink (no proto change: the `security.*` action name travels in entity_type, so SearchAudit filters work as-is). **Recorded:** failed logins (target = account uid; an attacker-submitted unknown email is NEVER stored — log injection into the admin UI), successful logins, password lock transitions (emitted after the commit, so the log never claims a rolled-back lock), PIN lock transitions, matrix denials (via a new `auth.Config.OnDenied` hook — audit imports auth, so auth cannot import audit back), and svc resource-guard denials. **Skipped, recorded in the report:** session revocations (mid-transaction, no clean after-commit seam; 5 of 6 call sites are routine housekeeping) and per-service denial scatter sites (the shared guards cover the seam paths). Admin UI renders the new rows via its existing raw-fallback path; full entry in the expanded JSON. Fixes #16 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(audit): record security events in the audit log
All checks were successful
check / web (push) Successful in 1m42s
check / go (push) Successful in 2m1s
f83523cee4
Failed and successful sign-ins, lockout transitions and permission
denials existed only as slog lines and OTel counters — invisible to
the admin AuditLog surface, which is the household's only
tamper-evident view of what happened. A security.* action family now
rides the existing best-effort audit queue: the dotted action name is
stored in entity_type (the log's generic string dimension), so the
AuditLog RPC filters it and the web UI renders the raw name with no
new proto surface, and the UNSPECIFIED mutation action keeps the
webhook mutation tap silent (ADR-0024's event surface is unchanged).

Emitted: login failed/succeeded/locked (Login + the miss recorder,
after commit), PIN chain lockout (SwitchProfile, after commit), matrix
denials (via a new auth.Config.OnDenied seam — auth cannot import
audit, which imports auth) and the svc guard denials. An unknown login
email is attacker-controlled text and is never stored — log injection
into the admin UI; only failures against existing accounts carry the
account uid. Session revocations are deliberately not recorded:
revokeChain runs mid-transaction without a ctx and mostly covers
routine housekeeping (logout, park supersession, idle expiry), so
there is no clean after-commit seam to tap.

Fixes #16
nalum force-pushed feat/audit-security-events from f83523cee4
All checks were successful
check / web (push) Successful in 1m42s
check / go (push) Successful in 2m1s
to 3d941607e0
Some checks failed
check / go (push) Has been cancelled
check / web (push) Has been cancelled
tag / tag (push) Has been cancelled
2026-08-13 11:33:32 +00:00
Compare
nalum changed target branch from fix/login-timing-oracle to main 2026-08-13 11:33:56 +00:00
nalum scheduled this pull request to auto merge when all checks succeed 2026-08-13 11:34:08 +00:00
nalum merged commit 3d941607e0 into main 2026-08-13 11:40:24 +00:00
nalum deleted branch feat/audit-security-events 2026-08-13 11:40:24 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
eagraiclainne/app!34
No description provided.