Configuration reference
lazybox reads ~/.lazybox/config.yaml. Every key is optional — lazybox runs
with an empty config by auto-detecting agents and editors and reading GitHub
credentials from gh auth token. The sections below document every top-level
key.
LAZYBOX_HOME overrides the base directory lazybox writes to, which moves the
config, state, and worktree paths accordingly.
This page tracks the config struct in
crates/config/src/lib.rs,
which is the canonical source of truth for defaults and field names.
Top-level keys
Section titled “Top-level keys”| Key | Purpose |
|---|---|
setup |
Wizard output: enabled providers / agents, filters, scopes, default agent |
desktop |
Private desktop analytics opt-in |
editors |
Override / extend the detected editors |
open_with |
Config-driven “Open with…” apps launched on a workspace (x o) |
repos |
Per-repo env, mounts, scripts, branch prefix |
agent |
Permission prompts, LLM gateway, agent state-detection timers |
agents |
Custom CLI definitions and per-agent model-tier overrides |
server |
Daemon-level tuning: ring buffer, credential cache, polling backoff |
worktree |
Global mounts, scripts, branch prefix, merged-cleanup |
scan |
Roots and depth for read-only external-checkout discovery |
terminal |
Terminal escape chord + scrollback behavior |
ui |
View state, key remaps, keymap preset, theme, timings, browser |
display |
Sort, filtering, glyphs |
attention |
Which signals raise the per-repo badge + notification delivery |
providers |
GitHub polling + filters, Linear teams / branch templates / cadence |
slack |
Slack mirror tokens + channels |
hooks |
Periodic maintenance scripts |
mention |
Auto-spawn on @lazybox mention |
auto_fix |
Auto-fix PRs on CI failure / conflict |
merge_on_green |
Opt bot authors into merge-on-green |
conventions |
Commit / PR conventions injected into the agent-work brief |
shell |
Shell command for the s spawn |
sandbox |
Remote dev-box lifecycle for lazybox sandbox … and the r-spawn |
remote |
Client-side --connect port-forward supervisor (remote.tunnel) |
account |
Cached non-secret platform organization, plan, and entitlement association |
Snippet workflows are not part of config.yaml — they live in their own
files: ~/.lazybox/snippets.yaml (global) and
<launch-dir>/.lazybox/snippets.yaml (loaded once for that client and wins on
key conflict). See
Use snippet workflows for fast submission,
Recent/]N memory, Ask Lazybox hot reload, and broadcast.
Annotated example
Section titled “Annotated example”# ── editors ──────────────────────────────────────────────────────────# Overrides/extends the editors lazybox auto-detects (Zed, VS Code, Cursor,# Windsurf, Fleet, IDEA, Gram). {path} expands to the worktree directory.editors: - id: zed display: Zed command: zed args: ["{path}"]
# ── repos ────────────────────────────────────────────────────────────# Per-repo overrides keyed by owner/name.repos: acme/widgets: # Injected into every shell/agent PTY in this repo's worktrees. env: DATABASE_URL: postgres://localhost/widgets_dev RUST_LOG: widgets=debug # Symlink shared directories into each worktree. mounts: - source: /Users/me/widgets-shared/node_modules link_at: node_modules placement: inside # inside | above # Materialize executables at <worktree>/_lazybox/scripts/<name>. scripts: - name: seed-db content: | #!/usr/bin/env bash set -euo pipefail psql "$DATABASE_URL" -f db/seed.sql # Override the worktree.branch_prefix for this repo only. branch_prefix: at # → at/issue-42
# ── setup ────────────────────────────────────────────────────────────# Written by the first-run wizard and the `,` Settings palette; editable# by hand.setup: providers: [github, linear] agents: [claude, codex, aider] default_agent: claude # what `w w` spawns; unset → claude
# ── desktop privacy ──────────────────────────────────────────────────desktop: analytics_enabled: false # explicit opt-in; fixed event names only
# ── agent ────────────────────────────────────────────────────────────agent: # Autonomous @lazybox work runs Claude with --dangerously-skip-permissions. # Leave unset to let the trigger decide: on for your own work, off for a # mention/label from someone other than you. Set true/false to pin it. # autonomous_skip_permissions: true # Skip permission prompts for interactively spawned agents too. skip_permissions: false # Point every spawned agent at your own LLM gateway (injected as # ANTHROPIC_BASE_URL / OPENAI_BASE_URL depending on the agent). llm_gateway_url: "http://gateway.internal" # Hard per-epic cap for Coordinator spawn_worker calls; 0 disables them. max_epic_workers: 6 # Override the extra startup brief for coordination workspaces (x c). # coordination_prompt: "Our planning and coordination procedure..."
# ── agents (per-agent overrides) ─────────────────────────────────────# Model-tier menu the `w S`/`w M`/`w L` and `a S`/`a M`/`a L` chords and# the `model:<tier>` task labels pick from. Claude ships a built-in# Haiku/Sonnet/Opus/Fable menu; Codex ships a pinned GPT-5.5 default.agents: codex: models: default: M # tier a bare spawn uses; overlays Lazybox's default tiers: - alias: M label: GPT-5 args: ["-m", "gpt-5"] aider: name: Aider command: aider args: [--model, sonnet] resume_args: [--resume] asking_patterns: ["Proceed?"]
# ── worktree ─────────────────────────────────────────────────────────worktree: branch_prefix: "" # "" → issue-42; "lazybox" → lazybox/issue-42 auto_cleanup_merged: false # reap worktrees when their PR merges mounts: - source: ~/shared/cache link_at: .cache placement: inside
# ── checkout discovery ──────────────────────────────────────────────# Used when `lazybox scan` receives no ROOTS on the command line.scan: roots: [~/code, ~/work] max_depth: 4
# ── terminal ─────────────────────────────────────────────────────────terminal: escape_char: "]" # press twice to open the terminal command menu escape_window_ms: 600 # window between the two presses agent_dead_on_arrival_ms: 10000 # preserve fast/failed exits for inspection
# ── shell ────────────────────────────────────────────────────────────shell: # Unset/empty → OS login shell, then $SHELL, then /bin/sh. command: /bin/zsh
# ── ui ───────────────────────────────────────────────────────────────ui: keymap_preset: default # base keymap layer: default | vim theme: Lazybox Dark # written back by the `t` theme picker terminal_new_layout: split # ordinary new terminals: split | tabs (`]]t` toggles) activity_pane_default: full # right pane start mode: full | summary | hidden (`Shift-P` cycles) confirm_default: # which Confirm button Enter highlights, by source destructive_shortcut: yes # a destructive chord (x x, g m, …): the chord is the intent event: no # an unsolicited prompt (merged-PR removal): don't destroy on a stray Enter # Remap any catalog action. Keys are snake_case action ids; values are # key-spec strings. Unset actions keep their default binding. action_keys: merge_pr: Ctrl-m refresh: Ctrl-r spawn_agent.claude: c # restore a top-level Claude spawn key spawn_agent.aider: "a z" # custom agents choose their own chord short_snooze: 4h long_snooze: 365d browser: Google Chrome log_path: /tmp/lazybox.log
# ── providers ────────────────────────────────────────────────────────providers: github: poll_interval: 60 # seconds detect_needs_reply: true # show/hide needs-reply badges filters: - org: acme - repo: acme/widgets - watch: acme/infra # all open PRs, regardless of involvement
# ── slack ────────────────────────────────────────────────────────────slack: bot_token: xoxb-your-bot-token app_token: xapp-your-app-token anchor_channel: lazybox-inbox per_workspace_channels: trueeditors
Section titled “editors”A list of editor definitions that override or extend the detected set
(Zed, VS Code, Cursor, Windsurf, Fleet, IDEA, Gram). An entry whose id
matches a builtin overrides it; a new id adds an editor.
| Field | Type | Description |
|---|---|---|
id |
string | Stable identifier |
display |
string | Name shown in the picker |
command |
string | Executable to run |
args |
list of string | Arguments; {path} expands to the worktree directory |
open_with
Section titled “open_with”A list of arbitrary apps launched on the focused workspace through the
“Open with…” picker (x o), decoupled from the single editors: code
editor. Empty by default.
| Field | Type | Description |
|---|---|---|
name |
string | Name shown in the picker |
command |
string | Executable to run (open on macOS hands off to Launch Services) |
args |
list of string | Arguments; {path} (worktree), {url} (PR/issue), {branch}, {repo} expand at launch. Defaults to ["{path}"] |
key |
string | Optional favorite key that binds this app to a direct chord, skipping the picker (remappable via ui.action_keys.open_with_app.<name>) |
Apps whose token the workspace can’t supply (a {url} app with no PR) are
hidden from the picker; a {path} app on a workspace with no worktree yet
provisions one first, and on a remote (--connect) daemon declines with a
pointer to s (the worktree is server-side). See
Workspaces & worktrees.
A map keyed by owner/name (the repo.full_name GitHub returns). Each entry
applies only to worktrees / spawns in that repo.
| Type | Description |
|---|---|
| map of string → string | Environment variables injected into every shell and agent PTY in that repo’s worktrees. Layered over the daemon env; a per-repo value wins over the global LLM-gateway injection (agent.llm_gateway_url) and the agent’s own spawn defaults. |
mounts
Section titled “mounts”A list of symlink definitions, stacked on top of worktree.mounts:
| Field | Type | Description |
|---|---|---|
source |
path | Existing directory to link (~/… expanded) |
link_at |
path | Where the symlink is created (relative) |
placement |
inside | above |
inside the worktree, or above it (parent). Default inside. |
scripts
Section titled “scripts”A list of executables materialized at <worktree>/_lazybox/scripts/<name>,
stacked on top of worktree.scripts:
| Field | Type | Description |
|---|---|---|
name |
string | File name under _lazybox/scripts/ |
content |
string | Inline script body |
source |
path | Alternative to content: symlink this file |
Provide either content or source per script, never both.
branch_prefix
Section titled “branch_prefix”| Type | Description |
|---|---|
| string | Override worktree.branch_prefix for this repo. "at" → at/issue-42; "" drops the prefix (issue-42); omit to inherit the global value. |
See Per-repo env & mounts for a walkthrough.
Written by the first-run wizard and the , Settings palette; safe to edit by
hand.
| Field | Type | Default | Description |
|---|---|---|---|
providers |
list of string | [] |
Provider ids currently enabled (github, linear) |
agents |
list of string | [] |
Agent ids currently enabled (claude, codex, …) |
filters |
map | {} |
Per-provider role/type filter keys (e.g. github: [pr.author, pr.reviewer]) |
scopes |
map | {} |
Per-provider scope ids (orgs / repos); empty = all |
default_agent |
string | unset | Agent the w w work shortcut spawns; unset falls back to claude |
wizard_completed |
bool | false |
Set once the wizard finishes, so an all-empty block doesn’t re-trigger it |
desktop
Section titled “desktop”| Field | Type | Default | Description |
|---|---|---|---|
analytics_enabled |
bool | false |
Record the desktop client’s fixed, content-free event names and timestamps locally. Provider and terminal contents cannot enter this boundary. |
server
Section titled “server”| Field | Type | Default | Description |
|---|---|---|---|
ring_buffer_bytes |
int | 2097152 |
Per-terminal ring buffer capacity in bytes for scrollback history replay on reconnect. Range: 64 KiB – 100 MiB. Raise for sessions with very long outputs; lower to reduce per-terminal memory usage. |
cred_cache_ttl_secs |
int | 300 |
Credential cache TTL in seconds. Command-provider credentials (e.g., gh auth token) are cached for this duration before running the command again, reducing subprocess churn. Default is 5 minutes. |
polling_backoff_cap_secs |
int | 120 |
Ceiling in seconds for provider polling’s exponential backoff on transient errors (timeouts, rate limits, 5xx). The backoff is clamped here so a persistently failing provider retries no slower than this. |
| Field | Type | Default | Description |
|---|---|---|---|
coordination_prompt |
string | built-in brief | Extra startup instructions for coordination workspaces (x c). Covers epics, owner contracts, cross-repo blockers, and minimal issue count. Read on every start/resume. An empty string disables this extra brief; shared Lazybox startup rules still apply. |
autonomous_skip_permissions |
bool | unset | Whether autonomous @lazybox work runs Claude with --dangerously-skip-permissions. Unset resolves per spawn from who triggered it: true for your own work (w, your own mentions/labels), but false for a spawn a foreign actor triggered — a mention from someone other than you, or a lazybox: label on an issue you didn’t author — so their attacker-influenceable issue text can’t drive an unattended skip-permissions agent on your host. Set true/false to pin it either way. |
skip_permissions |
bool | false |
Skip permission prompts for interactively spawned agents too |
llm_gateway_url |
string | unset | Global LLM-gateway base URL. When set, every spawned agent gets it injected as the base-URL env var its CLI reads (ANTHROPIC_BASE_URL for Claude, OPENAI_BASE_URL for Codex / Cursor). A per-repo env entry for the same var wins. Auth keys are deliberately not managed here. |
working_watchdog_secs |
int | 15 |
Fail-safe window: seconds a Working agent terminal may sit with no meaningful screen change before the daemon classifies the screen and forces the turn out of Working. 0 disables the watchdog. |
quiet_classify_secs |
int | 5 |
Quiet-timer window: seconds of PTY silence before a Working turn settles to Done. Cannot be disabled (0 falls back to 5); raise it to be less eager to call a turn finished. |
metering_proxy |
bool | false |
Route every spawned agent’s LLM traffic through lazybox’s local metering proxy — the real data source behind the header’s usage summary. The proxy forwards each request to the true upstream (or llm_gateway_url when set) and reads token counts off the response, so both Claude and Codex (and interactive terminal sessions) report real per-provider quota. Opt-in: it inserts a loopback hop in front of every agent API call. |
max_live_agents |
int | 32 |
Advisory ceiling on concurrently live agent terminals across all workspaces. Over the cap, spawns and startup recovery warn (a footer notice naming ]]x) but are never refused — lazybox advises, it does not forbid. 0 disables the warnings. |
max_epic_workers |
int | 6 |
Hard per-epic ceiling on Worker sessions a Coordinator may create through spawn_worker. Unlike max_live_agents, the tool refuses over the cap. 0 disables Coordinator worker spawning. |
nice |
int | 10 |
Scheduling niceness for spawned agent processes and their children, so a large fleet yields under contention and never starves the interactive UI (liveness over throughput). 0 disables (agents run at normal priority). Clamped to 0..=20. |
strict_mcp |
bool | false |
Launch unattended (skip-permissions) Claude spawns with --strict-mcp-config, disabling every ambient MCP server you configured. Default false: autonomous agents inherit your normal MCP setup. Read-only reviewer spawns stay strict regardless. |
reap_closed_after |
duration | 48h |
How long after a workspace’s PR/issue merges or closes its persistent sessions may keep running before the daemon reaps them (an idle agent is a ~110 MB memory ratchet). Reaped sessions stop being restored at startup; w w respawns one fresh and prompt history persists. 0s disables reaping entirely. |
The legacy name, command, args, resume_args, and asking_patterns
fields under agent remain accepted so old files parse, but new custom agents
belong under agents.<id>.
agents.<id>
Section titled “agents.<id>”Per-agent definitions and overrides keyed by agent id (claude, codex, …).
An entry with command registers a generic agent CLI at daemon startup. Add
that id to setup.agents, then give it a chord through
ui.action_keys.spawn_agent.<id>. Entries without command simply customize
a built-in. Claude ships a Haiku (S) / Sonnet (M) / Opus (L) model menu,
and Codex ships a pinned GPT-5.5 (L) default. Both built-ins pass an explicit
model on interactive, resume, and structured/headless launches; a missing or
malformed selected tier refuses the launch instead of inheriting a provider
CLI/account default. Other agents have no built-in menu.
| Field | Type | Default | Description |
|---|---|---|---|
name |
string | agent id | Display name for a custom CLI |
command |
string | unset | Executable for a custom CLI; its entry is registered only when this is set |
args |
list of string | [] |
Arguments appended to command for a fresh session |
resume_args |
list of string | unset | Arguments appended to command for resume; unset reuses args |
asking_patterns |
list of string | [] |
Output markers that classify the custom agent as Input Needed |
models.default |
string | built-in-dependent | Alias of the tier a bare spawn uses. Claude and Codex inherit Lazybox’s pinned built-in default; custom agents without a menu may use their own default model |
models.tiers |
list | [] |
Ordered tier menu. Each entry: alias (the chord key — a single uppercase letter binds as Shift, e.g. S → w S), label (shown in the popup and the ◆ tab badge), args (appended to the spawn argv) |
models.capability |
map | {} |
best / high / medium / low capability labels → tier alias, used when a spawn declares no explicit tier. The deprecated models.priority spelling still parses and is rewritten on save. A model:<tier> label names a models.tiers entry directly and needs no map |
auto_update |
bool | false |
Let lazybox apply this agent’s CLI updates automatically when the scheduled out-of-band check finds a newer version. Off by default: the check still runs and surfaces “update available”, but installing waits for the manual “update agent CLIs” action. |
worktree
Section titled “worktree”Global worktree layout — applied to every checkout, with repos.<owner/name>
overrides stacked on top.
| Field | Type | Default | Description |
|---|---|---|---|
mounts |
list | [] |
Symlinks to add to every worktree (same fields as repos.mounts) |
scripts |
list | [] |
Executables to materialize in every worktree (same fields as repos.scripts) |
auto_cleanup_merged |
bool | false |
When a tracked PR merges, reap the worktrees backing its sessions — only the ones with no locked / uncommitted / unpushed work and no live terminal |
branch_prefix |
string | "" |
Prefix for branches lazybox cuts itself (issues, blank workspaces). "" → issue-42; "lazybox" → lazybox/issue-42. Overridable per repo. |
Defaults for the read-only lazybox scan
checkout inventory. Positional roots and --depth override scan.roots and
scan.max_depth for a single invocation.
| Field | Type | Default | Description |
|---|---|---|---|
roots |
list of paths | [] |
Directories to walk when the command receives no roots; leading ~/ is expanded |
max_depth |
int | 4 |
Maximum directory levels to descend below each root |
terminal
Section titled “terminal”How you exit an embedded terminal back to the inbox, and how scrollback works.
| Field | Type | Default | Description |
|---|---|---|---|
escape_char |
char | ] |
Press twice to open the non-timed terminal command menu (q then exits to the sidebar) |
escape_window_ms |
int | 600 |
Time window between the two escape_char presses |
agent_dead_on_arrival_ms |
int | 10000 |
Grace period in milliseconds: an agent that exits cleanly before engaging is treated as failed-to-start and its final screen stays open with a restart affordance. Non-zero/signal exits are always preserved. |
View state lazybox writes back automatically (collapsed repos, splitter sizes), plus knobs you can set by hand.
action_keys — remapping keys
Section titled “action_keys — remapping keys”action_keys is the key-remapping surface. Keys are the snake_case action ids;
values are key-spec strings. lazybox consults this map first and falls back to
the catalog default for any action you don’t list. An unparseable value falls
back to the default rather than breaking the keyboard, so a typo is harmless.
ui: action_keys: merge_pr: Ctrl-m # was g m refresh: Ctrl-r # was Shift-R quit: Ctrl-q # single press (default is the q q chord) spawn_agent.claude: c # restore a top-level Claude spawn key (default a c) spawn_agent.aider: "a z"Agent spawn chords are remapped with spawn_agent.<agent-id> keys (the
built-in defaults are a c Claude, a x Codex, a u Cursor; custom agents
have no implicit chord).
Alternatives are separated by | ("g r | Shift-V").
Key-spec format:
- Modifiers
Ctrl-,Shift-,Alt-, stackable in that order (Ctrl-Shift-D). - A single character (
m,,). An uppercase letter implies Shift, soMandShift-Mare equivalent. - Named keys:
Tab,Enter,Esc,Space,Backspace,Up,Down,Left,Right,Home,End,PageUp/PgUp,PageDown/PgDn,Delete/Del,Insert. - Function keys
F1–F12(F5;F8is a shipped default — it toggles mouse capture). - Multi-key sequences separate the strokes with a space: a leader chord
(
g m), a double press (q q).
Common action ids (the full set is the snake_case names of every catalog
action in
crates/tui-core/src/action.rs):
| Action id | Default | Does |
|---|---|---|
refresh |
Shift-R |
Re-poll every provider |
open_help |
? |
Ask Lazybox |
open_settings |
, |
Settings palette |
quit |
q q |
Quit |
work |
w w |
Spawn the default/running agent with a contextual prompt |
spawn_shell |
s |
Open a shell in the worktree |
open_editor |
e |
Open the worktree in your editor |
mark_all_read |
m |
Mark the workspace read |
toggle_snooze |
z |
Snooze (~4h) |
merge_pr |
g m |
Merge the PR |
toggle_auto_merge |
g g |
Toggle lazybox’s merge-on-green arm |
manage_policies |
g p |
Open the unified automation-policy menu |
request_reviewers |
g r |
Request reviewers |
add_assignees |
g a |
Change assignees |
manage_labels |
g l |
Edit labels |
open_in_browser |
g o |
Open the PR / issue in the browser |
archive |
x x |
Archive the workspace |
new_workspace |
x n |
Repo-free floating workspace |
new_coordination_workspace |
x c |
Repo-free coordination workspace |
new_project |
x p |
New project / pick a repo |
adopt_sessions |
x a |
Move sessions into another workspace |
collapse_into_pr |
x j |
Join an issue workspace into its closing PR |
long_snooze |
x z |
Snooze the workspace for about a year |
close_issue |
x C |
Close an issue upstream |
See the keybindings reference for the full default keymap.
Other ui keys
Section titled “Other ui keys”| Field | Type | Default | Description |
|---|---|---|---|
collapsed_repos |
list of string | [] |
Repo names whose sidebar group starts collapsed (written back automatically) |
pinned_repos |
list of string | [] |
Repo names pinned to the top of the sidebar, in pin order (p toggles). A list, not a set — the order you pinned in is the display order. Written back automatically. |
focused_workspaces |
list of string | [] |
Workspace keys you’ve starred (“focused”), in focus order — lifted into a synthetic ★ Focused section at the top and numbered for ]]<digit> focus jumps. Written back automatically. |
spaces |
list | [] |
User-defined Spaces — the grouping tier above repo headers (x m moves a source into one). Each entry names a bucket and lists its assigned source labels; the list position is the display order. Written back automatically. |
collapsed_spaces |
list of string | [] |
Space names whose groups start collapsed (Space on a Space header). Written back automatically. |
last_space |
string | (unset) | Space most recently assigned via x m, preselected in the move-to-Space picker. Written back automatically. |
sidebar_pct |
int | 40 |
Sidebar width as a percent of the screen |
right_top_pct |
int | 25 |
Activity-row height as a percent of the right column |
auto_mark_delay |
duration | 1s |
How long the cursor sits on an unread row before it auto-marks read |
quit_double_tap_window |
duration | 800ms |
Window for the second q of the quit chord |
terminal_escape_char |
char | unset | Legacy alias for terminal.escape_char; when set, this compatibility value wins |
split_step_percent |
int | 3 |
Percent a Shift-arrow nudges the focused splitter |
task_body_max_rows |
int | 8 |
Max rows the description section expands to |
short_snooze |
duration | 4h |
z snooze duration |
long_snooze |
duration | 365d |
x z long-snooze duration |
log_path |
path | /tmp/lazybox.log |
Where the client writes its log |
browser |
string | OS default | Preferred browser for g o / terminal links. macOS: the app name for open -a ("Google Chrome"); Linux: the executable. |
keep_awake |
off | working | asking | always (or bool) |
off |
Hold an OS sleep inhibitor (caffeinate on macOS, systemd-inhibit on Linux) while agents are active. working (the historical true) holds while ≥1 agent is Working; asking also holds while one is parked on input (a run one keystroke from resuming); always holds for as long as lazybox runs. off (false) never inhibits. Released the moment nothing qualifies; re-read on every agent transition — no restart needed. macOS laptop: the assertion covers system sleep only on AC power and never a closed lid, so on battery the sidebar badge reads ☼ awake (AC only) rather than implying a protection the OS isn’t giving. |
keymap_preset |
default | vim |
unset | Base keymap layer shipped in-tree; your action_keys still layer on top (vim moves pane-cycling to Ctrl-w) |
theme |
string | unset | Active UI theme by exact name ("Lazybox Dark", "Lazybox Light", "High Contrast", …). Written back by the t theme picker (live preview; Esc restores); unknown / unset keeps the default theme. Full theme list: docs/themes.md. |
show_tips |
bool | true |
Show progressive feature-discovery tips (opt-out) |
terminal_new_layout |
split | tabs |
split |
How an ordinary second terminal opens. Explicit ]]| / ]]- splits are unaffected; ]]t toggles and persists this value. |
activity_pane_default |
full | summary | hidden |
full |
Where the right (activity) pane starts for a workspace you haven’t toggled. summary shows a one-line count of new activity / failing CI; hidden folds it away. Shift-P cycles the three per workspace; a workspace with nothing to show still auto-hides. |
focus_layout |
single | split-v | split-h | grid |
single |
Focus-mode multi-workspace layout: single (fullscreen terminal), two side by side, two stacked, or a 2×2 grid over the starred roster. ]]v cycles it inside focus mode and persists back here. |
usage_summary |
bool | true |
Show the always-visible per-provider usage summary in the sidebar header (a compact Claude ▓▓▓░░ 62% · 76k left widget per agent with a live terminal). Set false to hide the row. |
usage_budgets |
map of string → int | {} |
Plan-window token budget per agent id (claude: 200000000), the denominator for the usage summary’s percentage. OAuth/plan agents expose no usage API, so set the window size here to unlock the 62% · 76k left bar; without one an agent degrades to a bare token total. |
today_summary |
bool | true |
Show the always-visible “today” stats strip in the sidebar header: a terse today 3 sessions · 4 merged · $2.14 of the day’s persisted usage rollup. Groups drop lowest-first (cost, then merged) when the sidebar is narrow. Set false to hide the row. |
usage_limit_alerts |
bool | true |
Raise an escalating footer alert as agents hit their provider usage limit (transient notice → sticky banner naming the resume action → retracted once they recover). The ⏳ N limited header count and per-row pill are always shown; this only gates the footer escalation. Set false to keep the passive signals only. |
auto_wait_on_limit |
bool | false |
Auto-press “Wait” when a Claude agent hits its usage / monthly limit, so N agents hitting the cap at once don’t each need a manual visit — re-auth with another account and then Shift-K to resume them. Re-read on every transition. |
show_agent_model |
bool | true |
Show each running agent’s model + reasoning effort next to its sidebar badge (C Opus, X gpt-5.5 · xhigh) and on its terminal tab. Set false to keep the sidebar compact. |
credit_recovery_prompt |
string | built-in | Prompt submitted after a credit chooser has cleared and the provider composer is ready (the Ctrl-k recover-credit flow). |
confirm_default.destructive_shortcut |
yes | no |
yes |
Which button Enter highlights on a Confirm modal raised by a destructive chord (x x archive, g m merge, …). The chord is the intent, so Enter confirms; set no to require an explicit arrow-then-Enter. |
confirm_default.event |
yes | no |
no |
Which button Enter highlights on a Confirm modal raised unsolicited by a provider event (a merged-PR “remove this workspace?”). Defaults to no so a stray Enter can’t destroy a workspace you didn’t ask about. |
Duration values take a unit suffix (30s, 15m, 4h, 365d).
tour_seen and tips_seen (the list of tip ids already shown, so a tip never
repeats) also live here but are managed by lazybox; you rarely set them by
hand.
display
Section titled “display”| Field | Type | Default | Description |
|---|---|---|---|
sort_by |
priority | updated |
priority |
Default sort order |
show_archived |
bool | false |
Include archived workspaces |
activity_days |
int | 7 |
Only show sessions active within this many days (0 = all) |
hide_approved_by_me |
bool | true |
Hide PRs you’ve already approved |
assignee_is_reviewer |
bool | false |
Treat assignees as reviewers |
show_inactive_in_inbox |
bool | false |
Surface merged / closed PRs in the main Inbox |
ascii_glyphs |
bool | false |
Use ASCII p/i/l instead of unicode row glyphs |
attention
Section titled “attention”Which signals contribute to the “needs attention” badge on a repo header. The
booleans all default to true.
| Field | Type | Description |
|---|---|---|
unread |
bool | Unread activity |
ci_failing |
bool | Failing CI |
review_pending |
bool | A review is requested of you |
agent_asking |
bool | An agent is waiting on input |
mentioned |
bool | You were mentioned |
desktop_notify |
bool | Fire OS desktop notifications when an agent needs input or finishes, and when a workspace gains an enabled attention signal |
notifier |
auto | osc | subprocess |
How the desktop banner is delivered. auto (default) picks per environment — subprocess helpers (terminal-notifier / osascript on macOS, notify-send on Linux) locally, the terminal’s OSC escape sequence over SSH; osc / subprocess force one path. |
providers
Section titled “providers”providers.github
Section titled “providers.github”| Field | Type | Default | Description |
|---|---|---|---|
poll_interval |
seconds | 60 |
How often the GitHub provider polls |
detect_needs_reply |
bool | true |
Show needs-reply badges when GitHub reports a reply is needed |
filters |
list | [] |
Narrow which PRs appear (empty = everything) |
background_budget_share |
float | 0.55 |
Maximum share of each observed GitHub primary rate-limit budget that scheduled polling may consume; the remainder stays available to interactive gh, agents, and bursts. |
include_accessible_repos |
bool | false |
Widen the inbox scope to every repo you can reach — owned, org-member, and direct-collaborator — not just the scopes you ticked in setup. Involved PRs/issues in any of those surface without a manual tick; repos you can’t access stay hidden. |
app |
map | unset | A GitHub App registration giving the status sweep its own rate-limit budget. See providers.github.app. |
gh_shim |
map | on | How a session’s gh is routed through the daemon. See providers.github.gh_shim. |
Each filters entry has exactly one of:
| Field | Description |
|---|---|
org |
PRs involving you in this org |
repo |
PRs involving you in owner/name |
watch |
All open PRs in owner/name, regardless of involvement |
providers.github.gh_shim
Section titled “providers.github.gh_shim”lazybox puts a gh shim on every spawned session’s PATH so the agents it
starts share one GitHub budget instead of racing each other for it. Reads are
deduplicated across sessions, each session is paced by its own quota, and a
mutation tells the daemon what changed — so an issue closed with gh inside a
workspace flips its row within seconds, with no sweep and no budget spent.
Anything the shim does not recognise is passed through to real gh unchanged.
To bypass it entirely, run gh.real, or set LAZYBOX_GH_SHIM=0.
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Put the shim on spawned sessions’ PATH. Off removes it, so sessions reach real gh directly. |
read_cache_ttl |
seconds | 90s |
How long one read’s output answers an identical read from another session |
session_burst |
int | 20 |
Calls one session may make back-to-back before its bucket empties |
session_refill_per_min |
float | 10 |
Tokens one session’s bucket regains per minute — its sustained rate |
providers: github: gh_shim: enabled: true read_cache_ttl: 90s session_burst: 20 session_refill_per_min: 10providers.github.app
Section titled “providers.github.app”Optional. Without it, the daemon’s status sweep and every agent session run on your personal token and share its 5,000 requests/hour — and the sweep is the only consumer that keeps a reserve, so it is the one that starves. A busy fleet can spend the whole budget and leave the inbox frozen while it looks healthy.
A GitHub App installation token carries its own 5,000/hour, so polling never competes with agent traffic. Authoring — commits, PRs, comments, merges — keeps using your personal token either way, which is what should be attributed to you.
providers: github: app: app_id: 123456 private_key_path: ~/.lazybox/github-app.private-key.pem installation_id: 987654 # optional| Field | Type | Default | Description |
|---|---|---|---|
app_id |
integer | — | The App’s numeric id, from its settings page |
private_key_path |
path | — | The App’s private key (.pem). A leading ~/ is expanded |
installation_id |
integer | discovered | Which installation to poll as. Required only when the App has more than one installation |
LAZYBOX_GITHUB_APP_ID, LAZYBOX_GITHUB_APP_PRIVATE_KEY_PATH and
LAZYBOX_GITHUB_APP_INSTALLATION_ID override the config block.
The App needs read access to the repositories you poll — Metadata, Contents, Pull requests and Issues are enough for the status sweep.
Coverage is all-or-nothing. The sweep discovers work with a GraphQL search,
not a per-repo fan-out, so a credential that cannot see one scoped repo simply
returns fewer rows with no error to notice. If the installation does not reach
every repo, org and watch: entry in your scope — or if
include_accessible_repos is on, which makes the scope open-ended — lazybox
polls everything on your personal token instead and tells you why, rather than
letting part of the inbox go dark. Widen the installation (or narrow the scope)
and the sweep moves back onto the App budget on its own.
providers.linear
Section titled “providers.linear”Linear issue polling and the branch/checkout wiring for working on Linear
tickets. A ticket’s own repo is the synthetic linear/<team>; the teams map
routes each team to a clonable GitHub repo.
providers: linear: scope: [assigned, created, subscribed] handle: antoine teams: OBI: obin-ai/obin-platform branch_template: "{handle}/{type}-{id}-{slug}" label_types: Bug: fix Feature: feat poll_interval_secs: 60 idle_poll_interval_secs: 300| Field | Type | Default | Description |
|---|---|---|---|
scope |
list | [assigned, created] |
Which issues the poller requests, as or clauses: any subset of assigned / created / subscribed. subscribed is opt-in because Linear auto-subscribes you to entire teams. An unrecognized entry warns and is dropped; an empty list falls back to the default. |
handle |
string | git user.name |
Personal git handle for the {handle} branch token. |
teams |
map of string → string | {} |
Linear team key → GitHub owner/repo to clone when working on that team’s issues. An unmapped team fails loudly rather than cloning the synthetic linear/<team>. |
branch_template |
string | (unset) | Branch-name template. Tokens: {handle}, {type}, {id}, {slug}; empty tokens and their orphaned separators collapse. Unset → the generic linear-<id>-<slug>. |
label_types |
map of string → string | {} |
Linear label name → commit-type token (feat / fix / chore / …) for the {type} branch token. Multiple matches resolve by a fixed precedence (fix > feat > chore > docs). |
poll_interval_secs |
int | 60 |
Base cadence for the Linear poll while tickets are actively changing (decoupled from GitHub’s hot loop — Linear changes less often). |
idle_poll_interval_secs |
int | 300 |
Cadence the poll backs off to once several consecutive sweeps returned an unchanged ticket set. Clamped up to at least poll_interval_secs. |
| Field | Type | Default | Description |
|---|---|---|---|
bot_token |
string | — | Bot token (xoxb-…); env SLACK_BOT_TOKEN wins if set |
app_token |
string | — | App-level token (xapp-…) for Socket Mode; env SLACK_APP_TOKEN wins |
anchor_channel |
string | lazybox |
Channel the mirror anchors to |
channel_prefix |
string | "" |
Prefix for per-workspace channel names |
per_workspace_channels |
bool | true |
Give each workspace its own channel (vs. one anchor channel with threads) |
allowed_users |
list of string | [] |
Slack user ids (U…) allowed to drive agents from chat (empty = everyone, with a startup warning) |
See Mirror to Slack for setup.
Periodic maintenance scripts lazybox runs from hooks.dir/<bucket>/. lazybox
never inspects what they do.
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Master switch |
dir |
path | ~/.lazybox/hooks |
Directory with daily/, hourly/, on_idle/ subfolders |
schedule.daily |
duration | 24h |
How often the daily/ bucket runs |
schedule.hourly |
duration | 1h |
How often the hourly/ bucket runs |
schedule.on_idle |
duration | 15m |
How long the inbox must be quiet before the on_idle/ bucket runs |
script_timeout |
duration | 5m |
Max runtime per script before SIGTERM |
mention
Section titled “mention”Auto-spawn settings for @lazybox mentions in issues / comments.
| Field | Type | Default | Description |
|---|---|---|---|
allowed_logins |
list of string | [] |
GitHub logins whose @lazybox [agent] [model-alias] directives can auto-spawn an agent. Empty = just the authenticated viewer. |
Adding any login other than your own is a trust decision: that person can
turn an issue comment into an autonomous agent run — built from
attacker-influenceable issue text — with full git/gh and your machine’s
credentials. A mention from a non-viewer login therefore keeps its permission
prompts on (agent.autonomous_skip_permissions resolves to false)
unless you set that flag explicitly. See Trigger agents with @lazybox mentions.
auto_fix
Section titled “auto_fix”Auto-inject fix work when a PR you authored fails CI or hits a merge conflict. Opt-in — it pushes commits to your PRs with no manual nudge.
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Master switch |
opt_out_labels |
list of string | ["no-auto-fix", "do-not-lazybox"] |
Labels (case-insensitive) that opt a PR out |
max_attempts |
int | 3 |
Attempts per PR, per failure-kind, per window |
cooldown |
duration | 1h |
Minimum gap between attempts on the same PR (floored at 60s) |
window |
duration | 24h |
Rolling window the max_attempts budget is measured over |
merge_on_green
Section titled “merge_on_green”Tuning for lazybox’s “merge on green” arm (g g). By default the daemon only
auto-merges PRs you authored; this opts specific other authors in so their
green PRs land automatically once armed. The canonical use is a green Dependabot
bump: add its login, arm merge-on-green on the PR, and lazybox merges it the
moment CI passes. Logins match case-insensitively and a trailing [bot] is
ignored, so dependabot covers dependabot[bot] too. Arming merge-on-green on
a third party’s PR that isn’t listed here is refused with a reason rather than
lighting a pill that never fires.
| Field | Type | Default | Description |
|---|---|---|---|
allow_authors |
list of string | [] |
Non-author logins whose green PRs may auto-merge. Empty keeps the safe own-PRs-only behavior. |
conventions
Section titled “conventions”Commit / PR naming conventions injected into the agent-work brief. Defaults
reproduce the built-in guidance (Conventional Commits, a Closes #N. line that
collapses an issue and its PR into one row), so an unset block changes nothing.
Honored on autonomous spawns (@lazybox mentions, lazybox: labels, auto-fix)
and on the interactive w work command.
| Field | Type | Default | Description |
|---|---|---|---|
commit_style |
conventional | none | custom |
conventional |
Commit-message / PR-title-prefix style. conventional = Conventional Commits; none = no convention; custom = use custom_instruction. An unknown value falls back to conventional. |
custom_instruction |
string | (unset) | House style injected verbatim when commit_style: custom. A blank value falls back to the default guidance. |
include_closes |
bool | true |
Keep the Closes #N. body line that collapses an issue and its PR. Set false to have the brief tell the agent NOT to add it (repos that close issues manually). |
| Field | Type | Default | Description |
|---|---|---|---|
command |
string | OS login shell | Shell launched by the s spawn. When unset or empty, lazybox reads the account’s login shell from the OS passwd database, then falls back to $SHELL and /bin/sh. |
The Settings window shows the effective command and whether it is automatic or
configured. Changes apply to newly opened shells; an existing tmux-backed shell
keeps running its current process, so close that terminal and open a fresh one
to pick up the new command. Older generated configs may contain
command: bash; lazybox treats that former default as automatic. Use an
explicit path such as /bin/bash to select Bash intentionally.
account
Section titled “account”Cached, non-secret association written by lazybox account claim. An empty
block means the box is unlinked; claim codes and private keys are never stored.
account: platform_url: https://platform.lazybox.ai organization_id: org_42 organization_name: Example device_id: dev_7 plan: pro entitlement_active: true| Field | Type | Default | Description |
|---|---|---|---|
platform_url |
string | (unset) | Platform that accepted the claim. |
organization_id |
string | (unset) | Stable linked organization id. |
organization_name |
string | (unset) | Optional display name returned by the platform. |
device_id |
string | (unset) | Platform device record for this box. |
plan |
string | (unset) | Plan cached from the successful claim. |
entitlement_active |
bool | (unset) | Whether the claim response reported an active entitlement. |
entitlement_reason |
string | (unset) | Optional inactive/unknown explanation. |
remote
Section titled “remote”Client-side remote-access wiring for lazybox --connect. The block is omitted
from a written config when unset. Its one sub-block, remote.tunnel, replaces
the operator-run autossh of the bring-your-own-remote runbook: when set,
--connect spawns and keepalive-supervises an SSH (or IAP-tunneled SSH) forward
that carries the daemon socket and any workload ports before it dials.
To provision and drive the box itself (not just forward to one you already run),
use the higher-level sandbox block, which owns the box lifecycle
and the r-spawn.
remote.tunnel
Section titled “remote.tunnel”remote: tunnel: mode: ssh # ssh | iap host: me@box # ssh destination (mode: ssh) remote_socket: /home/me/.lazybox/run/daemon.sock ports: [3000, 8082] # workload TCP ports, localhost→localhostThe forward carries the daemon Unix socket (--connect’s endpoint) plus any
workload TCP ports, all bound to localhost on the client, supervised with
capped-backoff keepalive so a dropped link is re-established without operator
intervention.
| Field | Type | Default | Description |
|---|---|---|---|
mode |
ssh | iap |
ssh |
Transport: a direct ssh -N -L … (ssh), or SSH-over-GCP-Identity-Aware-Proxy for a box with no public IP (iap). |
host |
string | (unset) | SSH destination (user@host or a ~/.ssh/config alias). Required for mode: ssh. |
instance |
string | (unset) | GCE instance name. Required for mode: iap. |
user |
string | (unset) | Login user for mode: iap (the instance’s default OS-Login user otherwise). |
zone |
string | (unset) | GCE zone for mode: iap (falls back to gcloud’s active zone). |
project |
string | (unset) | GCP project for mode: iap (falls back to gcloud’s active project). |
remote_socket |
string | (unset) | Absolute path of the daemon socket on the box, forwarded to the local --connect socket. Unset → only the workload ports are forwarded. sshd does not expand ~, so give an absolute path. |
local_socket |
path | --connect path |
Local socket the forward binds. |
ports |
list of int | [] |
Workload TCP ports forwarded localhost:<p> → localhost:<p> on the box (e.g. 3000, 8082). |
server_alive_interval |
int | 15 |
SSH ServerAliveInterval — seconds of link idle before a keepalive probe. |
server_alive_count_max |
int | 3 |
How many missed probes before the link is torn down and re-established. |
sandbox
Section titled “sandbox”Remote dev-box lifecycle wiring, read by lazybox sandbox <ensure|wake|sleep| status|connect|rebuild|destroy> and by the sidebar r-spawn (which brings the
box up lazily on demand). Names the provider placement/template, the deployment
overlay, and the socket the connect forward carries. Every field is optional, so
the block round-trips out of a written config when unset, and each command’s
flags override what is set here. Omitted from a written config when empty.
sandbox: provider: gcp project: my-proj region: us-central1 zone: us-central1-a terraform_dir: ./terraform/sandbox/gcp deployment: ./.lazybox/sandbox.yaml remote_socket: /home/me/.lazybox/run/daemon.sock ports: [3000, 8082, 8787]E2B uses template and timeout_seconds instead of the GCP placement fields:
sandbox: provider: e2b template: lazybox-e2b timeout_seconds: 3600| Field | Type | Default | Description |
|---|---|---|---|
provider |
gcp | e2b |
(unset) | Provider id. |
project |
string | (unset) | GCP project. |
region |
string | (unset) | GCP region. |
zone |
string | (unset) | GCP zone (e.g. us-central1-a). |
template |
string | (unset) | E2B template id or alias. |
timeout_seconds |
int | (unset) | E2B running timeout; on expiry E2B performs a full-memory auto-pause. |
terraform_dir |
path | terraform/sandbox/gcp |
Terraform module ensure / destroy run against. |
deployment |
path | (unset) | Deployment overlay YAML deep-merged onto the embedded default; unset → the generic default recipe. |
user |
string | (unset) | SSH / gcloud login user for the IAP connect. |
remote_socket |
string | (unset) | Absolute daemon-socket path on the box the connect forward carries. |
local_socket |
path | --connect path |
Local socket the forward binds. |
ports |
list of int | [] |
Workload TCP ports the connect forward carries. |
install_lazybox |
bool | true |
Whether ensure provisions a box that builds + runs the lazybox daemon on boot. Set false for a bring-your-own-stack deployment that manages its own. |
auto_connect |
bool | false |
Connect to the box in the background at launch. Off by default: nothing touches the billed box until you connect (with Shift-C, or lazily on the first r-spawn). Governs only startup, not on-demand spawns. |
require_connect |
bool | false |
Hard-gate a remote (r-)spawn on an already-live connection. Off by default: an r-spawn while disconnected lazily brings the box up. Set true to refuse a spawn while disconnected and point at Shift-C. |
sandbox.auth
Section titled “sandbox.auth”How the provider authenticates to the cloud, so the box lifecycle runs off
configured credentials rather than whatever ambient gcloud auth login / ADC
the machine happens to have. Credentials are injected explicitly into every
provider call and scoped to a lazybox-owned gcloud config, so your own gcloud
is never touched. An empty block means ambient credentials.
sandbox: auth: service_account_key: ~/.lazybox/gcp-sa.json impersonate_service_account: deploy@my-proj.iam.gserviceaccount.com| Field | Type | Default | Description |
|---|---|---|---|
service_account_key |
path | (unset) | Service-account key (or any GOOGLE_APPLICATION_CREDENTIALS-compatible credential file). The headless / CI path. |
impersonate_service_account |
string | (unset) | Service account to impersonate; the base credentials (a service_account_key, else ambient) mint tokens for it. |
config_dir |
path | lazybox-owned dir | Override the provider-scoped CLOUDSDK_CONFIG directory. |
See Remote over SSH for the end-to-end setup.