Husk
GitHub

Computers

Lifetimes and the reaper

idleTimeoutSec, maxLifetimeSec, which providers are actually swept, and how a machine gets bound to a session.

A computer is disposable. Two ceilings decide when one goes away on its own.

FieldDefaultWhat it means
idleTimeoutSec900 (15 minutes)Destroy after this long with no exec
maxLifetimeSec0 (off)Destroy this long after creation, whatever the activity

Both are per-machine and both are disabled by 0.

computer:
  idleTimeoutSec: 600
  maxLifetimeSec: 7200
husk up scratch --idle-timeout 3600

"Idle" means no exec. Every exec on the local provider touches lastUsedAt and rewrites the record; the container providers read State.StartedAt from the engine.

The reaper

ComputerManager.startReaper(intervalMs = 60_000) starts a sweep every 60 seconds. The interval timer is unref'd, so it never holds a CLI process open, and startReaper is idempotent.

husk serve starts it. The CLI's one-shot commands do not — a husk exec that also garbage-collected somebody else's machine would be a surprise.

The sweep asks every provider that implements reap() and ignores the ones that do not.

A reaper failure in one provider is logged at debug and does not stop the others.

Stopping versus destroying

CommandFilesystemRecoverable
husk stop <name>keptyes, with husk start
husk rm <name>goneno

A stopped machine still counts against nothing — only running and creating machines count toward the eight-machine quota — but it still occupies disk.

Session bindings

ComputerManager.ensure(key, spec) maps a stable key onto a machine, so a conversation keeps one filesystem without the caller tracking ids.

The map lives in ~/.husk/computers/bindings.json, written with write-then-rename. When a binding points at a machine that no longer exists, it is cleared and a new machine is created.

MCP uses this: the session key defaults to $HUSK_SESSION, then to the literal mcp, and the machine is named mcp-<key>. It is created with idleTimeoutSec: 3600 — an hour, because a conversation has long pauses in it — and destroyed when the client disconnects unless you pass --keep.

claude mcp add husk-api -- npx -y @husk-ai/mcp --session api --flavor python
claude mcp add husk-web -- npx -y @husk-ai/mcp --session web --flavor node

Two keys, two machines, two filesystems, one client.

Shutdown paths

TriggerWhat happens
husk rmThe machine and its filesystem are destroyed; the binding is cleared
MCP client disconnects (stdin closes)The machine is destroyed unless --keep
SIGINT / SIGTERM to husk mcpSame
Ctrl-C during husk runThe run aborts between steps. The machine is left alone
ReaperOnly on local, ssh and fly

Ctrl-C during a run is deliberately non-destructive. You interrupted the agent, not the machine, and destroying the filesystem you were about to inspect would be the wrong reading of that keystroke.