itsgoin/docs/fof-spec/README.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
7.3 KiB
Markdown
Raw Permalink 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.

# Friend-of-Friend (FoF) Gating — Implementation Spec
**Status**: Skeleton drafted 2026-04-23 by Scott + Lead Claude. Crypto-level byte layouts and algorithm specifics are intentionally left as `TBD — OPUS` markers for Opus to fill in. See each layer file for scope.
---
## What this spec covers
FoF-gating is a new post-visibility mechanism that gates readership and commenting by **social reachability** rather than explicit membership lists. It complements, rather than replaces, the existing primitives (`Public`, per-recipient `Encrypted`, per-circle `GroupEncrypted`).
The central primitive is the **per-person vouch key** `V_me` — a symmetric key each persona owns, handed to everyone they vouch for. Cryptographic reachability for an author's FoF post emerges from the union of every `V_x` that author holds (own + received-from-vouchers), and every reader whose keyring intersects that set.
### User-facing model
Authors pick a post visibility from four levels:
| Level | Wrap slots include | Cryptographic reach |
|---|---|---|
| **Public** | (none) | All readers |
| **Friends-only** | `V_me` only | My vouchees |
| **Friends-of-Friends** | `V_me` + every `V_x` I hold | My vouchees + every vouchee of anyone who vouched for me (emergent FoF) |
| **Custom** | Author-selected subset of held `V_x` | Union of reach through each included key |
No centrally-computed membership list. Reach is a function of the wrap-slot set and which keys each reader holds.
### Key design properties
- **Unilateral**: vouching is a one-way act (Alice hands out `V_alice`). No bilateral handshake required for the FoF graph to form.
- **Graph-private**: wrap slots carry no recipient IDs. Observers cannot enumerate "who can read this post."
- **Anonymous prefilter**: each wrap slot carries a 2-byte `HMAC(V_x, post_id)[:2B]` tag. Readers precompute tags for keys in their ring and skip non-matching slots, cutting trial-decrypt cost ~65000× per post.
- **Slot count padding**: author pads wrap slots to power-of-2 buckets to blunt vouch-count leak.
- **Per-post keypair**: each FoF post generates a fresh `(priv_post, pub_post)`. Leak of one post's `priv_post` bounds blast radius to that post's comments; doesn't expose other posts or the author's identity key.
### Two modes
- **Mode 1 — `FOF_CLOSED`**: post body encrypted, comments encrypted. FoF-only readership + writership.
- **Mode 2 — `PUBLIC_FOF_COMMENT`**: post body public (indexable, cacheable, shardable), comments can be public OR FoF-encrypted per-comment.
---
## Layering / implementation order
Build and ship bottom-up. Each layer is independently shippable and exercised before moving to the next.
1. **[Layer 1](layer-1-vouch-primitive.md) — Vouch primitive.** `V_x` keys, per-persona keyring storage, epoch tag, distribution/exchange mechanism, minimal UI. No posts yet.
2. **[Layer 2](layer-2-mode2-fof-comments.md) — Mode 2: public posts with FoF-gated comments.** Easier implementation path; reuses existing public-post CDN path; extends `CommentPolicy` with a new `GroupMembersOfFoF` variant.
3. **[Layer 3](layer-3-mode1-fof-closed.md) — Mode 1: `FOF_CLOSED` posts.** New `PostVisibility::FoFClosed` variant. Wrap slots, anonymous prefilter tag. Receive-path integration.
4. **[Layer 4](layer-4-keypair-rotation.md) — Per-post keypair rotation.** Graceful `(priv_post', pub_post')` rotation record re-wrapped to current FoF set. Old comments still verifiable under old `pub_post`; new comments require new.
5. **[Layer 5](layer-5-prefilter-and-cache.md) — Unlock cache + prefilter optimization.** Author-direct fast path, winning-V_x-per-author cache, unreadable-posts retry table, re-try-on-new-V_x trigger. Performance-critical at realistic keyring sizes (400500 keys × 400500 slots).
6. **[Layer 6](layer-6-revocation.md) — Revocation & rotation cascades.** Deferred; may not be in v1. Drafted as a stub for design review.
---
## Intentionally out of scope (v1)
- **First-contact DM gating.** Deferred; Scott has a separate approach to sketch later.
- **Spam / abuse mitigation beyond vouch_mac.** Rate limits, PoW, reputation, reporting — tracked as separate work.
- **Intra-circle anonymity.** Ring signatures over the vouch set so even other FoF members cannot tell which voucher's chain a comment arrived through. v2.
- **Zero-knowledge audience gating.** v2.
- **PQ migration** of `priv_post` / identity sigs. The spec shape above is algorithm-agnostic; PQ (ML-DSA ~1.3KB sigs) swaps in without structural change. Tracked separately.
---
## Glossary
- **Persona**: a user's posting identity. A device may hold multiple. Each persona has its own `V_me` and its own received-vouches keyring.
- **Vouch**: a unilateral act by persona P1 declaring trust in persona P2. P1 gives P2 a copy of `V_P1`.
- **Vouch key (`V_x`)**: a symmetric key owned by persona `x`, distributed by `x` to everyone `x` vouches for. `V_me` refers to the current persona's own vouch key.
- **Keyring**: for a given persona, the set of vouch keys held = `{V_me}` `{V_x : x vouched for me}`.
- **Wrap slot**: an anonymous ciphertext in a post header, carrying the post's private material encrypted under some `V_x`. Readers trial-decrypt slots whose 2-byte prefilter tag matches an owned key.
- **pub_post / priv_post**: per-post ephemeral ed25519 keypair. `priv_post` is wrapped in the post's wrap slots; `pub_post` is in the header plaintext and used for group signature verification on comments.
- **Identity key**: persona's long-term ed25519 key, used to sign content as that persona. Distinct from `priv_post`. (In current ItsGoin terms: this is the persona's posting key. Worth naming-alignment review when specifying.)
- **Vouch MAC**: `HMAC(V_x, post_id || comment_hash)` — 16B truncated. Identifies which `V_x` a commenter holds. Inside encrypted payload (Mode 1) or alongside plaintext (Mode 2). Used by author strict-mode to verify the commenter is reachable via a known vouch chain.
---
## Integration with existing ItsGoin primitives
- **`Circle` + `GroupEncrypted`** remain as-is for named explicit-membership groups. FoF is a separate visibility class, not a replacement.
- **`PostVisibility`** gains one new variant (`FoFClosed`, from Layer 3). Mode 2 reuses `PostVisibility::Public` with extended `CommentPolicy`.
- **`CommentPolicy`** gains one new variant for Layer 2 (Mode 2 comment gating).
- **`InlineComment`** gets optional `group_sig` + `vouch_mac` fields (back-compat via `#[serde(default)]`, same pattern as Phase 2e `ref_post_id`).
- **`control::receive_post`** gets new verify-gate branches for `FoFClosed` posts (author_sig + wrap_slots well-formedness) and FoF comments (group_sig verifies against `pub_post` from the referenced parent post).
- **Multi-persona**: keyrings are per-persona. Unlock attempts iterate personas; the persona that successfully unlocks is recorded and drives comment-authorship defaults. See Layer 3 for detail.
---
## How to read this spec
- Every `TBD — OPUS` marker identifies a crypto specifier / byte layout / test vector that Opus will fill in.
- `Lead decisions` blocks capture commitments that came out of the Scott + Lead Claude conversation; these are not open to further design iteration.
- `Open questions` blocks flag things the Lead wants Opus's input on before proceeding.
Once Opus fills in the crypto layers, the Lead re-reviews + we move to implementation on a per-layer branch schedule.