Reference

Configuration

TOML

Configure panto with layered TOML: config.toml for settings, models.toml for model aliases and pricing. Prompt files and rigs add instructions and resources.

Core generally ignores unknown keys. Lua extensions see the whole merged document.

Files & layers

Four config.toml layers stack over a remembered-state file. Higher rows win.

LayerPathHolds
local./.panto/local/config.tomlPersonal project overrides; usually git-ignored.
project./.panto/config.tomlTeam-shared project settings.
user~/.config/panto/config.tomlYour settings.
base~/.local/share/panto/config.tomlGenerated defaults. Bundled resources sit in base/ beside it.
state~/.local/share/panto/state.tomlRemembered UI choices, extension-owned extension_state values, and installed rock specs.

$XDG_CONFIG_HOME and $XDG_DATA_HOME replace the two home directories when set. The data home is the data directory plus panto/.

  • Tables merge recursively; higher scalars and arrays replace lower ones.
  • Exception: the four [extensions] arrays accumulate.
  • Missing files are skipped; missing or invalid state is ignored.
  • models.toml, prompt files, and Lua directories use the same layers.
  • The base file is written only when absent; upgrades never overwrite it. It seeds most defaults below (shown as “base N” where they differ) plus API-key providers reading OPENAI_API_KEY and ANTHROPIC_API_KEY.
config.tomltoml
[providers.anthropic]
style = "anthropic_messages"
base_url = "https://api.anthropic.com"
auth = "anthropic"

[auth.anthropic]
key = "${env:ANTHROPIC_API_KEY}"

[defaults]
model = "anthropic:sonnet"

[providers.<name>]

A provider is the left half of a provider:alias reference. Network providers use a built-in API style; protocol providers hand everything to a Lua extension.

Network providers

KeyTypeDefaultMeaning
styleenum—openai_chat, openai_responses, or anthropic_messages. Required in flat form; picks the default endpoint otherwise.
base_urlstringrequiredAPI base URL.
authstringrequiredName of an existing [auth.<name>] session; OAuth credentials resolve when needed.
dialectstring—Only "codex", with openai_responses.
endpoints.<style>table—Multi-endpoint form: one table per style.
prompt_cachebooltrueAnthropic prompt caching.
extra_headersstring table—Extra request headers, also sent on OAuth calls. ${session:id} becomes the conversation ID.
model_catalog_namestringprovider namemodels.dev key for panto models sync.
CaseRule
Endpoint formPut base_url, dialect, and prompt_cache on each endpoint, not the provider. prompt_cache is valid only on the Anthropic endpoint.
Several endpointsSet a provider or per-model style. A sole endpoint is implicit.
HeadersEndpoint headers override same-named provider headers (case-insensitive). Non-strings are skipped.
Prompt cachingA non-boolean value on an Anthropic endpoint invalidates the provider. Flat non-Anthropic providers ignore it.
Unresolved keyAn API-key session with no key drops every provider using it.

Extension-protocol providers

KeyTypeMeaning
protocolstringNon-empty protocol name an extension registered.
model_catalog_namestringOptional models.dev key.

Do not mix protocol and network settings; the catalog name is allowed. The extension owns transport and auth. See Lua extensions.

config.tomltoml
[providers.example_extension]
protocol = "example.protocol"

[providers.multi]
auth = "service_key"
style = "openai_chat"

[auth.service_key]
key = "${env:SERVICE_API_KEY}"

[providers.multi.endpoints.openai_chat]
base_url = "https://service.example/v1"

[providers.multi.endpoints.anthropic_messages]
base_url = "https://service.example"
prompt_cache = true

[auth.<name>]

A named credential that providers reference by auth. Omit type to infer it:

typeInferred fromUse
api_keykeyStatic key, usually from the environment.
oauth_deviceclient_idDevice-code login via panto auth login.

If both inference keys exist, OAuth wins. An unknown type or neither key without a type is invalid.

API-key auth

KeyTypeMeaning
keystringCredential, usually ${env:VAR}. Empty or unset makes the session unavailable.

OAuth device auth

KeyTypeDefaultMeaning
dialectenumtokentoken or codex.
client_idstringrequiredOAuth client ID.
device_code_urlstringrequiredDevice-code endpoint.
token_urlstringrequiredToken endpoint (codex: exchange and refresh).
device_poll_urlstring—Codex only, required: polled for the code.
verification_urlstring—Browser URL. Codex requires it; token reads it from the device-code response.
scopestring—Requested scope (token only).
token_request_formatenumformform or json bodies (token only; codex sends JSON).
redirect_uristringOpenAI callbackCodex redirect; default https://auth.openai.com/deviceauth/callback.
account_id_jwt_claimstring—Literal ID-token claim name containing chatgpt_account_id; sends that field as the account-ID header.
exchange_urlstring—Post-login token exchange; absent disables it.
exchange_methodstringGETExchange method: POST sends POST; other values send GET (case-insensitive).
exchange_token_pathstringtokenDotted JSON path to the credential.
exchange_expires_pathstring—Dotted JSON path to the Unix-seconds expiry.
exchange_base_url_pathstring—Dotted JSON path to a dynamic base URL.

Substitution and storage

SyntaxExpands to
${env:VAR}Environment variable.
${name}Raw value of a sibling key in the same table, even one core ignores.
  • Works in key and all OAuth string fields; not in enum fields.
  • Missing references become empty. No recursion. An unterminated ${ stays literal.
  • OAuth tokens are saved as JSON under the data home's auth/. Treat them as secrets.
config.tomltoml
[auth.openai]
key = "${env:OPENAI_API_KEY}"   # inferred: api_key

[auth.gh]
client_id = "abc"               # inferred: oauth_device
domain = "github.com"
device_code_url = "https://${domain}/login/device/code"
token_url = "https://${domain}/login/oauth/access_token"

[defaults]

Default model and reasoning for new sessions.

KeyTypeMeaning
modelstringExactly one colon with non-empty parts: provider:alias. The provider must resolve; the alias needn't be in models.toml.
reasoningstringLabel such as high. Applies only if the model entry sets no reasoning, thinking, or effort; unsupported defaults warn and are ignored.
CaseRule
Model selectionCLI override → configured default → remembered model → sole alias of the sole provider. Otherwise, select a model explicitly. A unique bare alias works on the CLI.
Reasoning selection--reasoning beats per-alias settings, configured defaults, and remembered choices. An unsupported CLI value fails startup.
Remembered choicesModel and reasoning pickers save to state, below explicit config. See the CLI reference.
config.tomltoml
[defaults]
model = "anthropic:sonnet"
reasoning = "high"

[keys]

Override or add TUI command shortcuts. Each key maps to a slash-command invocation; registered extension commands are available too.

ValueMeaning
"/command args"Run that command when the chord is pressed.
""Unbind a default command shortcut.

Use canonical lowercase chords: ctrl+<a-z>, alt+<a-z>, or ctrl+alt+<a-z>. Host-reserved chords are rejected: Ctrl+C/D/Z/H/I/J/M, Ctrl+A/E/U/W/G, and Alt+B/F. Any invalid entry (including a malformed chord, a reserved key, or a value other than a slash-command string or "") fails config loading; it is not ignored. A configured chord replaces its default binding; other chords add bindings.

Defaults: Ctrl+T /model, Ctrl+R /reasoning, Ctrl+Y /theme, Ctrl+P /proceed, Ctrl+O /collapse, Ctrl+K /thinking, and Ctrl+L /redraw. During a live turn, only native commands registered for mid-turn dispatch run immediately; other configured command invocations are ignored, not queued.

config.tomltoml
[keys]
"ctrl+t" = "/reasoning"
"alt+m" = "/model"
"ctrl+y" = ""

[tui]

Startup UI behavior. See Key bindings for controls.

KeyTypeDefaultMeaning
themestringPantoStartup theme; case-sensitive except panto also selects Panto. --theme overrides.
tree_viewenumno_tool_use/tree view: no_tool_use, no_tools, user, all, or named.
show_thinkingbooltrueShow thinking. Ctrl+K toggles.
tools_collapsedbooltrueCollapse tool output. Ctrl+O toggles.
editorstring$VISUAL, $EDITOR, viCtrl+G editor command.
  • Core themes: Panto, Tidepool Tumble, Mossmallow, Peachy Pudding, Plum Pips, Gilded Inkblot. Extensions add more; unknown names fall back to Panto.
  • The theme picker (Ctrl+Y) previews choices; Enter accepts. It, the tree picker, and Ctrl+K save choices in state; explicit config wins.
  • Blank or non-string themes, invalid tree views, and non-boolean thinking values invalidate config. Unknown theme names fall back; wrong editor or collapse types are ignored.

[tui.diagrams]

Terminal rendering of Mermaid diagrams.

KeyTypeDefaultMeaning
rounded_bordersbooltrueRounded box corners.
box_border_paddinginteger ≥ 00Padding inside flowchart, class, and ER boxes.
graph_paddinginteger ≥ 02Gap between graph boxes.
sequence_spacinginteger ≥ 03Gap between sequence participants.

Non-table diagram settings and bad sizes invalidate config. A non-boolean rounded_borders is ignored.

config.tomltoml
[tui]
theme = "Tidepool Tumble"
editor = "code -w"

[tui.diagrams]
rounded_borders = false

[api]

Timeouts and retries for model requests.

KeyTypeDefaultMeaning
timeoutu320 (base 600)Seconds per attempt; 0 disables.
retriesu160 (base 3)Retries after the first attempt.

Bad types or ranges invalidate config. panto serve ignores this section and makes one attempt per call.

config.tomltoml
[api]
timeout = 600
retries = 3

[server]

Listener and limits for panto serve.

KeyTypeDefaultMeaning
hoststring127.0.0.1Non-empty bind address, without surrounding whitespace. --host overrides.
port1–655358080Listen port. --port overrides.
auth_token_envstring—Env var holding the inbound token. Unset or empty supplies no token.
max_body_bytespositive u3233554432Decoded request body.
max_header_bytes1024–104857665536Request headers.
max_response_bytespositive u3233554432Buffered response.
max_connections1–6416Open connections.
max_active_requestspositive u1616Active requests; ≤ connections.
max_queued_requestsu160Must be 0: overload is rejected, not queued.
max_retained_contextsu168Warm contexts; 0 disables.
context_idle_secondspositive u32120Retained-context idle lifetime.
request_deadline_secondspositive u32600Request deadline and upstream timeout.
header_deadline_secondspositive u3215Header-read deadline.
stalled_write_deadline_secondspositive u3230Stalled-write deadline.

Strict validation

  • Wrong types and out-of-range values invalidate config.
  • Token variable names start with a letter or underscore, then use letters, digits, or underscores.
  • Connections × body size and active requests × response size are each capped at 512 MiB.
  • A non-loopback host needs a non-empty token, never sent upstream.
config.tomltoml
[server]
host = "0.0.0.0"
auth_token_env = "PANTO_SERVER_TOKEN"

[extensions] policy & sources

Choose which Lua entries activate, shipped tools included, and add sources. Tools and extensions share one namespace; there is no separate tools section.

KeyTypeMeaning
allowstring arrayDeclared-name globs to allow, not filenames.
denystring arrayName globs to deny.
pathsstring arrayExtra source directories, relative to the working directory.
rocksstring arrayLuaRocks specs; first word is the module. Missing rocks install at startup; remembered rock specs are included.

All four default to empty and accumulate. Empty policy allows everything; non-array values act as empty and non-string members are skipped.

Matching and precedence

  • Globs are case-sensitive, dot-separated, and whole-name. * matches one segment; ** matches one or more. A star inside a segment is literal.
  • Unmatched names are allowed. Higher layers override lower ones. In a layer, the more specific rule wins; ties go to deny.
  • Whitelist with deny = ["**"] plus specific allows.

Source directories

LayerDirectories
local./.panto/local/{extensions,tools}/
project./.panto/{extensions,tools}/
user~/.config/panto/{extensions,tools}/
base<data-home>/base/{extensions,tools}/
  • Higher rows win. In a layer: canonical directories, then paths, then rocks.
  • Sources are .lua files or directories with init.lua. Dot-prefixed entries and _-prefixed Lua files are skipped.
  • Duplicate declared names at equal precedence are errors.
  • Manage rocks and git extensions with panto extensions (CLI).

Policy is not a sandbox

Deny only blocks activation. Sources are evaluated and rocks installed first.
config.tomltoml
[extensions]
# whitelist: only the read and edit tools
deny = ["**"]
allow = ["std.read", "std.edit"]

[compaction]

Choose how panto summarizes long conversations.

KeyTypeDefaultMeaning
keep_verbatimpositive u3220000Token cap on recent whole turns kept verbatim; the boundary-crossing turn is summarized. Missing, wrong-type, or non-positive values use the default.
modelstringchat modelprovider:alias to compact with; its provider must resolve at config load. Later resolution or request failures fall back to the chat model.

The prompt comes from COMPACTION.md; see System prompt.

config.tomltoml
[compaction]
keep_verbatim = 20000
model = "anthropic:haiku"

models.toml

Per-alias wire IDs, limits, reasoning, and pricing. Merges across the layers.

  • [anthropic.sonnet] defines the alias anthropic:sonnet. Quote names that aren't bare TOML keys, such as ones with dots.
  • An alias with no entry still works, using the alias as the wire ID.
  • Pricing is keyed by provider and wire ID. panto models sync rebuilds the base catalog.

Common keys

KeyTypeDefaultMeaning
modelstringaliasWire model ID.
styleenumprovider'sEndpoint override; see providers.
context_windowpositive u32unknownInput context, shown in the UI.
max_tokenspositive u3264000Output cap per request.
input, output, cache_read, cache_writenumberunknownUSD per million tokens. 0 is a known zero.

OpenAI-style reasoning

KeyTypeDefaultMeaning
reasoningenumdefaultdefault, off/none, minimal, low, medium, high, xhigh, or max.
reasoning_levelsstring arrayunknownSupported effort names above, excluding default. Missing means unknown; empty means no declared levels. When present, non-default reasoning must be listed.

Anthropic-style settings

KeyTypeDefaultMeaning
api_versionstring2023-06-01Version header.
thinkingenumdisableddisabled, enabled, or adaptive. If thinking is omitted, effort implies adaptive.
effortenummediumlow, medium, high, xhigh, or max (adaptive).
thinking_budget_tokenspositive u32max_tokens − 1Budget for enabled mode; 32000 without an entry.
thinking_interleavedboolfalseInterleaved-thinking beta header (enabled mode).
CaseRule
Invalid style or reasoningWrong types or unknown values fail model loading. Invalid capability lists or a selection outside a declared list also fail.
Missing or invalid valuesWire ID uses the alias; context is unknown; output cap, API version, thinking budget, effort, and interleaving use their shown fallbacks. An unknown thinking string disables thinking.
PricingMissing or unparseable rates are unknown; negative or non-finite rates become known zero.
Per-alias reasoningAny explicit reasoning, thinking, or effort key prevents the session default from replacing it.
models.tomltoml
[anthropic.sonnet]
model = "claude-sonnet-4-20250514"
max_tokens = 8192
thinking = "enabled"
thinking_budget_tokens = 4096
input = 3.0
output = 15.0
cache_read = 0.3
cache_write = 3.75

[anthropic.opus]
model = "claude-opus-4-8"
thinking = "adaptive"
effort = "high"

[openai.gpt]
model = "gpt-4o"
reasoning_levels = ["off", "low", "medium", "high"]

System prompt

Markdown files in each layer directory set the system and compaction prompts.

FileRuleFallback
SYSTEM.mdHighest layer wins.You are a helpful assistant.
APPEND_SYSTEM.mdAll appended after it, local first.—
COMPACTION.mdHighest layer wins.Built-in prompt

User, project, and local prompts sit beside their config files. Base prompts live in the data home's base/, which ships system and compaction defaults.

  • Changed files apply on resume as a new seed and append blocks; old messages stay.
  • The welcome header shows which files loaded.
  • A rig's prompts layer above the base prompts; your user, project and local prompts are not read.

Rigs

A rig is a trusted, self-contained bundle of config and resources. Pass it to a session or panto serve with --rig.

my-rig/text
my-rig/
├── config.toml        # rig settings
├── models.toml        # extra or overriding aliases
├── PROMPT.md          # first user message
├── SYSTEM.md          # also APPEND_SYSTEM.md, COMPACTION.md
├── extensions/
└── tools/
PathPurpose
config.tomlOptional. Merges over the base config and your user-level providers, auth, and default model.
models.tomlLayers over the base and user model catalogs.
extensions/, tools/Layer above the base's; the base and rig paths and rocks both load. Built-in tools apply unless denied (deny = ["std.*"]). Relative paths use the working directory, not the rig root.
PROMPT.mdOptional first message for a fresh agent session; never replayed on resume. Missing, empty, or whitespace-only adds nothing; invalid UTF-8 fails startup.
SYSTEM.md etc.Override the base prompts. Without SYSTEM.md, panto's default applies, never yours.
Your settingCarries into rig?
User auth, providers, models.tomlYes, under the rig's
User [defaults].modelYes; serve ignores it as a fallback
Remembered theme, view, thinking, model, reasoningYes, under the rig's config, and saved as usual; not in serve
Installed rocksNo
Project and local layersNo
User extension directories, [extensions]No
User [tui], [api], [compaction], [server]No
  • A directory is used in place without archive validation; writes persist.
  • Any other path is a ZIP (.rig): validated, extracted privately, deleted on exit. Put files at the archive root.
  • First message: explicit prompt → piped stdin (print mode) → rig prompt file.
  • --ignore-rig-prompt skips only the rig prompt file.
  • Remembered picks are read from and saved to state.toml; panto serve neither reads nor writes it.
  • panto serve ignores PROMPT.md. Examples are in the repo's examples/rigs/.
  • Configuration hooks use a rig-specific scope: only base and rig extension sources run, not user, project, or local extension hooks. Their panto.ext.config snapshot contains the merged base and rig settings, plus only the inherited user connection settings and (when remembered state is enabled) the selected UI fields described above; serve does not load remembered state.

Archive limits

Rejects unsafe paths, symlinks, special or colliding entries, over 10,000 entries, files over 64 MiB, or over 256 MiB total.

Data-home layout

The data home stores generated resources and state; there is no PANTO_HOME override.

WhenData home
XDG_DATA_HOME is set$XDG_DATA_HOME/panto
Otherwise$HOME/.local/share/panto
PathContents
config.toml, state.tomlBase config and remembered state.
models.tomlBase catalog from panto models sync.
base/Bundled prompts, tools, and extensions.
auth/<name>.jsonOAuth credentials. Secret.
rocks/lua-<version>/LuaRocks tree per Lua version; old trees kept.
dist/<name>/Git-managed extension clones, linked from your user extensions/.
sessions/<encoded-cwd>/Sessions per working directory. PANTO_SESSION_DIR overrides the sessions base directory.
debug/<session-id>.logLog when PANTO_DEBUG is non-empty and not 0.
.bootstrap.lockBootstrap lock.
state.toml.lockPersistent lock for state updates. Leave it in place while panto may be writing state.