From 73b1e24f9a4cb5de1a10292531849b3fe20df289 Mon Sep 17 00:00:00 2001 From: Scott Reimers Date: Wed, 13 May 2026 01:10:43 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20spec=20cleanup=20=E2=80=94=20Layer=205?= =?UTF-8?q?=20wording,=20Layer=203=20banner,=20Layer=206=20superseded?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Layer 5: replace two priv_post references in author-direct fast path with the correct per-V_x CEK + priv_x lookup. Cache/prefilter logic unchanged. Layer 3: replace the "partially superseded" warning banner with a plain scope note explaining the Mode 1/Mode 2 distinction reduces to "body encrypted vs body plaintext"; wrap-slot canonical form lives in Layer 2. Layer 6: mark as superseded by Layer 4. README updated to match. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/fof-spec/README.md | 2 +- docs/fof-spec/layer-3-mode1-fof-closed.md | 6 +++--- docs/fof-spec/layer-5-prefilter-and-cache.md | 4 ++-- docs/fof-spec/layer-6-revocation.md | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/fof-spec/README.md b/docs/fof-spec/README.md index 34f04c8..3343a0c 100644 --- a/docs/fof-spec/README.md +++ b/docs/fof-spec/README.md @@ -47,7 +47,7 @@ Build and ship bottom-up. Each layer is independently shippable and exercised be 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 (400–500 keys × 400–500 slots). -6. **[Layer 6](layer-6-revocation.md) — Revocation & rotation cascades.** Deferred; may not be in v1. Drafted as a stub for design review. +6. **[Layer 6](layer-6-revocation.md) — Revocation & rotation cascades.** Superseded by Layer 4. File retained as a record of alternatives considered. --- diff --git a/docs/fof-spec/layer-3-mode1-fof-closed.md b/docs/fof-spec/layer-3-mode1-fof-closed.md index b74ba89..944692f 100644 --- a/docs/fof-spec/layer-3-mode1-fof-closed.md +++ b/docs/fof-spec/layer-3-mode1-fof-closed.md @@ -1,10 +1,10 @@ # Layer 3 — Mode 1: `FOF_CLOSED` Posts -> **⚠️ Partially superseded by Layer 2 rewrite (2026-04-24).** Layer 2 now defines the canonical wrap-slot structure (dual read/sign derivation), `pub_post_set`, per-`V_x` signing keypair, and CDN-level verification. Layer 3 inherits all of that unchanged — the Mode 1 vs Mode 2 distinction reduces to "body encrypted under CEK (Mode 1) vs body plaintext (Mode 2)." The sections below still reflect the earlier single-keypair design and will be reconciled when Scott + Opus review Layer 3. +**Scope**: New `PostVisibility::FoFClosed` variant. Both post body AND comments are gated to the FoF graph. Body is encrypted under CEK; readership emerges from keyring intersection with `wrap_slots`. -**Scope**: New `PostVisibility::FoFClosed` variant. Both post body AND comments are gated to the FoF graph. Body is encrypted; readership emerges from keyring intersection with `wrap_slots`. +The wrap-slot structure, `pub_post_set`, per-`V_x` signing keypair, and CDN-level verification are all defined in [Layer 2](layer-2-mode2-fof-comments.md) (the canonical form). Layer 3 inherits Layer 2's structures unchanged — the Mode 1 vs Mode 2 distinction reduces to: **body encrypted under CEK (Mode 1) vs body plaintext (Mode 2)**. Wrap-slot read-part still carries the CEK in both modes; in Mode 2 the CEK is used only to derive `CEK_comments`, in Mode 1 it is used for both body and comments. -Builds on Layer 2's `pub_post` / `priv_post` / `wrap_slot` primitives — same structures, just that the CEK encrypting the body is *also* in the wrap slots (alongside `priv_post`). +The legacy `pub_post`/`priv_post` field names that appear in some sections below are retained for readability; semantically read them as the canonical per-V_x `(pub_x, priv_x)` from Layer 2. --- diff --git a/docs/fof-spec/layer-5-prefilter-and-cache.md b/docs/fof-spec/layer-5-prefilter-and-cache.md index 6194a2d..947fe09 100644 --- a/docs/fof-spec/layer-5-prefilter-and-cache.md +++ b/docs/fof-spec/layer-5-prefilter-and-cache.md @@ -18,7 +18,7 @@ This layer is load-bearing, not optional. Without it, a modest keyring × slot m - **Cache the winning `(persona, V_x)` per author.** First time persona `P` decrypts an FoFClosed post from author `A` using `V_x`, remember the tuple. Next post from `A`: try that `(P, V_x)` first. Author almost always re-wraps under the same set. - **Track unreadable posts.** If no key currently held unwraps a post, insert into `vouch_unreadable_posts` for later retry. Clearing this set is cheap and necessary — a newly-received `V_x` potentially unlocks an arbitrary number of old posts. -- **Author-direct fast path.** If `post.author` is one of the reader's persona IDs, the reader is the author and holds `priv_post` implicitly (author-local cache of per-post keypairs). No wrap-slot iteration needed. +- **Author-direct fast path.** If `post.author` is one of the reader's persona IDs, the reader is the author and holds the post's CEK + every `priv_x` directly from creation (author-local cache, keyed by `post_id`). No wrap-slot iteration needed. - **Prefilter tag precompute per-post, not per-feed-fetch.** At ingest time (once per post received), compute the reader's full set of `HMAC(V_x, post_id)[:2B]` tags and note which slots have matching prefilter values. Cache that index. Avoids recomputing HMACs on every re-render. --- @@ -76,7 +76,7 @@ TBD — OPUS: whether this table is worth it. Alternative: keep `wrap_slots` as On incoming FoFClosed post from author `A`: -1. **Author-direct check**: Is `A` in reader's list of personas? If yes → reader authored it; pull `priv_post` from local author cache. Done. +1. **Author-direct check**: Is `A` in reader's list of personas? If yes → reader authored it; pull CEK (and any needed `priv_x`) from local author cache keyed by `post_id`. Done. 2. **Cache lookup**: Query `vouch_unlock_cache` for `(any_persona, A)`. For each cached winning `V_x`: - Compute `prefilter_tag = HMAC(V_x, post_id)[:2B]`. - Find matching slot(s) in post's `wrap_slots`; attempt AEAD-open. diff --git a/docs/fof-spec/layer-6-revocation.md b/docs/fof-spec/layer-6-revocation.md index cea5e37..8104294 100644 --- a/docs/fof-spec/layer-6-revocation.md +++ b/docs/fof-spec/layer-6-revocation.md @@ -1,8 +1,8 @@ # Layer 6 — Revocation & Rotation Cascades -**Status**: Stub. May not be in v1. Drafted for design review only. +**Status**: **Superseded by [Layer 4](layer-4-keypair-rotation.md) (2026-05-13).** Layer 4 settled the revocation and cascade design: V_me rotation as the persona-wide revocation primitive, receiver-chain storage, grandfather-by-default with author-opt-in per-post cascades, and the optional `KeyBurnDiff` primitive for leaked-V_me scenarios. Nothing in Layer 6 below is load-bearing; this file is retained as a record of the alternatives that were considered before Layer 4 was written. -**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. +**Original scope (now resolved by Layer 4)**: 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. ---