| … | … | @@ -2734,6 +2734,31 @@ SessionState's truncate / reorg / named-session machinery. |
| 2734 | 2734 | the timer is due depends on the clock). Flush with `setImmediate`. |
| 2735 | 2735 | - **Divergence:** microtasks and callbacks hold a stream WEAKLY (an alive token). JS |
| 2736 | 2736 | would keep an unreferenced stream alive until they ran. |
| 2737 | - **4e-3c design (2026-10-04).** |
| 2738 | - **Built on ss/duplex + ss/noise.** |
| 2739 | - `NoiseSecretStream : ss::Duplex`, written line for line from index.js. |
| 2740 | - The raw stream is any `ss::RawStream : ss::Duplex`, whose optional |
| 2741 | `setContentSize` / `send` / `trySend` / `flush` / `toJSON` say whether they exist. |
| 2742 | - `Bridge` + `ReversePassThrough` are ss::Duplex subclasses (lib/bridge.js). |
| 2743 | - `Handshake` (lib/handshake.js) runs over `ss::NoiseState` with ed25519. |
| 2744 | - **Timers:** timeout-refresh's `Timer.once` / `Timer.on`, plus refresh / destroy / |
| 2745 | unref, sit behind an injectable `ss::Timers`. |
| 2746 | - **Randomness:** everything goes through `ss::randombytes`. That covers the static |
| 2747 | and ephemeral key pairs (a random Ed25519 seed, then seed_keypair), the Push header |
| 2748 | (24 random bytes, then init_pull's derivation: libsodium's init_push does exactly |
| 2749 | this), and the send counter (8 bytes). |
| 2750 | - **The deterministic trace gate** (`tools/ss/secret-stream-trace.{js,cpp}`): a pair of |
| 2751 | streams over bridges, or one stream with a scripted raw peer. In JS, sodium's |
| 2752 | `crypto_sign_keypair` / `init_push` / `randombytes_buf` draw from a seeded |
| 2753 | generator, and timeout-refresh's statics are replaced by fake timers that script ops |
| 2754 | fire; keetd uses the same generator and timers. The trace covers every raw chunk |
| 2755 | each side writes, every event, every write return, and `opened` / `connect` / |
| 2756 | `handshake`, through ops such as `setKeepAlive` / `setTimeout` / `end` / `destroy` / |
| 2757 | `send` / `alloc` / `flush`. Bad-input scripts: a wrong header length, a wrong |
| 2758 | stream id, a tampered frame, a frame shorter than ABYTES, a failed handshake. |
| 2759 | - **The interop gate:** JS ↔ keetd over a real pipe, both initiator roles, with real |
| 2760 | randomness and real timers: data both ways, a keepalive while idle, and a timeout |
| 2761 | that destroys. |
| 2737 | 2762 | - **4e-3c `ss/secret-stream`:** |
| 2738 | 2763 | - `Handshake`; |
| 2739 | 2764 | - `NoiseSecretStream` over a Duplex, with a raw-stream interface (write → drained, |