Skip to content

Commit 555d291

Browse files
committed
Add v1.9.0-rc2 release notes
1 parent d4d5f42 commit 555d291

1 file changed

Lines changed: 149 additions & 0 deletions

File tree

RELEASE_NOTES_v1.9.0-rc2.md

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# v1.9.0-rc2 — Release Notes
2+
3+
**Scope:** 11 commits since `v1.9.0-rc1` (2026-04-24 → 2026-04-27).
4+
5+
The headline of RC2 is **P1-010 closed** (both halves) — the
6+
tunnel-crypto-desync class of bugs that caused fire-and-forget RPCs
7+
(task submit, send-results) to silently lose data across rekey windows.
8+
P1-009 closed as a side effect: the `test_midrekey_*` regression repros
9+
all pass cleanly now.
10+
11+
## P1-010 — tunnel desync recovery
12+
13+
Four commits, four layers of the fix:
14+
15+
### 1. Salvage replay (`7f76e75`)
16+
17+
Per-peer plaintext ring buffer in `peerCrypto.salvage`. Every
18+
encrypted send copies the plaintext into the buffer. When
19+
`handleAuthKeyExchange` (or unauth `handleKeyExchange`) installs a
20+
fresh `peerCrypto` because the peer's pubkey changed, we re-encrypt
21+
the recent plaintexts with the new key and re-send via `writeFrame`.
22+
23+
Recovers fire-and-forget RPCs that were sent under stale crypto
24+
during the rekey window.
25+
26+
Webhook event: `tunnel.desync_salvage` with replay count.
27+
28+
### 2. Rekey retransmit loop (`a126a26`)
29+
30+
Per-peer `pendingRekey` state set on `sendKeyExchangeToNode`, cleared
31+
on first successful inbound decrypt. New goroutine
32+
`rekeyRetransmitLoop` retransmits stale entries every 4 s, capped at
33+
5 retries. Closes the case where our key_exchange or peer's reply was
34+
dropped on the wire — without this, the next chance to recover was
35+
the 5-min `RelayProbeInterval`.
36+
37+
Webhook event: `tunnel.rekey_gave_up` if a peer hits the retransmit
38+
cap (real reachability problem worth investigating).
39+
40+
### 3. Cap salvage at 4 entries (`5de736f`)
41+
42+
Original cap of 32 caused a replay-storm on rekey: the dataexchange
43+
retransmit layer's churn filled the buffer, and on rekey all 32
44+
frames went out in a tight loop. The receiver's freshly-installed
45+
`peerCrypto` had `maxRecvNonce = 0`; out-of-order arrival of nonces
46+
1..32 tripped the replay-window check. 4 covers the realistic shape
47+
(task submit + send-results + a couple of dataexchange retries)
48+
without overwhelming the receiver. Memory drops from ~48 KiB / peer
49+
to ~6 KiB; ~6 MiB worst-case fleet-wide at `maxCryptoPeers = 1024`.
50+
51+
### 4. Preserve nonce/replay state across duplicate key_exchange (`d4d5f42`)
52+
53+
The latent companion bug: `handleAuthKeyExchange` and
54+
`handleKeyExchange` used to **unconditionally replace** `tm.crypto[N]`
55+
with a freshly-derived `peerCrypto`, even when the peer's pubkey was
56+
unchanged. Same shared secret, but reset nonce counter and empty
57+
replay bitmap. Subsequent encrypted sends used counter 1, 2, 3...
58+
while peer's `pc[us]` had a high `maxRecvNonce` from before, so
59+
peer dropped them as "outside replay window."
60+
61+
Fix: replace only when there's no existing entry OR the pubkey
62+
actually changed. Pinned by
63+
`TestHandleKeyExchangeDuplicatePreservesCryptoState`.
64+
65+
(This same fix was attempted earlier in the session and reverted —
66+
it was incompatible with the 32-entry salvage cap. After the cap
67+
reduction in `5de736f`, the preservation fix is safe.)
68+
69+
## Tests added
70+
71+
- `pkg/daemon/tunnel_desync_salvage_test.go` — 7 unit tests for the
72+
ring buffer (size + age bounds, copy-not-reference, nil-safety).
73+
- `pkg/daemon/tunnel_rekey_retransmit_test.go` — 9 unit tests for the
74+
retransmit state machine (mark, clear, stale, give-up cap).
75+
- `pkg/daemon/tunnel_dup_keyexchange_test.go` — 2 unit tests pinning
76+
duplicate-preservation and real-rekey-replacement.
77+
- `tests/integration/local/test_tunnel_desync_recovery.sh` — black-box
78+
test: establish tunnel, restart receiver, submit task immediately,
79+
assert it lands within 30 s.
80+
81+
## Verified
82+
83+
| Test | Result |
84+
|---|---|
85+
| `go vet ./...` | clean |
86+
| `go test -parallel 4 ./pkg/... ./tests/` | all 10 packages pass |
87+
| `test_tunnel_desync_recovery` | 7/0 |
88+
| `test_midrekey_send_file` | 5/0 (was 4/1, P1-009 repro) |
89+
| `test_midrekey_send_message` | 6/0 (was 5/1, P1-009 repro) |
90+
| `test_midrekey_task_results` | 6/0 (was 4/2, P1-009 repro) |
91+
| `test_midrekey_task_submit` | 5/0 (was 3/2, P1-009 repro) |
92+
| `test_peer_restarted_send_file` | 4/0 (was 3/1) |
93+
| `test_peer_restarted_send_message` | 5/0 (was 3/2) |
94+
| `test_sender_clean_restart_midflight` | 6/0 |
95+
| `test_chaos_packet_loss` | 9/0 |
96+
| `test_force_relay_task` | 4/0 |
97+
| Full integration suite (`btjsk44z8`, -j 8) | 225/232 — best of cycle |
98+
99+
## Other (pre-RC2 but post-RC1) work folded in
100+
101+
- `b4237e3` + `71e5f56` — 30 open-data network blueprints (academic,
102+
geo, health, news, finance, etc.). Already deployed to production
103+
registry as IDs 44–73. Open-join, default-allow, full inter-agent
104+
communication.
105+
- `e7d9efb` — untracked runtime artifacts that should never have been
106+
committed (test logs, results); added matching `.gitignore` rules.
107+
- `aa0cb6a`, `08b3a34`, `c843c4a`, `22092c3` — website / blog content,
108+
no protocol impact.
109+
110+
## Backwards compatibility
111+
112+
Wire-compatible with `v1.8.0` and `v1.9.0-rc1` daemons. No new packet
113+
types, no new fields, no version negotiation. Mixed deployments are
114+
safe. The retransmit loop and salvage replay are visible to peers as
115+
ordinary key_exchange / encrypted frames respectively.
116+
117+
## Known limitations (unchanged from RC1)
118+
119+
- `test_cli` — env-gated (hits production `agent-alpha`).
120+
- `test_register_identity_new_endpoint` — architectural (container
121+
PID-1 dies on `pkill`). Needs Dockerfile init wrapper.
122+
- Vouching-chain transitive walk unimplemented.
123+
- Trust links global, not per-network (design choice).
124+
- `DefaultVerdict` policy field fails OPEN on pre-v1.9 daemons —
125+
upgrade daemons before pushing configs that rely on it.
126+
127+
## Upgrade
128+
129+
No coordinated rollout needed. Update daemons in any order:
130+
rendezvous → agents is the recommended sequence purely for the
131+
`DefaultVerdict` ordering risk. Old daemons keep working unchanged.
132+
133+
## Full commit log
134+
135+
```
136+
d4d5f42 Preserve nonce/replay state across duplicate key_exchange
137+
c843c4a Add blog post: AI agent discovery: master P2P networks in 2026
138+
5de736f Cap desync salvage at 4 entries (was 32) to prevent replay storm
139+
22092c3 Hero: decorative boids flock behind the headline
140+
a126a26 P1-010 tunnel-state half: retransmit pending key exchanges
141+
7f76e75 P1-010: salvage in-flight plaintext on peer-initiated rekey
142+
08b3a34 Add blog post: Overlay networking: Secure AI agent communication explained
143+
aa0cb6a Add blog post: Top 6 openanp.ai Alternatives 2026
144+
71e5f56 Open-data networks: full inter-agent communication; SHIPPED roster
145+
b4237e3 Add 30 open-data network blueprints
146+
e7d9efb Untrack runtime artifacts that should never have been committed
147+
```
148+
149+
`git log v1.9.0-rc1..v1.9.0-rc2 --oneline` (11 commits).

0 commit comments

Comments
 (0)