Kin / Use Kin
Modes & permissions
How kin decides whether a tool call runs, asks, or is refused — and how you steer that with modes and per-session grants.
Read as MarkdownHow kin decides whether a tool call runs, asks, or is refused — and how you steer that with modes and per-session grants.
Outcome
Choose a calm default posture, understand exactly why an action asks, and approve or deny it without granting broader authority than intended.
Fast path
- Start in
autofor workspace-confined edits and sandboxed shell. - Press ++shift+tab++ when you want the ask-first
strictposture. - Read the approval title and native command/diff body.
- Choose Allow once for this call, Always for the displayed session scope, or Deny.
- Use Deny with a note when Kin should try a different approach.
- Run
/grantsto inspect or revoke remembered session approvals.
Permission is a property of the tool, not just the mode. Every tool declares
a risk kind; the active mode maps each kind to a decision.
The two modes
There are two cyclable modes, and ++shift+tab++ toggles between them:
| Mode | What it allows |
|---|---|
auto |
The default. Safe autonomy: workspace-confined edits and bounded first-party publication run without asking, and shell runs under an OS sandbox instead of prompting. MCP tools still ask. See Auto mode & the OS sandbox. |
strict |
The review posture: read and search freely; stop and ask before a file edit or an MCP tool; auto-allow provably read-only shell, ask for the rest. |
auto is the launch default and the more autonomous tier; strict is the calm, ask-first posture. The cycle is a 2-way toggle, auto ↔ strict.
Planning is no longer a mode + legacy aliases
Earlier versions had
accept-editsandplanmodes and a four-way cycle. Those are gone:accept-editsfolded intoauto, and planning became a transient freeze you enter by dispatching aplannersubagent (or with/plan) rather than a mode you cycle to (see Planning). The legacy names are still accepted on the CLI, insettings.toml, and in a resumed journal — they alias onto the new pair (default → strict,accept-edits → auto,plan → strict) with a deprecation note. This is the canonical home for the legacy alias map — every other doc links here.
Tools × decisions
Each mode maps a tool’s kind to one of: allow (run, no prompt), ask (park on an approval modal), deny (soft-refuse with a result the model reads and moves on), auto (shell only: classify the command — provably read-only runs, else ask), or sandbox (shell only, in auto: run under the OS sandbox).
| Tool kind | Examples | auto |
strict |
|---|---|---|---|
read |
read_file, grep, glob, ls, web_* |
allow | allow |
meta |
todos, ask, task, skill, shell_list/output/kill |
allow | allow |
edit |
write_file, edit_file, edit_notebook |
allow | ask |
shell |
shell, run_code |
sandbox | auto |
mcp |
external MCP tools, outpost-send; Beam-only git-push/git-pull |
ask | ask |
network |
git.fetch/sync (update alias), GitHub PR reads, Beam-only git-fetch |
allow | ask |
publish |
normal git.push, github.open_pr |
allow | ask |
browser |
browser |
allow | ask |
outpost |
outpost |
allow | ask |
MCP tools are arbitrary remote code, so they ask in both modes — they are never auto-allowed, not even in auto. (A read-only MCP tool from an approved server is adapted as a read tool instead, so those do not prompt.) See MCP. The mutating outpost-send tool and Beam-only structured push/pull tools reuse the mcp kind because those remote mutations always ask and are denied while planning.
The first-party git and github tools classify
each concrete operation rather than carrying one static risk kind. Local reads
are read; branch/stage/commit operations are edit; fetch, fast-forward
sync/update, and PR reads are network; normal push and opening a PR are
publish. Both network and publish allow in auto, ask in strict, and
are denied while planning. This lighter posture is reserved for Kin-owned
schemas with fixed argv and bounded destinations; arbitrary MCP calls still
ask in both modes. A force-with-lease push pins itself to ask in both modes,
offers no reusable grant, and is still denied during planning.
Beam’s separate structured git-fetch also uses network; its internal
push/pull checkpoint remains mcp. The ordinary and Beam schemas do not share
grants.
The browser tool gets its own browser kind with the same allow-in-auto / ask-in-strict split. It’s a separate kind from network (which is documented as git-fetch-only) because a browser session — remote JavaScript, attacker-controlled pages — is a different risk class whose gate must be tunable independently. The lighter auto posture is sound because the tool’s own boundary does the heavy lifting: only http/https URLs are accepted, every top-level navigation is SSRF-checked before the browser sees it, and downloads / dialogs / popups are refused automatically. Like network, it is denied while planning — a browser action can submit a form, a side effect the read-only freeze must catch.
The read-only outpost tool similarly gets its own outpost kind rather than reusing network: it’s a read from the model’s perspective (list jobs, fetch a report, read the inbox) against a bearer-authed, operator-owned Outpost endpoint, so it gets the same lighter allow-in-auto posture—but remote HTTP to an operator service is distinct from Beam’s bounded Git transport. It’s denied while planning too. The mutating half, outpost-send, is a separate tool with kind mcp (see above)—submitting, cancelling, or answering always asks.
While the planning freeze is active, edit / shell / mcp /
network / publish / browser / outpost are denied as an overlay on
top of whichever mode is active — the underlying auto/strict policy is
untouched and returns the moment you approve a plan or leave the freeze.
Auto edits to protected paths still ask
Even in
auto, an edit that targets a path which changes how the agent or its tooling runs — anything under.git/, a lockfile (uv.lock,package-lock.json, …),.npmrc/.yarnrc,.kin/mcp.json,.mcp.json,.claude.json, a shell rc file,.pre-commit-config.yaml,pyrightconfig.json,~/.ssh/config, or.kin/settings.tomlitself — is downgraded from auto-allow to ask, so it can’t silently rewrite repo state, the dependency supply chain, or the harness config. See Auto mode & the OS sandbox.
The shell allowlist
In strict mode, the shell kind maps to auto: a command is auto-allowed only when it is provably read-only, and otherwise falls back to the approval modal. (The run_code interpreter shares the shell kind but carries no command string to classify, so it always asks in strict — arbitrary code is never provably safe — and runs sandboxed in auto.) The classifier is conservative — it only ever downgrades ask to allow for commands it can prove safe, and anything uncertain stays ask. (In auto mode, shell maps to sandbox instead — the classifier still runs, but a command it can’t prove safe runs contained rather than prompting.)
It works in two tiers:
- Always-safe commands — read-only regardless of flags (
ls,cat,grep,pwd,wc,diff, the*sumhashers, and more). - Conditional commands — safe only when a per-command checker passes (
gitread-only subcommands likestatus/log/diff,findwithout-exec/-delete, and similar).
A command with command/process substitution, output redirection, or a leading VAR=value assignment is never auto-allowed. Set KIN_SHELL_ALLOWLIST=0 to turn the allowlist off and send every shell command through the approval modal.
Note
The allowlist downgrades a prompt to a run; it is not the security boundary. Containment for un-vetted commands comes from the OS sandbox in
automode — see Auto mode & the OS sandbox.
Tri-state approvals
When a tool call asks, the approval modal normally offers three answers:
| Answer | Effect |
|---|---|
Allow once (a, or bare ++enter++ — it’s the focused default) |
Allow this one call. |
Always (s) |
Allow this call and remember it for the rest of the session — the button label names exactly what it will cover (e.g. Always: \npm` commands`). |
Deny (d / ++escape++) |
Refuse this call; the model reads the refusal and adapts. |
Deny with a note (n) |
Refuse and steer: type one line (“use pnpm instead”) and enter — the note rides back to the model as the denial reason, so it adapts without a follow-up message. Esc closes the note input first; a second esc denies plain. |
A fresh-state action may deliberately omit Always. Force-with-lease is the canonical example: the user must review every attempt after Kin observes the current branch and destination; an earlier session signature cannot authorize it.
The modal shows the call in its native form per risk kind: a file edit
renders the actual unified diff it would apply (computed with the same match
path the tool runs — what you approve is what happens); a shell command
renders syntax-highlighted with its timeout/background flags as dim metadata;
network, publish, and MCP calls render labeled argument rows (with a note that MCP
argument schemas come from the external server); anything else falls back to
the raw argument dump. The title names the kind (file edit · src/x.py,
shell command, MCP tool · server › name) and carries an amber accent only
for the riskier kinds (shell / MCP / network / publish / browser) — a plain file edit
reads calm. A bare edit_file/write_file “always” covers every future
edit this session, so those prompts carry an explicit “prefer allow once”
note.
The approval body shows the real action; the Always label names the scope it would remember for this session.
“Always” remembers a signature, not the literal call. A shell command groups
by its command head — approving npm once auto-allows later npm … calls —
but only for a single command with no substitution or redirect, so approving
one head can never green-light a different command hidden behind a ; or
&&. A compound command is scoped to that exact command string instead.
Ordinary Git/GitHub grants bind the repository, operation, remote, and branch;
GitHub publication grants also bind the base branch.
Beam Git grants bind remote + branch + that tool’s one risky flag. Every
other non-shell tool groups by tool name. The grant only ever turns an ask
into an allow; it never relaxes a deny, so the planning freeze
still refuses mutation and publication.
This tri-state modal governs ordinary mode/tool approvals. A
sandbox_permissions request
uses a separate capability modal because it must let you remove individual
paths/network/SSH authority and choose Once, Session, or Workspace without
turning a command head into a standing sandbox bypass.
Cycling modes
The mode is a human-only control — the model cannot call set_mode, so it can never self-escalate its own permissions. You change it three ways:
- ++shift+tab++ toggles between
autoandstrict. /modewith no argument toggles the same 2-way cycle./mode <name>sets a specific mode (autoorstrict; the legacydefault/accept-edits/planare still accepted — see legacy-mode-aliases).
The cycle is a 2-way toggle, auto ↔ strict. See Keybindings for the full key map.
Planning
Planning is a transient, read-only freeze on the session — not a mode.
While it is active, edits, shell, MCP, network, and publish tools are refused
(an overlay on top of auto/strict); reads, search, and todos still run,
so the model can research and write a plan but cannot act.
There is a deliberate asymmetry: the model may enter the freeze, but only you can leave it.
- The model enters planning by dispatching a read-only
plannersubagent (task(subagent_type='planner', ...)) when a task needs design before action; the subagent researches read-only and drafts the plan withwrite_plan, whose first call auto-enters the freeze on the top-level session — entering a read-only freeze only reduces capability, so it is safe to self-trigger this way. - You enter or leave it with
/plan:/plan <task>enters the freeze and sends<task>; bare/plantoggles the freeze (your escape hatch — ++shift+tab++ no longer controls planning). - The model presents a finished plan with the
present_plantool, which raises an approval modal. Only your choice there (or a bare/plan) lifts the freeze.
A ⏸ planning badge sits in the status bar’s left activity group (the mode badge is on the right) while the freeze is active. The freeze propagates to subagents, so a mid-plan task(...) stays read-only too. It is never persisted — a resumed session lands in strict (edits/shell ask-gated), and the unfinished plan survives as its file under .kin/plans/.
When you approve a plan, the modal offers three actions:
| Action | Effect |
|---|---|
| Approve & execute | Lift the freeze and carry out the plan with the current (primed) context. The default. |
| Approve, clear & re-inject | Drop the planning chatter and reseed history with just the plan plus an execution preamble — best for a large task where the research exploration would otherwise crowd the context. Plans are written for exactly this: the planner holds them to a self-containment bar (exact files, anchors, constraints, verification) so the plan can serve as the executor’s only context. |
| Review plan first (adversarial) | Sends the plan to a read-only critic agent. Kin can apply bounded targeted findings to the saved plan and re-present it while general file writes remain denied. Stays frozen. |
Pressing ++escape++ keeps planning. The plan is saved under
<workdir>/.kin/plans/, so review can survive a quit. Tool argument and
replacement mechanics are maintainer material in
REFERENCE.md; see
Configuration for the
user-facing file behavior.
Model-writable settings
The model can help tune your settings mid-conversation through the read_settings / propose_settings tools — turn the guidance on with /settings. Every change still goes through a human diff-approval modal (the same gate as /edit-settings), and a hard boundary limits what the model can touch. See the settings.toml reference for what each key does:
| Tier | Keys | The model can… |
|---|---|---|
| Model-writable | temperature, top_p, top_k, max_tokens, context_window, enable_thinking, effort, thinking_type, cache_ttl, model |
…propose a change (you approve a diff). Applies to new sessions. |
| Human-only | mode, sandbox, sandbox_strict, sandbox_grants, sandbox_always_allow, git_host_grants, shell_allowlist (containment/trust); api_key, base_url, brave_api_key (secrets/endpoints); provider, provider_preset, providers, model_favorites (presets); reasoning_effort (cross-wire conflict — use the wire-agnostic effort knob, which the picker translates per-serve; a writable reasoning_effort would let the model bypass the picker and write a value the Anthropic wire won’t read) |
…never. The tool refuses these with a banner naming the key — use /grants, /models, the key prompts, or hand-edit non-secret settings in ~/.kin/settings.toml. |
The model-writable set is an explicit positive allowlist, not “everything except the secrets” — a knob is denied until it’s deliberately added to the list, so a future setting can’t silently become model-writable. propose_settings is top-level only (a subagent can’t propose), is frozen during planning, and never writes secrets or endpoints (the diff masks every credential and endpoint value — api_key, base_url, and any nested [[providers]] token — so your private endpoint never rides into model context). Changing the live session is still a human-only act: use /mode and /model. read_settings masks all credentials to [SET]/[NOT SET].
Troubleshooting
- Always did not apply: remembered grants match the scope printed on the button. A different command head, repository/branch/remote, tool, server definition, Git route fingerprint, or Beam target asks again.
- An edit asks in
auto: protected paths such as lockfiles,.git/, MCP config, and Kin settings deliberately keep a human gate. - Mode will not change: planning is a freeze over the active mode. Review or dismiss the plan flow; ++shift+tab++ does not bypass it.
- A command was not classified read-only: uncertainty falls back to asking. Do not expand the allowlist merely to silence a legitimate prompt.
Managing grants
/grants opens one management surface for ordinary session “always allow”
signatures, session and persistent Git host-route trust, sandbox session
grants, denial-bound exact-operation session bypasses, and typed workspace
grants. Legacy sandbox_always_allow patterns also appear, clearly marked
inactive — reapproval required; they grant nothing in current Kin. For
scripted or quick use, /grants revoke <number|key> revokes one directly (and
clears the persistent trust store for an MCP-server grant so it is not silently
re-trusted next launch).
Sandbox revocation is live: Kin stops background shells started with expanded
authority and closes an expanded run_code kernel, so a process cannot keep a
capability after its grant disappears. Ordinary, Git-host session, and sandbox
session grants end when Kin closes. Workspace sandbox grants and Git host-route
grants are global human-owned settings; revoking one removes it from future
sessions too.