# Keep your tools' code away from your keys

> kindgi dev runs your pack's code sandboxed, so the code you or a coding agent writes can't read your keys, other projects or the Docker socket. What it covers, what it doesn't, and how to open a path a tool needs.

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:

```text
  ✓ 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
```

`kindgi doctor` checks it too:

```text
  ✓ 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

| 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](https://docs.kindgi.com/v0.1.9/guides/secrets/env-files.md) 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.hooksPath` of `.husky/`, `package.json` scripts, and a `Makefile`. Review what the agent writes before you run it outside `kindgi dev`.
- **It covers `kindgi dev` only.** 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](https://docs.kindgi.com/v0.1.9/guides/secrets/pack-env.md#in-a-deployment).

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/repository` and 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

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:

- **TypeScript**

  ```ts excerpt
  // in kindgi.config.ts
  dev: {
    sandbox: {
      allowRead: ['~/.aws'],
      allowUnixSockets: ['/tmp/.s.PGSQL.5432'],
    },
  },
  ```

- **Python**

  ```toml excerpt
  # in pyproject.toml
  [tool.kindgi.dev.sandbox]
  allowRead = ["~/.aws"]
  allowUnixSockets = ["/tmp/.s.PGSQL.5432"]
  ```

- **Java**

  ```jsonc excerpt
  // in kindgi.config.json
  "dev": {
    "sandbox": {
      "allowRead": ["~/.aws"],
      "allowUnixSockets": ["/tmp/.s.PGSQL.5432"]
    }
  }
  ```

- **Scala**

  ```jsonc excerpt
  // in kindgi.config.json
  "dev": {
    "sandbox": {
      "allowRead": ["~/.aws"],
      "allowUnixSockets": ["/tmp/.s.PGSQL.5432"]
    }
  }
  ```

* **`allowRead`** lists paths the code may read.
* **`allowUnixSockets`** lists 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:

```text excerpt
  ✓ 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](https://docs.kindgi.com/v0.1.9/guides/secrets/dev-sandbox.md#what-it-closes-and-what-it-doesnt) stay closed wherever they are, even inside a path you open.

## Where it runs

- **macOS:** Seatbelt (`sandbox-exec`), always there. Inside another sandbox that restricts anything, such as a coding agent's, macOS doesn't let `kindgi dev` add its own: run it in your own terminal.

- **Linux:** [bubblewrap](https://github.com/containers/bubblewrap) (`bwrap`). Install it if your system doesn't have it:

  ```sh
  sudo apt-get install bubblewrap   # Debian, Ubuntu
  sudo dnf install bubblewrap       # Fedora
  ```

  Ubuntu 23.10 and later restrict the user namespaces bwrap needs, as [Ubuntu's announcement](https://ubuntu.com/blog/ubuntu-23-10-restricted-unprivileged-user-namespaces) explains. Allow it with an AppArmor profile for `/usr/bin/bwrap`, then reload AppArmor:

  ```sh
  printf 'profile bwrap /usr/bin/bwrap flags=(unconfined) {\n  userns,\n}\n' | sudo tee /etc/apparmor.d/bwrap
  sudo systemctl reload apparmor
  ```

  On Linux, the secret files in the app are found each time the pack service starts. A `.env` file created while `kindgi dev` runs stays readable until the next start, and a save restarts it.

- **Windows:** none `kindgi dev` can use. Run `kindgi dev` under 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:

```text
  ⚠ 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:

```text excerpt
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

`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:

```text
  ⚠ dev sandbox: off (KINDGI_DEV_SANDBOX=off): your tools' code can read your files and keys as you can
```

## Next

- [Keep local values in env files](https://docs.kindgi.com/v0.1.9/guides/secrets/env-files.md)
- [Declare the environment your code reads](https://docs.kindgi.com/v0.1.9/guides/secrets/pack-env.md)
- [Your coding agent](https://docs.kindgi.com/v0.1.9/start/coding-agents.md)
