Context
Follow-up from #37 (docs/privacy/session-custody.md, requirement SC-01). PRD §7 requires Fork A's persisted storage-state to be "encrypted at rest (per-tenant keys)." Verified at audit time: no code path in this repo persists cookies or storage-state at all yet — TrajectoryRecorder, establishSession, and the live matrix driver all operate on ephemeral browser.newContext() sessions and never call Playwright's context.storageState() / context.cookies(). So there is nothing to encrypt today, and no gap has been exercised — but there is also no guarantee in place for the day this lands, and PRD phase 1 names "persisted browser profiles (cookies/storage-state) for session continuity" as the mechanism.
What to produce
- A storage-state persistence module that cannot be called without an encryption key — the write function's type signature should make an unencrypted call a compile error, not a runtime check.
- Per-tenant key derivation/storage (the mechanism, not necessarily a full KMS integration — size this appropriately and say what v1 defers).
- A canary test, in the shape of
tests/canary/store-leak.test.ts: write a fixture storage-state through the real persistence path, then read the raw bytes on disk and assert JSON.parse fails / the content is not recognizable plaintext. Prove the counter-case too (an intentionally-broken key path DOES leave plaintext), the same way the pool canary proves the taint rule is load-bearing via mutation testing.
Constraints
- No real session material anywhere in the repo, the doc, or test fixtures — synthetic/fake values only, exactly as the existing pool canary uses
CANARY_TENANT fake strings, not real ones.
- Decide and document what "at rest" covers for local dev vs CI vs prod before writing code — don't build against an unstated threat model.
How to test
npm run test:canary
npm run secret-scan
npm run ci
Before you open the PR
Context
Follow-up from #37 (
docs/privacy/session-custody.md, requirement SC-01). PRD §7 requires Fork A's persisted storage-state to be "encrypted at rest (per-tenant keys)." Verified at audit time: no code path in this repo persists cookies or storage-state at all yet —TrajectoryRecorder,establishSession, and the live matrix driver all operate on ephemeralbrowser.newContext()sessions and never call Playwright'scontext.storageState()/context.cookies(). So there is nothing to encrypt today, and no gap has been exercised — but there is also no guarantee in place for the day this lands, and PRD phase 1 names "persisted browser profiles (cookies/storage-state) for session continuity" as the mechanism.What to produce
tests/canary/store-leak.test.ts: write a fixture storage-state through the real persistence path, then read the raw bytes on disk and assertJSON.parsefails / the content is not recognizable plaintext. Prove the counter-case too (an intentionally-broken key path DOES leave plaintext), the same way the pool canary proves the taint rule is load-bearing via mutation testing.Constraints
CANARY_TENANTfake strings, not real ones.How to test
Before you open the PR
docs/privacy/session-custody.mdSC-01 status updated from "not addressed"