Skip to content

Daemon restarts reattach live sessions - #103

Merged
karngyan merged 2 commits into
mainfrom
holder-4-reattach
Aug 19, 2026
Merged

Daemon restarts reattach live sessions#103
karngyan merged 2 commits into
mainfrom
holder-4-reattach

Conversation

@karngyan

Copy link
Copy Markdown
Contributor

Fourth in the holder-sessions stack, on top of #102. This is the PR where the feature actually happens.

  • Boot reattach: ReattachHolders dials every live holder under the holders root and rebuilds its registry entry — same id, same ring, same running child. Dead dirs are swept; the revive pass then only handles snapshots for sessions that are not already back, and clears stale ones.
  • Reboot story unchanged: a holder answers SIGTERM (logout, shutdown) by writing exactly the revival snapshot the daemon used to write, agent resume hint included. Next boot revives it as before.
  • Service units: systemd gains KillMode=process (the default control-group kill would reap holders on every restart — without this the feature is dead under systemd), launchd gains AbandonProcessGroup. flue restart and flue update converge the installed unit file before bouncing (new service.UnitRefresher), which is the migration path for installs from before this change.
  • End to end: TestSessionsSurviveDaemonRestart builds the real binary, spawns via the real ws wire, SIGTERMs the daemon, starts a successor, and asserts the same session id is running with the same child pid and intact scrollback. Passes in ~1.4s.

Caveat worth stating: KillMode=process semantics verified by reading systemd docs, not on a live Linux box — holders left in the unit cgroup after a stop are expected to survive, but a Linux smoke before release would be good diligence.

🤖 Generated with Claude Code

@karngyan

Copy link
Copy Markdown
Contributor Author

Linux validation done, twice over:

Real Ubuntu (x86_64, systemd user unit) — dedicated test unit flue-holdertest with KillMode=process, isolated config and port; production flue on the same box verified untouched (same pid/invocation before and after). systemctl --user restart: holder pid unchanged, child alive, new daemon logs holder reattach reattached=1 swept=0, same session id listed running. Full stop also spares holder and child; the following start reattaches again. systemd shows the survivors in the cgroup without complaint.

Docker (arm64) — full go test ./... green on Linux including TestSessionsSurviveDaemonRestart, plus the same restart/stop/start drill under a real systemd 249 in a privileged container, same results.

One CI note: in a bare container (no init as PID 1) four kill-liveness tests fail on unreaped zombies — docker run --init fixes it. Worth remembering if the suite ever runs containerized.

Base automatically changed from holder-3-spawn to main August 19, 2026 09:04
karngyan and others added 2 commits August 19, 2026 15:24
The boot sequence gains a first pass: every live holder under the
holders root is dialed and its session rebuilt — same id, same ring,
same running child. Dead holder dirs are swept, and the revive pass
that follows only touches snapshots for sessions that are not already
back. Holders answer SIGTERM (logout, reboot) by writing exactly the
revival snapshot the daemon used to, so the reboot story is unchanged.

Service units harden to match: systemd gains KillMode=process so a
unit stop cannot reap the holders, launchd gains AbandonProcessGroup,
and flue restart / flue update converge the installed unit file before
bouncing so existing installs pick the settings up on their next
update.

An end-to-end test builds the real binary, spawns through a real
daemon, SIGTERMs it, starts a successor, and finds the same session:
same id, same child pid, scrollback intact.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
DialRemote's ErrHolderGone is the license to sweep; a holder that
answered the dial and then failed the handshake is alive, with a
plausibly running session behind it, and its directory is left for a
daemon that can speak to it. A missing or corrupt identity record no
longer costs a live session its reattach either — the hello carries
the spawn-time facts, so a zero record lets the holder's account
stand.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@karngyan
karngyan merged commit 5cebf14 into main Aug 19, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant