feat(#175): file shares work on per-user custody — recovery-open for the public surface #187

Merged
Cordy merged 8 commits from feat-share-recovery into main 2026-08-10 20:33:50 +00:00
Owner

The deepest beta finding: file share links have never worked under keycloak-profile/OpenBao custody — the anonymous /s/ surface has no user key to decrypt with, so every file link 404'd (diagnosed live on the dogfood).

Design: no new key material. The deployment recovery identity is a recipient on every write by design and already lives in this process's config; the strong-share pipeline re-encrypts to the link key, so the public surface still emits ciphertext only. The share record — checked before any open — is the authorization, and every recovery-path read is audited as share:<token>.

Mechanics: encrypt.Driver.OpenWithRecovery (decrypt-to-temp with recovery+legacy identities), RecoveryIdentities() on all three key providers, PublicHandler.openFile (normal stack first, fallback second, audited), viewer rendered from metadata before any content open, main.go captures the capability pre-wrapping and wires it with the audit hook. No-recovery instances get an explicit config-naming error; remote-crypto splits keep the old limitation, documented.

Six new tests: recovery-open round trip without user keys, refusal without capability/recovery, viewer-without-open, payload-via-fallback (asserts age header + audit), payload-without-fallback stays 404. CHANGELOG v0.6.4.

The deepest beta finding: file share links have never worked under keycloak-profile/OpenBao custody — the anonymous `/s/` surface has no user key to decrypt with, so every file link 404'd (diagnosed live on the dogfood). **Design:** no new key material. The deployment **recovery identity** is a recipient on every write by design and already lives in this process's config; the strong-share pipeline re-encrypts to the link key, so the public surface still emits ciphertext only. The share record — checked before any open — is the authorization, and every recovery-path read is audited as `share:<token>`. **Mechanics:** `encrypt.Driver.OpenWithRecovery` (decrypt-to-temp with recovery+legacy identities), `RecoveryIdentities()` on all three key providers, `PublicHandler.openFile` (normal stack first, fallback second, audited), viewer rendered from metadata **before** any content open, `main.go` captures the capability pre-wrapping and wires it with the audit hook. No-recovery instances get an explicit config-naming error; remote-crypto splits keep the old limitation, documented. Six new tests: recovery-open round trip without user keys, refusal without capability/recovery, viewer-without-open, payload-via-fallback (asserts age header + audit), payload-without-fallback stays 404. CHANGELOG v0.6.4.
Cordy merged commit 2b7bd45e55 into main 2026-08-10 20:33:50 +00:00
Sign in to join this conversation.
No reviewers
No labels
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: Cordy/Cairn#187
No description provided.