Skip to content

fix(server): kill spawned odek serve when bodek dies - #117

Merged
jkyberneees merged 4 commits into
mainfrom
fix/serve-orphan-watchdog
Sep 12, 2026
Merged

fix(server): kill spawned odek serve when bodek dies#117
jkyberneees merged 4 commits into
mainfrom
fix/serve-orphan-watchdog

Conversation

@jkyberneees

Copy link
Copy Markdown
Contributor

Problem

A kill -9ed or crashed bodek left its spawned odek serve child orphaned — Stop() only covers graceful exits, and macOS has no PDEATHSIG. Users kept finding stray odek serve processes after crashes.

Fix

  • Process group: odek serve now spawns with Setpgid, isolated from bodek's group.
  • Orphan guard: bodek re-execs itself as bodek __bodek-watchdog <parentPID> <serverPID> — a self-terminating guard process that polls the parent (500ms) and, when it dies for any reason, SIGINTs the server's process group (graceful), escalating to SIGKILL after 8s.
  • Graceful path unchanged: Stop() retires the guard first (kill + async reap) before its own SIGINT/group-SIGKILL shutdown.

Hardening (3 rounds of adversarial OS-expert review)

  • PID-reuse safety: the guard captures the server's start-time token (kinfo P_starttime on darwin, /proc/<pid>/stat field 22 on linux) and re-verifies identity before every signal — a recycled PID is never signalled.
  • Self-termination: the guard exits when the server dies while bodek lives, instead of lingering the whole session.
  • Fail-safe liveness: degraded observation (EPERM on foreign processes, hidepid, kinfo failure) reads as alive — at worst the guard never fires; a false-dead could kill a healthy server.
  • Zombie-aware probes: unreaped zombies count as dead on both platforms.

Windows: no-op (no process-group signalling); spawn/Stop behavior unchanged.

Verification

  • RED-first TDD throughout; new tests in internal/watchdog, internal/server, cmd/bodek (including re-exec dispatch through the real test binary)
  • Full gate green: fmt / vet / lint (0 issues) / test -race
  • Manual E2E with the built binary: SIGKILLed parent → orphaned target terminated by the guard
  • Round-3 adversarial verdict: CLEAN

bodek's Stop() path shuts the spawned server down gracefully, but a
SIGKILLed or crashed bodek left the child orphaned — macOS has no
PDEATHSIG. Spawn odek serve in its own process group and launch a
self-terminating re-exec guard (bodek __bodek-watchdog <parent> <server>)
that SIGINTs the server's group when the parent dies, escalating to
SIGKILL after a grace period. Zombie-aware liveness probes (kinfo on
darwin, /proc on linux). Graceful Stop retires the guard first; the
guard is best-effort and a no-op on windows.
Three findings from the OS-expert review round:
- PID-reuse kill: the guard now captures the target's start-time token
  (kern.proc kinfo on darwin, /proc stat field 22 on linux) and never
  signals a recycled PID — liveness AND identity must both hold.
- False self-termination: Run now exits when the server dies while the
  parent lives, instead of lingering the whole session (the lingering
  guard widened the PID-reuse window it was guarding against).
- Leader-only escalation: Stop now signals the server's whole process
  group (not just the leader) for both SIGINT and the SIGKILL fallback,
  so the server's own subprocesses follow it down.
Round-2 adversarial findings: when liveness observation degrades
(EPERM from kill -0 on foreign processes, unreadable /proc under
hidepid, kinfo failure on darwin), the watchdog previously concluded
'dead' — a false-dead kills a healthy server or an innocent recycled
PID. All degraded paths now fail safe (alive): at worst the guard
never fires, never over-kills. The bare-pid signal fallback keeps its
original reach but is documented as identity-guarded: every caller
verifies pid identity (start-time token or live Process handle)
immediately before signalling.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 12, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
bodek f2319b6 Commit Preview URL

Branch Preview URL
Sep 12 2026, 04:53 PM

The windows signalServer stub referenced syscall and os without
importing them; darwin CI never cross-compiled, so it only broke the
Windows Build & Vet leg. Verified with GOOS=windows go build && go vet.
@jkyberneees
jkyberneees merged commit 28b821d into main Sep 12, 2026
9 checks passed
@jkyberneees
jkyberneees deleted the fix/serve-orphan-watchdog branch September 12, 2026 16:55
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