From ab25c99db63ca9cbfbd906d3cb7fd14e19248dca Mon Sep 17 00:00:00 2001 From: matthew-pilot Date: Sun, 31 May 2026 01:59:36 +0000 Subject: [PATCH] fix(daemon): flush identity to disk on shutdown (PILOT-321) doStop() tears down IPC, tunnels, managed engines, and the network subscriber, but never calls SaveIdentity. Today every identity mutation (GeneateIdentity in startNetworked, RotateKey) persists eagerly, so this is a no-op on current code paths. The risk is forward-looking: a future code path that mutates d.identity in-memory without writing would lose the change on next start with zero diagnostic. Add a defense-in-depth SaveIdentity call as the final step of doStop() so the on-disk identity always reflects shutdown state. Mirrors the existing pattern in startNetworked (:525-531) and RotateKey (:2073-2077): guard on IdentityPath != "" and log a warning on failure. Closes PILOT-321 --- pkg/daemon/daemon.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pkg/daemon/daemon.go b/pkg/daemon/daemon.go index 099eba10..32d00575 100644 --- a/pkg/daemon/daemon.go +++ b/pkg/daemon/daemon.go @@ -1407,6 +1407,18 @@ func (d *Daemon) doStop() { // so the daemon.shutting_down event published above flows through // the bus to the still-subscribed plugin, which drains its // internal queue on Stop(). + + // Defense-in-depth: flush identity to disk on shutdown. + // Today all identity mutations persist eagerly (GenerateIdentity + // in startNetworked and RotateKey save synchronously), but a + // future code path that mutates d.identity in-memory without a + // write would lose the change on next start. Writing here ensures + // identity on disk always reflects the shutdown state. + if d.config.IdentityPath != "" { + if err := crypto.SaveIdentity(d.config.IdentityPath, d.identity); err != nil { + slog.Warn("identity flush on shutdown failed", "error", err) + } + } } // startManaged detects managed networks this node belongs to and starts engines.