Keep your tools' code away from your keys
This page describes the sandbox kindgi dev runs your pack's code in: what
it closes, what stays open, how to open a path a tool needs, and where it
runs.
Under kindgi dev, your pack's tools run in a pack service on your machine,
as you. A coding agent often writes much of that code. So kindgi dev runs
the pack service sandboxed. The code reaches what it needs: the app, the
runtime that runs it, and the network. It doesn't reach the rest of what you
can reach.
The indexer runs sandboxed too. It loads every tool, agent and flow module to list them, and with them any code at a module's top level.
At start, kindgi dev says the sandbox is on:
✓ dev sandbox: macOS Seatbelt: your tools' code can't read your keys or files outside the app, write outside it, or reach Docker; it still has the network and the app's own fileskindgi doctor checks it too:
✓ Dev sandbox: kindgi dev runs your tools' code sandboxed (macOS Seatbelt): it can't read your keys or reach Docker.What it closes, and what it doesn't
Section titled “What it closes, and what it doesn't”| Closed to your tools' code | Still open |
|---|---|
Your home folder: SSH keys, cloud credentials (~/.aws, ~/.config/gcloud), registry tokens (~/.npmrc), other projects |
The app's own files, read and write: its code and its data |
Secrets in the app, wherever they are: .env*, Kindgi's .kindgi folder, .git, kindgi.env, pack.env, .kindgirc.json, .npmrc, .pypirc, .netrc |
The runtime that runs it and the app's dependencies, to read |
The Docker socket, and every other UNIX socket (ssh-agent, a local database's socket); on Linux, those under the home folder, /tmp, /var/tmp and /run (Docker's, ssh-agent's, a local database's): a socket elsewhere stays reachable |
The network: any API a tool calls |
| Other tools' temp files; on Linux, other processes too | Its own temp folder (TMPDIR) |
Writing anywhere outside the app: on macOS every other folder (/opt/homebrew, /Users/Shared, /Applications), where a program it replaced would run later outside the sandbox; on Linux the system is read-only |
What kindgi dev hands it: the app's own settings from its env files, and the secrets a tool declares (ctx.secrets) |
Writing Kindgi's configuration (kindgi.config.*, pyproject.toml): kindgi dev loads it, and it says how wide the sandbox is |
|
On macOS, the keychain, and LaunchServices and Apple Events (opening an app, or a file with one, outside the sandbox); on Linux, the terminal kindgi dev runs in (the code gets its own session) |
A process the code starts, such as a git or a curl, is inside the
sandbox too.
What it doesn't do:
- It doesn't stop the network. Code that can read a value can send it
anywhere. So what the pack service is handed matters as much. That's why
Kindgi's own secrets and your model provider keys never reach its
environment, as Keep local values in env files explains.
On Linux, the network includes abstract UNIX sockets, which share its
namespace: an X11 display's
@/tmp/.X11-unix/X0, and some D-Bus buses. - It doesn't protect the app's own files, or what runs them later. A tool
can read and change your app's code and data, as the coding agent can.
That includes files something outside the sandbox runs later: git hooks in
the tree, such as a
core.hooksPathof.husky/,package.jsonscripts, and aMakefile. Review what the agent writes before you run it outsidekindgi dev. - It covers
kindgi devonly. In a deployment, the pack's image is the boundary, and the pack service keeps only the names the pack declares. See Declare the environment your code reads.
The runtime is opened to read wherever it is, and nothing else around it:
- Node.js, from nvm or Homebrew;
- a Python interpreter's own paths, such as a venv, or a uv-managed Python
under
~/.local/share/uv; - a JDK and every jar on the pack's classpath, such as
~/.m2/repositoryand coursier's cache.
A path that would open your whole home folder is never opened, and
kindgi dev says so.
Open a path or a socket a tool needs
Section titled “Open a path or a socket a tool needs”A tool that uses the AWS SDK's credential chain reads ~/.aws. A tool that
talks to a local Postgres over its socket connects to /tmp/.s.PGSQL.5432.
Over TCP, localhost:5432 needs nothing. Open what a tool needs in the
pack's config, rather than turning the sandbox off:
// in kindgi.config.tsdev: { sandbox: { allowRead: ['~/.aws'], allowUnixSockets: ['/tmp/.s.PGSQL.5432'], },},# in pyproject.toml[tool.kindgi.dev.sandbox]allowRead = ["~/.aws"]allowUnixSockets = ["/tmp/.s.PGSQL.5432"]// in kindgi.config.json"dev": { "sandbox": { "allowRead": ["~/.aws"], "allowUnixSockets": ["/tmp/.s.PGSQL.5432"] }}// in kindgi.config.json"dev": { "sandbox": { "allowRead": ["~/.aws"], "allowUnixSockets": ["/tmp/.s.PGSQL.5432"] }}allowReadlists paths the code may read.allowUnixSocketslists UNIX sockets the code may connect to.
A path is absolute, starts with ~/, or is relative to the pack's root.
kindgi dev names each opening at every start. So an opening an agent adds
shows on screen as well as in the diff:
✓ dev sandbox: macOS Seatbelt: your tools' code can't read your keys or files outside the app, write outside it, or reach Docker; it still has the network and the app's own files also reads /Users/…/.aws (dev.sandbox.allowRead)The secret files in the table in What it closes, and what it doesn't stay closed wherever they are, even inside a path you open.
Where it runs
Section titled “Where it runs”-
macOS: Seatbelt (
sandbox-exec), always there. Inside another sandbox that restricts anything, such as a coding agent's, macOS doesn't letkindgi devadd its own: run it in your own terminal. -
Linux: bubblewrap (
bwrap). Install it if your system doesn't have it:Terminal window sudo apt-get install bubblewrap # Debian, Ubuntusudo dnf install bubblewrap # FedoraUbuntu 23.10 and later restrict the user namespaces bwrap needs, as Ubuntu's announcement explains. Allow it with an AppArmor profile for
/usr/bin/bwrap, then reload AppArmor:Terminal window printf 'profile bwrap /usr/bin/bwrap flags=(unconfined) {\n userns,\n}\n' | sudo tee /etc/apparmor.d/bwrapsudo systemctl reload apparmorOn Linux, the secret files in the app are found each time the pack service starts. A
.envfile created whilekindgi devruns stays readable until the next start, and a save restarts it. -
Windows: none
kindgi devcan use. Runkindgi devunder WSL, where it uses the Linux one. -
Inside a container, such as a dev container or CI: the container is the boundary, and bwrap can't run inside a default one.
Where it can't run, kindgi dev warns at start and runs your tools without
it:
⚠ Your tools run without the dev sandbox on this system: they can read your files and keys. Why: kindgi dev is already inside a sandbox (a coding agent’s?) that won’t let it add its own. Run kindgi dev in your own terminal. More: https://docs.kindgi.com/v0.1/guides/secrets/dev-sandbox/. To refuse to run without it: KINDGI_DEV_SANDBOX=required; to drop this warning: KINDGI_DEV_SANDBOX=off.That one is from a Mac, inside another sandbox. The Why: line names the
reason on each system.
With the sandbox on, kindgi dev starts only with one Kindgi configuration
in the app. The code it runs can't change the one in use, but it could add
another under a name that's looked up first:
kindgi dev: more than one Kindgi configuration in /Users/…/acme-probe: kindgi.config.ts, kindgi.config.mts. With the dev sandbox on, kindgi dev starts with one only: the code it runs can't change it, but could add another by a name looked up first. Keep the one you use and remove the others.Require it, or turn it off
Section titled “Require it, or turn it off”KINDGI_DEV_SANDBOX sets it for a machine or a shell:
| Value | What kindgi dev does |
|---|---|
on (default) |
Runs the pack service sandboxed where it can; elsewhere, warns and runs it without. |
required |
Runs it sandboxed, or stops before anything starts. |
off |
Runs it without, and says so at every start. |
For one project, dev: { sandbox: false } in its config turns it off.
KINDGI_DEV_SANDBOX wins over it. With the sandbox off, kindgi dev says
so:
⚠ dev sandbox: off (KINDGI_DEV_SANDBOX=off): your tools' code can read your files and keys as you can