Configuration reference
The configuration editor and your text editor write the same files: a cascade of TOML that byre resolves at every develop. This page is the complete contract.
The cascade
~/.byre/default.config your personal baseline
~/.byre/templates/<name>/ template config (+ optional files)
~/.byre/layers/<name>/layer.config named layers, pulled in via `extends`
~/.byre/projects/<id>/byre.config this project's overrides (host-side)
Layers merge in that order – defaults, template, the extends chain
(root first), project – and the last layer to speak wins:
- Scalars override. A later layer’s
baseorenginereplaces an earlier one’s. (One deliberate exception:seed_prefsis a monotonic opt-in – once any layer turns it on, a later layer can’t turn it off.) - Lists union.
skills,apt,mountsand friends accumulate across layers. - A later layer can remove an inherited entry:
"!name"for named lists (skills, apt, npm_global, volumes; mounts by target), andremove = truefor ports (keyed by container port).!hostentries inegressare closures: they subtract from the final derived allowlist, skill-declared endpoints included. envhas no unset – override the value instead.- Raw blocks (
dockerfile_pre,dockerfile_post,run_args) are append-only unions: no per-line removal.
byre reads config only from its host-side store (~/.byre), never from
inside the project – the project mount is read-write, so the agent could
edit a config that lived there. The one repo-side artifact is a
preset (below), and it is inert until you apply it.
Key reference
The complete vocabulary. Everything here is legal in any layer, with the exceptions noted inline.
Composition
engine–"auto"(default: docker if present, else podman),"docker", or"podman".template– which~/.byre/templates/<name>to layer in. Project config only – banned in layers and template configs.agent–"claude","codex","gemini","grok", or"opencode": whose command launches in the foreground. Implicitly enables that agent’s skill.base– theFROMimage (Debian-derived).extends– name this file’s one parent layer. Chains are linear and walked to the root; cycles fail loudly. Legal in a project config or a layer; banned in templates and the default config.skills– enabled skills, by name (bundled bare:"firewall"; installed qualified:"owner/name").sources–id -> { uri, digest }acquisition hints for packages a preset references. Never fetched silently:preset applyuses them to chauffeur consented installs; everywhere else they’re only printed as install commands.
Grants (each live entry shows in byre status)
[[mounts]]–host,target,mode("ro"default,"rw"),disabled(kept and shown, but not bound). Extra host folders beyond the project.[[ports]]–container(required),host(defaults to mirror),interface(defaults to127.0.0.1– localhost-only unless you loudly say otherwise). Publishes a box port to the host.[env_from_host]– the one host-to-box data channel:KEY = "env:HOST_VAR"(a host env var, at runtime),KEY = "git:config.key"(fromgit config),KEY = "tz:"(your timezone),KEY = ""(disable an inherited entry). Values resolve at launch and are never baked into the image. Git identity,TERM, andTZpass through by default.egress– firewall allowlist extensions,"host[:port]"(port defaults to 443);"!host[:port]"closes a door, even a skill-declared one. Only meaningful with a network-posture skill enabled.egress_offered– declared-but-closed convenience doors; always inert until the config UI opens one intoegress.
Build (baked into the image)
apt– packages to install.npm_global– extra global npm tools.[env]– literal env vars. Baked into the image:docker historyshows them and they outlivebyre reset, so never put secrets here – credentials belong to the agents’ own login flows (orenv_from_hostfor runtime values).[files]– host paths copied into the image, read-only.dockerfile_pre/dockerfile_post– raw Dockerfile lines, emitted before / after the core block. The build-time escape hatch, and the honest place for project setup that should happen once per build rather than at every launch.
Runtime
[[volumes]]– named volumes:name,role("cache"or"state"),target, optionalscope = "machine"(per-user machine-wide; default is per-project), optionalseedfor state volumes (hostpath orliteral+path; never on machine scope – a seed populates a fresh state volume exactly once, a copy, never a live share). On the engine the names are legible:byre-<project-id>-<name>for project scope,byre-machine-u<uid>-<name>for machine scope – per user deliberately, so two users on a shared box never silently share state. See Volumes & state for the user-side model.run_args– rawdocker runflags, appended after byre’s own, so yours win. Cap resources (--cpus,--memory), change networking – anything the engine accepts. byre never parses inside it; posture claims inbyre statusdegrade honestly when it’s present. One documented footgun: identity-changing flags (--user,--userns) break the baked-UID ownership model and are unsupported.
Agent session wiring (declarations, not grants – see them with
byre mcp list / byre claude-skill list)
[[mcp]]– an MCP server:name, then eithercommand(argv, local stdio) orurl(remote);envnames the variables it may consume (names, never values),headerstemplates (${NAME}) expand at launch,egressadds hosts beyond the URL when the firewall is on.[[claude_skills]]– a Claude Skill:name+pathto a host directory whose root holdsSKILL.md.
Preferences (picker-owned; never a grant)
seed_prefs– one-time copy of the agent’s curated non-secret pref files into a fresh state volume.worktree_base– wherebyre worktreecreates worktrees:"sibling"or a host path; unset refuses with instructions.shared_auth– the first-run picker’s remembered favourite. A preference about future answers, stripped from every resolved config.
Presets: byre.preset
A preset is a complete proposed config in byre.config format,
conventionally shipped as byre.preset in a repo. Cloning gives you a
file, not a prompt: nothing takes effect until you run
byre preset apply, which chauffeurs any missing package installs (each
with its own grant summary and confirm), shows the composed box’s grants
with a diff against your current config – applying replaces the whole
file – and writes the project’s config on your confirm.
byre preset inspect is the same review without the write.
Named layers
A layer (~/.byre/layers/<name>/layer.config) is a config file any
project or other layer pulls in with extends. It carries everything a
config can except template, and it’s live: edit the layer once and
every extending project picks it up on its next develop. Layers aren’t
packages – no versions, no installing; to share one, send the file.
Every inherited setting is attributed to its layer in byre config, and
byre status shows the chain. Layer files sit outside --self-edit’s
writable set, so a boxed agent can never edit a file that propagates
into other boxes. Manage with byre layer new / list / validate.
Escape-hatch symmetry
Build side: base, apt, npm_global, [files], [env], then
dockerfile_pre/dockerfile_post for the rest. Runtime side:
[[mounts]], [[volumes]], [env_from_host], then run_args for the
rest. There is deliberately no full-Dockerfile opt-out – if you want to
own the whole file, byre dockerfile prints it and you can
leave.