itsgoin/sessions.md
Scott Reimers 971766cb3c docs: Layer 4 — rotation, revocation, key lifecycle
Captures the decisions from the Layer 4 conversation with Scott:

Default narrowing on a single post = Layer 2 revocation (existing).
Advanced narrowing of read access = full re-issue with optional
supersedes_post_id link (network-heavy, opt-in).

V_me rotation = the persona-wide revocation primitive. Generate new
V_me, distribute to non-revoked vouchees via next bio-post batch.
Receiver-chain model: receivers append new V_me alongside old (not
overwrite). Trial-unwrap iterates the chain.

Grandfather by default: CDN is V_me-blind, so rotation does NOT
auto-cascade comment deletions. Revoked vouchee retains comment
authority on old posts unless author opts to cascade per-pub_x
revocations.

Per-post cascade is opt-in. Local-only own_post_slot_provenance
table lets author query "which pub_x's in my posts were sealed
under V_me_old?" and publish per-pub_x RevocationEntries.

New optional KeyBurnDiff primitive (signed header-diff) swaps a
V_me_old wrap_slot for a V_me_new one in-place on a specific post.
For the leaked-V_me scenario. Body CEK unchanged.

Skeleton's PostKeyRotation record removed entirely.

Layer 1 updated: rotation is append-only at receivers; pointer to
Layer 4. Multi-epoch bio-post-batch toggle hook added.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 01:07:04 -04:00

234 lines
18 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.

# Contributor Sessions Log
Rolling log of active sessions on the ItsGoin repository. Every contributor — Lead or Jr — appends an entry on session start and updates it on session end. Newest entries at the top.
See `CONTRIBUTING.md` for the protocol. See `AGENTS.md` for the Claude-specific session-start checklist.
---
## 2026-04-24 — primary Claude (Lead) — `docs/fof-spec-layer1-bio-grants`
**Started**: April 24 UTC
**Instance**: Scott's primary Claude (Lead)
**Issue**: none (spec refinement)
**Branch**: `docs/fof-spec-layer1-bio-grants`
**Scope**: Fold Scott + Opus's Layer 1 design answer into the spec. Vouch distribution moves from DM-wrapped `VouchGrant` to HPKE-sealed per-recipient wrappers carried in the voucher's bio post, leveraging existing bio-post CDN propagation and HPKE (RFC 9180) key privacy for recipient anonymity.
**Key design commitments added to Layer 1**:
- HPKE RFC 9180 (DHKEM X25519 + HKDF-SHA256 + ChaCha20Poly1305) for per-recipient wrappers; one ephemeral pubkey per batch; 48B per wrapper.
- HKDF `info = "itsgoin/vouch-grant/v1/" || bio_post_id` — recipient-free (non-negotiable for key privacy).
- No prefilter tag on grants (no prior shared secret); full X25519 trial at ~60µs per wrapper per persona is tolerable (≤90ms even at 512×3 worst case).
- Scan policy: auto-scan bio posts of followed personas; manual "check bio" gesture for non-followed; scan cache keyed by `(scanner_persona, bio_author, bio_epoch)`.
- Bucket-padding (64/128/256/512) and per-publish wrapper shuffle for size/position opacity.
- No separate `vouches_issued` table on the wire; bio post IS the authoritative record. Local-only `own_vouch_targets` tracks what the persona has granted.
- Incremental grant-as-comment path (Scott's suggestion for avoiding full republish) deferred; v1 ships with full republish per change.
**Completed**:
- Rewrote `docs/fof-spec/layer-1-vouch-primitive.md` end-to-end.
- README updated: Layer 1 scope line + added bio-post integration bullet.
- Self-merged to master.
**Pending**:
- Opus confirmation passes still open on other layers (WrapSlot byte layout, AEAD choice for body, padding schemes).
- Layer 26 untouched in this pass.
**Stopping point**: Scott asked to hold merges until Layers 26 iterations complete. Branch stays open locally and on Forgejo; continuing to stack commits on it.
### Update 2026-04-24 — Layer 2 rewrite (CDN-level verification)
**Scope**: Scott shared Opus's Layer 2 design answer. Folded in.
**Design commitments added**:
- **Per-`V_x` signing keypair `(pub_x, priv_x)`** — replaces single per-post `pub_post/priv_post`. CDN can now verify comment signatures against a published `pub_post_set` before forwarding, killing the bandwidth-amplification DoS an admitted FoF member could otherwise mount.
- **Dual-derivation wrap slot**: `read_slot → CEK`, `sign_slot → priv_x`. One unwrap yields both capabilities. Slot structure is shared with Layer 3 (canonical form lives here).
- **Comment body encrypted under `CEK_comments = HKDF(CEK, "comments")`** — Mode 2 comments are genuinely FoF-read-gated now, not just FoF-sign-filtered at render (strengthening vs skeleton).
- **Propagation-node four-check accept rule**: valid `pub_x_index`, not in `revocation_list`, `group_sig` verifies, `identity_sig` verifies. Any fail → drop without forwarding.
- **Author-signed revocation diff** appended to post header; CDN honors on next sync. Per-chain revocation at propagation layer.
- **`pub_x_index` is a per-post pseudonym** — leaks "these N comments came through the same chain" within a single post; re-randomizes across posts. Accepted tradeoff for CDN-level DoS resistance.
- **v1 ships Ed25519 inline** (~77KB header at 500 vouchees). **PQ future** requires Merkle-commit over `pub_post_set` with per-comment inclusion proofs; deferred but spec shape doesn't preclude.
**Files touched**:
- `docs/fof-spec/layer-2-mode2-fof-comments.md` — rewritten end-to-end.
- `docs/fof-spec/layer-3-mode1-fof-closed.md` — prominent "partially superseded" banner added; body retained pending reconciliation when Scott + Opus review Layer 3.
- `docs/fof-spec/README.md` — glossary updated (`pub_x`/`priv_x`, `pub_post_set`, `revocation_list`); integration bullet updated for new `InlineComment` fields + CDN accept rule.
**Open questions I raised back to Scott** (awaiting his answer before finalizing):
1. `(pub_x, priv_x)` lifecycle: generated at `V_x` genesis (Layer 1) and stable across posts, vs regenerated per-post by author. Lead leaning per-post. Needs confirmation.
2. `pub_post_set` padding vs `wrap_slots` padding — real/dummy alignment when dummies shouldn't be indexable by `pub_x_index`.
3. Non-FoF rendering of comment count (reveal engagement? suppress?).
4. Who holds `priv_me` (author) — generated alongside `V_me` at Layer 1, vs per-post regeneration. Same as #1 but for author's own entry.
**Pending**:
- Scott reviews / answers open questions.
- Layer 3 reconciliation when Scott + Opus get to Mode 1.
- Layers 46 iterations.
**Stopping point**: commit `b8b38a6` (Layer 1) + new commit for Layer 2 both on branch; not merged. Awaiting Scott.
### Update 2026-05-13 — Layer 4 written (rotation + revocation + key lifecycle)
Iterative session with Scott. Recap of where the model landed:
**Rotation/revocation model (now in spec)**:
- Default narrowing of comment authority on a post = Layer 2 revocation (existing mechanism). No new wire primitive.
- Advanced narrowing of read access = full re-issue with `supersedes_post_id` link. Discouraged due to network overhead.
- `V_me` rotation = the persona-wide revocation primitive. Generate new V_me, distribute via next bio-post batch to non-revoked vouchees only. Revoked person retains old V_me.
- Receiver-chain model: receiver appends new V_me to `vouch_keys_received` (does NOT overwrite). Trial-unwrap iterates the chain. UX-wise the "current" key is the newest; older epochs are archived but kept for historical decrypts.
- **Grandfather-by-default**: CDN is V_me-blind, so rotation does NOT auto-cascade comment deletion. Revoked vouchee keeps comment authority on old posts unless the author opts to cascade per-pub_x revocations.
- **Per-post cascade is opt-in**: author can query a local `own_post_slot_provenance` table to find pub_x's sealed under V_me_old in any of their posts, then publish per-pub_x RevocationEntries to cascade.
- **Key-burn primitive (new, optional)**: signed `KeyBurnDiff` swaps an old wrap_slot for a new one in-place on a specific post. Used when V_me leaked and the author wants to scrub it from the CDN copy of old posts. Body CEK unchanged; affects future fresh-decrypts only.
**Cryptographic stack confirmed (Scott reconfirmed)**:
- Body encryption: symmetric ChaCha20-Poly1305 under CEK. PQ-safe.
- Wrap_slots: AEAD under V_x. PQ-safe.
- Comment signing: **asymmetric Ed25519** (per-V_x per-post `(pub_x, priv_x)`). NOT PQ-safe; ML-DSA-65 migration deferred. Scott confirmed the asymmetric-for-signing tradeoff is intentional — it's what makes CDN-level bandwidth-DoS filtering work.
**Files touched in this round**:
- `docs/fof-spec/layer-4-keypair-rotation.md`: full rewrite from skeleton.
- `docs/fof-spec/layer-1-vouch-primitive.md`: rotation language updated to point at Layer 4's append-only model; multi-epoch UI hook added.
**Branch state**: `docs/fof-spec-layer1-bio-grants` (despite the name, holds all Layer 14 spec work). Commit pending. Not merged per Scott's standing instruction.
**Pending**:
- Layer 5 (unlock cache + prefilter): existing skeleton text still reflects single per-post keypair model. Needs reconciliation with per-V_x model from Layer 2.
- Layer 3 (Mode 1): partially-superseded banner still present. Needs Scott/Opus reconciliation pass.
- Layer 6 (revocation): stub still. Largely obviated by Layer 4 work.
### Update 2026-04-24 — Layer 3 round 2 (last two open questions)
Two follow-up questions resolved:
- **Access-grant slot ordering**: **append at tail** (not re-shuffle). I'd initially overcorrected to a "switch comments from index to pub_x bytes so shuffles are free" change; Scott reverted that and clarified the choice. Append-at-tail preserves `pub_x_index` stability across the post's lifetime — already-stored comments stay verifiable, no write amplification on grant. Accepted positional-recency leak (tail = newest grants).
- **Minimum slot bucket**: **8**. Singleton/tiny-set posts pad up to 8 slots. Brand-new personas don't publish "I have no vouchees" headers.
**Files touched**:
- `docs/fof-spec/layer-2-mode2-fof-comments.md`: access-grant lead decision made explicit about append-at-tail and index stability.
- `docs/fof-spec/layer-3-mode1-fof-closed.md`: minimum-8 floor added to padding lead decision; both open questions moved to Resolved.
- `sessions.md`: this entry.
### Update 2026-04-24 — Layer 3 round 1 + cross-cutting padding rule (corrected)
Scott talked to Opus and resolved Layer 3 open questions + introduced a unified padding rule that supersedes Layer 2 round 2's `rand(32..=128)`.
**First-pass misread (corrected by Scott):** I initially wrote the rule as "power-of-2 up to 256, then `real + rand(0..=256)` above." That's wrong — the rule is **bucketed throughout**, not random above the threshold.
**Bucketed padding rule (applies to both slot count and body size)**:
- ≤256 real units → next power-of-2 bucket (8, 16, 32, 64, 128, 256).
- >256 real units → next linear-step bucket: +128 step for slots (384, 512, 640, …), +256KB step for body bytes (512KB, 768KB, 1024KB, …).
- Deterministic. Author publishes `next_bucket(real)`; dummies fill the gap.
Why this is stronger than random: observers learn the bucket but never the position within it. Across multiple posts from the same author, the bucket is stable until the author crosses a boundary — so no "min over many posts" attack converges tighter than the bucket bound. Random padding would have leaked `min(observed) - max_noise` as a floor.
Linear-step above 256 vs pure power-of-2: avoids the 2× waste of jumping 256→512 for an author with 257 vouchees. Above 256, step buckets are 128 (slots) or 256KB (body) so worst-case in-bucket overhead is bounded (~33% at the worst spot).
Applies uniformly to slot count and body size.
**Other resolved Layer 3 questions**:
- Custom mode UI deferred. v1 ships three presets only: Public / Friends-only / FoF.
- Slot dedup at `V_x` byte level. One slot per unique key.
- Body-length padding adopted.
**Files touched**:
- `docs/fof-spec/layer-3-mode1-fof-closed.md`: Lead decisions updated (hybrid padding, dedup, three-preset UI); open questions split into still-open + Resolved; ship criteria updated.
- `docs/fof-spec/layer-2-mode2-fof-comments.md`: padding rule promoted to hybrid scheme; size budget rewritten with three regime examples; privacy section rewritten for two-regime analysis; Resolved bullet superseded with pointer.
- `sessions.md`: this entry.
**Still-open Layer 3 questions worth flagging to Scott**:
1. Access-grant ordering — does appending a new slot re-shuffle the full `wrap_slots` / `pub_post_set` (preserves the random-order privacy property but invalidates `pub_x_index` values in already-stored comments), or is it append-only (`pub_x_index` is stable but tail-positional leak says "these are recent grants")? Lead leaning: **append-only**; index stability matters for revocation and stored-comment verification.
2. Minimum slot-count floor for tiny authors. Power-of-2-of-1 = 1, which leaks "this persona has one vouch (probably just themselves)." Lead leaning: minimum bucket of 8.
**Pending**: Layers 46 iterations. Scott to confirm two flagged questions.
### Update 2026-04-24 — Layer 2 round 2 (Scott answers all 5 questions)
Scott resolved all five open questions:
1. **Per-post `(pub_x, priv_x)`** — confirmed.
2. **Random-count dummy padding** (`rand(32..=128)`) replaces power-of-2 buckets, with dummy pubkeys in `pub_post_set` so `.len() == wrap_slots.len()`. Across multiple posts from the same author, an observer cannot even establish a reliable floor for the real vouch-set size.
3. **Non-FoF comment UX**: "Comments are private" affordance with optional "Request access via DM" button. No count leak.
4. **Author's own entry in `pub_post_set`** — confirmed.
5. **Revocation is retroactive delete + forward.** File-holders delete locally-stored comments signed by the revoked `pub_x`, then propagate the diff. Stronger than stop-forwarding — prior garbage is cleaned up as the diff sweeps the mesh.
**New primitive**: **access-grant author comment**. Author can retroactively widen a post's read-set by publishing an author-signed special comment appending a new `WrapSlot` + `pub_post_set` entry. Lets a newly-vouched persona gain read + comment access without republishing the whole post. Answers the "non-FoF requests access via DM, author approves" UX loop.
**Files touched**:
- `docs/fof-spec/layer-2-mode2-fof-comments.md` — updated Lead decisions, post-header, revocation flow (retroactive), added Access-grant author comment section, updated Privacy tradeoff (size-leak analysis with random padding), Open questions split into unresolved + Resolved, size budget, ship criteria.
- `sessions.md` — this entry.
Commit pending.
---
## 2026-04-23 — primary Claude (Lead) — `docs/fof-spec-skeleton`
**Started**: late April 23 UTC
**Instance**: Scott's primary Claude (Lead role)
**Issue**: none (spec-drafting work; hand-off to Opus for crypto fill-in)
**Branch**: `docs/fof-spec-skeleton`
**Scope**: Skeleton spec for Friend-of-Friend (FoF) post gating. Lays out the per-person vouch-key (`V_me`) primitive, four visibility levels (Public / Friends-only / FoF / Custom), Mode 1 (`FOF_CLOSED`) and Mode 2 (public post + FoF comments), and a six-layer implementation plan. Crypto byte layouts and algorithm specifics are marked `TBD — OPUS` for Opus to fill in.
**Completed in this session**:
- `docs/fof-spec/README.md` — top-level overview, user-facing model, design properties, layering plan, out-of-scope, glossary, integration with existing primitives.
- `docs/fof-spec/layer-1-vouch-primitive.md``V_x` keys, per-persona keyring, `VouchGrant` wire format (DM-wrapped).
- `docs/fof-spec/layer-2-mode2-fof-comments.md``CommentPolicy::FriendsOfFriends`, `pub_post` / `priv_post` / wrap-slot primitives, `group_sig` + `vouch_mac` on comments.
- `docs/fof-spec/layer-3-mode1-fof-closed.md``PostVisibility::FoFClosed`, wrap-slot byte layout, anonymous 2B prefilter, power-of-2 slot padding.
- `docs/fof-spec/layer-4-keypair-rotation.md``PostKeyRotation` record, explicit `pub_post_index` on comments, per-post re-gating.
- `docs/fof-spec/layer-5-prefilter-and-cache.md``vouch_unlock_cache`, `vouch_unreadable_posts`, author-direct fast path, keyring-change retry sweep.
- `docs/fof-spec/layer-6-revocation.md` — stub; candidate designs AD; Lead leaning is coarse-rotation with UX polish (Candidate D); revisit after 30 days of production data.
**Pending after this PR merges**:
- Opus review pass: fill in `TBD — OPUS` markers (AEAD specifier, key sizes, WrapSlot byte layout, prefilter tag algorithm confirmation, epoch granularity, etc.).
- Lead re-review after Opus fills in crypto.
- Per-layer branch schedule for implementation (Layer 1 ships first, independently exercised).
**Stopping point**: session ending after Lead self-merges `docs/fof-spec-skeleton` to master. Branch to be deleted locally + remote.
---
## 2026-04-23 — primary Claude (Lead) — `chore/workflow-adoption`
**Started**: late April 23 UTC
**Instance**: Scott's primary Claude (Lead role per `feedback_senior_role.md`)
**Issue**: none yet (inaugural PR; this is the chicken-and-egg case noted in CONTRIBUTING.md)
**Branch**: `chore/workflow-adoption`
**Scope**: Introduce the multi-contributor workflow to the repo. Creates `CONTRIBUTING.md`, `AGENTS.md`, and this file (`sessions.md`) with a seed entry.
**Pre-existing state at session start**:
- v0.6.2 shipped end-to-end earlier today: APK, AppImage, CLI, anchor (PID 3475521, up since ~17:39 UTC).
- Last merged-to-master commit before this branch: `2ce668a` — People-tab rewrite (recency sort, profile-post Discover, bio modal, ignore primitive, per-author feed filter).
- Active artifacts on `itsgoin.com/public_html/`: `itsgoin-0.6.2.apk` (183.7 MB), `itsgoin_0.6.2_amd64.AppImage` (177.7 MB), `itsgoin-cli-0.6.2-linux-amd64` (40.8 MB).
- No other contributors active.
**Completed in this session (before this commit)**:
- Designed the Lead role + amendments to the base workflow (hotfix carve-out, partnered build/deploy go-no-go, Lead-pulls-queue review pattern, re-evaluation triggers, Scott's contributor-alignment role, Lead self-merge authority).
- Saved `feedback_senior_role.md` to instance memory.
- Wrote `CONTRIBUTING.md`, `AGENTS.md`, `sessions.md` (this file). Originally drafted the agent guide as `CLAUDE.md` but that filename is `.gitignore`d at the repo root because it has historically been a credential-leak vector; switched to `AGENTS.md` with an explicit security banner.
- Iterated on Scott's role: initial draft put him in the PR-routing / build-authorizer position; revised to watch contributor alignment + partner on ship, Lead self-merges.
**Pending after this PR merges**:
- Phase 0 prereqs from `CONTRIBUTING.md`: Forgejo CI (`cargo check --workspace` + `cargo test -p itsgoin-core` on push + PR), branch protection on master (require PR + 1 review + green CI), second Forgejo account + SSH key for Jr Claude(s). Scott coordinates.
**Stopping point**: session ending — Lead self-merged this PR to master (inaugural exercise of the self-merge authority the PR itself establishes, per Amendment 6). Branch `chore/workflow-adoption` deleted locally + remote.
---
<!-- New entries go above this line, newest first. Template:
## YYYY-MM-DD — <instance> (<role>) — `<branch-name>`
**Started**: <time UTC>
**Instance**: <identifier>
**Issue**: #<num> <title>
**Branch**: <branch-name>
**Scope**: <one-line summary>
**Completed**:
- <point>
**Pending / blockers**:
- <point>
**Stopping point**: <exactly where work was left>
---
-->