Configuration
junco reads one JSON file: ~/.junco/config.json, resolved from the
environment only — the working directory never matters, and the deprecated
--config flag is ignored. A legacy ~/.config/junco/config.json
(respects XDG_CONFIG_HOME) keeps working until
junco data migrate moves it. Every field is optional — omit a key and its
default applies. The guided way to produce the file is the setup walkthrough
(junco dashboard on a first run); the headless way is
junco config init.
Levers marked live apply on the daemon's next poll after the file changes — no
restart. Levers marked restart need junco restart;
junco status lists any pending ones. Inspect and edit from the command line:
junco config list junco config get worker.pollIntervalSeconds junco config set worker.dailyBudgetUsd 5
A minimal config is the empty object {} — local endpoint, sandbox on, GitHub
bridge off. A typical hosted-catalog setup touches three things:
{
"model": { "id": "<provider>/<model-name>" },
"worker": { "dailyBudgetUsd": 5 },
"github": {
"enabled": true,
"repos": [{ "nwo": "acme/reef-api", "path": "~/code/reef-api" }]
}
}
junco config list print the real ones. An unset model.baseUrl means
the local default, http://127.0.0.1:1234/v1 — any OpenAI-compatible endpoint
works.
Deprecated path keys (vaultRoot, juncoSubdir,
observability.stateDir, git.worktreeRoot,
github.externalReposRoot) still work, warn once at startup, and are flagged by
junco doctor; junco data migrate unifies them under
dataDir. TOML configs are no longer read — a nearby config.toml
is a startup error, not a silent ignore.
Environment variables
JUNCO_LOG_JSON=1- Force JSON log lines even on a TTY.
GH_CONFIG_DIR- Injected by the daemon for bot-account GitHub traffic; not yours to set.
$VISUAL/$EDITOR- Editor opened by
junco analyze edit. XDG_CONFIG_HOME- Relocates the default config directory.
- provider key vars
- Hosted catalog models read their provider's conventional env var when
model.apiKeyis unset;model.apiKeyalso accepts a literal or a"$ENV_VAR"reference.
Every lever
Generated from junco config list at the version stamped below — names, types,
defaults, and descriptions are junco's own. read-only marks
structured levers you edit in the file directly rather than via
junco config set.
top-level
dataDirstring- Unified data root — queue, reviews, outbox, mirror, clones, worktrees, transcripts. default undefined · restart
Everything junco keeps on disk resolves under this one root (default
~/.local/state/junco):
<dataDir>/ queue/ inbox · processing · done · failed review/ parked assess findings · parked analyze drafts outbox/ queued GitHub writes (+ dead/ after 3 attempts) clones/ watched/ and external/ managed checkouts worktrees/ ephemeral per-ticket build worktrees transcripts/ <ticket-id>.jsonl per-run event streams watchlist.json · spend.json · worker.log · migrated.json
The root writes itself a .gitignore (*), so pointing
dataDir inside a git checkout can never dirty a commit. junco data
prints the live tree with counts and provenance; junco data migrate folds any
legacy-key layout into it.
vaultRootstring- Root directory Junco keeps its ticket queue under. default undefined · restart
juncoSubdirstring- Subdirectory under vaultRoot holding inbox/processing/done/failed. default "Junco" · restart
toolsstructured, read-only- Tool allowlist granted to the coding agent. default ["read","bash","edit","write","grep","find","ls"] · live
updateCheckboolean- Enable periodic update checks for new Junco releases. default true · live
model.*
model.idstring- Provider-prefixed model id, e.g. <provider>/<model-name>. default "local/my-model" · live
model.sourcestring- Model resolution mode: auto (catalog for non-local providers without an explicit baseUrl), catalog, or inline. default "auto" · live
model.modelsJsonstring- Path to a Pi-style models.json; when set, provider+model load from that file and the inline capability fields are ignored. default undefined · live
model.apistring- Pi API style, e.g. openai-completions. default "openai-completions" · live
model.baseUrlstring- OpenAI-compatible /v1 endpoint base URL. Unset = local default (http://127.0.0.1:1234/v1); setting it forces inline resolution. default undefined · live
model.apiKeysecret- API key for the inference endpoint. Literal, "$ENV_VAR" reference, or unset to use the provider's env var (e.g. <PROVIDER>_API_KEY) for hosted catalog models. default undefined · live
model.retry.maxRetriesnumber- SDK auto-retry attempts on transient provider errors; unset = SDK default (3). default undefined · live
model.retry.baseDelayMsnumber- Base delay for SDK auto-retry backoff in ms; unset = SDK default (2000). default undefined · live
model.reasoningboolean- Whether the model supports reasoning/thinking tokens. default true · live
model.inputstructured, read-only- Accepted input modalities for the model. default ["text","image"] · live
model.contextWindownumber- Model context window size in tokens. default 131072 · live
model.maxTokensnumber- Max output tokens per model call. default 49152 · live
model.cost.inputnumber- Cost per input token, for cost tracking/reporting. default 0 · live
model.cost.outputnumber- Cost per output token, for cost tracking/reporting. default 0 · live
model.cost.cacheReadnumber- Cost per cache-read token, for cost tracking/reporting. default 0 · live
model.cost.cacheWritenumber- Cost per cache-write token, for cost tracking/reporting. default 0 · live
model.thinkingLevelstring- Worker default reasoning/thinking level. default "medium" · live
model.thinkingLevelMapstructured, read-only- Thinking-level → chat-template effort-vocabulary translation; an override replaces the default map wholesale. default {"minimal":"low","low":"low","medium":"medium","high":"xhigh","xhigh":"xhigh"} · live
model.compatstructured, read-only- Provider compatibility overrides (developer role, reasoning effort, thinking format, etc.), merged over junco's defaults. default {} · live
worker.*
worker.defaultTimeoutMinutesnumber- Default per-ticket execution timeout in minutes. default 30 · live
worker.pollIntervalSecondsnumber- Seconds between inbox polls when idle. default 15 · live
worker.startupPollSecondsnumber- Seconds the daemon waits/polls at startup before normal polling begins. default 30 · live
worker.startupWaitboolean- Whether the daemon waits at startup for crash-recovery grace before claiming. default true · live
worker.endpointProbeauto | always | never- Endpoint probe policy: auto (probe local/inline, skip hosted catalog), always, or never. default "auto" · live
worker.maxTransientRetriesnumber- Max retries for transient failures before a ticket is marked failed. default 2 · live
worker.retryBackoffSecondsnumber- Backoff seconds before a requeued ticket becomes eligible again. default 60 · live
worker.maxConcurrentnumber- Parallel ticket slots; same-repo tickets always serialize (restart to apply — the serial-vs-scheduler mode fork is chosen once at startup). default 1 · restart
worker.commitLeftoversboolean- Sweep uncommitted leftovers into a final commit at run end. default false · live
worker.applyFallbackToAgentboolean- When a patch ticket's junco-patch series fails to apply, or applies cleanly but its own Verification block then fails, fall back to the agent (patch as spec) instead of failing the ticket terminally. default true · live
worker.dailyBudgetUsdnumber- Daily USD spend cap; claiming pauses once reached until local midnight (0 disables). default 0 · live
spend.json, surfaced on /health and the dashboard). At the cap,
claiming pauses — loudly, without burning any ticket's retry budget — and resumes at local
midnight or when you raise the cap. 0 disables the cap but keeps the ledger.supervisor.*
supervisor.enabledboolean- Enable the supervisor (nudge/escalate/kill loop-guard). default true · live
supervisor.budgetPerKindnumber- Budget of guard interventions allowed per kind before escalation. default 1 · live
supervisor.escalationWindowTurnsnumber- Turn window over which supervisor escalation is evaluated. default 3 · live
supervisor.outputBudgetPerTurnnumber- Max output budget per turn before the supervisor nudges. default 12000 · live
supervisor.outputBudgetPostCommitnumber- Max output budget per turn once a commit has landed. default 24000 · live
git.*
git.gitBinstring- Path/name of the git binary to invoke. default "git" · live
git.ghBinstring- Path/name of the GitHub CLI (gh) binary to invoke. default "gh" · live
git.defaultBaseBranchstring- Default base branch PRs are opened against. default "main" · live
git.branchPrefixstring- Prefix applied to branches Junco creates. default "junco/" · live
git.worktreeRootstring- Deprecated: overrides dataDir for per-ticket git worktrees only. default undefined · live
git.removeWorktreeOnSuccessboolean- Remove the git worktree after a successful run. default true · live
git.allowedRepoRootsstructured, read-only- Containment rail: when non-empty, PR-flow tickets may only target repos under these roots ([] = anywhere). default [] · live
repo: lies outside them fails plan-lint before any agent
runs. An empty list means any path — fine for a single-user machine, worth narrowing wherever
anything else can write tickets.pr.*
pr.draftByDefaultboolean- Open PRs as drafts by default. default true · live
pr.defaultLabelsstructured, read-only- Labels applied to every PR Junco opens. default [] · live
pr.secretScanboolean- Scan the diff about to be pushed for secret shapes; fail the ticket on a hit. default true · live
verify.*
verify.enabledboolean- Run the verify (build/test) phase before opening a PR. default true · live
verify.commandTimeoutnumber- Timeout in seconds for the verify command. default 60 · live
verify.blockOnFailboolean- Block PR creation when verify fails (vs. warn and continue). default false · live
verify.sandboxedboolean- Run ## Verification blocks under the ticket's sandbox (false = your repo's code runs unconfined). default true · live
sandbox.*
Agent tool subprocesses run OS-confined: writes jailed to the worktree plus scratch,
reads of secret paths denied, environment scrubbed, network egress denied unless a ticket
sets network: true. Backends: Seatbelt on macOS (built in), bubblewrap on Linux
(apt/dnf install bubblewrap).
bwrap the worker refuses to run tickets rather than run them unconfined; install
bubblewrap or set sandbox.backend: "none" deliberately.sandbox.enabledboolean- Wrap agent tool subprocesses in an OS sandbox (env scrub + path jail). default true · live
sandbox.backendauto | seatbelt | bwrap | none- Sandbox backend: auto (seatbelt on macOS, bwrap on Linux), a forced one, or none. default "auto" · live
sandbox.requireBackendboolean- With backend=auto, fail closed when the OS backend probe fails (tickets refuse, doctor ✗) instead of degrading to none. default false · live
sandbox.networkdeny | allow- Default network egress for agent tool subprocesses (per-ticket network:true widens it). default "deny" · live
sandbox.extraDenyReadstructured, read-only- Extra absolute paths whose reads are denied (added to the built-in secret deny-list). default [] · live
sandbox.extraAllowWritestructured, read-only- Extra absolute paths where writes are permitted (added to worktree + scratch). default [] · live
sandbox.bashTimeoutSecondsnumber- Ceiling (seconds) on one sandboxed bash call when the agent passes no timeout; 0 = none, max 2147483. The agent's explicit timeout wins when positive and finite. Live for sessions spawned after the change. default 600 · live
critic.*
critic.enabledboolean- Run the post-session critic review pass before finalizing a PR. default true · live
critic.maxRetriesnumber- Max critic-requested revision retries. default 1 · live
critic.thinkingstring- Thinking level used for critic review calls. default "minimal" · live
planLint.*
planLint.enabledboolean- Run plan-lint checks on ticket plans before execution. default true · live
planLint.blockOnErrorboolean- Block execution when plan-lint finds errors (vs. warn). default true · live
planLint.checkLabelsboolean- Include label checks in plan-lint. default true · live
observability.*
observability.healthEnabledboolean- Enable the /health metrics HTTP server. default true · restart
observability.healthHoststring- Bind address for /health; non-loopback exposes metrics (restart to rebind). default "127.0.0.1" · restart
observability.healthPortnumber- Port the /health metrics server binds (restart to rebind). default 8787 · restart
observability.logLeveldebug | info | warn | error- Daemon-wide log threshold (applied live). default "info" · live
observability.stateDirstring- Deprecated: overrides dataDir for the entire data root. default undefined · restart
observability.logToFileboolean- Write daemon logs to a file under dataDir. default true · restart
observability.transcriptsboolean- Write per-ticket event transcripts under dataDir. default true · restart
github.*
github.enabledboolean- Enable the GitHub issues→inbox bridge (disabled by default: zero gh calls). default false · restart
github.triggerLabelstring- Approval label that triggers the PR flow; lifecycle labels derive from it. Restart to apply — the reporter bakes in the label prefix at startup. default "junco" · restart
github.askLabelstring- Issue label that routes to the read-only Q&A path (defaults to `<triggerLabel>:ask`). Restart to apply (see triggerLabel). default undefined · restart
github.pollIntervalSecondsnumber- GitHub bridge sweep cadence, independent of the worker poll interval. default 60 · live
github.requireApprovalboolean- When false, a plan-ready GitHub ticket auto-executes on the next sweep. default true · live
false removes the human gate:
any plan-ready issue executes on the next sweep. On a repo with other collaborators, someone
with triage-only access could steer work they could never approve. Keep it true
anywhere that isn't a private personal repo.github.plannerModelIdstring- Planning-session model id override for GitHub tickets (same endpoint). default undefined · live
github.externalReposRootstring- Root directory for managed clones of unowned repos (fork-PR flow). default undefined · live
github.reposstructured, read-only- Explicit list of watched GitHub repos (owner/repo name-with-owner + local path). default [] · live
botAccount.*
botAccount.enabledboolean- Act as a dedicated bot account for all daemon GitHub traffic (log it in with: junco auth login). default false · restart
botAccount.configDirstring- Isolated gh config dir holding the bot login (GH_CONFIG_DIR for daemon gh/git). default "~/.junco/gh" · restart
planSets.*
planSets.enabledboolean- Enable plan-driven ticket compilation (Layer 2; Layer-1 dependency machinery is always on). default false · restart
planSets.mergePollSecondsnumber- Cadence for the plan-set merge sweep that stamps satisfied dependencies and cascades failed ones. default 60 · live
planSets.maxTasksnumber- Layer-2 compiler cap on tasks per plan set (reserved). default 10 · live
chat.*
chat.enabledboolean- Enable the dashboard chat (/chat/* routes on the health server). default true · live
chat.modelIdstring- Chat model id override (same endpoint); unset → github.plannerModelId, then model.id. default undefined · restart
chat.thinkingLevelstring- Chat thinking level; unset → model.thinkingLevel. default undefined · restart
chat.turnTimeoutMinutesnumber- Per-turn chat timeout; unset → worker.defaultTimeoutMinutes. default undefined · restart
chat.submitToolboolean- Register the junco_submit tool on new chat sessions (the model can submit a parked draft after you confirm). default true · restart
chat.confirmTimeoutMinutesnumber- How long a proposed chat submit waits for y/n in the dashboard before expiring. default 10 · restart
chat.thinkTagsauto | on | off- Split inline <think>…</think> spans from the answer into the thinking block; auto skips a turn that already streams native thinking. A running chat session keeps the value it started with (restart, or /new in the chat, to apply). default "auto" · restart
chat.maxFpsnumber- Dashboard frame cap while the chat streams (lower on a slow terminal or machine). default 60 · restart
chat.themeauto | dark | light- Palette for the chat's highlighted code fences: auto guesses from the terminal background (COLORFGBG), dark/light pin it. default "auto" · restart
assess.*
assess.maxIssuesPerRunnumber- Cap on issues filed per `junco audit` run. default 20 · live
assess.minSeveritycritical | high | medium | low- Drop assessment findings below this severity. default "low" · live
assess.npmBinstring- Binary for the dependency scan (npm audit --json). default "npm" · live
assess.fileAsme | bot- Identity `junco audit file` posts under: me (your gh login) or bot (the dedicated bot account; fails loud when its login is missing). default "me" · live
skills.*
skills.harnessDirsstructured, read-only- Harness skills dirs that receive a junco-dispatch symlink (standing consent; managed by 'junco skill install' and the wizard). default [] · restart