Design doc §28: update to reflect v0.6.0 shipped state

Mostly a framing refresh — the underlying architecture description
matches what v0.6.0 actually does. Per-subsection status badges so a
reader can see at a glance what's shipped, what's partial, and
what's deferred:

- §28.1 two-layer identity: Shipped
- §28.2 persona types: Shipped (minus ephemeral — folded into §28.4)
- §28.3 multi-device: Partial (export/import works; no QR linking UX)
- §28.4 ephemeral rotating DM IDs: Deferred + connection-model
  rationale written out
- §28.5 CDN holder sets: Shipped, with drop-migration note
- §28.6 DM privacy: Shipped (2 of 3 mechanisms; comment-as-intro
  machinery exists but no dedicated UX yet)
- §28.7 user-facing: split into Shipped and Not-yet items
- §28.8 key safety: Shipped (unchanged content)
- §28.9 phase-by-phase rollout: each phase tagged Shipped v0.6.0
  except Phase 6 (Deferred)

Final paragraph replaces the old "beta and stable are separate
networks" language with the hard-fork framing and a link to the
upgrade path on the download page.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Scott Reimers 2026-04-22 13:59:29 -04:00
parent d0d055839a
commit b789ab5a19

View file

@ -1704,63 +1704,66 @@ END</code></pre>
<!-- 28. Identity Architecture (Planned) --> <!-- 28. Identity Architecture (Planned) -->
<section id="identity-architecture"> <section id="identity-architecture">
<h2>28. Identity Architecture <span class="badge badge-planned">Planned</span></h2> <h2>28. Identity Architecture <span class="badge badge-partial">Mostly Shipped (v0.6.0)</span></h2>
<p>The 0.6.x beta line introduces a separation between <strong>network identity</strong> (per-device routing/connection key) and <strong>posting identity</strong> (the face/persona authoring content). This is the architectural foundation for multi-device, multi-persona, and DM-level traffic-graph privacy.</p> <p>v0.6.0 separates <strong>network identity</strong> (per-device routing/connection key) from <strong>posting identity</strong> (the face/persona authoring content). This is the architectural foundation for multi-device, multi-persona, and DM-level traffic-graph privacy. Five of the original six phases shipped as v0.6.0; ephemeral rotating DM IDs (§28.4) are deferred pending connection-model design work.</p>
<h3>28.1 Two layers of identity</h3> <h3>28.1 Two layers of identity <span class="badge badge-complete">Shipped</span></h3>
<p>Each device has ONE network key — used for QUIC connections, endpoint ID, mesh routing. It's never linked on the wire to any posting key.</p> <p>Each device has ONE network key — used for QUIC connections, endpoint ID, mesh routing. It's never linked on the wire to any posting key.</p>
<p>Each user can hold MANY posting keys simultaneously — no "active" persona, no switching. Each posting key is a persona (Public, Private, Work, Family, per-conversation ephemeral, etc). Posts are signed with the posting key chosen at compose time.</p> <p>Each user can hold MANY posting keys simultaneously — no "active" persona, no switching. Each posting key is a persona (Public, Private, Work, Family, etc). Posts are signed with the posting key chosen at compose time. On first launch after upgrading, the existing identity becomes the default posting key (same bytes as the network key for upgraders, so all prior signed content keeps verifying).</p>
<p><strong>Privacy invariant:</strong> peers cannot determine which network IDs belong to a given posting ID, which posting IDs belong to the same network ID, or which posting IDs belong to the same user. These associations are private to the device owner.</p> <p><strong>Privacy invariant:</strong> peers cannot determine which network IDs belong to a given posting ID, which posting IDs belong to the same network ID, or which posting IDs belong to the same user. These associations are private to the device owner.</p>
<h3>28.2 Persona types</h3> <h3>28.2 Persona types <span class="badge badge-complete">Shipped</span></h3>
<ul> <ul>
<li><strong>Public posting IDs</strong> — main persona(s), openly associated with "you"</li> <li><strong>Public posting IDs</strong> — main persona(s), openly associated with "you"</li>
<li><strong>Private posting IDs</strong> — smaller-context personas for close contacts or specific groups</li> <li><strong>Private posting IDs</strong> — smaller-context personas for close contacts or specific groups</li>
<li><strong>Contextual / ephemeral posting IDs</strong> — per-relationship, per-thread, or one-off; auto-generated and isolated</li> <li><strong>Contextual posting IDs</strong> — per-relationship or per-group; user creates one per context and sticks with it</li>
</ul> </ul>
<h3>28.3 Multi-device is a special case</h3> <h3>28.3 Multi-device is a special case <span class="badge badge-partial">Partial</span></h3>
<p>"Two devices holding the same posting key" is a trivial case of the multi-key model. Link happens out-of-band between the user's own devices (QR / file / copy-paste bundle). The network sees no cross-device message announcing the relationship. Each device pulls content for its posting IDs via the normal CDN — the fact that two network nodes hold the same posting key is only discoverable if an observer has private knowledge (which they shouldn't).</p> <p>"Two devices holding the same posting key" is a trivial case of the multi-key model. Link happens out-of-band between the user's own devices via the existing identity export/import bundle — the export includes all posting identities so they restore on the second device. The network sees no cross-device message announcing the relationship; each device pulls content for its posting IDs via the normal CDN. The fact that two network nodes hold the same posting key is only discoverable if an observer has private knowledge (which they shouldn't).</p>
<p>In-app QR / file-share flows for easy linking between an existing device and a new one are not wired yet — today it's manual export-then-import.</p>
<h3>28.4 Ephemeral rotating IDs for DM threads</h3> <h3>28.4 Ephemeral rotating IDs for DM threads <span class="badge badge-planned">Deferred</span></h3>
<p>DM threads and group messages use per-thread unique posting IDs that rotate per message. Each encrypted message includes a handshake field — the next posting ID to use. Observer sees a stream of distinct posting IDs with no cryptographic tie between them, defeating thread-level traffic correlation.</p> <p>The intent: DM threads and group messages use per-thread unique posting IDs that rotate per message. Each encrypted message would include a handshake field — the next posting ID to use. Observer sees a stream of distinct posting IDs with no cryptographic tie between them, defeating thread-level traffic correlation. Desync recovery via a sliding window of the last N accepted IDs. Message history kept in a local encrypted-to-self archive that replicates across linked devices via self-follow.</p>
<p>Desync recovery: receivers accept messages signed by any of the last N ephemeral IDs (sliding window). Message history (which can't be searched by rotating ID) is kept in a local <strong>encrypted-to-self archive</strong> — the archive is implemented as normal encrypted posts with recipient = user's own archive persona, replicating across the user's linked devices via self-follow.</p> <p><strong>Why deferred:</strong> the current connection model is per-network-ID (peers establish QUIC sessions keyed by stable NodeId; holder-set learning assumes durable addressable authors). Ephemeral authors that come into existence per-thread need a crisper design for how a freshly generated posting ID becomes reachable fast enough to receive replies before the next rotation. The pieces needed to implement rotation (per-file holder sets, merged pull with recipient-match, per-posting-ID signing) are all in place — what's missing is the cold-contact discovery semantics for ephemeral recipients.</p>
<h3>28.5 CDN restructure: per-file holder sets</h3> <h3>28.5 CDN: per-file holder sets <span class="badge badge-complete">Shipped</span></h3>
<p>The current upstream/downstream tree (which assumed a single author network endpoint) is replaced by a flat per-file holder set with header-diff propagation.</p> <p>The earlier upstream/downstream tree (which assumed a single author network endpoint) is replaced by a flat per-file holder set with header-diff propagation.</p>
<p>Each file (post, blob, manifest) has its own holder set. Each holder tracks up to 5 peers it recently interacted with about that specific file. When a new post is created, the creator updates the headers of recent prior posts (their manifests now reference the new post), then pushes the header diff to the up-to-5 known holders of each updated prior post. Recipients apply the header diff, see the reference to the new post, and pull it through normal sync.</p> <p>Each file (post, blob, manifest) has its own holder set (up to 5 most-recent peers, LRU-capped). When a new post is created, the creator updates the headers of recent prior posts (their manifests now reference the new post), then pushes the header diff to each updated prior post's holder set. Recipients apply the header diff, see the reference to the new post, and pull it through normal sync.</p>
<p>Notifications thus route via network-ID peers who happen to hold related files — not via any tree rooted at the author. Content always arrives via pull, never pushed directly.</p> <p>Notifications thus route via network-ID peers who happen to hold related files — not via any tree rooted at the author. Content always arrives via pull, never pushed directly. Four legacy tables (<code>post_upstream</code>, <code>post_downstream</code>, <code>blob_upstream</code>, <code>blob_downstream</code>) were dropped in v0.6; a one-way migration seeds <code>file_holders</code> from them on first launch.</p>
<h3>28.6 DM privacy model</h3> <h3>28.6 DM privacy model <span class="badge badge-complete">Shipped</span></h3>
<p>Three complementary mechanisms eliminate the "A messaged B" traffic signal:</p> <p>Two of the three originally-planned mechanisms shipped in v0.6.0 and eliminate the "A messaged B" traffic signal for follower-to-follower and cold-contact-via-pull scenarios:</p>
<ol> <ol>
<li><strong>CDN-only propagation.</strong> Direct <code>PostPush</code> for encrypted posts is removed. All encrypted posts propagate via the file-holder CDN, indistinguishable from any other encrypted content.</li> <li><strong>CDN-only propagation.</strong> Direct <code>PostPush</code> for encrypted posts is removed. All encrypted posts propagate via the file-holder CDN, indistinguishable on the wire from any other encrypted content.</li>
<li><strong>Merged pull + recipient-match.</strong> Pull sync's query is extended so peers return posts matching <code>author &isin; query_list</code> OR <code>recipient &isin; wrapped_keys</code>. Client always includes its own NodeId alongside follows. The search pattern is indistinguishable from routine pull — no "searching for DMs" traffic fingerprint.</li> <li><strong>Merged pull + recipient-match.</strong> Pull sync's query is a uniform list of NodeIds; server returns posts matching <code>author &isin; query_list</code> OR <code>wrapped_key.recipient &isin; query_list</code>. Client always includes its own NodeId alongside follows. No distinguishable "searching for DMs" traffic fingerprint — the query looks identical to a routine follow-pull.</li>
<li><strong>Comment-as-introduction for cold contact.</strong> Any public post with open comments serves as a message-request surface. Comments flow via engagement diffs through the post's holder network, reaching the author's linked devices via normal pull.</li> <li><strong>Comment-as-introduction for cold contact.</strong> Any public post with open comments serves as a message-request surface. The machinery exists (engagement diffs flow through the post's holder network); a dedicated UX affordance for "start a conversation by commenting" is still on the todo list.</li>
</ol> </ol>
<h3>28.7 What the user sees</h3> <h3>28.7 What the user sees</h3>
<ul> <ul>
<li>One merged incoming feed (all content to all personas), with filter-by-persona pills</li> <li>One merged incoming feed (all content to all personas), with filter-by-persona pills <span class="badge badge-complete">Shipped</span></li>
<li>Reply/comment defaults to the persona whose key decrypted the post (override available)</li> <li>Per-post "(you) as &lt;persona&gt;" label on own posts authored by a non-default persona <span class="badge badge-complete">Shipped</span></li>
<li>Persona picker only appears at post-creation time, with contextual defaults</li> <li>Persona picker at post-creation time, auto-hidden with a single persona <span class="badge badge-complete">Shipped</span></li>
<li>DMs to different personas appear as distinct conversation threads in the inbox</li> <li>Settings &rarr; Personas to create, delete, and set default <span class="badge badge-complete">Shipped</span></li>
<li>"New conversation with Alice" can offer a fresh per-thread ephemeral ID</li> <li>Reply/comment default persona = whichever key decrypted the post <span class="badge badge-planned">Not yet</span></li>
<li>Contextual compose defaults (e.g., posting to a circle auto-picks that circle's last-used persona) <span class="badge badge-planned">Not yet</span></li>
<li>DMs to different personas as distinct inbox threads <span class="badge badge-planned">Not yet</span></li>
</ul> </ul>
<h3>28.8 Key/collision safety</h3> <h3>28.8 Key/collision safety <span class="badge badge-complete">Shipped</span></h3>
<p>Posting keys and network keys are ed25519 seeds (256 bits of entropy). Birthday paradox reaches 50% collision at ~2<sup>128</sup> keys generated — not a concern even at aggressive rotation rates across a global userbase. The operational risk is weak RNG during key generation; we rely on the platform CSPRNG everywhere.</p> <p>Posting keys and network keys are ed25519 seeds (256 bits of entropy). Birthday paradox reaches 50% collision at ~2<sup>128</sup> keys generated — not a concern even at aggressive rotation rates across a global userbase. The operational risk is weak RNG during key generation; we rely on the platform CSPRNG everywhere.</p>
<h3>28.9 Phased rollout</h3> <h3>28.9 Rollout status</h3>
<ol> <ol>
<li><strong>Phase 1</strong> — Remove direct <code>PostPush</code> for encrypted posts (keeps existing CDN tree). Encrypted DMs propagate via ManifestPush like any other content.</li> <li><strong>Phase 1</strong> <span class="badge badge-complete">Shipped v0.6.0</span> — Direct <code>PostPush</code> for encrypted posts removed. Encrypted DMs propagate via ManifestPush like any other content.</li>
<li><strong>Phase 2</strong> — File-holder model + header-diff propagation replaces upstream/downstream. Diverse lateral holder networks per file.</li> <li><strong>Phase 2</strong> <span class="badge badge-complete">Shipped v0.6.0</span> — File-holder model + header-diff propagation replaces upstream/downstream. Legacy tables dropped with a one-way seed migration.</li>
<li><strong>Phase 3</strong> — Merged pull + recipient-match search. DM search becomes indistinguishable from follow-pull.</li> <li><strong>Phase 3</strong> <span class="badge badge-complete">Shipped v0.6.0</span> — Merged pull + recipient-match search. DM search indistinguishable from follow-pull. <code>post_recipients</code> index added.</li>
<li><strong>Phase 4</strong> — Posting-key / network-key split. Local-only linked-devices hints. Multi-persona plumbing.</li> <li><strong>Phase 4</strong> <span class="badge badge-complete">Shipped v0.6.0</span> — Posting-key / network-key split plumbing. <code>posting_identities</code> table; seed migration copies network key in as default persona for upgraders.</li>
<li><strong>Phase 5</strong> — Multi-persona UX.</li> <li><strong>Phase 5</strong> <span class="badge badge-complete">Shipped v0.6.0</span> — Multi-persona UX (Settings page, compose picker, feed filter pills, per-post persona labels).</li>
<li><strong>Phase 6</strong> — Ephemeral rotating IDs for DM threads + local self-archive.</li> <li><strong>Phase 6</strong> <span class="badge badge-planned">Deferred</span> — Ephemeral rotating IDs for DM threads + local self-archive. See §28.4 for the open design question.</li>
</ol> </ol>
<p><strong>Beta and stable are separate networks.</strong> The 0.6.x beta line does not interoperate with 0.5.3 stable. This is an explicit simplification — no dual-writing legacy tables, no mixed-version wire handlers, no cross-network testing matrices. Users can cross tracks via the existing export/import identity bundle.</p> <p><strong>v0.6 is a hard network fork from v0.5.</strong> The two versions do not interoperate. This was an explicit simplification once the user base was small enough to migrate everyone directly — no dual-write tables, no legacy message handlers, no mixed-network testing. Users cross via the existing export/import identity bundle; see the <a href="download.html">download page</a> for the upgrade path.</p>
</section> </section>
<!-- Appendix A: Timeouts --> <!-- Appendix A: Timeouts -->