System executor
Each agent runs as a host subprocess. Default backend, no Docker required.
The system executor runs each agent as a host subprocess. The runtime binary
and every BIN tool are materialised onto local disk under
~/.otters/agents/<id>/, the agent is spawned with a locked-down environment,
and ottersd supervises its lifecycle directly.
It's the default backend. Works on any platform that can run Go binaries โ macOS, Linux, the Apple Silicon laptop you're reading this on.
When to use it
- You're trying OpenOtters out and you don't want to install or configure Docker.
- You want the smallest possible runtime overhead per agent.
- You're happy with a process-level isolation boundary โ fine for trusted Agentfiles, less appropriate for code you didn't write.
How it works
On otters run <image> the daemon does, roughly:
- Resolves the agent image and every BIN tool image from the registry.
- Materialises an FHS-style root at
~/.otters/agents/<id>/โ runtime binary copied tousr/bin/runtime, every BIN binary tousr/bin/<name>, scratch dir atworkspace/. - Builds the locked-down env (PATH, HOME, XDG_*, TMPDIR all rooted under the agent dir).
- Forks the runtime as a subprocess, hands it the env + a unix socket addressed at the daemon for callbacks.
Stop / restart go through the same subprocess: SIGTERM, then SIGKILL
after a grace period.
Local layout
~/.otters/
โโโ agents/
โ โโโ <agent-uuid>/
โ โโโ usr/bin/runtime # materialised runtime binary
โ โโโ usr/bin/<tool> # materialised BIN binaries
โ โโโ home/ # agent's HOME
โ โโโ tmp/ # agent's TMPDIR
โ โโโ var/lib/ # persistent state (memory.db, โฆ)
โ โโโ etc/
โ โ โโโ Agentfile # verbatim DSL source (image layer)
โ โ โโโ agent.yaml # slim runtime config (see Workspaces)
โ โ โโโ context/ # auto-generated context files loaded into system prompt
โ โ โโโ data/ # static files baked in by ADD directives
โ โโโ workspace/ # agent CWD; the model's writable scratch dir
โโโ logs/<agent-uuid>.log # per-agent stdout/stderr
โโโ providers.yaml # LLM provider credentials
โโโ daemon.db # SQLite state
โโโ registry/ # embedded OCI store (system only)The system executor's registry is an embedded ORAS store under
~/.otters/registry/. Image pulls / pushes go through it. The Docker
executor uses Docker's image store instead and doesn't create this
directory.
Spawn environment
Every agent runs with a minimal env. Reserved keys are stripped, and the
Agentfile can opt extra OS env vars in via the
ENV instruction. Reserved (never settable from
the Agentfile):
PATHโ rooted at the agent'susr/bin/HOME,XDG_*,TMPDIRโ rooted under the agent dirLANG,OTTERS_AGENT_ROOTโ set by the runtime*_API_KEY,*_API_BASEโ managed byotters provider add
This keeps host secrets out of the agent's reach, even when the agent
spawns subprocesses via sh.
Sandbox properties
- Process isolation only. No kernel boundary; the agent can see whatever the host shell can see, modulo the locked-down env.
- Process groups handle lifecycle, mostly. Subprocess chains
(
sh -c "a | b") are tracked via PGID, but pathological cases (deep re-exec chains, daemonised grandchildren) can surviveotters stop. - File system access is wherever PATH and the agent's CWD can reach.
Bind-mounts declared at
otters run -vdon't actually mount anything on the system backend โ the path is exposed as a workspace entry but every file remains where the host put it.
If those properties don't fit, see the docker executor.
Configuration
The system executor takes no executor-specific config. Daemon-level knobs
(socket path, listener, pool tuning) live in
~/.otters/ottersd.yaml and apply to both
backends.