Configuration reference¶
Auto-generated from the config.Config Go types (the same
reflection that produces schemas/treeman.schema.json).
Run just sync-docs after touching a config field to refresh.
For worked examples and guidance see configuration.md.
Config layers¶
treeman reads config from two files that merge into one effective config:
- User-global —
~/.config/treeman/config.yaml($XDG_CONFIG_HOME/treeman/config.yaml). Machine-wide defaults shared by every repo. Scaffold it withtreeman init --global. - Per-repo —
<repo>/.treeman.yaml(plus an optional git-ignored.treeman.local.yamloverlay). Project-specific settings.
Later layers override earlier ones. Each top-level key has a scope that determines which file it may appear in — a key in the wrong file is a hard error at load time (no flag relaxes it):
- global — only valid in the user-global config.
- repo — only valid in a repo
.treeman.yaml. - both — valid in either; the global value is the default, a repo value overrides it.
| Key | Scope |
|---|---|
include |
both |
daemon |
global |
connections |
both |
snapshots |
global |
durables |
both |
worktrees |
both |
env_sources |
repo |
patches |
repo |
databases |
repo |
hooks |
repo |
debounce_ms |
both |
frameworks |
both |
logs |
global |
auto_fetch |
both |
main_worktree |
repo |
ports |
both |
status |
global |
notifications |
global |
Generated examples¶
Complete examples covering every key valid in each layer, generated from the schema (placeholder values — replace with real ones).
User-global ~/.config/treeman/config.yaml¶
# Other config files to merge BEFORE this file's own keys
include:
- '...'
# Daemon process settings: stderr log level. Typically lives in
daemon:
log_level: '...'
# Connection blocks per supported engine (MySQL, Postgres,
connections:
mysql: '...'
postgres: '...'
mongodb: '...'
redis: '...'
elasticsearch: '...'
s3:
endpoint: '...'
region: '...'
access_key: '...'
secret_key: '...'
use_path_style: false
container: '...'
compose_service: '...'
compose_project: '...'
container_engine: '...'
container_network: '...'
sqlite:
base_dir: '...'
# Snapshot retention/eviction policy for cached post-migration
snapshots:
cap_per_repo: 0
keep_per_source: 0
max_age_days: 0
max_total_gb: 0
gc_interval_minutes: 0
# Retention for branch_scoped durable copies: how long a branch's
durables:
max_age_days: 0
# Worktree creation behaviour: root path, symlinked mirrors
worktrees:
root: '...'
links:
- '...'
copies:
- '...'
# DebounceMs is the file-watcher debounce window in
debounce_ms: 0
# User-defined migration frameworks keyed by name. Use this when
frameworks:
<name>:
markers:
- '...'
migration_dirs:
- '...'
file_pattern: '...'
lockfiles:
- '...'
engine_hint: '...'
migrate_run: '...'
migrate_env:
<name>: '...'
rollback_run: '...'
rollback_env:
<name>: '...'
# Logs retention. Daemon-side prune drops rows older than
logs:
keep_days: 0
# AutoFetch policy. Daemon-side periodic `git fetch --all --prune`
auto_fetch:
enabled: false
interval_minutes: 0
mode: ff
# Ports declares per-worktree port slots. Each entry is a named
ports:
<name>:
- 0
# Status configures the `treeman status` widget output (icons,
status:
icons:
stable: '...'
up: '...'
down: '...'
failed: '...'
labels:
stable: '...'
up: '...'
down: '...'
failed: '...'
separator: '...'
header: '...'
row: '...'
main_marker: '...'
formats:
<name>: '...'
# Notifications opts into desktop notifications (notify-send on
notifications:
enabled: false
events:
- stable
backend: auto
Per-repo .treeman.yaml¶
# Other config files to merge BEFORE this file's own keys
include:
- '...'
# Connection blocks per supported engine (MySQL, Postgres,
connections:
mysql: '...'
postgres: '...'
mongodb: '...'
redis: '...'
elasticsearch: '...'
s3:
endpoint: '...'
region: '...'
access_key: '...'
secret_key: '...'
use_path_style: false
container: '...'
compose_service: '...'
compose_project: '...'
container_engine: '...'
container_network: '...'
sqlite:
base_dir: '...'
# Retention for branch_scoped durable copies: how long a branch's
durables:
max_age_days: 0
# Worktree creation behaviour: root path, symlinked mirrors
worktrees:
root: '...'
links:
- '...'
copies:
- '...'
# EnvSources is the ordered list of `.env*` files the credential
env_sources:
- '...'
# Files to rewrite inside each worktree with per-worktree values
patches:
- file: '...'
format: dotenv
set:
<name>: '...'
# One entry per database the project owns. Each entry pairs an
databases:
- engine: mysql
connection: '...'
name_template: '...'
dump: '...'
migrate:
run: '...'
env:
<name>: '...'
rollback:
run: '...'
env:
<name>: '...'
seed:
run: '...'
env:
<name>: '...'
inputs:
- '...'
test_clones:
clones: auto
name_template: '...'
key_prefix: '...'
fanout: 0
prewarm: 0
physical_clone_min_bytes: 0
branch_scoped: false
# Lifecycle hooks fired around worktree create/delete/checkout and
hooks:
create-before-engines:
- run: '...'
continue_on_error: false
cwd: '...'
container: '...'
compose_service: '...'
compose_project: '...'
container_engine: '...'
create-after-engines:
- run: '...'
continue_on_error: false
cwd: '...'
container: '...'
compose_service: '...'
compose_project: '...'
container_engine: '...'
delete-before-engines:
- run: '...'
continue_on_error: false
cwd: '...'
container: '...'
compose_service: '...'
compose_project: '...'
container_engine: '...'
delete-after-engines:
- run: '...'
continue_on_error: false
cwd: '...'
container: '...'
compose_service: '...'
compose_project: '...'
container_engine: '...'
checkout:
- run: '...'
continue_on_error: false
cwd: '...'
container: '...'
compose_service: '...'
compose_project: '...'
container_engine: '...'
file-change:
- run: '...'
continue_on_error: false
cwd: '...'
container: '...'
compose_service: '...'
compose_project: '...'
container_engine: '...'
match: '...'
# DebounceMs is the file-watcher debounce window in
debounce_ms: 0
# User-defined migration frameworks keyed by name. Use this when
frameworks:
<name>:
markers:
- '...'
migration_dirs:
- '...'
file_pattern: '...'
lockfiles:
- '...'
engine_hint: '...'
migrate_run: '...'
migrate_env:
<name>: '...'
rollback_run: '...'
rollback_env:
<name>: '...'
# AutoFetch policy. Daemon-side periodic `git fetch --all --prune`
auto_fetch:
enabled: false
interval_minutes: 0
mode: ff
# MainWorktree opts the repo's main checkout (repo root) into the
main_worktree:
enabled: false
databases:
- name_template: '...'
key_prefix: '...'
test_clones:
clones: auto
name_template: '...'
fanout: 0
# Ports declares per-worktree port slots. Each entry is a named
ports:
<name>:
- 0
Top-level keys¶
include (array of string) [both]¶
Other config files to merge BEFORE this file's own keys
(depth-first, cycle-checked). Paths are relative to the
including file. Lets a fleet of repos share one
databases:/connections: fragment (#78).
daemon (DaemonConfig) [global]¶
Daemon process settings: stderr log level. Typically lives in the user-global config.
connections (ConnectionsConfig) [both]¶
Connection blocks per supported engine (MySQL, Postgres, MongoDB, Redis, Elasticsearch). Treeman dials these to create per-worktree clone databases, run migrations.
snapshots (SnapshotsConfig) [global]¶
Snapshot retention/eviction policy for cached post-migration template snapshots: per-repo cap, per-source keep, max age, max total size, GC cadence.
durables (DurablesConfig) [both]¶
Retention for branch_scoped durable copies: how long a branch's preserved databases survive unused while the branch still exists.
worktrees (WorktreesConfig) [both]¶
Worktree creation behaviour: root path, symlinked mirrors (links), and copied files (copies).
env_sources (array of string) [repo]¶
EnvSources is the ordered list of .env* files the credential
resolver consults when looking up DB passwords and other
secrets. Later entries override earlier ones. Empty means no
env files are read — treeman init scaffolds a
framework-tailored env_sources list.
Per-worktree rewriting of these files lives in patches:.
patches (array of Patch) [repo]¶
Files to rewrite inside each worktree with per-worktree values
(slug-substituted DB names, cache prefixes, etc.). Supports
dotenv key=value files, phpunit.xml <env> blocks, generic
YAML, JSON, TOML, and INI. Each patched file is wired through
git's clean/smudge filter so the rewrite is hidden from
git status while still letting git pull / git checkout
overwrite the file on incoming changes (the smudge filter
re-applies the per-worktree value on the way back to the
working tree).
Re-applied on every treeman worktree finalize so a branch switch
inside an existing worktree re-evaluates each patch against
the new HEAD's slug.
databases (array of DatabaseConfig) [repo]¶
One entry per database the project owns. Each entry pairs an engine with a dump path, migration source, test-clone fanout, and optional namespace template.
hooks (HooksConfig) [repo]¶
Lifecycle hooks fired around worktree create/delete/checkout and on watched-file changes. A flat block of trigger-keyed action lists (create-before-engines, create-after-engines, delete-before-engines, delete-after-engines, checkout, file-change); see HooksConfig. Dispatched non-blocking via the daemon; when the daemon is unreachable they run inline (blocking) in the CLI.
debounce_ms (integer) [both]¶
DebounceMs is the file-watcher debounce window in milliseconds. Coalesces editor save bursts into one re-prep dispatch. Default 500.
frameworks (map of name → CustomFramework) [both]¶
User-defined migration frameworks keyed by name. Use this when the built-in framework presets don't cover your tool — declare the markers, migration dirs, file pattern, lockfiles, and engine hint explicitly.
logs (LogsConfig) [global]¶
Logs retention. Daemon-side prune drops rows older than
keep_days from the events, hook_runs, and hook_log_chunks
tables on a fixed interval. Set 0 to keep forever (no prune).
auto_fetch (AutoFetchConfig) [both]¶
AutoFetch policy. Daemon-side periodic git fetch --all --prune
per registered repo, followed by a fast-forward (merge
--ff-only @{u}) or rebase per active worktree, per
auto_fetch.mode. Skips dirty trees, non-ff branches, and
upstreamless branches. Enabled by default at a 15-minute cadence.
main_worktree (MainWorktreeConfig) [repo]¶
MainWorktree opts the repo's main checkout (repo root) into the
same watcher-driven prepare/migrate/teardown lifecycle that
linked .worktrees/<slug> checkouts already get. Off by default
— flipping it on for an existing repo will start creating
per-branch databases when the user switches branches at the
repo root.
ports (map of name → PortSpec) [both]¶
Ports declares per-worktree port slots. Each entry is a named
slot with a port range; treeman allocates a free port per slot
at worktree create time and exposes it via the {port_<name>}
template token (usable in patches[].set[*] values).
Persisted in SQLite so the assignment survives across daemon
restarts; freed on worktree delete.
Use slot names that match the role they fill in your app (e.g.
octane, webpack, reverb) — the name shows up in every
{port_<name>} reference and in wt show output.
status (StatusConfig) [global]¶
Status configures the treeman status widget output (icons,
labels, hover lines, custom bar formats). Lives in the global
config since the widget aggregates worktrees across every repo.
notifications (NotificationsConfig) [global]¶
Notifications opts into desktop notifications (notify-send on Linux, the native banner via osascript on macOS) when a worktree changes lifecycle state. Off by default. Lives in the global config since the daemon that emits them is a single cross-repo process.
Types¶
Action¶
One action: a run (string or list of strings) plus optional cwd + container wrapping.
run (one of: string, array of string) — required¶
Shell work for this action. String = single step; list = sequenced steps chained with &&. Required.
continue_on_error (boolean)¶
Allow a non-zero command exit without aborting later lifecycle phases. Still waits for completion and records the failure. Rendering and launch errors remain fatal.
cwd (string)¶
Working directory for every step in this action. Relative paths resolve against the worktree root (host) or container WORKDIR (when wrapped).
container (string)¶
Container name or ID. When set, every step is wrapped in <engine> exec. Mutually exclusive with compose_service.
compose_service (string)¶
Docker Compose service name. Resolves the container via compose labels and wraps every step in <engine> exec. Mutually exclusive with container.
compose_project (string)¶
Docker Compose project name (-p flag). Defaults to $COMPOSE_PROJECT_NAME or parent dir name. Only meaningful with compose_service.
container_engine (string)¶
Container engine binary used for the exec wrap: docker (default), podman, nerdctl, finch.
AutoFetchConfig¶
AutoFetchConfig — auto_fetch: block. Periodic daemon-side
git fetch --all --prune per registered repo, followed by a best-
effort git merge --ff-only @{u} per active worktree. Pull is
safe: it skips when the working tree is dirty, when the branch
has no upstream, or when the merge would not be a fast-forward.
Each failure logs a warning and the loop continues.
Use enabled: false (per-repo .treeman.yaml override) to opt a
repo out, e.g. when a separate scheduler already pulls or when
network policy forbids unsolicited fetches.
enabled (boolean)¶
Enabled toggles the loop. Default true. A pointer would let the schema distinguish "unset" from "explicit false", but defaults are applied centrally in applyDefaults and there is no inheritance subtlety — a missing key means "use default".
interval_minutes (integer)¶
Cadence in minutes. Default 15. Minimum 1 (values below are clamped at use-site to avoid a runaway tight loop on a typo).
mode (string)¶
Mode controls how a worktree's HEAD is advanced after fetch.
- "ff" (default): git merge --ff-only @{u}. Refuses on
divergence; user's unpushed commits are never touched.
- "rebase": git rebase --autostash @{u}. Replays local
commits on top of upstream. Auto-aborts on conflict so the
working tree never lands in a half-rebased state.
Opt-in only — keep the safe ff path as the default.
Allowed: ff, rebase
ClonesSetting¶
Number of test-clone databases to pre-warm. Either the literal string auto (treeman detects the test framework and uses the CPU count for per-worker runners) or a non-negative integer (0 disables pre-warming).
ConnectionsConfig¶
ConnectionsConfig — connections: block.
mysql (one of: MysqlConn, map of name → MysqlConn)¶
MySQL / MariaDB / TiDB connection. Set when any databases:
entry uses one of those engines. Treeman dials this server to
create clones, dump templates.
postgres (one of: PostgresConn, map of name → PostgresConn)¶
PostgreSQL connection. Required when any databases: entry
uses engine: postgres (or the postgresql alias).
mongodb (one of: MongoConn, map of name → MongoConn)¶
MongoDB connection. URI form (mongodb://...); host/port get
rewritten at dial time when a container ref is set.
redis (one of: RedisConn, map of name → RedisConn)¶
Redis connection. URL form (redis://...); ContainerRef
semantics match MongoDB.
elasticsearch (one of: EsConn, map of name → EsConn)¶
Elasticsearch / OpenSearch connection. HTTP URL form.
s3 (one of: S3Conn, map of name → S3Conn)¶
S3-compatible object storage connection (AWS S3, MinIO, RustFS,
Garage, Ceph RGW, Backblaze B2, Cloudflare R2, ...). Required when any
databases: entry uses engine: s3. One connection serves many
per-worktree buckets named via the entry's key_prefix.
sqlite (one of: SqliteConn, map of name → SqliteConn)¶
File-backed engines (sqlite / duckdb). Entirely optional — files
need no server, so the family works with no connections block at
all. base_dir relocates the per-worktree database files (and
their cached templates) outside the worktree; by default each
file lives at its rendered name_template path inside the
worktree.
CustomFramework¶
CustomFramework — frameworks: entry, lets users declare
migration frameworks treeman doesn't know about natively. Added to
the detector registry (via RegistryFor) consulted by treeman fw
detect and treeman doctor; treeman init and the MCP
fw_detect tool use only the built-in registry and ignore these
entries. At runtime treeman watches databases[].inputs[] directly.
markers (array of string) — required¶
Files (relative to repo root) whose presence indicates this
framework is in use. All markers must be present for treeman
frameworks detect / treeman doctor to recognise the framework.
Example: ["alembic.ini", "migrations/env.py"].
migration_dirs (array of string) — required¶
Glob patterns for the directories holding migration files.
Carried on the detection Spec reported by treeman frameworks detect /
treeman doctor; not emitted into inputs[] by treeman init
(init scaffolds only from built-in frameworks).
file_pattern (string) — required¶
Glob pattern for individual migration files within
migration_dirs. Example: [0-9]*_*.py (alembic) or
V*__*.sql (flyway).
lockfiles (array of string)¶
Lockfiles (e.g. requirements.txt, pyproject.toml,
composer.lock) carried on the detection Spec. To fold a
lockfile into the snapshot hash, declare it under
databases[].inputs[].
engine_hint (string)¶
Optional hint about the database engine this framework
targets — mysql, postgres, etc. Carried on the detection
Spec; not used by treeman init (which scaffolds only from
built-in frameworks).
migrate_run (string)¶
MigrateRun is the shell command treeman init writes into the
scaffolded migrations.migrate.run field (e.g. php artisan
migrate --force). Declaring it lets an unlisted framework get a
runnable migrate block instead of a hand-written one (#74).
migrate_env (object)¶
MigrateEnv is the env-var override map treeman init writes into
migrations.migrate.env. Each value may use the {target_db}
placeholder so the runtime substitutes the per-run template DB.
rollback_run (string)¶
RollbackRun is the shell command treeman init writes into the
optional databases[].rollback.run field. Treeman injects the
step count via the TREEMAN_ROLLBACK_STEPS env var, so the command
references it directly.
rollback_env (object)¶
RollbackEnv is the env-var override map for the rollback command.
DaemonConfig¶
DaemonConfig — daemon: block. Only log_level is user-tunable;
the socket path and event-log location are derived from
$XDG_RUNTIME_DIR / $XDG_STATE_HOME respectively and are not
configurable through YAML (one source of truth — the runtime
dirs the OS already manages).
log_level (string)¶
Log level for daemon stderr: debug, info (default),
warn, error. Read once at startup; reload by restarting
the daemon. Hook output is always captured regardless.
DatabaseConfig¶
DatabaseConfig — one databases: entry. The engine discriminator
gates which sub-fields are valid.
Fanout is the optional override for the outer concurrency cap
during clone restore + DropMatching. Leave unset (omitempty / 0)
to use the safe per-engine default from internal/prepare. Raise
only if the server is provisioned for it (max_connections raised,
PG pg_database lock contention acceptable, etc.).
engine (string) — required¶
Engine discriminator. Gates which connection block is dialed
and which sub-fields (dump, migrations, namespaces) are valid.
postgresql is an alias for postgres; opensearch is an
alias for elasticsearch; valkey and dragonfly are aliases
for redis (same wire protocol, same key-prefix scoping).
The rendered schema's enum is injected from engine.Known by
schema.Reflect, so the alias list lives in exactly one place.
Allowed: mysql, mariadb, tidb, postgres, postgresql, mongodb, redis, valkey, dragonfly, elasticsearch, opensearch, s3, sqlite, duckdb
connection (string)¶
Connection selects a named block from connections.<family>
(see ConnectionsConfig's named-blocks docs). Omitted means the
singular block — valid when the family declares one. Required
when the family declares ONLY named blocks, and always an error
when it names a block that doesn't exist. Two entries on one
family with different connection: values prepare on different
servers (monorepo services, app+reporting splits).
name_template (string)¶
Template for the per-worktree database/index name. Supports
{slug}, {slug_dash}, {slug_redis_queue}, {slug_redis_cache}
(see the template package for definitions). Example:
app_{slug} → app_feature-x. Required for engines that
scope by database name (MySQL, Postgres, Mongo). Validated at
config-load time — typos fail loud.
dump (DumpList)¶
Source dump(s) used to seed the source DB before migrate/seed run. Each path is relative to the repo root. Treeman hashes every dump into the snapshot key, so changes invalidate the cache. Three shapes:
dump: seed.sql # bare string dump: { path: seed.sql, optional: true } # mapping dump: # sequence - base.sql - { path: extras.sql, optional: true }
Sequence entries load in declared ORDER, so a base schema dump followed by per-feature patches is supported without splicing them into one file. Order matters for the fingerprint too — reordering reproducible dumps is a content change.
migrate (Step)¶
Migrate is the shell command that brings a freshly-loaded source DB up to the current schema. Required when any input glob matches migration files; optional otherwise (e.g. a DB that's purely seed-driven).
rollback (Step)¶
Rollback is the OPTIONAL shell command that unwinds the most recently applied migrations, used to keep templates correct when an already-applied migration's content is edited. Treeman runs it against a clone of the prior template, then re-runs Migrate forward — the only path that re-applies an edit to a migration whose ledger row is baked into the source dump.
Treeman injects the number of migrations to unwind as the env var
TREEMAN_ROLLBACK_STEPS; reference it in Run, e.g.
rollback: { run: "php artisan migrate:rollback --step=$TREEMAN_ROLLBACK_STEPS" }
(Run is passed verbatim to sh -c; only Env values are
{placeholder}-rendered, so use the shell env var, not a brace
placeholder.)
WARNING: rollback runs the migration's CURRENT down() — the edited file on disk — not the original down() that matched the applied schema. For edits that change down(), or for lossy/irreversible down()s, this can produce a wrong or failing schema. Treeman hard-falls-back to a full cold rebuild on ANY rollback or migrate error, so a broken down() degrades to "slow but correct", never "fast but wrong"-at-the-engine — but a down() that succeeds while not faithfully inverting up() is the user's responsibility. A prior template built from a source ahead of the branch (migrations the branch does not contain) or holding a migration file the branch removed is never chosen — the branch has no down() for it and step-based CLIs skip it silently, baking the schema change into the template — so those divergences always cold-rebuild. Leave this unset to disable the rollback path entirely (cold rebuild only).
seed (Step)¶
Seed is the shell command that populates non-migration data (fixtures, ES mappings, Redis warm-cache keys, etc.). Runs AFTER dump-load + migrate as the final cold-build step, before treeman snapshots the populated state into the template DB.
inputs (array of Input)¶
Inputs declare every file that determines this database's
template state. Each entry:
1. Contributes a hash to the snapshot fingerprint (so any
change auto-invalidates the cached template).
2. Subscribes fsnotify so changes trigger a re-prep.
3. Carries an optional label: that hooks.file-change
actions can match against.
Glob patterns are repo-root-relative. Every matched file is content-hashed (BLAKE3); a content, add, or remove moves the fingerprint.
Cache-hit vs cold-build is derived purely from the input
hashes — there's no separate on: rebuild knob. If you want
to force a rebuild, change an input.
test_clones (TestClonesSpec)¶
Test-clone fanout: how many parallel database clones to pre-warm for paratest/pytest-xdist/Jest workers/etc.
key_prefix (string)¶
KeyPrefix scopes every key/index a worktree creates under a
per-worktree prefix. Used by engines that don't scope by
database name — Redis (key prefix in DB 0) and Elasticsearch
/ OpenSearch (index-name prefix). Example: {slug}: →
feature-x:. The app must honour the prefix — Laravel's
CACHE_PREFIX, Rails Rails.cache.options[:namespace],
ioredis keyPrefix, etc.
Supports the same placeholders as name_template: {slug},
{slug_dash}, {slug_redis_queue}, {slug_redis_cache}.
Validated at config-load time.
fanout (integer)¶
Outer concurrency cap for clone restore + DropMatching. Defaults to the per-engine safe value from internal/prepare. Range 0–64. Raise only if the server is provisioned (max_connections, PG pg_database lock contention, etc.).
min: 0 · max: 64
prewarm (integer)¶
Prewarm keeps N spare clones pre-restored from this database's
cached template (Postgres only — other engines have no
constant-time whole-database rename, so the knob is rejected at
config load). A cache-hit prepare claims a spare via
ALTER DATABASE … RENAME (milliseconds, size-independent)
instead of paying CREATE DATABASE … TEMPLATE per restore; a
detached replenisher then tops the pool back up. Spares are
named <template>_spare<n>, survive worktree teardown (they
belong to the template cache, not a worktree), and are dropped
with their template on snapshot eviction. Range 0–16; default
0 (off). Mutually exclusive with branch_scoped.
min: 0 · max: 16
physical_clone_min_bytes (integer)¶
PhysicalCloneMinBytes is the MySQL-only source-size floor (in bytes) at or above which snapshots switch from the logical INSERT…SELECT loader to InnoDB transferable tablespaces. Below the floor logical is faster (physical pays a fixed per-table FLUSH/copy/IMPORT cost that dominates for many-small-tables schemas); at or above it physical wins because tablespaces copy at disk speed instead of row speed. Raise it when physical clone's per-table overhead swamps a wide-and-tiny schema even at size; lower it to take the disk-speed win earlier on a large dataset. Default 1073741824 (1 GiB). The TREEMAN_MYSQL_PHYSICAL_MIN_BYTES env var overrides this key (config < env) — e2e tests set it to 0 to exercise the physical path on small fixtures. Rejected at config load for non-MySQL engines so the knob never reads as dead config.
min: 0
branch_scoped (boolean)¶
BranchScoped turns this database into a git-for-databases working copy: the app always talks to one stable ACTIVE namespace, while treeman keeps a DURABLE per-branch copy of its contents and swaps them in/out as the branch changes. One flag, the whole lifecycle — no per-feature sub-fields, no user-configured durable names (treeman derives them internally).
The active namespace is fixed per checkout, so the app's
connection string (.env) is patched once and never churns:
- main worktree → the main_worktree.databases[].name_template
overlay (typically a bare, unprefixed name the repo-root app
already points at, e.g. kontainer).
- linked worktree → name_template (or key_prefix for
prefix-scoped engines) rendered against the worktree's
branch-independent slug, so switching branches inside the
worktree doesn't rename its DB.
Lifecycle, driven by HEAD changes + create/delete:
- create / first switch onto a branch → seed the active
namespace from the branch's own durable copy (resume) or,
failing that, from its parent branch's data (tracked
upstream, via the main overlay or a sibling worktree), or
dump.path, or empty.
- switch off a branch → capture the active namespace into that
branch's durable copy first (manual data changes live on).
- switch back → restore that durable copy. treeman db reset
drops the durable copy + re-seeds from the live parent.
Engine-agnostic: name-scoped engines (MySQL, Postgres, MongoDB)
swap whole databases; prefix-scoped engines (Redis,
Elasticsearch/OpenSearch) swap the key/index namespace under the
rendered key_prefix. All five participate.
Mutually exclusive with test_clones / fanout: a branch_scoped
database is a stateful per-branch snapshot the app mutates in
place, not a reproducible source for throwaway parallel-test
clones. Config-load rejects the combination.
Postgres caveats (both stem from CREATE DATABASE … TEMPLATE,
which needs exclusive access to its source):
- Capturing a branch on a switch briefly force-disconnects every
session on the active database (the swap fences it and
terminates backends), so the app momentarily loses its DB
connections and must reconnect. Expected; not data loss.
- Seeding a branch from its parent branch's LIVE database fails
while other sessions are connected to the parent (treeman does
NOT force-terminate another worktree's connections). Close
those connections, or set dump.path to seed from the dump.
MySQL/MongoDB copy logically and have neither constraint.
DatabaseOverlay¶
DatabaseOverlay holds the subset of DatabaseConfig fields that
can be tweaked per-context (main wt vs linked wt). Engine + Dump
are intentionally absent — changing engines per-context produces
schema chaos, and main / linked worktrees should always share the
same seed dump for snapshot-cache coherence.
Field semantics:
- Strings: empty value means "inherit". Setting to a non-empty value replaces the base template.
- TestClones: nil pointer means "inherit". A non-nil value replaces the entire spec (Clones + NameTemplate).
- Fanout: nil pointer means "inherit". A non-nil value (including 0) replaces the base — necessary because uint32(0) is a valid "use per-engine default" sentinel that's distinct from "no override".
name_template (string)¶
key_prefix (string)¶
test_clones (TestClonesSpec)¶
fanout (integer)¶
DumpList¶
Source dump(s). Accepts a bare path string, a single mapping, or an ordered array of either.
DurablesConfig¶
DurablesConfig — durables: block. A branch_scoped durable is
dropped as soon as its local branch is gone; this bounds the ones
whose branch lingers. Dropping one discards the branch's saved
database state: checking the branch out again re-seeds it from the
parent branch, the base branch's durable or the dump, then migrates.
max_age_days (integer)¶
MaxAgeDays drops a durable not captured or used to seed
a child branch within N days, on the daemon's GC sweep
(snapshots.gc_interval_minutes). The durable a live worktree's
active namespace currently mirrors is never dropped. Negative
keeps durables forever. Default 30.
EsConn¶
elasticsearch connection — bare URL string OR structured object.
FilteredAction¶
On-file-change action with optional label filter. Same shape as a hook Action plus a match: field (string or list).
run (one of: string, array of string) — required¶
Shell work for this action. String = single step; list = sequenced steps chained with &&. Required.
continue_on_error (boolean)¶
Allow a non-zero command exit without aborting later lifecycle phases. Still waits for completion and records the failure. Rendering and launch errors remain fatal.
cwd (string)¶
Working directory for every step in this action. Relative paths resolve against the worktree root (host) or container WORKDIR (when wrapped).
container (string)¶
Container name or ID. When set, every step is wrapped in <engine> exec. Mutually exclusive with compose_service.
compose_service (string)¶
Docker Compose service name. Resolves the container via compose labels and wraps every step in <engine> exec. Mutually exclusive with container.
compose_project (string)¶
Docker Compose project name (-p flag). Defaults to $COMPOSE_PROJECT_NAME or parent dir name. Only meaningful with compose_service.
container_engine (string)¶
Container engine binary used for the exec wrap: docker (default), podman, nerdctl, finch.
match (one of: string, array of string)¶
Restrict this action to watch events carrying one of the named labels. Omit to fire for every event.
HooksConfig¶
HooksConfig — hooks: block. A flat set of trigger-keyed action
lists. Each key's value is a list of Actions that fire when that
trigger happens. Actions in the same list run in parallel; the
trigger key itself encodes BOTH the lifecycle phase AND the timing
point, so there's no separate when: field anywhere.
Triggers (all optional — omit any you don't need):
- create-before-engines — during
worktree create, after patches + bring-in (copies/links), BEFORE engine prepare. Standard home of dependency installs (composer/yarn/pip) so migrate can find vendor/. The engine databases do NOT exist yet in this phase, so a hook that BOOTS THE APP and queries the DB (e.g. a Laravelcomposer installwhose post-autoload-dump runs a framework command) fails with "Unknown database". Keep DB-touching work in create-after-engines. - create-after-engines — during
worktree create, after engine prepare. Use when actions need a populated database (cache warming, seed verification). - delete-before-engines — during
worktree delete, BEFORE DB drop. Graceful shutdown: drain queues, docker compose stop. - delete-after-engines — during
worktree delete, AFTER DB drop + git worktree remove. External notifications (Slack, CDN purge) that should announce only once the data is gone. - checkout — fires when the HEAD watcher sees a branch switch inside an existing worktree. Re-runs in addition to the regular finalize-on-HEAD-change behaviour.
- file-change — fires when any
databases[].inputs[]glob matches a filesystem event. Each action can optionallymatch: <label>to filter by the input entry's label.
Daemon execution is always non-blocking from the CLI's perspective — each list of actions dispatches in parallel.
create-before-engines (array of Action)¶
OnCreateBeforeEngines — actions fire after worktree create + patches + bring-in, before engine prepare. The engine databases do not exist yet: a hook that boots the app and touches the DB here fails with "Unknown database" — put DB-touching work in create-after-engines.
create-after-engines (array of Action)¶
OnCreateAfterEngines — actions fire after engine prepare completes.
delete-before-engines (array of Action)¶
OnDeleteBeforeEngines — actions fire before DB drop on delete.
delete-after-engines (array of Action)¶
OnDeleteAfterEngines — actions fire after DB drop + worktree remove on delete.
checkout (array of Action)¶
OnCheckout — actions fire when the HEAD watcher detects a branch switch inside an existing worktree.
file-change (array of FilteredAction)¶
OnFileChange — actions fire when any databases[].inputs[]
glob matches a filesystem event. Each action can optionally
match: <label> to fire only when the matched input entry
carries that label; actions without match: fire for every
input event (any engine, any label).
The subprocess receives extra env vars naming the trigger: TREEMAN_WATCH_PATH — absolute path that fired TREEMAN_WATCH_LABEL — the label on the matched watch entry (or "") TREEMAN_WATCH_ENGINE — engine of the owning database (mysql, postgres, …) TREEMAN_WATCH_DB_NAME — rendered name_template of the owning database
Input¶
One source of file state for the template fingerprint. Bare string OR full mapping.
LogsConfig¶
LogsConfig — logs: block. One knob: how long to keep events +
hook_runs + their attached log chunks. The daemon's prune loop
applies the cutoff per table; chunks cascade via FK so dropping
a hook_runs row also drops its captured stdout/stderr.
keep_days (integer)¶
KeepDays sets the retention window in days. Rows older than
now - keep_days * 24h are removed on each daemon prune tick.
0 disables pruning (keep forever). Default 14.
MainWorktreeConfig¶
MainWorktreeConfig — main_worktree: block. Opt-in handle that
promotes the repo root into a first-class worktree so the daemon's
HEAD watcher, file watcher, and prepare orchestration treat it the
same as a .worktrees/<slug> linked checkout. Off by default; flip
enabled: true to start producing per-branch databases at the
repo root.
One knob today (Enabled). The branch-switch policy is implicit:
FinalizeWorktree re-runs against the new branch's slug, which uses
the snapshot cache so a re-visit of any previously-seen branch is
a near-instant clone. Drop/keep policies will land as additional
fields once their teardown plumbing exists — premature wiring of
on_branch_switch would have shipped a config surface no code
consumed.
enabled (boolean)¶
Enabled toggles main-wt enrollment for this repo. Default false — every existing install sees zero behaviour change until the flag is set. Once true, the daemon ensures a worktrees row with is_main=1 exists for the repo, spawns the per-wt HEAD + file watchers against the repo root, and re-runs prepare on every branch switch.
databases (array of DatabaseOverlay)¶
Databases is a sparse, index-aligned overlay over databases:.
Each entry's set fields replace the same field on the main-
indexed database when finalize runs against the main worktree;
unset fields inherit the top-level value. Linked worktrees are
untouched.
Common uses: a different name_template so the main wt's app
DB lives at app_dev_{slug} while linked wts use app_{slug};
disabling test-clone fanout in main (test_clones.clones: 0)
because the main checkout doesn't run parallel test workers.
Overlay length must be <= len(databases). Entries beyond the declared count are a config error caught at load time.
MongoConn¶
mongodb connection — bare URL string OR structured object.
MysqlConn¶
MySQL connection — bare DSN string OR structured object.
NotificationsConfig¶
NotificationsConfig — notifications: block. Opt-in desktop
notifications fired by the daemon when a worktree crosses a lifecycle
status boundary. Backend is auto-detected per OS (notify-send on
Linux, osascript -e 'display notification' on macOS); platforms
without a known sender silently no-op.
Each notification is keyed to one of the four treeman status
buckets:
- stable: a worktree finished preparing and is ready (finalize done)
- up: a worktree began preparing (finalize started)
- down: a worktree began tearing down
- failed: a worktree's finalize errored
up and down are transient and chatty, so the default
(events: unset) only notifies on stable + failed — ready and
errored, the two resting states worth surfacing. Set events:
explicitly to opt into the transient ones, or to a subset.
enabled (boolean)¶
Enabled toggles the whole feature. Off by default — every existing install sees zero behaviour change until it's set.
events (array of string)¶
Events is the set of status buckets that fire a notification.
Allowed values: stable, up, down, failed. When unset (nil) the
default of [stable, failed] applies (see applyDefaults). An
explicit empty list (events: []) disables every bucket while
leaving the feature otherwise "enabled" — useful as a base for a
per-repo override that re-adds buckets.
backend (string)¶
Backend forces a specific sender instead of OS auto-detection.
Allowed values: auto (default), notify-send, osascript, none.
none disables sending without unsetting enabled — handy to
mute notifications on one host while keeping the shared config.
Allowed: auto, notify-send, osascript, none
Patch¶
Patch — one entry in the top-level patches: block. Each entry
targets one file under the worktree root and rewrites it with
per-worktree values via the set: map. Values are template
strings that accept {slug}, {slug_dash},
{slug_redis_queue}, {slug_redis_cache}. Validated at
config-load time — unknown keys fail loud.
The driver is picked from format: when set, otherwise auto-
detected from the file extension:
dotenv — `.env`, `.env.*`
phpunit — `.xml`, `.xml.dist` (phpunit.xml `<env>` blocks)
yaml — `.yaml`, `.yml`
json — `.json`
toml — `.toml`
ini — `.ini`, `.cfg`
Path syntax inside set: is uniform across drivers:
dotenv / phpunit — flat key (e.g. `DB_DATABASE`)
ini — `section.key` (top-level keys allowed too)
yaml / json / toml — dotted path, optionally with `[N]` indices
(e.g. `services[0].host`)
Each patched file is wired through git's clean/smudge filter so
the rewrite is hidden from git status while still letting
git pull / git checkout overwrite the file on incoming
changes — the daemon's HEAD watcher (or the smudge filter itself)
re-applies the patch after. The file must be tracked by git for
the filter to engage; gitignored files are patched in-place
without any git interaction.
file (string) — required¶
File path relative to the worktree root. Required.
format (string)¶
Driver name. Optional — leave unset to auto-detect from the
file extension. Explicit when the extension is ambiguous
(e.g. phpunit for a .xml that isn't standard XML).
Allowed: dotenv, phpunit, yaml, json, toml, ini
set (object)¶
Key → value-template map. Path syntax depends on the driver (see the type doc-comment).
PortSpec¶
Per-worktree port slot: an inclusive [min, max] range. Shorthand [min, max] is accepted; the long form is {range: [min, max]}.
PostgresConn¶
Postgres connection — bare DSN string OR structured object.
RedisConn¶
redis connection — bare URL string OR structured object.
S3Conn¶
endpoint (string)¶
region (string)¶
access_key (string)¶
secret_key (string)¶
use_path_style (boolean)¶
container (string)¶
compose_service (string)¶
compose_project (string)¶
container_engine (string)¶
container_network (string)¶
SnapshotsConfig¶
SnapshotsConfig — snapshots: block. Carries the retention /
eviction policies for cached engine templates. Snapshot state
lives entirely in SQLite + the engines themselves (template DBs);
no on-disk cache directory needs configuring.
CapPerRepo is the hard cap that triggers eviction on every
RecordSnapshot. LRU rows above the cap are dropped immediately
(in a background goroutine) so a busy worktree workflow never
accumulates unbounded cached templates per repo.
KeepPerSource, MaxAgeDays, MaxTotalGb, GcIntervalMinutes
drive the periodic daemon-side sweep; they're not consulted by
the inline-on-write eviction path.
cap_per_repo (integer)¶
Hard cap on cached snapshots per repository. Eviction runs
inline on every RecordSnapshot: rows above the cap (LRU
order) are dropped immediately in a background goroutine.
Default 8. Set to 0 to disable the inline cap (rely on the
periodic sweep only).
keep_per_source (integer)¶
Periodic-sweep policy: keep at most N snapshots per source
(a stable key derived from migration content). Default 500.
max_age_days (integer)¶
Periodic-sweep policy: drop snapshots older than N days. Default 30.
max_total_gb (integer)¶
Periodic-sweep policy: drop oldest snapshots once the cache dir exceeds N gigabytes on disk. Default 50.
gc_interval_minutes (integer)¶
Cadence (minutes) of the daemon's periodic snapshot-sweep goroutine. Default 60.
SqliteConn¶
SqliteConn is the (optional) connection block for the file-backed engine family. There is nothing to dial; the only knob is where the per-worktree database files live.
base_dir (string)¶
BaseDir is the directory per-worktree database files are placed
in (the rendered name_template file name is joined onto it).
Empty means "inside the worktree", the default. Templates are
cached as siblings of the rendered files, so keep the directory
on the same filesystem as the worktrees to get reflink
(copy-on-write) clones.
StatusBuckets¶
StatusBuckets carries one string per worktree bucket. Reused for
both the icons and labels maps so the four bucket names stay in
lockstep across the schema.
stable (string)¶
up (string)¶
down (string)¶
failed (string)¶
StatusConfig¶
StatusConfig configures treeman status — the bar/waybar widget
that aggregates worktree health across every registered repo. Each
active worktree falls into one of four buckets:
stable — ready (last finalize succeeded, or never ran)
up — being prepared (finalize in progress)
down — being torn down (teardown in progress)
failed — last finalize errored
All knobs below feed the {key} template syntax used elsewhere in
.treeman.yaml (no separate templating engine). The built-in
--format values are icon, hover, waybar, and json; entries
in formats add or override named single-line formats.
icons (StatusBuckets)¶
Icons holds the glyph for each bucket. Exposed to format
templates as {icon_stable} / {icon_up} / {icon_down} /
{icon_failed}, plus {icon} for the worst non-empty bucket.
Defaults to Nerd Font glyphs; set an icon to a single space to
suppress it (an empty string falls back to the default).
labels (StatusBuckets)¶
Labels holds the text label for each bucket. Exposed as
{label_stable} etc. Defaults to the bucket name.
separator (string)¶
Separator joins the segments of the built-in icon line and is
exposed to templates as {sep}. Default " | ".
header (string)¶
Header is the {key} template for each repo heading in the
built-in hover format. Tokens: {repo}, {total},
{stable}, {up}, {down}, {failed} (repo-scoped counts).
Default "{repo} ({total})".
row (string)¶
Row is the {key} template for each worktree line in the
built-in hover format. Tokens: {branch}, {slug},
{state}, {bucket}, {main}, {state_suffix}, {path},
{icon}. Default " {main}{branch}{state_suffix}".
main_marker (string)¶
MainMarker is substituted for {main} on a repo's main-worktree
row (empty string on linked worktrees). Default "★ ".
formats (object)¶
Formats declares named single-line {key} templates selectable
with treeman status --format <name>. A name matching the
built-in icon line overrides it; the structured built-ins
hover/waybar/json are reserved and cannot be overridden.
Available tokens match
the icon line: {total}, {stable}, {up}, {down},
{failed}, {icon_*}, {icon}, {label_*}, {class},
{sep}. A flat template cannot express the multi-line hover
body — customize that with header / row instead.
Step¶
Step is one user-declared shell command executed against a target
database. Used by both databases[].migrate: and
databases[].seed: — the same shape, different lifecycle slots.
The framework's CLI reads its target DB from its own config — for
most stacks that's a connection string the user already keeps in
.env (or wires up in config/database.yml, data-source.ts,
etc.). Treeman builds per-worktree template databases with names
like myapp_template_feature-x and needs to redirect the command
at that DB, not the one the committed .env references. The
Env map says which env-var names to override; values are
rendered through the same template pass that produces the
per-run DB name. The scaffold convention is to set DB_NAME
(Laravel's preset uses the framework-native DB_DATABASE); the
user weaves ${DB_NAME} into the relevant slot of their Run
command (typically inside a DSN) or wires their config to read
it. Supported substitutions:
{target_db} — resolved per-run database name / key prefix
{slug} — the slug value
{slug_dash} — slug with underscores → hyphens
{slug_redis_queue} — slug-derived Redis queue DB index (6..15)
{slug_redis_cache} — slug-derived Redis cache DB index (6..15)
{db:<index>} — rendered name of `databases[<index>]` in the
current context (main-worktree overlay applied),
for a step that also migrates a sibling engine
Unknown keys fail loud at config-load time. Treeman also exports
TREEMAN_TARGET_DB to the subprocess unconditionally as a safety
net for tooling that wants the resolved name without a custom env
mapping.
run (string) — required¶
Run is the shell command treeman invokes via sh -c.
Required; an empty Run aborts prepare with a clear error.
env (object)¶
Env is a map of env-var names to value templates. Each entry
is set on the subprocess (overriding the framework's config
file). See the type doc-comment for the full list of supported
{placeholder} keys; literal values pass through unchanged.
TestClonesSpec¶
TestClonesSpec — test_clones: sub-block. Used by every parallel
test runner (paratest, pest, pytest-xdist, Jest workers, Go
-parallel, cargo nextest, …). clones is either auto (treeman
detects the test framework and uses the CPU count for per-worker
runners) or an explicit integer.
clones (ClonesSetting)¶
Number of test-clone databases to pre-warm. auto detects the
project's test framework and pre-warms one clone per CPU
(runtime.NumCPU) when that framework clones per-worker,
otherwise 1 (falling back to the CPU count when no framework is
detected). Explicit integer overrides; 0 disables pre-warming
entirely.
name_template (string) — required¶
Template for clone database names. Supports the same
placeholders as databases[].name_template plus {n} —
the 1-based clone index (only valid here). Required.
Example: app_{slug}_test_{n}.
WorktreesConfig¶
WorktreesConfig — worktrees: block.
root (string)¶
Path (relative to the main worktree) where new worktrees are
created. Defaults to .worktrees. Override with e.g.
../foo-worktrees for the sibling-dir convention.
links (array of string)¶
Paths (relative to the main worktree) to symlink into each
new worktree on create. Use for committed-in-main-only caches
that the worktree should read but never mutate per-branch —
e.g. node_modules, vendor. The symlink points at the main
worktree's copy so all worktrees share one on-disk cache.
Glob meta-characters expand against the main worktree root.
copies (array of string)¶
Paths (relative to the main worktree) to copy into each new
worktree on create. Use for gitignored files that the worktree
needs in its own copy so it can be patched per-branch without
affecting the main worktree's copy — e.g. .env, .env.local.
Glob meta-characters expand against the main worktree root.
Directories are recursed; existing destinations are left alone
(idempotent re-runs).