| … | … | @@ -2667,6 +2667,52 @@ SessionState's truncate / reorg / named-session machinery. |
| 2667 | 2667 | sets it before `send`); |
| 2668 | 2668 | - JS ↔ keetd over a pipe in both directions: XX, the `handshake` option, keepalive, |
| 2669 | 2669 | timeout, a bad header, a tampered frame, `send`. |
| 2670 | - **4e-3 design (2026-10-04).** |
| 2671 | - **Read in full:** |
| 2672 | - @hyperswarm/secret-stream 6.9.2 (index, lib/handshake, lib/bridge); |
| 2673 | - noise-handshake 4.2.0 (noise, symmetric-state, cipher, hkdf, hmac, dh); |
| 2674 | - noise-curve-ed 2.1.0; |
| 2675 | - sodium-secretstream 1.2.0: a thin wrapper over libsodium's |
| 2676 | `crypto_secretstream_xchacha20poly1305`, so keetd calls libsodium directly and gets |
| 2677 | the same bytes; |
| 2678 | - timeout-refresh 2.0.1; |
| 2679 | - hypercore-crypto 3.7's `namespace`; |
| 2680 | - streamx 2.28.1's Duplex core (index.js 1–1087, lib/errors.js). |
| 2681 | - **User decision: the streamx Duplex CONTRACT only.** |
| 2682 | - streamx's Duplex core is a bit-flag state machine: WritableState / ReadableState |
| 2683 | update loops, the after* callbacks, `destroy` / `_predestroy`, and `newListener`'s |
| 2684 | 'data' / 'readable' / 'drain'. It is ported LITERALLY (the same flags), which is more |
| 2685 | faithful and smaller than re-deriving it. |
| 2686 | - NOT ported: pipe / Pipeline, Transform, `from` / async iterators, `setEncoding`, |
| 2687 | `map`. |
| 2688 | - It runs on an `ss::Loop`: a microtask queue (qmt) plus timers with `refresh` on the |
| 2689 | Clock. |
| 2690 | - **Randomness, injectable (`ss::Random`).** There are four sources: the static key pair |
| 2691 | (`Handshake.keyPair()`), the ephemeral (`curve.generateKeyPair()`), the Push header |
| 2692 | (`init_push`), and the send counter (`randombytes`). |
| 2693 | - The JS harness patches sodium the same way. `init_push` with a fixed header equals |
| 2694 | `init_pull` on that header (libsodium's own derivation). |
| 2695 | - Byte-exact transcripts are compared JS vs keetd. |
| 2696 | - **Sub-milestones, each with its gate and drill:** |
| 2697 | - **4e-3a `ss/noise`:** noise-handshake's whole surface. Patterns NN / NNpsk0 / XX / |
| 2698 | XXpsk0 / IK / XK, psk, prologue, preshared keys; both curves (dh.js X25519 and |
| 2699 | noise-curve-ed Ed25519); HKDF-expand's double `info`; the 65535 cap; "Insufficient |
| 2700 | bytes" / decrypt failures. Gate: vectors from JS with fixed statics and preset |
| 2701 | ephemerals, every pattern both ways, mutated messages. |
| 2702 | - **4e-3b `ss/duplex`:** the streamx Duplex core. Gate: an event-trace harness. One op |
| 2703 | script drives a JS Duplex subclass and keetd's: write / end / push / destroy / pause / |
| 2704 | resume / listeners, plus when `_open` / `_write` / `_read` / `_final` / `_destroy` |
| 2705 | call back. The ordered events and callbacks must be identical. |
| 2706 | - **4e-3c `ss/secret-stream`:** |
| 2707 | - `Handshake`; |
| 2708 | - `NoiseSecretStream` over a Duplex, with a raw-stream interface (write → drained, |
| 2709 | end, destroy, pause / resume, optional setContentSize / send / trySend); |
| 2710 | - Bridge; |
| 2711 | - keepalive / timeout, header verification, the send box, toJSON, the statics. |
| 2712 | |
| 2713 | Gates: vectors (deterministic, byte-exact wire transcripts for XX and the `handshake` |
| 2714 | option), then JS ↔ keetd over a raw pipe in both directions with real randomness, |
| 2715 | plus the destroy and timeout paths. |
| 2670 | 2716 | - **4e-4, `createProtocolStream` / `getProtocolMuxer` / `replicate`.** |
| 2671 | 2717 | - The four `isInitiator` forms: a muxer, an existing stream, `opts.stream`, or a bool |
| 2672 | 2718 | → a new NoiseSecretStream. |