Fold in Scott's answers: - Per-post (pub_x, priv_x); confirmed. - Random rand(32..=128) dummy padding replaces power-of-2 buckets; dummy pubkeys in pub_post_set so .len() == wrap_slots.len(). Floor count is unrecoverable across multiple posts. - Non-FoF UX: "Comments are private" + optional "Request access via DM" button. No count leak. - Author's own (pub_me, priv_me) in pub_post_set; confirmed. - Revocation is retroactive delete + forward: file-holders delete locally-stored comments signed by revoked pub_x on diff arrival, then propagate. Stronger than stop-forwarding. New primitive: access-grant author comment. Author appends a WrapSlot + pub_post_set entry for a newly-vouched persona via a signed special comment — retroactive read widening without republish. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| layer-1-vouch-primitive.md | ||
| layer-2-mode2-fof-comments.md | ||
| layer-3-mode1-fof-closed.md | ||
| layer-4-keypair-rotation.md | ||
| layer-5-prefilter-and-cache.md | ||
| layer-6-revocation.md | ||
| README.md | ||
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'spriv_postbounds 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.
- Layer 1 — Vouch primitive.
V_xkeys, per-persona keyring storage, epoch tag, HPKE-sealed anonymous-wrapper distribution via the voucher's bio post, scan-on-follow + scan cache, minimal UI. No FoF-gated posts yet. - Layer 2 — Mode 2: public posts with FoF-gated comments. Easier implementation path; reuses existing public-post CDN path; extends
CommentPolicywith a newGroupMembersOfFoFvariant. - Layer 3 — Mode 1:
FOF_CLOSEDposts. NewPostVisibility::FoFClosedvariant. Wrap slots, anonymous prefilter tag. Receive-path integration. - Layer 4 — Per-post keypair rotation. Graceful
(priv_post', pub_post')rotation record re-wrapped to current FoF set. Old comments still verifiable under oldpub_post; new comments require new. - 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 (400–500 keys × 400–500 slots).
- 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_meand 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 personax, distributed byxto everyonexvouches for.V_merefers 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_x / priv_x: per-
V_xed25519 keypair (Layer 2).priv_xis wrapped in the sign-slot of eachV_x;pub_xis published in the post'spub_post_set. Comments reference apub_x_indexso propagation nodes can verify the comment signature without unwrapping. Replaces the earlier singlepub_post/priv_postper-post keypair. - pub_post_set: list of all
pub_xfor a post's FoF set, in randomized order. Inline in post header. Comments reference entries by index. - revocation_list: author-appended signed entries that tell CDN propagation nodes to drop comments under a named
pub_x. Stops compromised voucher-chains at the propagation layer. - 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 whichV_xa 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+GroupEncryptedremain as-is for named explicit-membership groups. FoF is a separate visibility class, not a replacement.PostVisibilitygains one new variant (FoFClosed, from Layer 3). Mode 2 reusesPostVisibility::Publicwith extendedCommentPolicy.CommentPolicygains one new variant for Layer 2 (Mode 2 comment gating).InlineCommentgetspub_x_index,group_sig,identity_sig, encryptedciphertext, and innervouch_mac(inside the ciphertext). Back-compat via#[serde(default)], same pattern as Phase 2eref_post_id.- Propagation-node accept rule for comments on FoF posts: valid
pub_x_index+ not inrevocation_list+group_sigverifies +identity_sigverifies. Any failure → drop without forwarding. Makes bandwidth-amplification DoS infeasible. control::receive_postgets new verify-gate branches forFoFClosedposts (author_sig + wrap_slots well-formedness) and FoF comments (group_sig verifies againstpub_postfrom 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.
- Bio post (
VisibilityIntent::Profile): Layer 1 adds an optionalvouch_grantsfield carrying an HPKE-sealed per-recipient wrapper batch. Existing bio-post CDN propagation carries vouch distribution — no new control-message type. See Layer 1 for wrapper format and scan policy.
How to read this spec
- Every
TBD — OPUSmarker identifies a crypto specifier / byte layout / test vector that Opus will fill in. Lead decisionsblocks capture commitments that came out of the Scott + Lead Claude conversation; these are not open to further design iteration.Open questionsblocks 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.