Strong shares phase 1: ciphertext-only share payloads (#19) #114
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/strong-shares"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Phase 1 of the #19 links lane (design agreed 2026-08-04). Server pipeline only — the recipient viewer page is phase 2, so for now a strong share downloads as
<name>.age.What it does: on every download of a strong share, the server reads the object through the normal storage stack (decrypting with the owner's/space's key like any read) and re-encrypts the stream on the fly to a share-specific age recipient. No stored second copy, revocation stays instant, memory stays flat (streaming — the #5 lesson), and the crypto cost rides the
--target=cryptorole (#33) like all other encryption work.Modes (alternatives, because age refuses to mix scrypt with other recipient types):
fragment(default for new links) — an ephemeral X25519 identity is minted at creation; only the public recipient is persisted, the identity is returned once in the create response and appended to the URL as#<key>. Browsers never send fragments, so the server cannot see it again.password— payload encrypted to an age scrypt recipient. The passphrase both unlocks the existing server-side gate and is the key material: the gate session now carries it in memory for the session TTL. The stored argon2id hash remains a verifier derived independently of the content key, so ciphertext can't be harvested for offline brute-forcing.plain— the legacy plaintext payload, still selectable; existing pre-#19 links (empty mode) keep working untouched.Policy (#62) is unchanged and still enforced at the same chokepoint:
shareRequirePassword(a password implies password mode),shareMaxExpiry,shareUploadsForbidden. Password mode without a password is refused.Details worth reviewing: strong payloads set
Accept-Ranges: none+no-store(the ciphertext is minted per request, so client-side byte offsets are meaningless); HEAD returns headers only; a mid-stream encryption failure truncates the payload, which is self-signalling since an incomplete age file never decrypts.Tests: fragment roundtrip (payload is a real age file, plaintext absent, only the fragment identity opens it), password roundtrip + wrong-passphrase rejection, missing-key-material refusals for all three modes, fragment recipient uniqueness. Full suite green.
Next: phase 2 (share dialog mode selection + typage viewer page), phase 3 (handbook page with the claim boundary).