Kin / Reference
Auto mode & the OS sandbox
auto mode lets the agent edit and run commands without per-call prompts, while a kernel-enforced sandbox keeps shell commands contained. This is safe autonomy — a contained replacement for "YOLO" mode — and it is the…
Read as Markdownauto mode lets the agent edit and run commands without per-call prompts, while a kernel-enforced sandbox keeps shell commands contained. This is safe autonomy — a contained replacement for “YOLO” mode — and it is the launch default.
What auto does
In auto mode, file edits and shell commands run without asking:
- Edits (
write_file,edit_file,edit_notebook) auto-allow, but are confined to the workspace. An edit that targets a path outside the workspace is refused with a result the model reads, not prompted. A write to a protected path is downgraded to ask instead of auto-allowed. - Shell commands run under the OS sandbox instead of prompting.
- Bounded Git/GitHub publication auto-allows through the first-party structured tools. Force-with-lease still asks every time.
- MCP tools still ask. An MCP tool’s description is attacker-influenceable, so it is never auto-approved, even here.
auto is the default and one half of the 2-way cycle (auto ↔ strict); toggle to strict and back with ++shift+tab++ or a bare /mode. See Modes & permissions.
The OS sandbox
A command-pattern allowlist is not a security boundary — a shell composes unbounded ways to dodge a lexical filter — so the real containment in auto mode is an OS sandbox the kernel enforces regardless of what the classifier thinks.
| Platform | Backend | Mechanism |
|---|---|---|
| macOS | Seatbelt | sandbox-exec with a generated SBPL profile |
| Linux | bubblewrap | bwrap with a read-only root, a writable workspace bind, and a private /tmp |
The allowlist classifier still runs (a provably-safe git status is the common case); the sandbox is the layer that holds when the classifier is wrong.
What’s confined
Under the sandbox a shell command gets:
- Writes confined to the workspace — plus the per-user temp dir and
/tmp. The workspace’s.git/hooksand.git/configstay read-only (so a command cannot plant a hook), while the rest of.gitstays writable so the agent can stillgit add/commit. - Network available by default — package managers, HTTP clients, public Git transports, and live tests work inside the filesystem sandbox. Set global-only
sandbox_network = false(orKIN_SANDBOX_NETWORK=0) to restore full network isolation; with networking off, sandboxeduv/uvx/taskruns also getUV_NO_SYNC=1so uv runs against the already-populated.venvinstead of dying on a DNS-blocked implicit sync. - Secrets and SSH identity scrubbed by default — API keys, tokens, and any
*_KEY/*_TOKEN/*_SECRET/AWS_*variable are stripped before the command runs. Credential stores (~/.aws,~/.netrc, and similar),SSH_AUTH_SOCK, SSH client configuration, and private keys stay masked. Network authority and SSH identity are separate: a command that genuinely needs agent-backed SSH asks for both network and SSH-agent access through the scoped access flow. The grant exposes only the agent socket and read-only public client metadata; private-key files remain hidden.
The same posture covers the run_code interpreter and diagnostics subprocesses: each uses the resolved global network setting, workspace confinement, and secret scrub.
This is a containment boundary, not a confidentiality promise against the network. A networked command can transmit any readable workspace content. An SSH-agent grant can additionally ask your agent to authenticate, but cannot read its private keys. Use sandbox_network = false when network egress itself is unacceptable; omit or decline SSH access when identity use is unacceptable.
Protected-write paths
The OS sandbox only wraps shell subprocesses — write_file / edit_file write in-process, so in auto mode they also carry their own containment. Beyond the workspace-confinement check above, a write to a path whose modification would change how the agent or its tooling runs is downgraded from auto-allow back to ask, even in auto:
- anything under a
.git/tree (repo state), - lockfiles —
uv.lock,package-lock.json,yarn.lock,pnpm-lock.yaml,Cargo.lock,poetry.lock,Gemfile.lock,go.sum(the audited dependency supply chain), - package-manager / registry config —
.npmrc,.yarnrc,.yarnrc.yml, - harness / tooling config —
.kin/mcp.json,.mcp.json,.claude.json,.pre-commit-config.yaml,lefthook.yml,pyrightconfig.json, - shell rc / env files —
.bashrc,.zshrc,.bash_profile,.profile,.envrc, ~/.ssh/config— the SSH config thesshtool trusts (a rewrite could repoint a deploy alias or inject aProxyCommand),.kin/settings.toml— the file that governs this very gate (mode, sandbox, permissions), so it can’t quietly loosen itself.
This is the write-side companion to the read-deny list (which guards secret exfiltration); it guards a silent self-rewrite of the dev environment. The match is realpath-aware, so a symlink or .. spelling that resolves onto one of these can’t dodge it. In strict mode every edit already asks, so this only changes behavior in auto.
Structured Git credentials and Beam supervision
The generic shell remains the development surface for SSH, package managers,
HTTP clients, unusual Git administration, and live tests. It stays inside
Seatbelt/bubblewrap with secrets and credential stores masked. Everyday Git
instead uses the first-party git tool: fixed argv,
workspace/repository confinement, and only SSH or HTTPS remotes. The
github tool uses the installed gh CLI for bounded
PR reads and creation.
These structured tools own a narrow credential exception that generic shell does not inherit. Before remote Git can use an SSH agent or global/system HTTPS credential helper, Kin asks in a dedicated modal for the resolved host route. The session and persistent lifetimes store no secret: only transport, resolved host/user/port, and a fingerprint of the effective trusted route. Repository config cannot install a credential helper, proxy, include, askpass, or SSH command for this path.
For a recognized github.com remote, Kin adapts both saved SSH and HTTPS URLs
to one command-scoped authenticated HTTPS route. The saved remote is not
edited. Only gh configuration and the platform keyring are unmasked for that
fixed helper; private SSH keys and unrelated Git credential files remain
hidden. Kin asks gh to emit only token-free account fields, and inherited
GH_* / GITHUB_* tokens are stripped. If no healthy account exists, one
human connection modal parks the operation, runs the fixed web login outside
the model, then continues that same operation. This is a credential handoff,
not a generic sandbox expansion.
Safe sync’s local history mutation uses the ordinary workspace sandbox and fixed Git argv. It starts only from a clean repository root, pins the exact pre-sync object, disables executable/history side channels, and restores that object on conflict or interruption. Its recovery refs carry object IDs, not credentials or new authority.
sandbox_network = false controls generic sandboxed shell egress. It does not
disable an explicitly selected structured Git/GitHub operation: those tools
have their own network / publish permission kinds and host/auth boundary.
Disable or remove the remote/auth broker if that capability should not exist,
or use strict so every network/publication call asks.
Beam remains separate. An Outpost child whose authenticated
v1_metadata.beam value is exactly JSON true receives the internal
KIN_BEAM=1 posture. Only that child registers git-push / git-pull /
git-fetch, and its shell refuses raw versions of those verbs. The exact
structured git-push call remains the bounded halt/approval/resume checkpoint;
ordinary git / github schemas and their grants are not registered there.
Fail-closed
When no sandbox backend is available — an unsupported platform, a missing bwrap, or KIN_SANDBOX=off — auto mode does not run commands unconfined. Edits still auto-apply (confined to the workspace), but shell falls back to the allowlist’s posture: a provably read-only command runs, and anything else asks for approval. An un-vetted command never runs without either containment or your explicit approval.
The one deliberate exception is KIN_SANDBOX=container, which trusts an external boundary (a disposable container) in place of the OS sandbox — see Trust the container below.
kin surfaces the containment posture so it’s never a surprise. On a cold start in auto, it appears as a concise dim line in the welcome banner — e.g. “auto · shell asks for anything not provably safe”, “auto · shell sandboxed (Seatbelt)”, or “auto · shell runs unconfined (container is the boundary)”. When you cycle into auto mid-session (where there’s no banner), kin prints the fuller posture as a one-off system note instead — and with no OS sandbox it says so loudly: “auto mode — no OS sandbox available; edits auto-apply (confined to the workspace), and shell will ASK for anything not provably safe.”
Warning
Fail-closed is the load-bearing safety property. Cycling into
autoon a box with no sandbox is safe precisely because a non-safe command still asks rather than running unconfined.
Scoped sandbox access
The sandbox can now widen the smallest relevant capability without discarding
the whole boundary. shell and run_code may request a typed
sandbox_permissions object before execution:
write_paths— up to 16 existing directory roots that need writes;network— outbound network access;ssh_agent— the SSH agent socket plus read-only public client metadata (requiresnetwork = true);unconfined— run this exact command with the sandbox fully lifted, when no granular capability can express what it needs (a syscall the profile blocks). This is the escape hatch, gated by one modal that shows the command before it runs; it caps at Session lifetime and still scrubs secrets from the environment. Requesting it prospectively is the intended alternative to provoking a denial just to obtain a retry id.reason— the model’s explanation, shown to you but never treated as authority.
External directories are intentional. A request is not limited to the current
repository, so a build can ask for its real cache, sibling checkout, or output
directory. Kin canonicalizes every path, requires an existing directory the
current user can actually write, rejects virtual/device trees and
filesystem-wide ancestors such as / or your home directory, and keeps the
workspace, temp roots, .git hook/config protections (including in an
approved sibling repository), credential masks, and secret scrub as
higher-priority rules.
You get one dedicated modal showing the immutable executor, working directory, operation, reason, and requested capabilities. You may deselect capabilities and choose:
- Once — only this call.
run_codeuses a fresh one-shot kernel so the permission cannot leak into later cells. - Session — subsequent sandboxed executors may use the selected capabilities until Kin closes.
- Workspace — persist the selected profile for this exact canonical workspace in global settings.
- Decline or Decline with a note — no expanded execution occurs and the model receives your reason.
Sensitive/system write roots and a complete sandbox bypass are capped at Session. Ordinary external directories, network, and SSH access may be workspace grants. The modal is the approval gate; Kin does not add a second generic tool-approval prompt.
Reactive denials and exact retry
When shell or run_code is denied, Kin classifies Seatbelt/bubblewrap
fingerprints including EPERM, Linux EROFS / [Errno 30] Read-only file system, network isolation, and SSH authentication/configuration failures. A
recognized denial returns an opaque sbx_… denial id and a suggested granular
profile. The model calls:
request_sandbox_access(denial_id="sbx_…")
The model does not repeat or reconstruct the command/code. The harness
retains the exact executor arguments and working directory, consumes the id
once, asks you for a capability subset and lifetime, then retries that
immutable operation. A shell command may have partially executed before the
denial; the modal says so. A run_code retry uses a fresh kernel and says that
state is not carried across.
If no granular path/network/SSH capability can express a real classified
denial, the reactive request may propose unconfined = true. That is the escape
hatch, not the normal path: it is bound to the exact operation, never persists
beyond the session, and still uses Kin’s strict unconfined environment scrub.
The same escape hatch can be requested prospectively — a shell/run_code
call whose sandbox_permissions carries unconfined = true — so that when the
model already knows a command needs the sandbox lifted (and you have said so), it
asks once, showing you the actual command, instead of provoking a denial to farm
a retry id. It always prompts: a prospective bypass never rides auto mode’s
silent allow.
DNS and routing failures are classified against the resolved network posture.
With sandbox_network = false, common resolver and no-route errors are
Kin-owned isolation denials. With networking already enabled, the same output
records no denial id because removing Kin’s filesystem boundary would not fix
an upstream DNS, proxy, or host failure.
Inheritance, resume, and revocation
Child agents cannot open approval UI. They report the required capability or denial id to the parent; session/workspace grants resolved by the root are then part of the root’s effective sandbox profile. In headless mode a request is journaled as needs-human. Resuming that journal in the TUI reopens the typed modal before the next model turn and, if accepted, retries the exact persisted arguments.
/grants lists ordinary approval
signatures, Git host-route trust, sandbox session grants, exact-operation
session bypasses, and workspace grants together. Revoking sandbox authority
also stops expanded background shells and closes an expanded run_code kernel
so a live process cannot retain authority that the UI says is gone. Revoking a
persistent Git host route makes the next remote Git operation ask again.
Legacy sandbox_always_allow command patterns are still readable for
migration, but are deliberately inactive: they cannot skip the sandbox.
/grants labels them as requiring reapproval and can remove them. The old
ask_sandbox_override name remains only as a dispatch alias for short-lived
compatibility; its old command/pattern arguments are rejected.
Note
Scoped sandbox access is registered only when Kin owns a usable local sandbox. Under
KIN_SANDBOX=offorKIN_SANDBOX=containerthere is no inner boundary to expand, so proactive requests fail closed and the reactive tool is absent.
Choosing a backend
KIN_SANDBOX selects the backend (or the matching sandbox key in the global settings file):
| Value | Behavior |
|---|---|
auto |
Resolve by platform — Seatbelt on macOS, bubblewrap on Linux. The default. |
seatbelt |
Force the macOS Seatbelt backend. |
bwrap |
Force the Linux bubblewrap backend. |
off |
Disable the sandbox. auto mode then falls back to the allowlist’s ask posture (fail-closed). |
container |
Trust an external boundary. No OS sandbox, but auto mode’s shell runs allowed and unconfined instead of asking — see below. |
Networking is a separate global-only switch:
| Env | Settings key | Effect |
|---|---|---|
KIN_SANDBOX_NETWORK=0 |
sandbox_network = false |
Isolate networking inside Seatbelt/bubblewrap and restore network-denial override hints. |
It has no effect under KIN_SANDBOX=off or container, where Kin does not own the network boundary.
Trust the container
KIN_SANDBOX=container is for running kin inside a disposable, isolated container (or VM) that is already the security boundary — the Outpost sets it by default. It is the opposite of off:
off— “no sandbox, and nothing replaces it” → fail-closed: a non-safe shell command asks.container— “no sandbox because the environment is the boundary; trust it” → a non-safe shell command runs, unconfined, with the full environment.
Use it only where the boundary is real, because it removes the per-command approval for shell. It is a deliberate, operator-set grant — kin never auto-detects a container and silently drops the gate (that would let an injected prompt enumerate its own relaxed posture, false-positive on VMs/WSL, and erase the audit trail). And it is narrow: it relaxes only the shell gate the OS sandbox stood in for — MCP still asks, the planning freeze still denies, and the protected-write downgrade still asks. The launch note announces it (“auto mode — external sandbox trusted (KIN_SANDBOX=container)…”) so the grant is visible in the transcript.
You can also opt strict mode into the same sandbox so an un-vetted command runs contained instead of prompting:
| Env | Settings key | Effect |
|---|---|---|
KIN_SANDBOX_STRICT=1 |
sandbox_strict = true |
Sandbox shell in strict mode |
KIN_SANDBOX=off always wins over the per-mode opt-in. The sandbox and per-mode keys are global-only, so a cloned repo’s project file cannot weaken your containment — see settings.toml reference.
Legacy
sandbox_defaultThe old
KIN_SANDBOX_DEFAULT/sandbox_defaultknob (and the now-removedsandbox_accept_edits) predate the two-mode collapse.sandbox_defaultis still honored as a fallback forsandbox_strict— since the olddefaultmode now aliases tostrict, an existingsandbox_default = truekeeps flipping the same opt-in rather than going inert.
For the full “why it is safe” invariants — the SBPL profile, the bubblewrap argv, and the security guarantees — see the “Auto mode & OS sandbox” section of REFERENCE.md.