fix(network): retain the mesh across daemon restarts#1504

merged9
dns-split/wireguard-dumps
dns-split/retain-mesh
openedOct 10, 2026byNNick
View on GitHubGitHub import enabledPR last imported 21d ago

Summary

A daemon restart used to replace WireGuard peers, briefly disconnecting retained Corrosion and running services. Keep the installed mesh after validating its Machine identity, preserve live endpoints, and apply peer changes from complete local observations.

mermaid
1flowchart LR
2 Restart[Daemon restart] --> Validate[Validate retained Machine identity]
3 Validate --> Adopt[Keep peers and live endpoints]
4 Adopt --> Observe[Complete local Machine observation]
5 Observe --> Delta[Update changed peers individually]
6 Reset[Reset or uninstall] --> Cleanup[Remove retained resources before Machine state]

This layer completes restart continuity for the native stack. Cleanup stays in this PR because retained resources must be removed safely before re-enrollment. Closes #1502.

Evidence

  • Refreshed onto current main without force-push. Independent review returned SHIP for each updated layer. The combined refreshed tree passed 607 Rust unit tests, the multipart kernel regression, Clippy with warnings denied, the uninstall contract, formatting, and whitespace checks. Fix round 1 then changed the mesh firewall and WireGuard reset in #1504, so the mesh source no longer matches the live-tested version. The two-Machine live runs below predate that round.
  • Fix round 1 writes each address family's firewall in one iptables-restore --noflush transaction, and moves the INPUT jump and nat RETURN only when another rule is ahead of them. Codex's probe lost 222 of 2,691 mesh datagrams over five reapplications before; it now loses 0 of 2,376 and 0 of 2,217, against 0 of 2,183 and 0 of 2,330 with no reapplication. reapplying_the_firewall_never_drops_admitted_traffic loses 153 of 735 on the old code and none on the new.
  • A mismatched retained ployz-wg now stops the daemon with the no-restart exit status 78 and a recovery step. Reset deletes it instead of refusing; reset_deletes_a_mismatched_retained_device covers that path in a namespace with a fake Docker socket.
  • scripts/test-network-restart.py fails against the previous binary with 97 missing-peer samples out of 107. The combined binary passes both Machines through three restarts each, SIGKILL, and a 12-second stop/start. It records 10,415 DNS queries without loss, 310 HTTP 200 responses, and 280 membership samples all Alive.
  • Live checks preserve a roamed endpoint over stale saved state, persist that endpoint, recover interrupted keyless initialization, and complete Reset, interrupted Reset, re-enrollment, and uninstall.
  • The live-tested tree passed 76 focused Rust tests, the lifecycle/uninstall contract, Clippy with warnings denied, formatting, and whitespace checks. The prerequisite's multipart kernel regression also passed.

Merge danger

Door: Two-way for restarts. Reset and uninstall intentionally delete Machine state after validated cleanup succeeds.

Blast radius: Networking. Conflicting identity or network configuration refuses adoption and preserves the record for recovery. Reset still refuses an unproven Docker network but deletes a mismatched ployz-wg, since that name is Ployz's. Live verification covers two Linux Machines. The firewall namespace tests pass; non-Linux builds were not run.

Checks

23 successful
All checks passed
e5a0d9a

Added 1 commit

  • fix(network): retain the mesh across daemon restarts55453ac

Added 8 commits

  • Merge branch 'dns-split/wireguard-dumps' into dns-split/retain-mesh4d9bff3
  • Merge branch 'dns-split/wireguard-dumps' into dns-split/retain-mesh4a60ee7
  • Apply each firewall policy in one restore transactionf175b30
  • Stop instead of restarting on a mismatched WireGuard interface28f1e35
  • Say the private network keeps running while Ployz is stopped29c10cb
  • Merge branch 'dns-split/wireguard-dumps' into dns-split/retain-meshce0cbc0
  • Close queued Machine RPC connections when startup refuses a network conflict56fbb8c
  • Say when Private DNS stays idle and find TCP port 53 ownerse5a0d9a