Skip to main content
Complete configuration reference for beads. Last reviewed: 2026-09-10 Freshness source: cmd/bd/main.go, cmd/bd/config.go, and internal/configfile/. beads has two complementary configuration systems:
  1. Tool-level configuration (YAML, managed by Viper) — startup flags and tool behavior, stored in config.yaml files. These are user preferences: output format, auto-commit behavior, CLI ergonomics.
  2. Project-level configuration (managed by bd config) — integration credentials, status maps, and project-specific settings, stored in the Dolt database. Some keys are routed to config.yaml instead (see YAML-only keys below).
The split is deliberate: tool settings are user-specific; project config is team-shared and travels with the database when you run bd dolt push. That is also why secrets are refused in the database — see Security. Dolt is the only storage backend. Embedded mode (the default) stores data at .beads/embeddeddolt/; server mode (bd init --server or BEADS_DOLT_SERVER_MODE=1) uses .beads/dolt/. See Dolt architecture.

Configuration Locations

config.yaml is searched in this order, with later files overriding earlier ones:
  1. ~/.beads/config.yaml (legacy user-level, lowest priority)
  2. ~/.config/bd/config.yaml (user-level; this exact path is checked even on platforms whose native user-config directory differs)
  3. <repo>/.beads/config.yaml (project-level, walked up from the current directory)
  4. $BEADS_DIR/config.yaml (highest priority, when BEADS_DIR points at a different workspace)
A config.local.yaml next to the project config.yaml is also merged in last for machine-specific overrides that should not be committed.

Precedence

For Viper-managed (YAML) keys, highest to lowest:
  1. Command-line flags (e.g. --json, --db, --actor)
  2. Environment variables (BD_*, plus a small set of legacy BEADS_* names — see below)
  3. config.yaml files (in the order listed above)
  4. Built-in defaults
Project-level keys written via bd config set (Jira, Linear, GitHub, status maps, etc.) live in the Dolt database. They are read at command time and have no env var override. When a config.yaml value or environment variable shadows a database key, bd config list prints an override warning, and bd config show reports the source of every effective key.

Managing Configuration

bd config set automatically routes the write to the right location: keys in the YAML namespace (see below) are written to the project config.yaml; everything else is written to the Dolt database. beads.role is stored in git config. Unrecognized keys produce a warning with a did-you-mean suggestion; use the custom.* namespace for user-defined keys.

YAML-only Keys (Startup Settings)

These keys must live in config.yaml, not the database, because they are read before the database is opened. Writing them with bd config set automatically updates config.yaml. The full namespaces routed to YAML are: routing.*, sync.*, git.*, directory.*, repos.*, external_projects.*, validation.*, lint.*, hierarchy.*, ai.*, backup.*, export.*, dolt.*, federation.*, metrics.*, list.*, audit.*, storage-class.* lint.* holds lint settings: lint.sections.<type> is a comma-separated, additive list of sections that bd lint additionally requires for issues of that type (built-in required sections still apply; unset means no behavior change). Plus these individual keys: no-db, json, db, actor, identity, no-push, no-git-ops, agent.profile, create.require-description, import.auto, import.path, prime.max-memories, prime.max-memory-chars, memories.budget-chars, and the secret keys github.token, gitlab.token, jira.api_token, ado.pat, linear.api_key, linear.oauth_client_id, linear.oauth_client_secret, notion.token. Any key whose name contains api_key, api-key, secret, token, or password is treated as a secret: it is refused on git-tracked config.yaml files unless you pass --force-git-tracked. Prefer exporting the value as an environment variable instead (e.g. LINEAR_API_KEY). bd config unset on one of these secret keys clears the config.yaml entry and also deletes any copy an older bd stored in the database, reporting which of them it removed. Keys reached YAML routing at different points in bd’s history, so a workspace configured before the move can still hold the value in a table that bd dolt push replicates. The database half is best effort: the command never creates a database to look in, so it is skipped where reaching the row would mean provisioning one — but not for a server-mode or proxied workspace whose database is real and simply not on local disk; a database that refuses the delete is reported as a row that is still stored. Deleting it locally does not unpublish it — rotate a secret that has already been pushed.

Tool-Level Settings (config.yaml)

JSONL export is opt-inexport.auto and export.git-add are disabled unless configured explicitly. .beads/issues.jsonl is an optional export for viewers, interchange, and issue-level migration. It is not the canonical source of truth, not cross-machine sync, and not a full database backup.Workflows that depend on a fresh, git-staged JSONL file should opt in:
Use bd dolt push / bd dolt pull for sync and bd backup for restorable database backups.
Routing note: output.title-length and agents.file are functionally tool-level settings, but bd config set writes them to the Dolt database. They are typically read from config.yaml when set there directly. bd config show is the source of truth for what’s currently effective on your machine, including provenance.

Dolt History, Backup, and Push

Three post-write behaviors run after each successful write command, in this order: auto-commit, auto-backup, auto-push.

Auto-commit: SQL Commits vs Dolt Commits

There are two different kinds of “commit”:
  • SQL transaction commit — what happens when a bd command updates tables successfully (durable in the Dolt working set).
  • Dolt version-control commit — what records those changes into Dolt history (visible in bd history, and what push/pull/merge workflows operate on).
By default (dolt.auto-commit: on), bd creates a Dolt history commit after each successful write command, so changes are never left only in the working set. The cost is more Dolt commits over time — one per write command — which is intentional; use bd compact to squash old history. Disable for a single command:
Or in config.yaml:
batch and off defer the Dolt history commit instead of skipping the write: the change is still durable in the working set, and bd dolt commit records the accumulated batch as a single Dolt commit. This applies on every storage mode — embedded, direct SQL-server, and proxied-server. In proxied-server mode the deferral covers the writes the CLI makes on that route, including workspace config and version metadata, and bd dolt commit is the only flush point there: the SIGTERM/SIGHUP flush of a live batch-mode process needs an open store, which proxied mode never has (it does run in embedded and direct SQL-server modes). Two kinds of write stay outside the deferral, in every mode:
  • Explicit commit points — bd batch (whose commit message defaults to a synthesized one when -m is absent, so every batch commits), bd mol bond, bd mol pour, bd mol squash, bd mol wisp create, and the wisp half of bd mol burn — commit what they wrote, with the message they were given: the commit is that command’s contract rather than auto-commit policy. Burning a persistent molecule is an ordinary delete, so it honors the policy and defers with everything else.
  • Writes made by a bd serve process, whose HTTP API requests carry their own context instead of the CLI’s, so a daemon keeps committing per write even while the CLI on the same database defers.

Auto-backup

Periodic Dolt-native backup to .beads/backup/ provides a recovery path independent of the live database. Local Dolt commits (via dolt.auto-commit) remain the primary safety net; backup is a secondary layer. Unlike bd export or .beads/issues.jsonl, this is a full database backup: it preserves tables, branches, commit history, and working-set data.
How it works:
  • After each write command, bd compares the Dolt HEAD commit hash against the last backup state.
  • If data changed and the throttle interval has passed, bd checks the destination directory’s on-disk size against backup.size-cap-mb. CALL DOLT_BACKUP('sync', ...) only ever adds new chunks to the destination — it never prunes ones that became unreachable on the source (history rewrites, superseded data), and Dolt exposes no supported way to GC a backup destination in place. Left uncapped, the destination can only grow until disk fills; this was the root cause of a 2026-06-19 outage where a 1.7GB store produced a 43GB backup directory. Once the cap is reached, auto-backup pauses — nothing is deleted, but no further syncs run until you raise backup.size-cap-mb or point backup.git-repo at a different git repository, whose backup/ directory then becomes the destination. bd backup init does not end the pause: it configures the separate destination that manual bd backup sync pushes to. Set size-cap-mb: 0 to disable the cap entirely.
  • If the destination is under its cap, a Dolt-native backup is synced to .beads/backup/ (or to a backup/ directory inside backup.git-repo when configured).
  • State is tracked in backup_state.json inside the backup directory. bd backup status prints PAUSED (cap exceeded) when the destination is over its cap, and its --json output reports the same state as size_cap.exceeded: true — so an agent/CI caller relying on --json or --quiet can see that auto-backup has stopped instead of reading a reassuring “Last backup” line while nothing further syncs.
  • The cap applies only to auto-backup; manual bd backup / bd backup sync are not capped.
Manual commands (see bd backup):

Auto-push

By default, bd does not push automatically after write commands. Auto-push is explicit opt-in because concurrent pushes to git-protocol Dolt remotes can corrupt or strand remote history when multiple writers race.
How it works:
  • After each write command (after auto-commit and auto-backup), bd checks whether a push is due.
  • Pushes are debounced: skipped if the last push was less than dolt.auto-push-interval ago.
  • Change detection: skipped if the Dolt HEAD commit hasn’t changed since the last push.
  • Push failures are warnings only (non-fatal), and failed attempts are throttled too.
  • Last push time and commit are tracked in .beads/push-state.json, a per-machine file (not in the database, to avoid merge conflicts across machines).
Before pushing, bd verifies the local chunk store with dolt fsck --quiet, bounded by a 30-second timeout. For large stores, raise it with the runtime-only BEADS_FSCK_TIMEOUT environment variable (accepts durations like 2m or bare seconds like 90).

Actor Identity Resolution

The actor name (used for created_by and audit trails) is resolved in this order:
  1. --actor flag (explicit override)
  2. BEADS_ACTOR environment variable
  3. BD_ACTOR environment variable (deprecated alias)
  4. git config user.name
  5. $USER environment variable
  6. "unknown" (final fallback)
For most developers no configuration is needed — issue authorship matches commit authorship automatically. To override, set BEADS_ACTOR in your shell profile:

Project-Level Settings (Database)

These are written to the Dolt database by bd config set and have no env var override. Common namespaces: Issue prefix (issue_prefix) is not settable via bd config set — use bd init --prefix, bd bootstrap, or bd rename-prefix.

Custom Statuses and Types

Custom statuses supplement the built-ins (open, in_progress, blocked, deferred, closed). Each entry is name or name:category:
The category controls how the status behaves: Custom types extend the built-in issue types:
Use bd statuses and bd types to list everything configured.

Sequential Counter IDs

By default, beads generates hash-based IDs (e.g. bd-a3f2). For projects that prefer short sequential IDs (bd-1, bd-2, …), enable counter mode:
Counter mode behavior:
  • Each prefix (bd, plug, …) has its own independent counter, so multi-repo or routed setups don’t interleave.
  • The counter is stored atomically in the database; concurrent creates within a single Dolt session are safe.
  • On first use (including switching an existing repository to counter mode), the counter seeds itself from the highest existing numeric ID for that prefix, so new IDs don’t collide with old ones.
  • An explicit --id flag on bd create bypasses ID generation entirely; the counter is not incremented.
  • Counter mode applies only to regular issues, not wisps.
Tradeoff — hash vs. counter:

Adaptive Hash IDs

Hash IDs size themselves to the database: lengths start at min_hash_length and grow toward max_hash_length to keep the collision probability under max_collision_prob.

Claim Pools

A dispatcher can pre-assign issues to a pool alias, a placeholder assignee such as fable-crew, and let any actor take them with bd update <id> --claim. List the aliases in claim.pools:
Claiming reads this key from the database only. A claim.pools value in config.yaml or in an environment variable has no effect, even though bd config show lists a config.yaml value with the source (config.yaml). A value that claiming uses shows the source (database).
  • Exact match. The value is split on commas and each entry is trimmed of surrounding whitespace. An issue counts as pool-assigned only when its assignee equals one of the entries exactly, including case: Crew-A is not the pool crew-a.
  • Claiming. Taking a pool-assigned issue works like taking an unassigned one: the claimer becomes the assignee, the status moves to in_progress, and the claim gets the normal lease.
  • Anti-steal. Issues assigned to a real actor, or to an alias that is not listed, keep their protection: --claim refuses them.
  • Reassigning. bd assign and bd update <id> --assignee can move an in_progress issue that a pool alias holds without --force.
  • bd ready --claim takes only unassigned issues, so it skips pool-assigned ones even though bd ready lists them. Claim those by ID.
  • Lease expiry. If the claimer’s lease expires, bd reclaim sets the issue back to open with no assignee. It does not return the issue to the pool alias, so a dispatcher that wants it back in the pool has to reassign it.

Sync and Federation

Beads syncs exclusively through Dolt remotes (bd dolt push / bd dolt pull) with cell-level merge. Use bd export for issue portability and bd backup for restorable database backups. Federation settings live in config.yaml:
  • federation.remote: Dolt remote URL (dolthub://org/beads, gs://bucket/beads, s3://bucket/beads, az://account.blob.core.windows.net/container/beads, file://...)
  • federation.sovereignty: data sovereignty tier:
    • T1: Full sovereignty — data never leaves controlled infrastructure
    • T2: Regional sovereignty — data stays within region/jurisdiction
    • T3: Provider sovereignty — data with trusted cloud provider
    • T4: No restrictions — data can be anywhere
bd config validate checks the remote URL format, the sovereignty tier, federation.allowed-remote-patterns, and routing.mode.

Integration Configuration

Tracker settings are project-level config under the tracker’s namespace; secrets (jira.api_token, linear.api_key, github.token, gitlab.token, ado.pat, notion.token) are YAML-routed and better supplied as environment variables. Every tracker records <tracker>.last_sync automatically after a sync, enabling incremental syncs.

Jira

jira.custom_fields.<field> applies to every issue pushed to Jira. jira.custom_fields.<JiraType>.<field> applies only when the mapped Jira issue type matches <JiraType>; per-type fields override global fields with the same field key. Values beginning with { or [ are sent as JSON (useful for select-like fields); other values are sent as strings. jira.url, jira.project/jira.projects, and jira.api_token fall back to the JIRA_URL, JIRA_PROJECT/JIRA_PROJECTS, and JIRA_API_TOKEN environment variables. See bd jira.

Linear

When linear.team_ids is set, bd linear sync fetches issues from all listed teams; push with multiple teams configured requires an explicit --team. The singular linear.team_id remains supported. Mapping namespaces — linear.priority_map.* (Linear 0–4 → beads 0–4), linear.state_map.* (Linear state types and custom state names → beads statuses, e.g. bd config set linear.state_map.in_review in_progress), linear.label_type_map.* (Linear labels → bd issue types), and linear.relation_map.* (Linear relations → bd dependencies; imported only when pulling with --relations) — are documented with defaults in bd linear. Staleness detection: after each successful pull, bd writes a timestamp to .beads/last_pull (a local-only, per-machine file covered by the .beads/.gitignore template). bd linear sync --pull-if-stale pulls only when data is older than the threshold (--threshold, default 20m), and a 5-minute debounce prevents agent loops. bd prime and other core commands never contact Linear — run bd linear sync --pull-if-stale from a session-start hook to keep data fresh in agent sessions.

GitHub

See bd github.

Azure DevOps

Connection keys (ado.pat, ado.org, ado.project, ado.projects, ado.url) each have an AZURE_DEVOPS_* environment variable equivalent; config keys take priority over env vars. When ado.projects is set, bd ado sync fetches work items from all listed projects in a single query. State maps default to the Agile process template (override with ado.state_map.* / ado.type_map.* for Scrum or CMMI), and priority mapping (ADO 1–4 ↔ beads 0–4, with backlog collapsing to low) is automatic and not configurable. Full setup, mapping tables, and sync commands: Azure DevOps integration and bd ado.

Environment Variables

The Viper env prefix is BD_. Config keys map to env vars by upper-casing and replacing . and - with _ (e.g. dolt.auto-commit → BD_DOLT_AUTO_COMMIT, validation.on-create → BD_VALIDATION_ON_CREATE). Selected commonly-used variables: Integration secrets follow tracker-specific conventions: LINEAR_API_KEY, GITHUB_TOKEN, GITLAB_TOKEN, JIRA_API_TOKEN, AZURE_DEVOPS_PAT, ANTHROPIC_API_KEY. These are preferred over storing the value in config.yaml for git-tracked projects. bd config show will display the source of every effective key, making overrides explicit.

Migration Freeze

Maintenance that runs outside bd — moving a database to a new host, rewriting schema by hand — needs a way to stop writes for its duration without uninstalling the tool. Create a file named MIGRATION-FREEZE and every write command refuses to run until you delete it. bd looks for the marker in the workspace directory and in the working directory, plus every ancestor of each, so one file above a tree of repositories freezes all of them at once — and targeting a frozen workspace from elsewhere (BEADS_DIR=…, bd -C …) is refused the same way. Markers found in a world-writable sticky directory such as /tmp are ignored: anyone on a shared machine could plant one there, and the sticky bit would stop you removing it. Put the marker at the root of the tree you are freezing. A freeze marker must be a regular file. A directory or a symlink named MIGRATION-FREEZE that the ancestor walk finds is ignored — the symlink with a one-line warning on stderr, so a stray link never disarms the gate in silence. To drive the freeze through an indirection on purpose, name that link with BD_MIGRATION_FREEZE_FILE (below), which bd follows.
Blocked commands exit 14 — a stable code scripts branch on to tell “someone is migrating this workspace, come back later” from a generic failure worth an immediate retry. That includes the destructive ones: bd init --reinit-local and bd bootstrap are refused too. Reads (bd list, bd show, bd ready, …) keep working during a freeze, and bd’s own background maintenance stands down with them — version-bump migration, JSONL auto-import, Dolt auto-commit, auto-backup, auto-export and auto-push are all skipped, so running a read does not leave a commit in the store you are migrating.
A bd serve process started before the marker appeared keeps accepting HTTP writes: the freeze is a CLI-invocation gate, and a running server never re-reads it. Stop the server before you freeze.
If bd cannot tell whether a marker is present — a permission error on the marker or on a directory above it — it refuses the write and says so, rather than assuming the workspace is open. BD_MIGRATION_FREEZE_FILE names one explicit path to consult instead of walking ancestors, for markers that live outside the tree. It is authoritative: when it is set, nothing else is checked. Because you name that path deliberately, it may be a symlink — bd follows it and freezes when the link resolves to a regular marker file — so an indirection such as current -> releases/42/MIGRATION-FREEZE works as the authoritative signal.

Security: Where Secrets Live

  • Tokens and API keys are never stored in the Dolt database — database config is pushed to remotes, which would expose secrets and trip GitHub secret scanning. bd config set routes secret keys to the local config.yaml instead.
  • Writing a secret to a git-tracked config.yaml is refused unless you pass --force-git-tracked; environment variables are the safer default.
  • bd init writes a .beads/.gitignore that keeps the database directories (embeddeddolt/, dolt/), runtime files, push state, and the federation credential key out of git.

Example .beads/config.yaml

For machine-specific overrides that should not be committed, drop them in .beads/config.local.yaml; it is merged in last.

Per-Command Override

Use in Scripts

Configuration is designed for scripting; every bd config subcommand takes --json:

Viewing Active Configuration