Kin / Reference
settings.toml keys
Persistent configuration keys, defaults, scope, and reviewed settings changes.
Read as MarkdownReference for persistent Kin configuration. Configuration explains precedence, live reload, and which saved values resume restores.
File locations
kin reads layered TOML files, parsed with the standard-library tomllib (no extra dependency). The kin home (~/.kin, relocatable with KIN_HOME) holds separate stores with distinct owners:
| File | Path | Role |
|---|---|---|
| Global | ~/.kin/settings.toml |
Your machine-wide baseline — preferences and the provider catalogue. The file this page’s keys normally live in. |
| Credentials | ~/.kin/credentials.toml |
Kin-managed secret store (provider_keys, api_key, brave_api_key), created 0600. Written by kin connect, /models, and the key prompts—you never need to edit it. |
| ChatGPT sign-in | ~/.kin/chatgpt_auth.json |
Kin-managed private OAuth state for direct personal subscription access. Written by ChatGPT sign-in and refresh; removed by kin logout chatgpt. Never edit or share it. |
| Kinra profile cache | ~/.kin/kinra_profile.json |
Kin-managed, atomic 0600 last-known-good service profile, bounded login identity, and the exact fields Kin last applied. It contains no credential. A 12-hour refresh advances only fields still equal to Kin’s prior value; a divergent value is an operator pin. A managed route that is absent altogether is restored, and a managed assignment is while Kinra is the active provider. |
| Grants | ~/.kin/grants.toml |
Kin-managed authority stores (workspace_trust, git_host_grants, plus temporarily retained inert migration rows). Audit it freely; manage it via /grants and the approval modals. |
| Project | <workdir>/.kin/settings.toml |
Per-repo overlay, merged on top of the global layer. |
The TOML layers merge into one view — every key below behaves identically wherever it is stored; the sibling files exist so settings.toml stays a short file that is entirely yours. A secret or grant key found in a legacy settings.toml still works and moves to its sibling file automatically on the next settings write. Run kin settings to see the effective configuration, which file supplies each key, and configuration lint.
The JSON profile cache and OAuth store are separate from the TOML settings view.
The project file overlays the global layer key by key. Every file is optional — a missing or malformed file degrades to “no settings” with a warning (set KIN_STRICT_SETTINGS=1 to raise instead). A fresh machine’s first interactive run writes a short comments-only starter that links to this full key reference.
Project files can’t set global-only keys
A handful of keys are stripped (with a warning) from any project
.kin/settings.toml— see Global-only keys. Set those in the global file, the environment, or a flag.
Keys
The scope column marks whether a key may live in a project file (project-ok) or only in the global file / environment / CLI (global-only).
Backend and model
| Key | Meaning | Default | Scope | Env |
|---|---|---|---|---|
provider |
Client/wire family: openai selects the OpenAI-compatible family; anthropic is the stable spelling for Messages-compatible endpoints (Anthropic, Z.ai, MiniMax, or local /v1/messages). It does not require an official Anthropic account. |
openai (or anthropic when a bare base_url is set and neither identity axis is selected) |
project-ok | KIN_PROVIDER |
wire_api |
OpenAI wire shape: public values chat or responses. Either implies provider = "openai" and bypasses the bare-URL Anthropic heuristic. provider = "openai" without this key and the built-in OpenAI preset both remain on Chat. Anthropic Messages is an internal value, not valid settings input. |
chat for OpenAI |
project-ok | KIN_WIRE_API |
model |
Model id. Set to default (any stable alias you register with your server) to follow whatever model is currently loaded; the harness probes /v1/models on session start and piggybacks response.model off each turn so the displayed name always reflects the live server. The alias survives model swaps on the wire. |
gpt-4o-mini (openai) / claude-opus-4-8 (anthropic) |
project-ok | KIN_MODEL |
base_url |
Compatible endpoint URL for the default backend (trailing /v1 trimmed for the Messages wire). A selected provider preset owns its endpoint; only an explicit caller URL overrides that preset identity. |
none | global-only | KIN_BASE_URL |
api_key |
Endpoint auth. | none (not-needed is sent) |
global-only | KIN_API_KEY / OPENAI_API_KEY / ANTHROPIC_API_KEY |
context_window |
Advertised context window used to derive prompt limits and the below-composer current / hard-limit display. Pins/overrides numeric server limits (max_model_len / context_length / max_input_tokens from /v1/models); capability metadata is still discovered. |
200000, then refined from the server’s /v1/models metadata when base_url is set |
project-ok | KIN_CONTEXT_WINDOW |
max_tokens |
Per-response output cap. When unset, a versioned Kinra model profile may replace the generic wire default with limits.recommended_max_output_tokens; any file, environment, route, or resumed-session pin wins, and the recommendation must fit the effective context window plus any advertised output ceiling. |
16000 (anthropic) / 8192 (openai), then optionally refined by a service profile |
project-ok | KIN_MAX_TOKENS |
provider_preset |
Provider preset id (openai, anthropic, minimax, zai, or a [[providers]] id). Bundles wire/endpoint/auth identity; curated rows also supply a default model, while direct OpenAI/Anthropic keep it explicit. |
none | global-only | KIN_PRESET |
provider_keys |
Per-provider key table ({<preset_id> = "<key>"}) — the recommended shape when you swap between multiple providers. Read first by the factory’s chain (most specific selector — beats the generic api_key + env vars) so a stray ANTHROPIC_API_KEY baked into an image can’t silently shadow the key you stored for the active provider. /providers writes here when you paste a key. |
none | global-only (also _SECRET_SETTINGS_KEYS — redacted wholesale to [SET] on read) |
— |
provider_tiers |
Per-provider capability tier table. MiniMax uses plus / max / ultra and Z.ai uses lite / pro / max to size ordinary subagent capacity. /providers writes the selected tier. |
{} (provider defaults: MiniMax plus, Z.ai lite) |
global-only | — |
model_favorites |
List of "provider_id/model" strings the bare /model picker pins to the top (a ★ Favorites group). Toggle a row with f in the picker. |
[] |
global-only | — |
model_routes |
Named route tables ([model_routes.<id>]) that bind a trusted provider preset to an explicit model and optional capability/execution overrides. See Model routes. |
{} |
global-only | — |
model_assignments |
Operator bindings for mca, vision, task, and utility (housekeeping side-calls), plus [model_assignments.profiles]. Values are route ids. |
{} |
global-only | — |
Custom endpoints
A custom endpoint is a [[providers]] array-of-tables block — a self-contained
preset (id, wire, base URL, default model) you can refer to from
provider_preset. Use a distinct id: built-in ids take precedence over
custom duplicates. The first matching custom row is authoritative; if it is
invalid, a later duplicate cannot rescue it. An unknown or invalid selected
provider_preset fails with exit 1 rather than choosing another provider.
The same rule applies to --preset and KIN_PRESET; see
preset selection.
[[providers]]
id = "openrouter"
provider = "openai"
wire_api = "chat" # or "responses"
base_url = "https://openrouter.ai/api/v1"
default_model = "anthropic/claude-3.7-sonnet"
models = ["anthropic/claude-3.7-sonnet"]
OpenAI custom rows may set wire_api = "chat" (the default) or opt into
wire_api = "responses". Anthropic rows imply Messages and reject an OpenAI
wire value. A row may carry reasoning_effort = "<level>" as the endpoint’s
own default effort (opaque string, serve-owned vocabulary); on a Responses row
this also opts the stream into reasoning-summary deltas — required for
thinking-by-default serves like DS4 (see
Models & providers).
The special provider-row value reasoning_effort = "inherit" masks a broader
top-level effort while emitting no reasoning request field. It is deliberately
different from removing the row value, which would expose the top-level setting
again; KIN_REASONING_EFFORT remains the explicit one-run override.
Custom rows may also set reasoning_profile = "poolside". That profile is
valid only on an anthropic row and selects Laguna’s per-request
chat_template_kwargs.enable_thinking control; an unknown profile or an
OpenAI-wire pairing is rejected instead of guessed. See
Laguna S 2.1 on vLLM.
For the qualified text-only Responses contract available from compatible local
servers, see llama.cpp-compatible Responses
endpoints.
Add a custom endpoint via /providers → “Custom endpoint…” or by hand.
See Provider presets for the full shape and
which keys are recognized.
modelis an alias fordefault_modelon custom rowsThe one-key
model = "default"shape (nodefault_model, nomodels) is accepted as a convenience for one-line single-model / stable-alias rows — the parser aliasesmodel→default_modeland seedsmodels = [model]so the/modelpicker still surfaces a pickable row. Use the explicit two-key form (default_model+models = [...]) when the picker should show every model your server actually serves.
Per-provider keys ([provider_keys])
When you swap between multiple providers, the recommended storage shape is the [provider_keys] table — one key per preset id. The factory’s resolution chain (_preset_key in src/kin/harness/backends/factory.py) reads provider_keys[<active_preset_id>] before falling back to environment variables / api_key, so a key stored here is the most specific selector. Direct OpenAI consults OPENAI_API_KEY then KIN_API_KEY; direct Anthropic consults ANTHROPIC_API_KEY then KIN_API_KEY, and neither borrows the other vendor’s conventional key. Compatibility/custom presets retain the broad legacy environment chain because their wire does not identify the credential issuer:
# ~/.kin/settings.toml (human-owned, non-secret selection)
provider_preset = "zai" # which preset is "active" for next session
model = "glm-5.3-flash"
[provider_tiers]
minimax = "plus"
zai = "lite"
Kin writes the corresponding secret table to its 0600 credential store:
# ~/.kin/credentials.toml (Kin-managed; shown only to explain storage)
[provider_keys]
zai = "sk-..."
minimax = "sk-..."
/providers writes to this table when you paste a key, so the next session and future routed calls find the key—no /reload, no manual settings.toml editing. For direct OpenAI/Anthropic rows, leaving the exact-model field blank refreshes the catalog for future route dispatch without switching the live MCA; running children keep their prior snapshot. Top-level api_key is the generic fallback (one key, one endpoint); [provider_keys] is the per-provider shape.
Model routes
Retired workflow assignments and "workflow" placement tokens are inert; they
do not grant task placement. See Workflow migration.
Model routes separate what a call is for from the endpoint wire. Every route
references an existing built-in or custom provider preset; routes never own a
URL or credential. Direct openai and anthropic preset ids are built in, so
they work beside MiniMax, Z.ai, and custom [[providers]] rows. Their
empty preset URL means the vendor SDK endpoint and does not inherit an ambient
KIN_BASE_URL used by the default backend.
# ~/.kin/settings.toml
[provider_keys]
openai = "sk-..."
anthropic = "sk-ant-..."
[model_routes.primary]
provider_preset = "anthropic"
model = "your-text-model-id"
description = "Main collaboration model"
vision = false
effort = "high"
max_tokens = 16000
selectable_for = ["task"]
[model_routes.visual]
provider_preset = "openai"
model = "your-vision-model-id"
description = "Isolated image inspection"
vision = true
selectable_for = ["task"]
[model_assignments]
mca = "primary"
vision = "visual"
task = "primary"
utility = "visual" # housekeeping side-calls (session auto-titles)
[model_assignments.profiles]
researcher = "primary"
Each [model_routes.<id>] accepts only these fields:
| Field | Required | Meaning |
|---|---|---|
provider_preset |
yes | Built-in or custom preset id. This is the endpoint and credential trust boundary. |
model |
yes | Exact model id sent on that preset’s wire. |
description |
no | Operator description shown to humans and, only when selectable, to the task model. |
vision |
no | Authoritative true / false semantic-vision override. Omitted starts as unknown; Kin caches a supported or capability-rejected media attempt until reload. |
reasoning_profile |
no | Closed route request contract. qwen-llamacpp enables budget-labelled Qwen choices without applying nonstandard fields to other routes/providers. |
effort |
no | Per-route effort value, validated against that preset/model’s picker vocabulary. |
max_tokens |
no | Positive per-response output cap for this route. |
selectable_for |
no | Explicit model-choice allowlist containing only "task". Operator assignments do not need this opt-in. |
Resolution is deterministic: profile assignment, then consumer assignment,
then an allowed requested route, then a requested legacy model, then the
current MCA backend. route and model are mutually exclusive, and an invalid
model clone is an error rather than a silent fallback. Forks retain their
parent’s exact backend. See Models & providers
for media isolation, persistence, usage, and /reload behavior.
For reasoning_profile = "qwen-llamacpp", valid effort values are default,
off, thinking-128, thinking-512, thinking-1024, and thinking-2048.
The route’s max_tokens is a total output allowance and must exceed an enabled
budget. A Utility assignment also requires 512 final-answer tokens beyond the
budget. See Qwen bounded thinking.
utility is assignment-only: it binds kin-internal housekeeping side-calls
(session auto-titles today) to a lightweight route so tiny requests stay off
the main endpoint’s prompt cache. It is never model-selectable, its calls are
best-effort (a busy route skips the call rather than queueing), and a failed
utility call keeps the first-message fallback title instead of retrying on the
main backend. Unassigned, housekeeping stays on the main backend as before.
Sampling and compatible reasoning controls
temperature, top_p, and top_k apply only on the OpenAI-compat wire. The
Anthropic wire ignores per-request sampling, so set it on the server instead.
enable_thinking has the explicit Poolside exception described below.
| Key | Meaning | Default | Scope | Env |
|---|---|---|---|---|
temperature |
Sampling temperature (0 = greedy). |
provider default | project-ok | KIN_TEMPERATURE |
top_p |
Nucleus sampling. | provider default | project-ok | KIN_TOP_P |
top_k |
Top-k sampling (via extra_body). |
provider default | project-ok | KIN_TOP_K |
enable_thinking |
Chat-template thinking soft switch for Qwen-style OpenAI-compatible serves and an Anthropic custom provider with reasoning_profile="poolside" (false hard-disables thinking). |
unset (model/server decides) | project-ok | KIN_ENABLE_THINKING |
reasoning_effort |
OpenAI-proper reasoning-effort hint (real OpenAI / OpenRouter / Z.ai OpenAI-compat; any custom Responses-wire endpoint). Top-level wire field on chat.completions.create — NOT inside extra_body; on the Responses wire it rides reasoning: {"effort": ..., "summary": "auto"} (the summary opt-in is what makes a thinking-by-default serve stream reasoning deltas). Per-serve vocab (3-value OpenAI vs 8-value Z.ai vs the Responses pass-through) is dispatched by the picker (see Models & providers); the resolver itself is opaque. Not for Qwen vLLM Chat serves (use enable_thinking above). A [[providers]] row may carry its own reasoning_effort default, or inherit to mask this top-level value and defer to the service; the higher-precedence KIN_REASONING_EFFORT environment override still wins. Cross-wire knob for the picker is effort, so this key is not in the model-writable set — reach it via effort=... which goes through the human-approval gate. |
unset | project-ok | KIN_REASONING_EFFORT |
strict_tools |
Strict tool schemas on the streaming registry path (OpenAI-compat wire only). Each tool spec is normalized at request-build time (additionalProperties: false, all-required, optionals as ["type","null"] unions — the registry’s canonical schemas are never mutated) and stamped strict: true, so endpoints that honor it (OpenAI-proper; vLLM ≥ 0.24 auto tool choice) grammar-enforce arguments. A no-op on vLLM 0.23 auto calls; forced tool calls (internal structured-output path) are strict-normalized unconditionally regardless of this key. Human-only (not model-writable). See Strict decoding. |
false |
project-ok | KIN_STRICT_TOOLS |
Fleet safety
Governance knobs for a shared, self-hosted endpoint — see Fleet safety in the headless guide.
| Key | Meaning | Default | Scope | Env |
|---|---|---|---|---|
token_budget |
Per-run cumulative token budget: once summed non-cached prompt + completion tokens across every model call (root, subagent, vision, and compaction included) cross this, the crossing round finishes and the root gets one final tools-off wrap-up round before done reason token_budget (headless exit 1). Children receive no grace once the shared budget is exhausted. A “run” is one root turn plus everything it fans out; the next turn starts fresh. Malformed or negative provider counters are treated as zero, and cached tokens cannot exceed prompt tokens. Project-ok: the default is unlimited, so a project file can only impose a tighter cap (fail-safe). Human-only — deliberately not model-writable: the budget exists to stop a runaway model, so the model can never raise its own leash. 0 = off. |
0 (off) |
project-ok | KIN_TOKEN_BUDGET |
max_turns |
Per-turn model-round cap: the maximum model↔tool round-trips one user turn may run before it ends with done reason turn_cap (headless exit 1). Independent of token_budget — the two limits are checked at the same round boundary. 0 = unlimited (the default) — the doom-loop guard (3× identical call+result) is the real no-progress backstop, so an unbounded cap is safe for large-model, hours-long runs. Project-ok: the default is unlimited, so a project file can only impose a tighter cap (fail-safe). Human-only — not model-writable (an operator leash, like token_budget). |
0 (unlimited) |
project-ok | KIN_MAX_TURNS |
request_priority |
Legacy vLLM priority-scheduling passthrough (OpenAI-compat wire only). Rides extra_body["priority"] on every request; lower = scheduled earlier, with KV-swap preemption. Serve-gated: only meaningful when the server runs --scheduling-policy priority — vLLM errors on any non-zero priority otherwise, and the current hosted contract exposes no such control, which is why the default is absent from the request, not 0. Human-only (not model-writable). See Models & providers. |
unset (absent) | project-ok | KIN_REQUEST_PRIORITY |
Media and caching
| Key | Meaning | Default | Scope | Env |
|---|---|---|---|---|
image_detail |
OpenAI vision tier for tool-returned images (low / high / auto). |
low |
project-ok | KIN_IMAGE_DETAIL |
effort |
Anthropic adaptive-thinking effort. The Z.ai preset narrows this to the GLM-5.3 family’s max / high / low vocabulary and sends it as output_config.effort. |
high |
project-ok | KIN_EFFORT |
thinking_type |
Anthropic-wire thinking.type knob — z.ai / MiniMax / Fable honor this as a thinking toggle. Common values: adaptive (default), enabled (force on), disabled (off on compatible models; GLM-5.3 remains an always-reasoning family and maps this to lightweight reasoning). |
adaptive |
project-ok | KIN_THINKING_TYPE |
cache_ttl |
Anthropic system-prompt prefix-cache TTL (1h opts into the 1-hour cache). |
5 minutes | project-ok | KIN_CACHE_TTL |
Permissions and workspace trust
| Key | Meaning | Default | Scope | Env |
|---|---|---|---|---|
mode |
Permission mode at launch (auto / strict; the legacy default / accept-edits / plan still resolve via the alias map). |
auto |
global-only | KIN_MODE |
workspace_trust |
Kin-managed [[workspace_trust]] rows in grants.toml. Each row stores an opaque wst_… id, canonical path, and filesystem device/inode. A replaced path or malformed row is stale/inert. Broad roots are session-only and never persisted. Manage with /grants, not by hand. |
[] |
global-only | — |
git_host_grants |
Human-approved, secret-free [[git_host_grants]] rows written by the Git credential modal’s Trust host action. Each ghg_… row binds SSH/HTTPS + resolved user/host/port to a route fingerprint and display label; it never stores a token, key, socket path, remote URL, or repository path. A changed SSH/helper route asks again. Invalid rows are inert. Manage with /grants. |
[] |
global-only | — |
shell_allowlist |
When true, provably read-only shell commands auto-allow; false sends every command to the approval modal. | true |
global-only | KIN_SHELL_ALLOWLIST |
The retired sandbox, sandbox_network, sandbox_strict,
sandbox_default, sandbox_uv_redirect, sandbox_grants, and
sandbox_always_allow names remain recognized temporarily so an existing file
still loads. They are inert: no runtime reads them, /grants may report old
rows only as migration data, and none can create workspace trust.
Planning
| Key | Meaning | Default | Scope | Env |
|---|---|---|---|---|
persist_plans |
Retired, no-op. Plans are now saved to <workdir>/.kin/plans/<date>-NN-<slug>.md unconditionally on every write_plan call — this key no longer gates anything; kept only so an existing settings.toml doesn’t error. |
true |
project-ok | KIN_PERSIST_PLANS |
Sessions and persistence
| Key | Meaning | Default | Scope | Env |
|---|---|---|---|---|
session_dir |
Where session journals are written. | ~/.kin/projects |
global-only | KIN_SESSION_DIR |
no_save |
Run ephemerally: write no session journal or durable prompt history (not resumable; composer recall remains memory-only for this process). | false |
global-only | KIN_NO_SAVE |
prompt_history_cap |
Number of interactive composer submissions retained per project for ↑/↓ and ++ctrl+r++ across restarts. Includes model prompts, slash commands, and literal ! command text, but never shell output; headless kin -p prompts are excluded. 0 disables durable loading and writes without deleting the existing store, so re-enabling restores retained entries. Negative or malformed values fall back to 100. --no-save, KIN_NO_SAVE, and no_save = true always make recall memory-only. Human-only (prompt retention is a privacy choice, not a model knob). |
100 |
project-ok | KIN_PROMPT_HISTORY_CAP |
Notifications
| Key | Meaning | Default | Scope | Env |
|---|---|---|---|---|
notify |
The terminal bell / OSC 9 walk-away notification — a bare BEL always, plus a native desktop notification on a verified OSC-9-capable terminal (Ghostty / iTerm2 / WezTerm / kitty). Fires on a turn that ran 15s+ and on every needs-input prompt (TUI), or on process completion when stderr is a real terminal (kin -p). Default on; opt-out kill switch. Project-ok (it only toggles whether a static-string escape sequence is written; carries no model/server text). |
true |
project-ok | KIN_NOTIFY |
update_check |
The session-start update-availability check: one best-effort GET of https://get.kinra.ai/version.json (24h-cached) that renders the one-line “Update available … run: kin update” note. Privacy: the request carries only your kin version in its User-Agent. Default on; opt-out. Project-ok (the endpoint is a constant — a project file can only turn the check off). Explicit kin update / kin doctor runs don’t consult it. |
true |
project-ok | KIN_UPDATE_CHECK |
Appearance
| Key | Meaning | Default | Scope | Env |
|---|---|---|---|---|
theme |
TUI theme name. kin inherits the terminal background; kin-graphite is the opaque Graphite+ fallback. Bare /theme cycles that pair, while an explicit registered Textual theme name is also accepted. The command persists the choice here. Project-ok (pure display — no egress, secret, or containment impact). |
kin |
project-ok | KIN_THEME |
spinner |
The busy-spinner style name (rain · braille · arc · toggle · line — the vibe table lives in Identity). Unknown names fall back to the default; KIN_ASCII forces line. Pick interactively with /spinner (which persists here). Project-ok (pure display — no egress, secret, or containment impact). |
rain |
project-ok | KIN_SPINNER |
Context and compaction
| Key | Meaning | Default | Scope | Env |
|---|---|---|---|---|
env_context |
Include the Environment + git-state blocks in the system prompt. | true |
project-ok | KIN_ENV_CONTEXT |
compact_threshold |
Auto-compact history once prompt usage crosses this fraction of the window (0.1–0.98). Defaults to 0.45; set 0 to disable. The effective trigger is capped below the hard input limit after reserving max_tokens output plus framing margin. |
0.45 |
project-ok | KIN_COMPACT_THRESHOLD |
compact_keep_turns |
Minimum recent turns to preserve when compacting. | 4 |
project-ok | KIN_COMPACT_KEEP_TURNS |
compact_keep_fraction |
Ceiling on the estimated tokens the retained tail may occupy, as a fraction of the window. compact_keep_turns stays the floor; when the two conflict the ceiling wins, down to one kept turn. Under exact-count pressure Kin may tighten this ceiling using the observed token density; it never loosens it. |
0.16 |
project-ok | KIN_COMPACT_KEEP_FRACTION |
prune_threshold_chars |
Elide a completed tool result longer than this before paying for a summary. Tool-call identity, error state, and block structure are preserved, so only the content changes; recent turns are never pruned and the original stays in the journal as a shadowed record. 0 disables pruning. |
8192 |
project-ok | KIN_PRUNE_THRESHOLD_CHARS |
prune_head_chars |
Leading characters a pruned tool result keeps. | 4096 |
project-ok | KIN_PRUNE_HEAD_CHARS |
prune_tail_chars |
Trailing characters a pruned tool result keeps. | 1024 |
project-ok | KIN_PRUNE_TAIL_CHARS |
spill_max_bytes |
Per-file cap for the spill store, which keeps the output a truncated tool result used to destroy. Files live beside the session journal (0700 directory, 0600 files), Git/GitHub output is redacted before it is stored, and the store is purged with its journal. 0 disables spilling; every no-save route disables it regardless. Human-only — retention is a privacy choice, not a model knob. |
8388608 |
project-ok | KIN_SPILL_MAX_BYTES |
Skills and web
| Key | Meaning | Default | Scope | Env |
|---|---|---|---|---|
strict_skills |
Historical name retained for compatibility: raise on malformed agent-profile frontmatter instead of skipping the profile. Skill bundles are always validated strictly and ignore this setting. | false |
project-ok | KIN_STRICT_SKILLS |
brave_api_key |
Explicit local Brave Search override; enables direct web_search / web_context and takes precedence over hosted routing. Hosted shared Brave/Exa credentials never belong in this file. |
none | global-only | BRAVE_API_KEY / KIN_BRAVE_API_KEY |
search_enabled |
Register the search_workspace tool — ranked, chunk-aware lexical full-text search over workspace content (stdlib SQLite FTS5; the complement to grep, not semantic/vector search). Default on; set false to remove it. The index DB lives outside the workspace under ~/.kin/index/. Project-ok (a local index only — no network/egress/secret). |
true |
project-ok | KIN_SEARCH_ENABLED |
run_code_enabled |
Register the run_code / run_code_reset interpreter tools — the stateful per-session Python kernel. Default on; the tools ride the full shell permission gate (direct in trusted auto, asks in strict), so this is an opt-out kill switch. Project-ok (it only unregisters tools; it grants nothing the gate doesn’t). |
true |
project-ok | KIN_RUN_CODE |
diagnostics_after_edit |
Append a lint pass’s output to write_file/edit_file results on a nonzero exit (ruff check, v1 — shutil.which-gated, never blocks or fails the write). Default on; opt-out kill switch. |
true |
project-ok | KIN_DIAGNOSTICS |
diagnostics_ty |
Also run an opt-in ty typecheck pass inside diagnostics-after-edit. Default off — ty is slow and typed-project-specific. |
false |
project-ok | KIN_DIAGNOSTICS_TY |
snapshots |
The file snapshot-before-write safety net — a git commit-tree checkpoint under refs/kin-snapshots/ taken before the first edit of every turn, restorable via /rewind files/both. Default on; opt-out kill switch. A no-op outside a git workspace. Project-ok (only toggles whether the checkpoint runs; never touches the real index/HEAD/worktree). |
true |
project-ok | KIN_SNAPSHOTS |
paste_file_mentions |
Convert Finder drag-drops / terminal pastes of absolute file paths into @-mentions in the Composer (default on). Off → pastes insert verbatim, identical to pre-Rock-3 behavior. Project-ok (it only re-routes a paste from raw-text-insert to mention-insert — no egress / secret / containment surface beyond what a manual paste would carry); human-only (a UX toggle, NOT model-writable). |
true |
project-ok | KIN_PASTE_MENTIONS |
ssh_hosts |
Deprecated no-op retained so existing settings files keep loading. Ordinary SSH uses shell; KIN_SSH_HOSTS is no longer read. |
[] |
global-only | — |
Observability
| Key | Meaning | Default | Scope | Env |
|---|---|---|---|---|
spans_enabled |
Write OTel-GenAI-shaped JSONL spans — one invoke_agent per turn, a chat per model round, an execute_tool per tool call — to <session_id>.spans.jsonl next to the journal. Metadata only (timing / names / token counts; never prompt or tool text). Project-ok (a local file, no network/egress/secret) and human-only (not model-writable). |
false |
project-ok | KIN_SPANS |
pricing_enabled |
Display a running USD cost total alongside the /tokens counts (verbose breakdown) and as a recessive cell on the StatusBar ($0.04). Sourced from a stdlib-only pricing table keyed by (provider, model id) — see Cost display. Default off (opt in) — the primary endpoint is local vLLM (no cost) and the subscription providers (MiniMax/z.ai) make per-token cost noise; turn it on with KIN_PRICING=1 / pricing_enabled = true / the /cost on slash command (which also persists the choice). An unpriced serve (local vLLM, an id not in the table) shows nothing rather than a misleading $0.00. Project-ok (a local computation + display only; no network, no secret, no containment impact) and human-only (observability, never a sampling knob). |
false |
project-ok | KIN_PRICING |
Background subagents
| Key | Meaning | Default | Scope | Env |
|---|---|---|---|---|
agent_stall_timeout |
Seconds without model/tool activity before an active, non-paused task agent becomes advisory stale. Staleness regains the MCA’s attention but never cancels or restarts the child. Human-only and clamped to 30–3600 seconds. |
600 |
project-ok | KIN_AGENT_STALL_TIMEOUT |
Editing settings from inside kin
Open /config → settings to start a visible conversation that reads the live,
redacted settings through read_settings, explains the safe choices, and can
publish a model-writable patch through propose_settings. /settings is the
optional toggle that injects the same guidance into ordinary turns. Both routes
share one narrow AI boundary:
| Tier | Keys the model may propose |
|---|---|
| **Model-writable** | `temperature`, `top_p`, `top_k`, `max_tokens`, `context_window`, `enable_thinking`, `effort`, `thinking_type`, `cache_ttl`, `model` |
| **Human-only** (refused) | `mode`, `workspace_trust`, `git_host_grants`, `shell_allowlist` (authority/trust), plus retired inert `sandbox*` names; `api_key`, `base_url`, `brave_api_key` (secrets/endpoints); `provider`, `wire_api`, `provider_preset`, `provider_tiers`, `providers`, `model_favorites`, `model_routes`, `model_assignments` (presets/routes); `reasoning_effort` (cross-wire conflict — use `effort` which the picker translates per-serve) |
The model-writable list is an explicit positive allowlist — a key is refused until it’s deliberately added, so a future setting can’t silently become model-writable. read_settings masks every credential to [SET]/[NOT SET]. A proposed change goes through a fresh diff modal and applies to new sessions only (the live session’s mode/model are human-only — use /mode and /model). Human-only settings remain available through their dedicated UI/commands or manual editing; there is no broader AI settings editor. See Model-writable settings.
Global-only keys
These keys are honored from the global ~/.kin/settings.toml, the environment, or a CLI flag — but are stripped (with a one-line warning) from any project .kin/settings.toml:
base_url, api_key, mode, sandbox, sandbox_strict, sandbox_default,
sandbox_grants, sandbox_always_allow, git_host_grants, workspace_trust, shell_allowlist,
session_dir, no_save, brave_api_key,
provider_preset, providers, provider_keys, provider_tiers, model_favorites,
model_routes, model_assignments, sandbox_network, ssh_hosts
The reason is the threat model of a cloned repository. A project file you
didn’t write could otherwise repoint your wire and leak your key (base_url +
api_key), choose the permission posture (mode, shell_allowlist), claim
workspace or Git credential trust (workspace_trust, git_host_grants),
redirect where sessions land
(session_dir, no_save, brave_api_key), or route your global api_key
through a third-party endpoint (provider_preset + providers +
provider_keys), or assign prompts and images to one of those endpoints
(model_routes + model_assignments). Keeping them global-only means a checkout can’t silently
retarget your endpoint, authorize its own remote route, route your key through
an attacker-controlled host, or weaken your safety posture.
The retired sandbox names and ssh_hosts remain in the set only as inert
migration-compatible data, preserving fail-closed project stripping until
their staged removal. The exact denylist and the layering rules are
documented in the repo’s
docs/invariants.md § Settings & config layering.
Secrets (api_key, provider_keys, and brave_api_key) belong to Kin’s global credential store at
~/.kin/credentials.toml, which is created mode 0600 and never committed.
Use Kin’s key prompts instead of editing it. A legacy secret in global
settings.toml remains readable and migrates on the next settings write.