Skip to content

Configuration

Everything straitjacket reads from ctx.toml, in one place. New here? You don’t need any of this to start — ctx setup writes sensible defaults and most workspaces never touch them. Reach for this page when you want to tune a budget, tighten the guard, or add a monorepo scope.

straitjacket reads exactly one file: ctx.toml at the workspace root. There is no upward search and no user-global or machine-global config — configuration is per-workspace and committed with the repo. ctx init (or ctx setup) writes a starter ctx.toml and a .ctxignore.

The workspace root is resolved in this order: an explicit --workspace flag → the path your host passes in → the nearest ancestor directory containing a ctx.tomlgit rev-parse --show-toplevel → the nearest ancestor with an .agents/ directory → the current directory.

Missing or malformed config never breaks anything. If ctx.toml is absent, or contains a TOML syntax error, straitjacket silently falls back to the built-in defaults documented below rather than erroring. Unknown keys inside a known section are ignored. (One consequence worth knowing: a typo in ctx.toml does not raise — it quietly reverts the affected setting to its default. If a change seems to have no effect, check the file parses.)

version = 1
[budgets]
digest_tokens = 480
result_tokens = 1200
turn_retrieval_tokens = 2800
max_inline_bytes = 16384
digest_head_lines = 5
digest_tail_lines = 5
[guard]
mode = "guarded" # advisory | guarded | strict
unknown_command = "force_ask"
internal_error = "allow" # fail-open: a broken guard must not brick the workspace

Everything below is optional; the defaults shown are what you get without the key.

[budgets] — how much the model is allowed to see

Section titled “[budgets] — how much the model is allowed to see”

Token and byte ceilings on every model-visible surface. Anything above a ceiling is captured and shown as a bounded digest instead.

KeyDefaultMeaning
digest_tokens480Token budget for a digest.
result_tokens1200Token budget for a single retrieval (ctx get).
turn_retrieval_tokens2800Cumulative retrieval budget per turn.
max_inline_bytes16384Native-read size (input side) above which a read is bounded rather than pasted whole.
max_inline_lines240Line cap injected when an oversized read is rewritten to a bounded window.
max_matches80Maximum match count returned by a search.
session_read_budget_bytes262144 (256 KiB)Cumulative native-read budget per session before graduated pressure kicks in.
max_tool_output_bytes16384Emission gate (output side): a tool result larger than this is digested before it reaches the model.
window_pressure_pct70Context-window fullness (%) at which budgets start tightening.
failure_budget_factor2.0Failing runs get this multiple of the standard digest budget — a failure carries the evidence you need.
digest_head_lines5Head lines shown for a generic text flood.
digest_tail_lines5Tail lines shown for a generic text flood.

max_inline_bytes (input, native reads) and max_tool_output_bytes (output, tool results) are deliberately separate knobs — tune the flood you actually have.

The guard is the PreToolUse classifier that decides what happens to each command before it runs.

KeyDefaultMeaning
mode"guarded"advisory | guarded | strict. advisory makes the guard a no-op — nothing is steered.
unknown_command"force_ask"Disposition for a command the classifier doesn’t recognize: allow | deny | ask | force_ask.
internal_error"allow"What to do if the guard itself errors. The default is availability-safe (fail-open); set "deny" to fail-closed.
steering"auto"auto | rewrite | deny. deny disables all transparent rewrites, so you see plain denials instead of substitutions.
collapsetrueMaster switch for the transparent command-substitution surface. Set collapse = false to break-glass it off.
speculative_nativetrueOn Claude Code and Codex only, let one explicitly named pytest node run without the ctx run wrapper while the session is passive and that signature has not flooded. The fail-closed PostToolUse gate still captures any result over max_tool_output_bytes. Set false to always capture tests at birth.

Secret-bearing paths and outside-workspace access are always force-asked and never rewritten, regardless of steering or collapse.

Before unknown_command is consulted, the command-span registry recognizes bounded/structured queries for direct execution and known read-only noisy commands for capture. Unknown and mutation-shaped commands intentionally remain at the configured permission boundary. On hosts without input substitution, capture is emitted as an exact bounded rerun instruction rather than being executed transparently.

The speculative-native fast path never applies to whole suites, directories, file-only pytest targets, shell expressions, active/high-pressure sessions, or hosts without output substitution. An unexpected flood is digested once and marks that signature so subsequent calls return to birth-time capture. This policy is the first reviewed AlphaEvolve product canary; its measured tradeoffs and remaining proof boundary are documented in the optimization guide.

To make the harness stricter, mode and steering are different axes: mode sets how much is classified (advisory off → guardedstrict), while steering sets what happens to a flooding command — auto/rewrite transparently reroute it through ctx run, and steering = "deny" blocks it outright so nothing runs until you re-issue it yourself. A locked-down install typically pairs mode = "strict" with steering = "deny".

KeyDefaultMeaning
allow_outside_rootfalsePermit access outside the workspace root.
follow_symlinksfalseFollow symlinks during capture.
nested_repos"separate"How nested git repositories are treated.
respect_gitignoretrueHonor .gitignore during capture.

Controls how quickly the harness ramps from staying out of the way to actively steering, so small sessions aren’t taxed.

KeyDefaultMeaning
mode"auto"auto | active | passive.
activate_after_calls8Interception count at which a session graduates passive → active.
lean_modelsbuilt-in listModels treated as “lean” (steered more conservatively).
KeyDefaultMeaning
backend"user-state"user-state | local.
retention_days30How long artifacts are kept before garbage collection is eligible.

The store lives outside the repo by default, under ~/.local/state/ctx (or $CTX_STATE_HOME / $XDG_STATE_HOME if set). local selects .ctx-session-reads/store and is now an effective backend, not an advisory hint. If user-state is read-only under a managed sandbox, ctx proves that with an actual write, falls back to the local backend, and records a path-free sticky route so later commands can still resolve the run handles already emitted. ctx doctor reports the effective backend. It fails only when neither location is writable; see Troubleshooting.

[plan] — bounds on compiled investigations

Section titled “[plan] — bounds on compiled investigations”

Ceilings a ctx plan / ctx plan run program may only tighten, never exceed.

KeyDefaultMeaning
max_nodes24Maximum plan nodes.
max_fanout64Maximum plan fan-out.
wall_seconds120.0Wall-clock bound for a plan run.
replans1Allowed replans.

[surface] — the input side (MCP tool schemas)

Section titled “[surface] — the input side (MCP tool schemas)”

Governs the discretionary-context budget and the pre-flight gate. That gate runs on SessionStart for Claude Code and Codex; Antigravity has no such event, so there it runs on PreInvocation and injects the advisory as an ephemeral message (that hook fires before every model call, so a persistent one would re-accumulate context on each).

KeyDefaultMeaning
max_static_tokens8000Discretionary-surface token budget per turn.
gate"warn"off | warn — pre-flight gate mode.
default_profile""Profile suggested when over budget.
gatewayfalseUse the MCP gateway delivery (progressive disclosure).
probetrueMeasure real MCP tool schemas (cached) during the gate.
KeyDefaultMeaning
enabledtrueEnable secret redaction in model-visible output.
patternsfull built-in setActive redaction patterns.

A guard rail worth knowing: if patterns is set to something that isn’t a list (a bare string typo, say), straitjacket rejects it and falls back to the full default set — a typo can’t silently disable redaction.

Define named scopes so --scope <name> selects a subtree:

[scopes.payments]
roots = ["services/payments", "libs/payments-common"]

Each [scopes.<name>] needs a roots list; a non-list roots is ignored.

Map ws:<alias> selectors to other workspace paths (absolute, or relative to this root):

[aliases]
api = "../api-service"

Then address across workspaces with ws:api/repo:src/main.py.

A fixed built-in denylist always blocks capture of secret-bearing paths — you cannot remove these, only add to them:

.env .env.* **/.env **/.env.* **/secrets/** **/credentials/**
**/*.pem **/*.key **/id_rsa* **/id_ed25519* **/.aws/**
**/.config/gcloud/** **/.ssh/**

.ctxignore at the workspace root adds to that list — one glob per line, # comments and blank lines ignored. It is additive only: nothing you write can remove a built-in secret denial. ctx init seeds it with the secret list plus common noise (node_modules, .venv, dist, build).

Terminal window
ctx doctor # validate the install, store, hooks, and classifier
ctx doctor --antigravity # also validate the Antigravity plugin

ctx doctor prints one / per check and exits non-zero if any fail. See Troubleshooting for what each failing check means.


Getting started · CLI guide · Troubleshooting · Concepts