itsgoin/docs/fof-spec/layer-6-revocation.md
Scott Reimers 1fdf9a94cc docs: FoF-gating spec skeleton (hand-off to Opus)
Drafts the Friend-of-Friend post-gating spec with crypto specifics
marked TBD — OPUS for Opus to fill in. Six-layer implementation plan;
each layer independently shippable.

Includes README overview + six layer files:
- Layer 1: V_me vouch primitive (keys, keyring, VouchGrant wire format)
- Layer 2: Mode 2 — public post + FoF-gated comments
- Layer 3: Mode 1 — FoFClosed (encrypted body via wrap_slots + prefilter)
- Layer 4: per-post keypair rotation
- Layer 5: unlock cache + prefilter optimization (perf-critical)
- Layer 6: revocation (stub; likely deferred post-v1)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:20:56 -04:00

94 lines
4.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Layer 6 — Revocation & Rotation Cascades
**Status**: Stub. May not be in v1. Drafted for design review only.
**Scope**: Mechanism for a persona to un-vouch a specific vouchee without rotating `V_me` (which affects everyone), and for cascading rotations when a down-chain vouchee is un-vouched.
---
## The problem
In Layers 15, the only way to revoke a vouch is to rotate `V_me` and re-distribute to every remaining vouchee. This is:
- **Coarse**: rotates everyone to remove one.
- **Expensive at scale**: O(remaining_vouchees) DM sends.
- **Traceable**: observers can't see who, but a flurry of DMs hints at a change.
- **Cascading**: if Alice rotates her `V_me`, every FoFClosed post Alice authored that was open to "friends" was open under `V_alice_old`; new readers on the updated `V_alice_new` can't decrypt old posts. Post-level Layer 4 rotation fixes this only if the author re-wraps every post under the new set.
Layer 6 is where we explore whether any of these frictions is worth addressing, and at what cost.
---
## Out of scope for v1
Without Layer 6, here's what breaks vs. what holds:
- **Holds**: FoF gating works. Revocation works (via coarse `V_me` rotation). Privacy properties intact.
- **Breaks**: surgical un-vouch. If Alice wants to remove Bob but keep Charlie, she must rotate `V_alice`, re-give to Charlie. If she has 50 vouchees, that's 49 re-grants. Probably tolerable for a social-graph change that's inherently rare. Sharp corner only at large vouchee fanout.
Lead leaning: **v1 ships without Layer 6**. Revocation is via coarse `V_me` rotation. Revisit after usage data shows whether surgical revocation is load-bearing.
---
## Candidate designs (if we do Layer 6 later)
### Candidate A: Revocation List signed by voucher
Voucher publishes `(revoked_vouchee_id, since_epoch)`. Readers exclude posts whose `vouch_mac` identifies a chain through a revoked vouchee.
Drawbacks:
- Requires `vouch_mac` to be public (not just for author strict mode).
- Publishes the social graph — the thing we worked to keep private.
- Non-starter without Zero-Knowledge proofs.
### Candidate B: Per-vouchee key derivation
Instead of one `V_me`, derive a per-vouchee key `V_me_bob = HKDF(V_me_master, bob_id)`. To revoke Bob, rotate `V_me_master` (affects everyone again — same problem).
Alternative: author re-wraps posts under `V_me_current \ {V_me_bob}`. This is per-post rotation (Layer 4) with deliberate exclusion. Feasible but requires the author to know which slot was Bob's — which breaks anonymous wrap slots.
### Candidate C: Forward-secrecy ratchet
`V_me` ratchets forward on a cadence. Old vouchees retain access to content encrypted before revocation; lose access to new. Avoids explicit revocation. Close to Signal's group ratchet.
Complexity is significant. Would require a per-persona state machine, out-of-band sync of current epoch between persona's devices, etc.
### Candidate D: Accept the coarse rotation
Acknowledge `V_me` rotation IS the revocation primitive. Smooth the UX:
- UI: "Remove Bob from your Friends" → warns "This will re-distribute your Friends key to your other 49 Friends."
- Background rotation task handles the N DMs.
- Existing FoFClosed posts don't auto-re-wrap (remain under old `V_alice`). Author can opt to re-wrap specific posts via Layer 4.
Lead leaning: **Candidate D**. The "problem" is mostly UX friction, addressable with good affordances.
---
## Open design questions (deferred)
- Is there user demand for surgical revocation that doesn't rotate everyone?
- What's the actual fanout distribution? If p95 vouchee count is <20, coarse rotation is a ~20-DM operation tolerable.
- Does Candidate D's UX feel heavy enough that users avoid revoking at all? (Anti-pattern: graph accumulates stale vouches.)
- For Mode 2 (public posts with FoF comments), revocation has different urgency author cares about who can still COMMENT, not about access to past content. Is surgical comment-only revocation simpler? (Hint: Layer 4 rotation on a specific post already does this coarsely rotate `priv_post`, re-wrap under the narrowed set.)
---
## What Layer 6 should deliver IF we build it
Not decided. Placeholder:
- A defined revocation primitive (candidate selected).
- Cascading / not-cascading behavior specified.
- UI surface consistent with Layers 15.
- Does NOT introduce per-vouchee public identifiers in any wire format.
---
## Decision point
**Revisit after Layers 15 ship and 30 days of production usage.** Signals that would move Layer 6 into scope:
- p95 vouchee count > 50 (coarse rotation cost meaningfully high).
- User reports of "I want to remove X but not everyone."
- Privacy audit finding that the DM-flurry of coarse rotation leaks social-graph change timing.
Absent those, Layer 6 stays deferred.