Skip to content

Commit c80f849

Browse files
committed
v1.9.1-rc3
Tag bump to republish rc2's content with a fresh codesigned + checksummed artifact set from the release workflow. No new behavior vs rc2. Documents the residual long-running-tunnel desync against older-version peer daemons as a known limitation (out-of-scope structural protocol work; CLI workaround: 'pilotctl daemon stop && pilotctl daemon start'). Adds peerCrypto.createdAt — placeholder for a future "stale-state" heuristic that needs more design before it can be safely turned on.
1 parent 956f184 commit c80f849

2 files changed

Lines changed: 33 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,30 @@ Detailed per-release notes for tagged versions are published on the
99

1010
## [Unreleased]
1111

12+
## [1.9.1-rc3] - 2026-05-03
13+
14+
Same content as rc2; tag bump to publish a fresh signed/notarized
15+
artifact set. The release workflow's macOS ad-hoc codesign step
16+
(`.github/workflows/release.yml:81-91`) re-signs each darwin binary on
17+
every tag — rc3 picks up that signature and the corresponding
18+
`checksums.txt`.
19+
20+
### Tracked, not blocking rc3
21+
22+
- Long-running tunnels against older-version peer daemons can desync
23+
after ~minutes-to-hours of idle: the peer's daemon resets its send
24+
counter (or rotates AEAD state without rotating X25519 keys), our
25+
preserved peerCrypto holds a high maxRecvNonce, and the peer's
26+
resumed-from-1 packets get rejected as replays. User symptom: a
27+
burst of pings works on a fresh daemon, then `dial timeout after
28+
7.5 s` until daemon restart. Workaround: `pilotctl daemon stop &&
29+
pilotctl daemon start`. Fix candidates explored in rc2-dev (reset
30+
recv replay state on duplicate key exchange, reset crypto on stale
31+
contexts, prewarm trusted-peer tunnels) all introduced regressions
32+
of their own and were reverted. The structural fix is a protocol-
33+
level "session epoch" handshake — out of scope for the rc series.
34+
Adds `peerCrypto.createdAt` for a future heuristic.
35+
1236
## [1.9.1-rc2] - 2026-05-03
1337

1438
Release candidate 2. Builds on rc1 with three classes of fixes:

pkg/daemon/tunnel.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@ type peerCrypto struct {
5757
ready bool // true once key exchange is complete
5858
authenticated bool // true if peer proved Ed25519 identity
5959
peerX25519Key [32]byte // peer's X25519 public key (for detecting rekeying)
60+
// createdAt is when this peerCrypto was installed in tm.crypto. Used
61+
// by handleAuthKeyExchange to decide between preserving existing state
62+
// (fresh handshake retransmit/reply within seconds) and resetting it
63+
// (long-lived peer's own state desynced — e.g. older-version daemon
64+
// rotated its send counter without rotating X25519 keys, which
65+
// otherwise leaves us with a high maxRecvNonce that rejects the
66+
// peer's resumed packets as replays).
67+
createdAt time.Time
6068

6169
// P1-010 desync salvage: ring buffer of recent plaintext sent with this
6270
// key. On a peer-initiated rekey (keyChanged in handleAuthKeyExchange),
@@ -1394,7 +1402,7 @@ func (tm *TunnelManager) deriveSecret(peerPubKeyBytes []byte) (*peerCrypto, erro
13941402
}
13951403

13961404
// Generate random nonce prefix for domain separation
1397-
pc := &peerCrypto{aead: aead, ready: true}
1405+
pc := &peerCrypto{aead: aead, ready: true, createdAt: time.Now()}
13981406
copy(pc.peerX25519Key[:], peerPubKeyBytes)
13991407
if _, err := rand.Read(pc.noncePrefix[:]); err != nil {
14001408
return nil, fmt.Errorf("nonce prefix: %w", err)

0 commit comments

Comments
 (0)