Guest services survive host crashes and SIGKILL (registry is memory-only, no boot reaper)

Author: ICEY16360Created Sep 22, 2026Updated Sep 22, 2026
Labelsenhancement

The graceful paths are covered now by #3812 : signal handlers, POST /api/system/shutdown and stop() all reach runShutdown, which stops guests before exit.

What is left is the host that never gets to run its shutdown. A SIGKILL, an OOM kill, a power loss or a kernel panic leaves the guest services running with no parent. lib/guests/service.js keeps its runtimes in a module-level Map, with no pidfile and no on-disk record, so the next boot has nothing to find and nothing to clean.

This one matters more than a stray process. An allowed service has no sandbox and runs with the user's full access (stated in lib/guests/DOCUMENTATION.md), so each orphan keeps loopback ports and the granted sockets indefinitely, until the machine reboots or somebody kills it by hand.

Field data from one daemon host: about 11 orphans a day, 22-56 MB each.

I think the fix is the pattern the OpenCode reaper already uses in lib/opencode/managed-process-registry.js: record the pids we spawn, run a reaper at boot, and re-verify identity before any kill. Guests carry a host-minted OPENCHAMBER_SERVICE_TOKEN in their environment, so identity can be checked from the environment before killing, plus PPid == 1 on Unix to confirm the owner is gone.

Two design constraints worth settling up front. /proc/<pid>/environ is Linux only, so macOS needs ps and Windows needs tasklist; Windows also has no reparent-to-1 semantics, which the OpenCode reaper already works around by requiring the recorded owner pid to be dead. And several windows and hosts run at once, so the record has to be one file per spawned process rather than a shared file, for the same reason the OpenCode registry is laid out that way.

There is also a smaller gap of the same owner: a guest request that lands between the guest stop and the HTTP listener closing during shutdown can respawn a service and orphan it.