Configuration
Thrum uses .thrum/config.json as the single source of truth for user
preferences. Everything works with sensible defaults — you only need to edit
config.json when you want to change something.
Config File
Located at .thrum/config.json in your repository:
{
"daemon": {
"local_only": true,
"sync_interval": 60,
"ws_port": "auto",
"peer_port": "auto",
"single_agent_mode": true,
"log_level": "info"
},
"peers": {
"auto_connect": true,
"pairing_code_length": 16
},
"worktrees": {
"base_path": "~/.thrum/worktrees/myproject",
"beads_enabled": true,
"thrum_enabled": true
},
"orchestration": {
"merge_target": "main",
"default_autonomy": "end_only"
},
"permission_supervisors": ["coordinator"],
"project_name": "myproject",
"identity": {
"daemon_id": "d_01HYTESTULID01234567890AB",
"repo_name": "myproject",
"hostname": "mymachine",
"repo_path": "/Users/me/dev/myproject",
"git_origin_url": "https://github.com/me/myproject",
"init_at": "2026-04-17T06:30:00Z"
},
"identity_guard": {
"cross_worktree": "strict",
"quickstart_self_rename": "strict",
"quickstart_name_collision": "strict",
"non_git_bootstrap": "strict",
"unauthenticated_rpc": "strict",
"daemon_writer_liveness": "strict",
"prime_ownership": "strict",
"dead_pid_auto_reclaim": "warn"
},
"restart": {
"max_lines": 200,
"auto_threshold": 0,
"graceful_timeout": 30
},
"backup": {
"dir": "/path/to/backups",
"schedule": "24h",
"retention": {
"daily": 5,
"weekly": 4,
"monthly": -1
},
"post_backup": "aws s3 sync /path/to/backups s3://my-bucket/thrum",
"plugins": [
{
"name": "beads",
"command": "bd backup --force",
"include": [".beads/backup/*"]
}
]
}
}
After thrum init, a minimal config looks like:
{
"runtime": { "primary": "claude" },
"daemon": { "single_agent_mode": true }
}
This file is created during thrum init and can be edited at any time.
Schema Reference
daemon.local_only
Disable remote git sync (local-only mode).
- Type: boolean
- Default:
true - Override:
THRUM_LOCAL=falseenvironment variable
daemon.sync_interval
Seconds between git sync operations.
- Type: integer (seconds)
- Default:
60 - Override:
THRUM_SYNC_INTERVALenvironment variable
daemon.ws_port
WebSocket server port.
- Type: string
- Default:
"auto"(find free port dynamically) - Values:
"auto"or a specific port number like"9999" - Override:
THRUM_WS_PORTenvironment variable
daemon.peer_port
Tailscale peer-to-peer listener port for cross-repo communication.
- Type: string
- Default:
"auto" - Values:
"auto"or a specific port like"9100" - Override:
THRUM_TS_PORTenvironment variable
daemon.single_agent_mode
Disable messaging infrastructure for single-agent workflows. When enabled, Thrum skips the background listener, cron watchdog, stop hook checks, and inbox processing. Context management features (prime, context save/show, sessions) remain active.
- Type: boolean
- Default:
false(butthrum initsets it totruefor new workspaces) - Toggle:
thrum single-agent-mode true|false
See Single-Agent Mode for details.
daemon.log_level
Slog log level for the daemon log file (.thrum/var/daemon.log).
- Type: string
- Default:
"info" - Values:
"debug","info","warn"(or"warning"),"error" - Case: insensitive
The daemon uses lumberjack for log
rotation: 10 MB max file size, 4 rotated backups, 28-day retention, gzip
compression. View logs with thrum daemon logs (see
CLI Reference).
Worktrees
Settings for thrum worktree create/teardown/list (alias:
thrum worktree setup). These are written automatically by thrum init and can
be edited in config.json. See Orchestrator Role for
how the orchestrator uses these.
worktrees.base_path
Directory where thrum worktree create puts new worktrees.
- Type: string (absolute path)
- Default:
~/.thrum/worktrees/<project>(inferred from repo name; migrated from~/.workspaces/<project>in v0.10.0 — set the legacy path explicitly if you want to keep using it)
worktrees.beads_enabled
Whether to create .beads/redirect in new worktrees.
- Type: boolean
- Default:
true(set bythrum init)
worktrees.thrum_enabled
Whether to create .thrum/redirect and .thrum/identities/ in new worktrees.
- Type: boolean
- Default:
true(set bythrum init)
Orchestration
Settings for the orchestrator role's execution lifecycle. See Orchestrator Role for details.
orchestration.merge_target
Branch for the final merge after all epics complete.
- Type: string
- Default:
"main"
orchestration.default_autonomy
When to request human approval during plan execution.
- Type: string
- Default:
"end_only" - Values:
"per_epic"(approve after each epic) or"end_only"(approve only at the end)
Permission Supervisors
permission_supervisors
List of recipients for permission-prompt nudges from tmux-managed agents. When a tmux agent pauses waiting for a permission confirmation, the daemon delivers a nudge message to every entry in this list.
- Type: array of strings
- Default:
["coordinator"](applied at nudge-dispatch time)
Each entry is one of:
- A role name (
"coordinator","orchestrator") — broadcasts to every active agent with that role - A specific agent name (
"@impl_team_fix") — direct delivery to that agent - A specific user (
"@user:leon-letto") — direct delivery; auto-forwarded to Telegram if the bridge is configured for that user
Example:
{
"permission_supervisors": ["coordinator", "@user:leon-letto"]
}
project_name
Short human-readable identifier used to form the @supervisor_<project>
pseudo-agent sender identity on permission-prompt nudges. Falls back to
filepath.Base(repo_root) at daemon boot if empty or absent.
- Type: string
- Default: (none — derived from repo root directory name)
- Example:
"thrum"→ nudges appear from@supervisor_thrum
Peers
Settings for cross-repo peer connections. See Architecture — Peer System for how this works.
peers.auto_connect
Automatically connect to all known peers when the daemon starts.
- Type: boolean
- Default:
true
peers.pairing_code_length
Length of generated pairing codes for thrum peer add.
- Type: integer
- Default:
16
Backup
Thrum can archive your message history and agent data using a
Grandfather-Father-Son (GFS) rotation scheme. The backup section is omitted
from config.json if you are using all defaults.
backup.dir
Directory where backup archives are written.
- Type: string (path)
- Default:
.thrum/backup(relative to repo root)
backup.schedule
Automatic backup interval. The daemon runs a backup at this interval when
running. Use thrum backup schedule to configure via CLI.
- Type: string (Go duration format)
- Default: none (scheduled backups disabled)
- Examples:
"24h","12h","8h","168h"(1 week) - CLI:
thrum backup schedule 24h/thrum backup schedule off - Note: Daemon must be restarted after changing the schedule
backup.retention.daily
Number of daily backup archives to retain.
- Type: integer
- Default:
5 - Special:
0keeps no daily backups;-1keeps all
backup.retention.weekly
Number of weekly backup archives to retain.
- Type: integer
- Default:
4 - Special:
0keeps no weekly backups;-1keeps all
backup.retention.monthly
Number of monthly backup archives to retain.
- Type: integer
- Default:
-1(keep all monthly backups) - Special:
0keeps no monthly backups
backup.post_backup
Shell command to run after each backup completes successfully. Useful for offloading archives to cloud storage or triggering external notifications.
- Type: string
- Default: none
- Example:
"aws s3 sync /path/to/backups s3://my-bucket/thrum"
backup.plugins
List of third-party backup plugin definitions. Each plugin is an object with the following fields:
| Field | Type | Description |
|---|---|---|
name |
string | Unique plugin identifier |
command |
string | Shell command to run before collecting files |
include |
array of strings | Glob patterns for files to collect into the backup |
Use thrum backup plugin add --preset beads to add the built-in Beads preset,
or define custom plugins manually. The command runs from the repo root before
file collection; include patterns are evaluated after the command completes.
Example:
{
"backup": {
"dir": "~/.thrum-backups",
"schedule": "24h",
"retention": {
"daily": 7,
"weekly": 4,
"monthly": -1
},
"post_backup": "rsync -a ~/.thrum-backups/ backup-host:/thrum/",
"plugins": [
{
"name": "beads",
"command": "bd backup --force",
"include": [".beads/backup/*"]
}
]
}
}
Restart
Session restart settings for context snapshot behavior. See Session Restart & Context Recovery.
restart.max_lines
Maximum lines in a restart snapshot. The snapshot now appends a brief git log
/ git status / bd ready / thrum inbox summary at the tail, so the
effective context delivered on restart is richer per line. The default was
reduced from 1000 to 200 in v0.9.0 to reflect this.
- Type: integer
- Default:
200
restart.auto_threshold
Context window usage percentage that triggers automatic restart. Set to 0 to disable.
- Type: integer (0-100)
- Default:
0(disabled)
restart.graceful_timeout
Seconds to wait for an agent to save its own snapshot during graceful restart before falling back to force extraction.
- Type: integer (seconds)
- Default:
30
Example config with restart enabled:
{
"restart": {
"max_lines": 200,
"auto_threshold": 80,
"graceful_timeout": 30
}
}
Daemon Identity
The identity block stores the daemon's per-repo fingerprint. It is populated
automatically by thrum init (and by the first thrum daemon start on a
pre-v0.9.0 install that upgrades). You never need to edit this block manually.
identity block
{
"identity": {
"daemon_id": "d_01HYTESTULID01234567890AB",
"repo_name": "thrum",
"hostname": "leonsmacm1pro",
"repo_path": "/Users/leon/dev/opensource/thrum",
"git_origin_url": "https://github.com/leonletto/thrum",
"init_at": "2026-04-17T06:30:00Z"
}
}
| Field | Set once or refreshed | Description |
|---|---|---|
daemon_id |
Set once | ULID-based (d_ + 26 chars). Generated at thrum init; rotated only when migrating from a legacy hostname-derived id. Never changes on re-init. |
repo_name |
Refreshed | filepath.Base(repo_root). Updated on every Bootstrap call (daemon start or thrum init). |
hostname |
Refreshed | os.Hostname() result. Updated on every Bootstrap call. |
repo_path |
Refreshed | Absolute repo path. Updated on every Bootstrap call. |
git_origin_url |
Set once | Output of git config --get remote.origin.url. Set at init; not updated if already non-empty. |
init_at |
Set once | RFC 3339 UTC timestamp of daemon_id creation or rotation. Not changed on re-init of an existing valid ULID. |
The "set once" fields are the stable identity keys. The "refreshed" fields are informational metadata that keeps the block current across hostname changes, path moves, and re-clones. See Identity System for the full daemon identity lifecycle.
Identity Guards
The identity_guard block configures the enforcement mode for each of Thrum's
eight identity ownership checkpoints. Every guard defaults to strict if the
block is absent.
identity_guard block
{
"identity_guard": {
"cross_worktree": "strict",
"quickstart_self_rename": "strict",
"quickstart_name_collision": "strict",
"non_git_bootstrap": "strict",
"unauthenticated_rpc": "strict",
"daemon_writer_liveness": "strict",
"prime_ownership": "strict",
"dead_pid_auto_reclaim": "warn"
}
}
Enforcement modes:
| Mode | Behavior |
|---|---|
strict |
Guard fires; RPC or command is refused. The default for all guards. |
warn |
Guard fires; the violation is logged but the action proceeds. Useful for incident diagnosis. |
off |
Guard check is skipped entirely. Use only when you understand why it is firing. |
Guard keys:
| Key | What it checks |
|---|---|
cross_worktree |
Caller's ancestor PID chain does not contain the identity file's agent_pid. The central ownership enforcement rule. |
quickstart_self_rename |
Caller already owns an identity in this directory and is attempting to re-register under a new name (G1a). |
quickstart_name_collision |
Requested agent name is already held by a live foreign process (G1b). |
non_git_bootstrap |
thrum daemon start or thrum init was called from outside a git-anchored directory (G2). |
unauthenticated_rpc |
Mutating RPC with no verifiable caller identity, or a caller asserting an identity the daemon cannot corroborate (G3). The identity_mismatch sub-case (forgery) is unconditional and fires regardless of this mode setting. |
daemon_writer_liveness |
Daemon attempted to write to an identity file whose recorded agent PID is no longer alive (G4). |
prime_ownership |
thrum prime was called from inside a sub-agent whose closest runtime ancestor is not the identity file's owner (G5). |
dead_pid_auto_reclaim |
Dead owner's identity was reclaimed by a new caller. Informational; defaults to warn to log reclaims without blocking them. |
Daemon-level overrides: .thrum/var/guard-daemon.json accepts the same key
shape as identity_guard and is merged on top of the repo-level config. Use
this to change a single guard mode without editing the tracked config.json.
Daemon-level values take precedence over repo-level values; both layers take
precedence over the built-in strict defaults.
For per-error remediation steps, see Troubleshooting — Identity Guards.
Tmux Configuration
If you're using tmux-managed sessions, create a
~/.tmux.conf with mouse support:
# ~/.tmux.conf
set -g mouse on
Without this, scrolling doesn't work in tmux sessions and the experience feels broken. This one line makes tmux behave like a regular terminal.
Priority Chain
When the same setting can come from multiple sources, this order applies:
CLI flag > Environment variable > config.json > Default
Environment variables are intended for CI/automation overrides, not primary
configuration. For day-to-day use, edit config.json.
Environment Variable Reference
| Variable | Overrides | Example |
|---|---|---|
THRUM_LOCAL |
daemon.local_only |
THRUM_LOCAL=false |
THRUM_SYNC_INTERVAL |
daemon.sync_interval |
THRUM_SYNC_INTERVAL=120 |
THRUM_WS_PORT |
daemon.ws_port |
THRUM_WS_PORT=9999 |
THRUM_NAME |
Agent identity selection | THRUM_NAME=alice |
THRUM_ROLE |
Agent role | THRUM_ROLE=planner |
THRUM_MODULE |
Agent module | THRUM_MODULE=backend |
THRUM_HOME |
Repo path for all commands | THRUM_HOME=/path/to/repo |
THRUM_AGENT_ID |
Caller identity for daemon RPC | THRUM_AGENT_ID=alice |
THRUM_SOCKET |
Unix socket path override | THRUM_SOCKET=/tmp/t.sock |
THRUM_TS_AUTHKEY |
Tailscale auth key for peering | THRUM_TS_AUTHKEY=tskey-… |
THRUM_TS_PORT |
Tailscale listener port | THRUM_TS_PORT=9100 |
THRUM_TS_HOSTNAME |
tsnet hostname override | THRUM_TS_HOSTNAME=myhost |
THRUM_TS_STATE_DIR |
tsnet state directory | THRUM_TS_STATE_DIR=… |
THRUM_TS_ENABLED |
(deprecated) Tailscale toggle | THRUM_TS_ENABLED=true |
THRUM_NO_HINTS |
Suppress Shape B stderr hints | THRUM_NO_HINTS=1 |
THRUM_NO_HINTS suppresses the contextual hint lines that some commands emit to
stderr after execution (thrum send, thrum tmux create, thrum init). Truthy
semantics: any non-empty value except "0" or "false" (case-insensitive)
disables hints. The --quiet flag and --json flag also suppress hints (with
--json, hints move into the JSON output hints array instead of being dropped
entirely). See CLI Hints for the full hint system reference.
THRUM_HOME pins all thrum commands to the specified repo path regardless of
the current working directory. This is set automatically by thrum-startup.sh
to prevent identity drift when an agent cds into a different worktree.
THRUM_AGENT_ID pins the caller identity for daemon RPC calls, bypassing
identity file lookup. When set, commands like thrum prime and thrum overview
use this agent ID directly.
Runtime Templates
During thrum init, Thrum can generate configuration files for various AI
coding runtimes:
- Claude Code - CLAUDE.md and .claude/agents/
- Augment - .augment/ directory
- Cursor - .cursorrules
- Codex - codex.yaml
- Gemini - .gemini/
- CLI-only - No runtime configuration files
Use thrum init --runtime <name> to specify which runtime template to generate.
The selected runtime is saved as runtime.primary in config.json. The
generated template files (CLAUDE.md, .cursorrules, etc.) are not tracked in
config.json — only the primary runtime choice is.
Viewing Configuration
Use thrum config show to see the effective configuration and where each value
comes from:
Thrum Configuration
Config file: .thrum/config.json
Runtime
Primary: claude (from config.json)
Detected: claude, augment
Daemon
Local-only: true (config.json)
Sync interval: 60s (default)
WS port: 9999 (active)
Status: running (PID 7718)
Socket: .thrum/var/thrum.sock
Identity
Agent: claude_planner
Role: planner
Module: coordination
Overrides (environment)
THRUM_NAME=claude_planner
Use thrum config show --json for machine-readable output.
Role Config
The role_config top-level key persists role template answers written by
/thrum:configure-roles and thrum roles save-config. It is never set by hand
— the skill and CLI command are the intended writers.
role_config structure
{
"role_config": {
"schema_version": 1,
"roles": {
"implementer": {
"autonomy": "autonomous",
"scope": "auth module",
"rendered_hash": "sha256:abc123..."
},
"coordinator": {
"autonomy": "strict",
"scope": "",
"rendered_hash": "sha256:def456..."
}
}
}
}
Per-role fields (under role_config.roles.<role-name>):
| Field | Type | Description |
|---|---|---|
autonomy |
string | Template variant selected: "strict" or "autonomous" |
scope |
string | User-supplied scope string embedded into the rendered template (may be empty) |
rendered_hash |
string | SHA-256 of the shipped template body used when this role was last rendered (drift check) |
Top-level fields (under role_config):
| Field | Type | Description |
|---|---|---|
schema_version |
integer | Schema version of the saved config; compared against the current shipped version for drift detection |
Writes are atomic: both /thrum:configure-roles and
thrum roles save-config write via a temp file + rename, preserving every other
top-level key (backup, daemon, identity, telegram, etc.) byte-identical
via json.RawMessage round-trip.
Run thrum roles refresh after a Thrum upgrade to re-render templates from
saved answers without re-running the interactive skill. See
Role-Based Preamble Templates for the full workflow.
What's NOT in config.json
These remain separate for good reasons:
- Identity files (
.thrum/identities/*.json) — per-agent config, one file per registered agent - Context files (
.thrum/context/*.md) — volatile session state - Runtime templates — generated config files for your AI runtime (CLAUDE.md, .cursorrules, etc.)
Note: daemon, backup, and runtime settings are all stored in
config.json. The items above are intentionally kept as separate files because
they are per-agent or volatile state, not global repository settings.
Monitor Jobs
Monitor jobs watch long-running processes and emit matches as synthetic Thrum
messages. Configuration lives in the monitor state file managed by
thrum monitor start/list/show/stop/logs/restart — not in config.json.
Key behavior:
- Debounce: leading-edge, default 60s, minimum 30s. First match fires immediately; subsequent matches within the window are suppressed.
- Persistence: monitors survive daemon restarts automatically.
- Scope: local-socket-only. Monitors don't sync to remote peers.
See Monitor Jobs for the full command reference and configuration options.
Next Steps
- Single-Agent Mode — how
single_agent_modechanges daemon behavior and when to disable it - Daemon Architecture — how the daemon reads config.json and applies the settings at startup
- Sync Protocol — the sync loop behavior controlled by
local_onlyandsync_interval - Identity System — the identity files that live alongside
config.json in
.thrum/ - CLI Reference —
thrum config show,thrum peer, and other configuration-related commands