Configuration reference
Every env var june reads, grouped by section. The Ctrl-F page. Source of truth: [`src/config.mjs`](../src/config.mjs).
Every env var june reads, grouped by section. The Ctrl-F page. Source of truth: src/config.mjs.
Env vars are loaded in this order (first-set wins):
process.envas inherited from the shell / process supervisor.~/.config/june/.env— written byjun setup, the canonical location for global installs.<repo>/.env— fallback for thegit clone && npm linkflow.
process.env beats every file. Use the XDG file for normal installs; use launchd / systemd / Docker --env-file to inject in production.
jun config show prints the resolved value of every key below and which file each came from. jun config edit opens the .env in $EDITOR.
Required
| Variable | Notes |
|---|---|
LINEAR_API_KEY | Linear personal API key (lin_api_…). june fails to boot without it. See setup.md → Linear personal API key. |
LINEAR_WEBHOOK_SECRET | HMAC-SHA256 signing secret for incoming webhook bodies. Must match the secret set on the Linear webhook. june fails to boot without it. |
For the setup scripts (register-webhook, ensure-states, ensure-labels, acceptance:live) and the waiting sweep, JUNE_TEAM_KEY is also required.
Network
| Variable | Default | Notes |
|---|---|---|
JUNE_PORT | 8787 | TCP port the HTTP server listens on. Also accepts the legacy PORT env var. |
JUNE_HOST | 127.0.0.1 | Interface to bind. Leave on loopback when behind a reverse proxy / tunnel; set to 0.0.0.0 only inside a container. |
JUNE_PUBLIC_WEBHOOK_URL | (unset) | The publicly-reachable URL for the Linear webhook (e.g. https://example.com/webhook/linear). jun setup writes this; jun doctor re-verifies it. |
JUNE_ALLOW_UNSIGNED_WEBHOOKS | 0 | Set to 1 to skip HMAC verification on /webhook/linear. Only for local testing. See security.md. |
Paths
| Variable | Default | Notes |
|---|---|---|
JUNE_APP_DIR | The repo root. | Where data/, logs/, and guards/ live. Override to point at a different install location. |
JUNE_AGENT_ROOT | process.cwd() | Working directory the runners chdir to before exec. Set this to your actual workspace. |
JUNE_DB | <appDir>/data/agent.sqlite | SQLite database path. june creates the directory if missing. |
JUNE_LOG | <appDir>/logs/agent.log | Plain-text log file. Tail it with jun logs -f. |
Linear
| Variable | Default | Notes |
|---|---|---|
JUNE_TEAM_KEY | (unset) | Linear team key (e.g. ENG). Required for register-webhook, ensure-states, ensure-labels, acceptance:live, and the waiting sweep. At runtime the orchestrator reads the team from each webhook, so serving works without this. |
JUNE_BOT_USER_EMAILS | (empty) | Comma-separated list of Linear user emails to suppress as bot input. The orchestrator's own viewer email is added automatically at boot; use this for additional bot accounts in your workspace. |
Runner: Claude (default)
| Variable | Default | Notes |
|---|---|---|
JUNE_CLAUDE_BIN | claude | Path to the Claude Code CLI. Use an absolute path under launchd / systemd if the binary isn't on the stripped PATH. |
JUNE_RUN_TIMEOUT_MS | 0 (off) | Per-run hard timeout that applies to every runner. Per-runner overrides below win when set. |
JUNE_CLAUDE_TIMEOUT_MS | 0 (off) | Per-run hard timeout. 0 disables. When positive, the run is SIGTERMed at the deadline (then SIGKILLed 5 s later). |
JUNE_CLAUDE_TRIAGE_ENABLED | 1 | When JUNE_CLAUDE_TIMEOUT_MS fires, spawn a read-only triage Claude to decide RESUMABLE vs BLOCKED. Set 0 to skip. |
JUNE_CLAUDE_TRIAGE_TIMEOUT_MS | 300000 | Budget for the triage Claude (5 min). |
JUNE_CLAUDE_HEALTH_CHECK_INTERVAL_MS | 900000 | Watchdog tick interval (15 min). 0 disables the watchdog entirely. |
JUNE_CLAUDE_WATCHDOG_TIMEOUT_MS | 120000 | Budget for the watchdog Claude (2 min). |
JUNE_CLAUDE_WATCHDOG_MODEL | (unset) | Override the model the watchdog Claude uses. Unset = Claude's default. |
JUNE_CLAUDE_WATCHDOG_HARD_IDLE_MINUTES | 25 | If the main run's session log hasn't been written to in this many minutes, takeover without consulting the watchdog. |
JUNE_CLAUDE_POST_RESULT_GRACE_MS | 30000 | After Claude emits its final result event, wait this long for the CLI to exit cleanly before SIGTERMing. |
See concepts.md → Timeout triage and → Health watchdog for the behaviour.
Runner: Codex
| Variable | Default | Notes |
|---|---|---|
JUNE_CODEX_BIN | codex | Path to the Codex CLI. |
JUNE_CODEX_TIMEOUT_MS | JUNE_RUN_TIMEOUT_MS | Per-run hard timeout for Codex runs. |
JUNE_CODEX_LABEL | codex | Linear label that routes to Codex. Useful if you want a different label name. |
Codex's model and effort come from codex config (its own persisted settings); june doesn't pin them. The codex label still forces xhigh and fast still forces medium via the label-modifier path inside the orchestrator.
Runner: OpenCode
| Variable | Default | Notes |
|---|---|---|
JUNE_OPENCODE_BIN | opencode | Path to the OpenCode CLI. |
JUNE_OPENCODE_ROOT | JUNE_AGENT_ROOT | Working directory OpenCode runs in. Override to sandbox OpenCode to a subdirectory. |
JUNE_OPENCODE_PERMISSION | (see below) | JSON policy passed as OPENCODE_PERMISSION. Default denies external-directory access and arbitrary bash while allowing pwd, ls, git status, git diff, git log, npm test, node --test. |
JUNE_OPENCODE_TIMEOUT_MS | JUNE_RUN_TIMEOUT_MS | Per-run hard timeout for OpenCode runs. |
JUNE_OPENCODE_EXPORT_TIMEOUT_MS | 30000 | Budget for the opencode export <session-id> post-run harvest. |
JUNE_OPENCODE_LABEL | opencode | Linear label that routes to OpenCode. |
OPENCODE_PERMISSION | (see above) | Read as a fallback when JUNE_OPENCODE_PERMISSION is unset. |
OpenCode's model / agent / variant come from opencode config (its own persisted settings); june doesn't pin them.
Runner: Antigravity (gemini label)
| Variable | Default | Notes |
|---|---|---|
JUNE_ANTIGRAVITY_BIN | agy | Path to Google's Antigravity CLI. |
JUNE_ANTIGRAVITY_TIMEOUT_MS | JUNE_RUN_TIMEOUT_MS | Per-run hard timeout for Antigravity runs. |
JUNE_GEMINI_LABEL | gemini | Linear label that routes to Antigravity. (The label name is gemini for historical reasons.) |
The legacy Gemini CLI runner is still in the codebase as a rollback path but is not currently routed to. Its env vars (JUNE_GEMINI_BIN, JUNE_GEMINI_TIMEOUT_MS) are read for legacy callers but Google has deprecated the gemini CLI for non-Enterprise tiers on 2026-06-18.
Runner: Hermes
| Variable | Default | Notes |
|---|---|---|
JUNE_HERMES_BIN | hermes | Path to the Hermes Agent CLI. |
JUNE_HERMES_TOOLSETS | terminal,web,skills,session_search,todo,code_execution | Comma-separated toolset list. Default omits the file / search_files toolsets, which loop on large prompts. |
JUNE_HERMES_TIMEOUT_MS | JUNE_RUN_TIMEOUT_MS | Per-run hard timeout for Hermes runs. |
JUNE_HERMES_LABEL | hermes | Linear label that routes to Hermes. |
Hermes' provider and model come from ~/.hermes/config.toml; june doesn't pin them.
Routing labels
| Variable | Default | Notes |
|---|---|---|
JUNE_DEFAULT_RUNNER | claude | Runner used when no routing label matches. Don't change this from claude — Claude is the only runner whose absence blocks boot. |
JUNE_CODEX_LABEL | codex | See above. |
JUNE_OPENCODE_LABEL | opencode | See above. |
JUNE_GEMINI_LABEL | gemini | See above. |
JUNE_HERMES_LABEL | hermes | See above. |
JUNE_GOAL_LABEL | goal | Linear label that wraps the first prompt with /goal . See runners.md → Modifier labels. |
States
JUNE_*_STATES are all comma-separated lists of Linear workflow state names. The first match wins; matches are case-insensitive. The orchestrator looks each list up against the current team and falls back to a state type when no name matches.
| Variable | Default | Fallback type | What it controls |
|---|---|---|---|
JUNE_WORKING_STATES | Working,In Progress | started | State the issue moves into while a run is active. |
JUNE_REVIEW_STATES | Ready for Review,Done | completed | State the issue moves into when a run succeeds. |
JUNE_BLOCKED_STATES | Blocked,Canceled | canceled | State the issue moves into when a run fails verification or is manually stopped. |
JUNE_WAITING_STATES | Waiting | (none) | State the waiting sweep moves stale Ready for Review issues into. |
JUNE_WAITING_FROM_STATES | Ready for Review | (none) | States the waiting sweep considers stale-candidates. Comma-separated. |
Behaviour tuning
| Variable | Default | Notes |
|---|---|---|
JUNE_COMMENT_DEBOUNCE_MS | 30000 | Wait this long after a first comment to gather follow-ups into a single run. 0 disables debounce. |
JUNE_COMMENT_MAX_AUTO_FLUSHES | 3 | Cap on consecutive steer-flush restarts triggered by back-to-back comments arriving during a run. |
JUNE_PROGRESS_COMMENTS_ENABLED | 1 | Include the Progress comments block in the first-run prompt. Set 0 to suppress it. |
JUNE_THREAD_REPLIES | 0 | When 1, threaded comments instead of top-level. Both progress and final comments are threaded under the triggering comment root. |
JUNE_WAITING_SWEEP_ENABLED | 1 | Run the waiting sweep on an interval. Set 0 to disable. |
JUNE_WAITING_SWEEP_INTERVAL_MS | 300000 | Sweep tick interval (5 min). |
JUNE_WAITING_AFTER_MS | 3600000 | An issue is sweep-eligible if its latest comment is from the bot and is older than this (1 h). |
Admin
| Variable | Default | Notes |
|---|---|---|
JUNE_ADMIN_TOKEN | (unset) | Bearer token gating the /admin/* endpoints. Unset means 403 admin disabled on both endpoints. |
Defaults at a glance
If you want to bring everything up explicitly, here's a .env with every key set to its current default:
# Required
LINEAR_API_KEY=lin_api_...
LINEAR_WEBHOOK_SECRET=...
# Network
JUNE_PORT=8787
JUNE_HOST=127.0.0.1
# JUNE_PUBLIC_WEBHOOK_URL=
JUNE_ALLOW_UNSIGNED_WEBHOOKS=0
# Paths
# JUNE_APP_DIR=... # defaults to the repo root
# JUNE_AGENT_ROOT=... # defaults to process.cwd()
# JUNE_DB=<appDir>/data/agent.sqlite
# JUNE_LOG=<appDir>/logs/agent.log
# Linear
# JUNE_TEAM_KEY=ENG
# JUNE_BOT_USER_EMAILS=
# Run timeouts (apply to every runner; per-runner overrides win)
JUNE_RUN_TIMEOUT_MS=0
# Claude
JUNE_CLAUDE_BIN=claude
# claudeEffort is hardcoded to "max" — Claude doesn't persist /effort between
# runs, so the orchestrator passes --effort max every spawn. No env var.
JUNE_CLAUDE_TIMEOUT_MS=0
JUNE_CLAUDE_TRIAGE_ENABLED=1
JUNE_CLAUDE_TRIAGE_TIMEOUT_MS=300000
JUNE_CLAUDE_HEALTH_CHECK_INTERVAL_MS=900000
JUNE_CLAUDE_WATCHDOG_TIMEOUT_MS=120000
JUNE_CLAUDE_WATCHDOG_HARD_IDLE_MINUTES=25
JUNE_CLAUDE_POST_RESULT_GRACE_MS=30000
# Codex
JUNE_CODEX_BIN=codex
JUNE_CODEX_LABEL=codex
# OpenCode
JUNE_OPENCODE_BIN=opencode
JUNE_OPENCODE_LABEL=opencode
JUNE_OPENCODE_EXPORT_TIMEOUT_MS=30000
# Antigravity (gemini label)
JUNE_ANTIGRAVITY_BIN=agy
JUNE_GEMINI_LABEL=gemini
# Hermes
JUNE_HERMES_BIN=hermes
JUNE_HERMES_LABEL=hermes
JUNE_HERMES_TOOLSETS=terminal,web,skills,session_search,todo,code_execution
# Routing
JUNE_DEFAULT_RUNNER=claude
JUNE_GOAL_LABEL=goal
# States
JUNE_WORKING_STATES=Working,In Progress
JUNE_REVIEW_STATES=Ready for Review,Done
JUNE_BLOCKED_STATES=Blocked,Canceled
JUNE_WAITING_STATES=Waiting
JUNE_WAITING_FROM_STATES=Ready for Review
# Behaviour
JUNE_COMMENT_DEBOUNCE_MS=30000
JUNE_COMMENT_MAX_AUTO_FLUSHES=3
JUNE_PROGRESS_COMMENTS_ENABLED=1
JUNE_THREAD_REPLIES=0
JUNE_WAITING_SWEEP_ENABLED=1
JUNE_WAITING_SWEEP_INTERVAL_MS=300000
JUNE_WAITING_AFTER_MS=3600000
# Admin
# JUNE_ADMIN_TOKEN=You almost certainly don't want all of these in your .env — jun setup writes the minimal shape and june fills in defaults from src/config.mjs at boot. Run jun config show to see what your install actually resolved to.
What's not here
These were env vars in earlier versions; they've been removed because the per-runner CLI is the canonical place to set them, and double-pinning was just noise:
JUNE_CLAUDE_EFFORT— Claude Code doesn't persist/effort, so the orchestrator hardcodes--effort maxper spawn. Thexhigh/fastlabel modifiers still apply.JUNE_CODEX_EFFORT,JUNE_CODEX_MODEL— set viacodex configinstead.JUNE_OPENCODE_MODEL,JUNE_OPENCODE_AGENT,JUNE_OPENCODE_VARIANT— set viaopencode configinstead.JUNE_GEMINI_MODEL— Antigravity binds the model to the desktop session; the legacygeminiCLI is deprecated anyway.JUNE_HERMES_MODEL,JUNE_HERMES_PROVIDER— set in~/.hermes/config.tomlinstead.
Runners
june routes one Linear issue to one runner. The runner is a CLI on the host that june spawns as a child process, feeds the prompt over `argv`, and reads the reply back from stdout. The default is Claude Code; per-issue L
Security
An honest threat model — what june does, what it can't do, and what an operator needs to think about. Not alarmist, but not hand-waving either.