Skip to content

ADR 036 Customer Session Recovery Audit

The legacy customer web restores login from CodeIgniter session keys and a long-lived encrypted AUTH_TOKEN cookie. Reservation and payment controllers then compare mutable numeric user and reservation rows. Those sources prove legacy behavior, but they cannot become target audit authority: recording the cookie, bearer, raw Auth subject, resume key, or raw draft reference would turn credentials and internal identifiers into durable log data.

ADR-035 provides a backend-owned authenticated customer session and account-link proof. P04 still needs evidence for every draft-resume decision, including denials that occur before a valid actor exists.

domain/security_audit/features/customer_session_recovery owns CustomerSessionRecoveryAuditEvent. The event records only:

  • one UUID event id and the fixed customer_reservation_draft surface;
  • recovered or denied;
  • one closed reason code;
  • SHA-256 digests of the resource and, when known, actor references;
  • the exact recovered draft revision only on success; and
  • timezone-aware occurrence time.

Raw authorization headers, bearer/access tokens, browser resume keys, Auth subjects, and draft references are not event fields and are not database columns.

Reservation Operations owns the consumer contract

Section titled “Reservation Operations owns the consumer contract”

P04 owns CustomerReservationSessionRecoveryAuditRecord and its recorder port. The GET draft recovery boundary records:

  1. missing_bearer;
  2. invalid_authorization_scheme;
  3. invalid_session;
  4. draft_not_found;
  5. draft_ownership_mismatch;
  6. draft_expired;
  7. draft_inactive; or
  8. active_draft_recovered with the exact positive draft revision.

An authentication or authority infrastructure outage is not a customer denial and must not be recorded as invalid_session. Audit persistence is fail-closed: if the denial event cannot be stored, HTTP returns 503 instead of falsely returning an unaudited 401.

Orchestration is the only cross-context bridge

Section titled “Orchestration is the only cross-context bridge”

The P04 application feature does not import Security Audit. The orchestration bridge maps the consumer record to RecordCustomerSessionRecoveryCommand. The Security Audit application handler hashes raw internal references before it creates the domain event, and the Supabase adapter receives only the closed event.

application/reservation_operations/features/customer_reservation_creation
\
orchestration bridge
/
application/security_audit/features/customer_session_recovery
← adapters/security_audit/features/customer_session_recovery

This preserves Layer-first Context → Feature Slice boundaries. The audit feature is command-only and does not create an empty queries/ directory.

The database ledger is private and append-only

Section titled “The database ledger is private and append-only”

Migration 20260718153000_customer_session_recovery_audit.sql creates sanmopia_customer_session_recovery_audits with forced RLS. anon and authenticated have no access. service_role receives only SELECT and INSERT; UPDATE, DELETE, and TRUNCATE remain denied. A trigger rejects privileged update or delete attempts with SQLSTATE 55000.

The database repeats the outcome/reason/revision and 64-character lowercase SHA-256 constraints. No policy or adapter may reconstruct or persist the raw references.

  • Session recovery decisions have one durable Security Audit owner.
  • Reservation Operations emits a consumer-owned fact and keeps direct cross-feature imports forbidden.
  • Successful recovery can be correlated by digest and exact draft revision without storing a credential or raw identifier.
  • Infrastructure outages remain distinguishable from customer denials.
  • ADR-037 closes the frozen reservation-party/expected-revision gap. P04 remains RED/partial because production-source completeness, legal acceptance, finalized booking, confirmation readiness, and a new actor/browser/WebP run remain open.

Backend 7e2b86db9d21c8d483957f0888620b06b1eb03ad implements the Layer-first Security Audit domain/application/adapter slices, the P04 consumer port and GET decision mapping, the orchestration bridge, Supabase runtime composition, and the migration.

The repository Supabase CLI advanced a disposable PostgreSQL 17 Supabase database through the complete current migration chain, including 20260718153000. Actual SQL verification proved:

  • service_role insert/select succeeds with exact 64-character digests;
  • its ACL is exactly ar (INSERT, SELECT);
  • service_role update and truncate fail with 42501;
  • anon and authenticated reads fail with 42501;
  • privileged update and delete hit the append-only trigger with 55000; and
  • forbidden raw credential/reference columns count is 0.

The final full Python regression reports 7018 passed. Tach MCP reports 3093 passed / 3904 deselected for HEAD^..HEAD, and exact dependency/interface/external checks report zero diagnostics. The architecture suite reports 13 passed; Vulture confidence 100 reports zero candidates on the changed production paths. Ruff and diff checks pass.

No new Chromium, screenshot, animated WebP, stage, or production deployment was created for this backend/database increment. Existing P04 visual evidence remains historical and does not prove the new audit runtime.

  • 2026-07-18: accepted and implemented privacy-safe, append-only customer session recovery auditing.