Security4 of 10
The sandbox setting
KOTOBA_SANDBOX takes three values. It decides where — and whether — her shell and execute_code
tools run anything.
KOTOBA_SANDBOX=local # default
KOTOBA_SANDBOX=docker
KOTOBA_SANDBOX=none
It is also live-settable in Settings → Security, and a value saved there is written to
~/.kotoba/settings.yaml, which wins over the environment variable. If api/.env says docker
and the settings file says local, the effective value is local. kotoba doctor reports the value
actually in force.
A value that is not one of the three is rejected on read as well as on write, with a warning in the
log, and the effective value falls back to local. Measured: rocket and an empty string both
resolve to local; NONE resolves to none, so case does not matter. Guarding only the settings
file reproduced the bug this exists for — the panel rendered local while the backend picker got the
invalid value, so execution was silently off with every surface reporting a working sandbox.
local — the default
The name is the one thing about this setting that can mislead, so it is worth being blunt: local
is not isolation. The module's own description is accurate — run on the HOST: a child process in a
jailed workdir, scrubbed env, timeouts.
What you actually get:
- A child process on your machine, as your user account. Measured: a command run through this
backend printed the machine's hostname, listed
/etc, and reported the account name as the account running the server. - A starting directory, not a cage. The process starts in the working folder. It is not confined to it. What confines things is the approval gate deciding whether the command runs at all, plus the path jail on her file tools — which is a separate mechanism and does not apply to a shell command once it is running.
- A scrubbed environment. The child gets an allow-list of names and nothing else. Measured on a
process whose parent held
OPENAI_API_KEY,ELEVENLABS_API_KEYandGITHUB_PERSONAL_ACCESS_TOKEN: none of the three reached the child. The full list is in Secrets. - A named interpreter. POSIX gets
sh -c. Windows gets PowerShell invoked by argv with an encoded command, never whateverCOMSPEChappens to name. - Its own process group, and a timeout. On expiry the whole group gets
SIGTERM, thenSIGKILLafter 5 seconds; the call returns exit code 124 withtimed out after Ns and was killed. Measured. - Output caps. 200 000 bytes per stream, read continuously so a runaway can neither flood memory nor block on a full pipe.
Temporary files go to ~/.kotoba/tmp, exposed to the child as TMPDIR, TMP and TEMP —
deliberately not the system temp directory and not the Files library.
This is the right default for an assistant on a machine you own, and it is the reason the approval
gate matters as much as it does. On local, the card is the boundary.
docker — opt-in isolation
Her commands run in a container started per session and reached with docker exec. The flags, read
out of the code:
--network none no outbound network from the container
--read-only the image filesystem is not writable
--tmpfs /tmp:rw,size=64m,mode=1777 a small writable scratch
--memory 512m memory cap
--pids-limit 256 process cap
--cap-drop ALL no Linux capabilities
--security-opt no-new-privileges no setuid escalation
--user <uid>:<gid> runs as the server's own user (POSIX; Docker Desktop maps this itself)
-v <workdir>:/work -w /work only the working directory comes from the host
The image is python:3.12-slim unless KOTOBA_SANDBOX_IMAGE says otherwise. The host environment is
never forwarded: docker exec uses the image's environment only, so no key of yours is inside.
The approval gate is looser here, on purpose. With the container as the boundary, an ordinary
command runs without a card, and so does an ordinary Python snippet. Measured with the docker backend
selected: ls, npm install, python x.py, cat /etc/passwd and rm notes.txt all run unasked,
while anything matching the dangerous list still stops and asks. execute_code behaves the same way
and needs no saved grant here — see execute_code.
Two consequences to hold on to:
rm notes.txtrunning unasked means files in your working directory can be deleted without a card./workis a bind mount of a real host directory; only that directory is exposed, and it is fully writable.cat /etc/passwdrunning unasked is reading the container's file, not yours.
If you want to be asked about everything, run local. The looser card behaviour is the trade you
make for real isolation.
The container is removed at session teardown (docker rm -f). A command still running inside it may
outlive a cancelled turn until that happens.
none — no execution at all
The strictest of the three, not the loosest.
What it covers. No tool of hers starts a child process on your machine:
shellandexecute_codeare not offered to the model at all. Measured: withnoneselected, both tools' availability checks answerFalseand no sandbox object is created.search_filesskips the ripgrep path and answers from its own directory walk instead — a literal substring search, not a regex, bounded by a wall-clock deadline.
What it does not cover. This setting governs the agent, not the application. Two things are installed and approved by their own separate routes and still launch:
- MCP servers. A stdio MCP server is a process on your machine, started by the MCP layer, not by the sandbox. Installing one goes through its own approval card.
- The browser. When she uses the browser, a real Chromium-family browser is launched on a
dedicated profile, or connected to via
KOTOBA_BROWSER_CDPif you point it at one. That launch does not consultKOTOBA_SANDBOXeither.
So none means "she runs no commands", not "nothing runs". If you need the stronger statement, do
not install MCP servers and do not use the browser toolset.
Choosing
| You want | Set |
|---|---|
| An assistant on your own machine, asked about everything that is not a read | local (default) |
| Commands genuinely isolated, at the cost of being asked less often | docker |
| No shell and no code execution from her at all | none |
Under docker, ordinary commands are out of scope for a vulnerability report — the container is the
boundary there, by design. Under local, anything she does after you approve a card is likewise
the product working as documented. See Reporting a vulnerability.
