itsgoin/docs/fof-spec
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
..
layer-1-vouch-primitive.md docs: FoF-gating spec skeleton (hand-off to Opus) 2026-04-23 23:20:56 -04:00
layer-2-mode2-fof-comments.md docs: FoF-gating spec skeleton (hand-off to Opus) 2026-04-23 23:20:56 -04:00
layer-3-mode1-fof-closed.md docs: FoF-gating spec skeleton (hand-off to Opus) 2026-04-23 23:20:56 -04:00
layer-4-keypair-rotation.md docs: FoF-gating spec skeleton (hand-off to Opus) 2026-04-23 23:20:56 -04:00
layer-5-prefilter-and-cache.md docs: FoF-gating spec skeleton (hand-off to Opus) 2026-04-23 23:20:56 -04:00
layer-6-revocation.md docs: FoF-gating spec skeleton (hand-off to Opus) 2026-04-23 23:20:56 -04:00
README.md docs: FoF-gating spec skeleton (hand-off to Opus) 2026-04-23 23:20:56 -04:00

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 — Vouch primitive. V_x keys, per-persona keyring storage, epoch tag, distribution/exchange mechanism, minimal UI. No posts yet.
  2. Layer 2 — 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 — Mode 1: FOF_CLOSED posts. New PostVisibility::FoFClosed variant. Wrap slots, anonymous prefilter tag. Receive-path integration.
  4. Layer 4 — 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 — 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 — 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.