Workspaces
The per-agent FHS root, the model's CWD, and the context files that tell the agent what it can do.
Every agent gets its own workspace โ a directory it can read and write, plus a set of auto-generated context files the runtime injects into the model's system prompt at start-up. The context files are how the model learns about its tools, its environment, and what it's allowed to do, without you re-stating any of it in the Agentfile.
On-disk layout
Under the system executor, the workspace lives
at ~/.otters/agents/<agent-uuid>/ on the host โ the runtime runs as
a plain subprocess and sees those host paths directly. Under the
docker executor, each top-level subtree of
that host directory bind-mounts onto the matching standard Linux path
inside the container, so the agent sees a normal FHS rooted at / โ
no /agent prefix.
<agent-root>/ โ in-container (docker)
โโโ workspace/ โ /workspace # CWD; the model's writable scratch
โโโ home/ โ /home # HOME โ agent dotfiles, never host's
โโโ tmp/ โ /tmp # TMPDIR โ wiped on restart
โโโ var/lib/ โ /var/lib # persistent state (memory.db, etc.)
โโโ opt/bins/ โ /opt/bins # flat dir of symlinks โ /opt/bin-images/<name>/<name>
โโโ etc/
โโโ context/ โ /etc/context # auto-generated context files (see below)
โโโ data/ โ /etc/data # static files from ADD directives
โโโ Agentfile โ /etc/Agentfile # verbatim DSL source โ what the operator wrote
โโโ agent.yaml โ /etc/agent.yaml # slim runtime config (see "agent.yaml" below)BIN tool binaries on docker come from per-tool OCI image mounts at
/opt/bin-images/<name>/<name> (hidden from the model); the symlinks
in /opt/bins/ are what the runtime puts on PATH. On system, BIN
binaries are materialised as plain files under usr/bin/<name>.
The model is launched with CWD = /workspace. Any file the agent
creates lands there. The other directories exist so HOME, TMPDIR, and
PATH resolve to agent-local paths instead of the host's, but the model
doesn't have to know about them โ its mental model is "I write to my
workspace".
agent.yaml
etc/agent.yaml is the runtime's slim config file. The daemon writes
it at materialise time; the runtime reads it at start-up.
id: 86106816-โฆ
name: Homelab
model: anthropic-local/claude-opus-4-7
workspace: /workspace
# OCI provenance โ what bytes the agent was materialised from.
image: { ref: kubectl:latest, digest: sha256:3b97b227โฆ }
runtime: { ref: ghcr.io/openotters/runtime:latest, digest: sha256:1ab55f94โฆ }
# Runtime tunables (the CONFIG directive's flat map, kebab-case
# keys mirroring the Agentfile). Consumed by the runtime โ
# max-tokens / max-iterations / memory-strategy / memory-max-*.
configs:
max-tokens: 2048
max-iterations: 10
# Every env key this agent expects. KEYS ONLY โ values live in
# daemon.db (operator overrides) and the Agentfile (spec defaults),
# and get merged into the spawn env at start time. No secrets on
# disk in this file by construction.
envs:
- key: KUBECONFIG
description: Kubeconfig path inside the agent.
# Spec-declared mounts. Operator -v host paths live in daemon.db;
# only the target / description / read-only flag persist here.
mounts:
- target: /kubeconfig.yaml
description: Kubeconfig for the cluster the agent should operate on.
read_only: true
# Ordered context files. Absolute paths from the agent root.
# Replaces the older dir-scan of etc/context/ โ the file is the
# authoritative declaration of what loads into the system prompt.
context:
- /etc/context/SOUL.md
- /etc/context/SCENARIOS.md
tools:
- name: kubectl
description: kubectl CLI.
binary: /opt/bins/kubectl
ref: ghcr.io/openotters/tools/kubectl:latest
digest: sha256:efghโฆ
usage: /etc/data/bins/kubectl/USAGE.mdThe original Agentfile (verbatim DSL, with comments and formatting
intact) rides along the image as its own layer and lands beside this
file at /etc/Agentfile. Reading the two together gives you what the
operator wrote and the daemon's resolved view.
Context files
At start-up the runtime auto-generates a small set of Markdown files in the workspace root and pulls them into the agent's system prompt. The model reads them like any other context.
AGENT.md
The agent's identity card. Describes:
- Declared bins โ every BIN the agent can call, by name, with the one-line description from the Agentfile.
- Environment โ every
ENVkey the agent was started with, with the description and the operator-supplied value (redacted for obvious secret-shaped keys). - Capabilities โ a positive section listing what the runtime can do for the agent (network, persistent memory, async jobs, persistent workspace). Stops the model from hallucinating that it can't do things it can.
- Binaries allowlist โ a strict directive that the agent can only call the listed BINs. Stops the model from hallucinating tools it doesn't have.
Generated fresh on every start, so an -e override or a --model
swap shows up immediately.
WORKSPACE.md
The FHS-style layout of the workspace tree, with absolute paths the
model uses verbatim in tool calls. The model copies paths out of this
file when constructing sh -c "cat /workspace/..." style invocations
โ no guessing, no hallucinated paths.
MOUNTS.md
Present only when the agent was started with otters run -v mounts.
Lists every host bind-mount the operator declared, with:
- The target path inside the workspace.
- The optional description from the
-vdeclaration. - Whether the mount is read-only or read-write.
Without this file the model has no way to learn about ad-hoc operator
mounts โ they're not in the Agentfile, they don't show up in env vars,
the path just exists. MOUNTS.md makes them explicit.
One file per CONTEXT heredoc
Every CONTEXT NAME <<EOF โฆ EOF block in the Agentfile becomes its
own file (SOUL.md, SCENARIOS.md, IDENTITY.md, โฆ). The optional
description on the directive renders as a sub-header above the body.
The convention is to give each section a short, capitalised name so the model can refer to it ("per SOUL.md, you โฆ"). The runtime feeds all CONTEXT files in declaration order, so order in the Agentfile controls the reading order in the prompt.
Lifecycle
- Created on the first
otters runfor a given agent name. The directory is materialised from the agent image. - Persistent across
otters stop/otters start. The model's workspace files survive restarts. - Wiped on
otters rm. Removing an agent removes its workspace.
The runtime never garbage-collects workspace contents on its own โ files the agent wrote stay until the operator removes the agent or empties the dir manually.
Operator-supplied bind-mounts
otters run -v HOST:TARGET[:DESC][:ro|rw] exposes a host path as a
workspace entry. Use this to give the agent access to project sources,
data files, or a credentials file the Agentfile shouldn't carry.
The mount is recorded in MOUNTS.md so the model sees it. :ro
makes the mount read-only at the executor boundary โ the model can
read but not write, regardless of what its tools try.
otters run my-coder -v "$PWD:/workspace/src:Project source"
otters run my-data -v "/data/customers.csv:/workspace/data/customers.csv:ro"See also
- Agentfile
CONTEXTโ declaring context heredocs. - Agentfile
ENVโ environment vars surfaced inAGENT.md. - Executors โ where workspace bytes actually live (host filesystem vs container bind-mount).