Configuration Reference
This page is auto-generated from the Pydantic schema models in yaml_schema.py. Every field listed here is validated at load time — unknown keys are rejected, catching typos before they silently do nothing.
JSON Schema files (for editor autocompletion and validation):
:material-download: project.schema.json{: .md-button }
:material-download: config.schema.json{: .md-button }
project.yml
Per-project configuration. Located at <projects-root>/<id>/project.yml, where the projects root is discovered via user_projects_root() (default ~/.config/terok/projects, overridable via paths.user_projects_root in config.yml) or the system config root.
Top-level keys
| Key |
Type |
Default |
Description |
default_agent |
string or null |
— |
Default agent provider (e.g. claude, codex) |
default_login |
string or null |
— |
|
shared_dir |
boolean or string or null |
— |
Shared directory for multi-agent IPC (true = auto-create under tasks root, or absolute path) |
agent |
mapping |
{} |
Agent configuration dict (model, subagents, MCP servers, etc.) |
project:
| Key |
Type |
Default |
Description |
id |
string or null |
— |
Unique project identifier (lowercase, [a-z0-9_-]) |
name |
string or null |
— |
Human-readable project name (display only) |
security_class |
string |
"gatekeeping" |
Security mode: gatekeeping (gated mirror, default) or online (direct push) |
isolation |
string |
"shared" |
shared (bind mounts) or sealed (no mounts) |
git:
| Key |
Type |
Default |
Description |
upstream_url |
string or null |
— |
Repository URL to clone into task containers |
default_branch |
string or null |
— |
Default branch name (e.g. main) |
human_name |
string or null |
— |
Human name for git committer identity |
human_email |
string or null |
— |
Human email for git committer identity |
authorship |
string or null |
— |
How agent/human map to git author/committer. Values: agent-human, human-agent, agent, human |
ssh:
| Key |
Type |
Default |
Description |
use_personal |
boolean or null |
— |
Opt in to the user's ~/.ssh keys for host-side gate-sync. Default false — terok uses only its vault-managed key. Resolves through ConfigStack: terok-global config.yml → project.yml → CLI --use-personal-ssh (highest). |
tasks:
| Key |
Type |
Default |
Description |
root |
string or null |
— |
Override task workspace root directory |
name_categories |
list or null |
— |
Word categories for auto-generated task names (string or list of strings) |
gate:
| Key |
Type |
Default |
Description |
enabled |
boolean |
true |
Enable the host-side git gate mirror for this project |
path |
string or null |
— |
Override git gate (mirror) path |
gatekeeping:
| Key |
Type |
Default |
Description |
staging_root |
string or null |
— |
Staging directory for gatekeeping builds |
expose_external_remote |
boolean |
false |
Add upstream URL as external remote in gatekeeping containers |
gatekeeping.upstream_polling:
| Key |
Type |
Default |
Description |
enabled |
boolean |
true |
Poll upstream for new commits |
interval_minutes |
integer |
5 |
Polling interval in minutes |
gatekeeping.auto_sync:
| Key |
Type |
Default |
Description |
enabled |
boolean |
false |
Auto-sync branches from upstream to gate |
branches |
list of string |
[] |
Branch names to auto-sync |
run:
| Key |
Type |
Default |
Description |
shutdown_timeout |
integer |
10 |
Seconds to wait before SIGKILL on container stop |
gpus |
string or boolean or null |
— |
GPU passthrough: true, "all", or omit to disable |
memory |
string or null |
— |
Podman --memory value (e.g. "4g", "512m", "4gib", plain "1024" for bytes); None = unlimited. Format mirrors what podman accepts — see man podman-run(1) --memory. |
cpus |
string or null |
— |
Podman --cpus value (e.g. "2.0", "0.5"); None = unlimited. Non-negative decimal. |
nested_containers |
boolean |
false |
Declares that the project runs podman/docker inside its container. When true, the outer container is launched with --security-opt label=nested and --device /dev/fuse so rootless nested containers work under SELinux without disabling labels wholesale. |
runtime |
Literal or null |
— |
OCI runtime: crun (default) for conventional containers, or krun for KVM-microVM isolation (experimental). None resolves to crun — the OCI runtime podman picks by default on every supported distro. krun requires the global experimental: true flag at task launch. |
timezone |
string or null |
— |
IANA timezone for the task container (e.g. Europe/Prague, UTC). Propagated as TZ — resolved against the image's tzdata. Unset (default) means follow the host's timezone. |
run.hooks:
| Key |
Type |
Default |
Description |
pre_start |
string or null |
— |
|
post_start |
string or null |
— |
|
post_ready |
string or null |
— |
|
post_stop |
string or null |
— |
|
shield:
| Key |
Type |
Default |
Description |
drop_on_task_run |
boolean or null |
— |
Drop shield (bypass firewall) when task container is created |
on_task_restart |
Literal or null |
— |
Shield policy on container restart: retain or up |
image:
| Key |
Type |
Default |
Description |
base_image |
string |
"fedora:44" |
Base container image for builds |
family |
Literal or null |
— |
Package family for the L0/L1 build (deb or rpm). Leave unset to auto-detect from base_image; set explicitly when the image is outside the known allowlist. |
agents |
string or null |
— |
Comma-separated roster entries to install in L1, or "all". Prefix a name with "-" to exclude it from the selection (e.g. "all,-vibe" or just "-vibe" — both mean "everything except vibe"). Inherits from the global config when unset. |
user_snippet_inline |
string or null |
— |
Inline Dockerfile snippet injected into the project image |
user_snippet_file |
string or null |
— |
Path to a file containing a Dockerfile snippet |
Full example
project.ymlproject:
# Unique project identifier (lowercase, [a-z0-9_-])
id:
# Human-readable project name (display only)
name:
# Security mode: gatekeeping (gated mirror, default) or online (direct push)
security_class: gatekeeping
# shared (bind mounts) or sealed (no mounts)
isolation: shared
git:
# Repository URL to clone into task containers
upstream_url:
# Default branch name (e.g. main)
default_branch:
# Human name for git committer identity
human_name:
# Human email for git committer identity
human_email:
# How agent/human map to git author/committer. Values: agent-human, human-agent, agent, human
authorship:
ssh:
# Opt in to the user's ~/.ssh keys for host-side gate-sync. Default false — terok uses only its vault-managed key. Resolves through ConfigStack: terok-global config.yml → project.yml → CLI --use-personal-ssh (highest).
use_personal:
tasks:
# Override task workspace root directory
root:
# Word categories for auto-generated task names (string or list of strings)
name_categories:
gate:
# Enable the host-side git gate mirror for this project
enabled: true
# Override git gate (mirror) path
path:
gatekeeping:
# Staging directory for gatekeeping builds
staging_root:
# Add upstream URL as external remote in gatekeeping containers
expose_external_remote: false
upstream_polling:
# Poll upstream for new commits
enabled: true
# Polling interval in minutes
interval_minutes: 5
auto_sync:
# Auto-sync branches from upstream to gate
enabled: false
# Branch names to auto-sync
branches: []
run:
# Seconds to wait before SIGKILL on container stop
shutdown_timeout: 10
# GPU passthrough: true, "all", or omit to disable
gpus:
# Podman --memory value (e.g. "4g", "512m", "4gib", plain "1024" for bytes); None = unlimited. Format mirrors what podman accepts — see man podman-run(1) --memory.
memory:
# Podman --cpus value (e.g. "2.0", "0.5"); None = unlimited. Non-negative decimal.
cpus:
# Declares that the project runs podman/docker inside its container. When true, the outer container is launched with --security-opt label=nested and --device /dev/fuse so rootless nested containers work under SELinux without disabling labels wholesale.
nested_containers: false
# OCI runtime: crun (default) for conventional containers, or krun for KVM-microVM isolation (experimental). None resolves to crun — the OCI runtime podman picks by default on every supported distro. krun requires the global experimental: true flag at task launch.
runtime:
# IANA timezone for the task container (e.g. Europe/Prague, UTC). Propagated as TZ — resolved against the image's tzdata. Unset (default) means follow the host's timezone.
timezone:
hooks:
pre_start:
post_start:
post_ready:
post_stop:
shield:
# Drop shield (bypass firewall) when task container is created
drop_on_task_run:
# Shield policy on container restart: retain or up
on_task_restart:
image:
# Base container image for builds
base_image: "fedora:44"
# Package family for the L0/L1 build (deb or rpm). Leave unset to auto-detect from *base_image*; set explicitly when the image is outside the known allowlist.
family:
# Comma-separated roster entries to install in L1, or "all". Prefix a name with "-" to exclude it from the selection (e.g. "all,-vibe" or just "-vibe" — both mean "everything except vibe"). Inherits from the global config when unset.
agents:
# Inline Dockerfile snippet injected into the project image
user_snippet_inline:
# Path to a file containing a Dockerfile snippet
user_snippet_file:
# Default agent provider (e.g. claude, codex)
default_agent:
default_login:
# Shared directory for multi-agent IPC (true = auto-create under tasks root, or absolute path)
shared_dir:
# Agent configuration dict (model, subagents, MCP servers, etc.)
agent: {}
config.yml
Global configuration shared by all terok ecosystem packages (terok, terok-sandbox, terok-executor). Each package reads only the sections it understands — terok validates the full file, while lower-level packages silently ignore sections they don't own.
Search order:
$TEROK_CONFIG_FILE (explicit override)
${XDG_CONFIG_HOME:-~/.config}/terok/config.yml
sys.prefix/etc/terok/config.yml
/etc/terok/config.yml
Top-level keys
| Key |
Type |
Default |
Description |
experimental |
boolean |
false |
Cross-package opt-in for experimental features. Gates terok's krun runtime and sandbox's krun-only host-binary prereq probes (ip). Lives on the top level rather than in any one section because it's shared between sandbox, executor, and terok — the topmost layer (terok) inherits this declaration. |
default_agent |
string or null |
— |
|
default_login |
string or null |
— |
|
agent |
mapping |
{} |
|
credentials:
| Key |
Type |
Default |
Description |
dir |
string or null |
— |
Shared credentials directory (proxy DB, agent config mounts) |
passphrase |
string or null |
— |
Unsafe headless fallback for the SQLCipher passphrase; only set when no OS keyring or systemd-creds is available. |
use_keyring |
boolean |
false |
Opt-in switch for the OS keyring tier of the passphrase resolution chain. Off by default because Linux Secret Service has per-collection (not per-item) ACLs. |
passphrase_command |
string or null |
— |
Operator-supplied shell command (e.g. pass show terok-sandbox/vault-passphrase) that prints the SQLCipher passphrase on stdout. Tokenised with shlex.split; resolver tier slots between OS keyring and the plaintext config-file fallback. Canonical headless option for hosts without systemd ≥ 257 — covers pass, bw, op, HashiCorp vault, and the cloud secret-manager CLIs (AWS, GCP, Azure). |
paths:
| Key |
Type |
Default |
Description |
root |
string or null |
— |
Namespace state root shared by all ecosystem packages (Podman model — one config, multiple readers) |
build_dir |
string or null |
— |
Build artifacts directory (generated Dockerfiles) |
sandbox_live_dir |
string or null |
— |
Container-writable runtime data (tasks, agent mounts). For hardened installs, mount the target with noexec,nosuid,nodev |
user_projects_dir |
string or null |
— |
User projects directory (per-user project configs) |
user_presets_dir |
string or null |
— |
User presets directory (per-user preset configs) |
port_registry_dir |
string or null |
— |
Shared port registry directory for multi-user isolation |
shield:
| Key |
Type |
Default |
Description |
bypass_firewall_no_protection |
boolean |
false |
Dangerous: disable egress firewall entirely |
profiles |
dict or null |
— |
Named shield profiles for per-project firewall rules |
audit |
boolean |
true |
Enable shield audit logging |
drop_on_task_run |
boolean |
true |
|
on_task_restart |
Literal |
"retain" |
|
services:
| Key |
Type |
Default |
Description |
mode |
Literal |
"socket" |
|
vault:
| Key |
Type |
Default |
Description |
bypass_no_secret_protection |
boolean |
false |
|
port |
integer or null |
— |
|
ssh_signer_port |
integer or null |
— |
|
gate_server:
| Key |
Type |
Default |
Description |
port |
integer or null |
— |
Gate server listen port |
repos_dir |
string or null |
— |
Override gate repo directory (default: state_dir/gate) |
suppress_systemd_warning |
boolean |
false |
Suppress the systemd unit installation suggestion |
network:
| Key |
Type |
Default |
Description |
port_range_start |
integer |
18700 |
|
port_range_end |
integer |
32700 |
|
ssh:
| Key |
Type |
Default |
Description |
use_personal |
boolean or null |
— |
Opt in to the user's ~/.ssh keys for host-side gate-sync. Default false — terok uses only its vault-managed key. Resolves through ConfigStack: terok-global config.yml → project.yml → CLI --use-personal-ssh (highest). |
run:
| Key |
Type |
Default |
Description |
shutdown_timeout |
integer |
10 |
Seconds to wait before SIGKILL on container stop |
gpus |
string or boolean or null |
— |
GPU passthrough: true, "all", or omit to disable |
memory |
string or null |
— |
Podman --memory value (e.g. "4g", "512m", "4gib", plain "1024" for bytes); None = unlimited. Format mirrors what podman accepts — see man podman-run(1) --memory. |
cpus |
string or null |
— |
Podman --cpus value (e.g. "2.0", "0.5"); None = unlimited. Non-negative decimal. |
nested_containers |
boolean |
false |
Declares that the project runs podman/docker inside its container. When true, the outer container is launched with --security-opt label=nested and --device /dev/fuse so rootless nested containers work under SELinux without disabling labels wholesale. |
runtime |
Literal or null |
— |
OCI runtime: crun (default) for conventional containers, or krun for KVM-microVM isolation (experimental). None resolves to crun — the OCI runtime podman picks by default on every supported distro. krun requires the global experimental: true flag at task launch. |
timezone |
string or null |
— |
IANA timezone for the task container (e.g. Europe/Prague, UTC). Propagated as TZ — resolved against the image's tzdata. Unset (default) means follow the host's timezone. |
run.hooks:
| Key |
Type |
Default |
Description |
pre_start |
string or null |
— |
|
post_start |
string or null |
— |
|
post_ready |
string or null |
— |
|
post_stop |
string or null |
— |
|
image:
| Key |
Type |
Default |
Description |
base_image |
string |
"fedora:44" |
Base container image for builds |
family |
Literal or null |
— |
Package family for the L0/L1 build (deb or rpm). Leave unset to auto-detect from base_image; set explicitly when the image is outside the known allowlist. |
agents |
string or null |
— |
Comma-separated roster entries to install in L1, or "all". Prefix a name with "-" to exclude it from the selection (e.g. "all,-vibe" or just "-vibe" — both mean "everything except vibe"). Inherits from the global config when unset. |
user_snippet_inline |
string or null |
— |
Inline Dockerfile snippet injected into the project image |
user_snippet_file |
string or null |
— |
Path to a file containing a Dockerfile snippet |
tui:
| Key |
Type |
Default |
Description |
default_tmux |
boolean |
false |
Default to tmux mode when launching the TUI |
external_editor |
boolean |
true |
Open instruction-editing actions in $EDITOR when it is set, instead of the integrated text editor. Honoured only on a local-terminal TUI — the web TUI (terok-web / textual-serve) always uses the integrated editor, as there is no terminal to suspend to. Set false to always use the integrated editor. |
desktop_entry |
Literal |
"auto" |
XDG desktop-entry install policy for terok setup (default: auto). auto installs only when xdg-utils is on PATH and otherwise skips with a hint. skip always skips silently — recommended for headless hosts that will never resolve the launcher. install always installs, using the built-in fallback writer when xdg-utils is missing. |
logs:
| Key |
Type |
Default |
Description |
partial_streaming |
boolean |
true |
Enable typewriter-effect streaming for log viewing |
tasks:
| Key |
Type |
Default |
Description |
name_categories |
list or null |
— |
Word categories for auto-generated task names (string or list of strings) |
git:
| Key |
Type |
Default |
Description |
human_name |
string or null |
— |
Human name for git committer identity |
human_email |
string or null |
— |
Human email for git committer identity |
authorship |
string or null |
— |
How agent/human map to git author/committer. Values: agent-human, human-agent, agent, human |
Full example
config.ymlcredentials:
# Shared credentials directory (proxy DB, agent config mounts)
dir:
# Unsafe headless fallback for the SQLCipher passphrase; only set when no OS keyring or systemd-creds is available.
passphrase:
# Opt-in switch for the OS keyring tier of the passphrase resolution chain. Off by default because Linux Secret Service has per-collection (not per-item) ACLs.
use_keyring: false
# Operator-supplied shell command (e.g. pass show terok-sandbox/vault-passphrase) that prints the SQLCipher passphrase on stdout. Tokenised with shlex.split; resolver tier slots between OS keyring and the plaintext config-file fallback. Canonical headless option for hosts without systemd ≥ 257 — covers pass, bw, op, HashiCorp vault, and the cloud secret-manager CLIs (AWS, GCP, Azure).
passphrase_command:
paths:
# Namespace state root shared by all ecosystem packages (Podman model — one config, multiple readers)
root:
# Build artifacts directory (generated Dockerfiles)
build_dir:
# Container-writable runtime data (tasks, agent mounts). For hardened installs, mount the target with noexec,nosuid,nodev
sandbox_live_dir:
# User projects directory (per-user project configs)
user_projects_dir:
# User presets directory (per-user preset configs)
user_presets_dir:
# Shared port registry directory for multi-user isolation
port_registry_dir:
shield:
# Dangerous: disable egress firewall entirely
bypass_firewall_no_protection: false
# Named shield profiles for per-project firewall rules
profiles:
# Enable shield audit logging
audit: true
drop_on_task_run: true
on_task_restart: retain
services:
mode: socket
vault:
bypass_no_secret_protection: false
port:
ssh_signer_port:
gate_server:
# Gate server listen port
port:
# Override gate repo directory (default: state_dir/gate)
repos_dir:
# Suppress the systemd unit installation suggestion
suppress_systemd_warning: false
network:
port_range_start: 18700
port_range_end: 32700
ssh:
# Opt in to the user's ~/.ssh keys for host-side gate-sync. Default false — terok uses only its vault-managed key. Resolves through ConfigStack: terok-global config.yml → project.yml → CLI --use-personal-ssh (highest).
use_personal:
run:
# Seconds to wait before SIGKILL on container stop
shutdown_timeout: 10
# GPU passthrough: true, "all", or omit to disable
gpus:
# Podman --memory value (e.g. "4g", "512m", "4gib", plain "1024" for bytes); None = unlimited. Format mirrors what podman accepts — see man podman-run(1) --memory.
memory:
# Podman --cpus value (e.g. "2.0", "0.5"); None = unlimited. Non-negative decimal.
cpus:
# Declares that the project runs podman/docker inside its container. When true, the outer container is launched with --security-opt label=nested and --device /dev/fuse so rootless nested containers work under SELinux without disabling labels wholesale.
nested_containers: false
# OCI runtime: crun (default) for conventional containers, or krun for KVM-microVM isolation (experimental). None resolves to crun — the OCI runtime podman picks by default on every supported distro. krun requires the global experimental: true flag at task launch.
runtime:
# IANA timezone for the task container (e.g. Europe/Prague, UTC). Propagated as TZ — resolved against the image's tzdata. Unset (default) means follow the host's timezone.
timezone:
hooks:
pre_start:
post_start:
post_ready:
post_stop:
# Cross-package opt-in for experimental features. Gates terok's krun runtime and sandbox's krun-only host-binary prereq probes (ip). Lives on the top level rather than in any one section because it's shared between sandbox, executor, and terok — the topmost layer (terok) inherits this declaration.
experimental: false
image:
# Base container image for builds
base_image: "fedora:44"
# Package family for the L0/L1 build (deb or rpm). Leave unset to auto-detect from *base_image*; set explicitly when the image is outside the known allowlist.
family:
# Comma-separated roster entries to install in L1, or "all". Prefix a name with "-" to exclude it from the selection (e.g. "all,-vibe" or just "-vibe" — both mean "everything except vibe"). Inherits from the global config when unset.
agents:
# Inline Dockerfile snippet injected into the project image
user_snippet_inline:
# Path to a file containing a Dockerfile snippet
user_snippet_file:
tui:
# Default to tmux mode when launching the TUI
default_tmux: false
# Open instruction-editing actions in $EDITOR when it is set, instead of the integrated text editor. Honoured only on a local-terminal TUI — the web TUI (terok-web / textual-serve) always uses the integrated editor, as there is no terminal to suspend to. Set false to always use the integrated editor.
external_editor: true
# XDG desktop-entry install policy for terok setup (default: auto). auto installs only when xdg-utils is on PATH and otherwise skips with a hint. skip always skips silently — recommended for headless hosts that will never resolve the launcher. install always installs, using the built-in fallback writer when xdg-utils is missing.
desktop_entry: auto
logs:
# Enable typewriter-effect streaming for log viewing
partial_streaming: true
tasks:
# Word categories for auto-generated task names (string or list of strings)
name_categories:
git:
# Human name for git committer identity
human_name:
# Human email for git committer identity
human_email:
# How agent/human map to git author/committer. Values: agent-human, human-agent, agent, human
authorship:
default_agent:
default_login:
agent: {}
Validation behavior
All config models use Pydantic v2 with extra="forbid". This means:
- Typos are caught at load time — e.g.
projecct: instead of project: produces a clear error with the field path.
- Type mismatches are reported — e.g.
shutdown_timeout: "ten" fails with a descriptive message.
- Enum values are validated —
security_class must be online or gatekeeping.
- Null sections get defaults — writing
git: with no sub-keys is equivalent to omitting the section entirely.
Note
project.yml validation is strict: errors produce a clear message and abort the operation. config.yml validation is lenient: errors are logged as warnings and the file falls back to defaults, so a typo in global config never prevents the TUI or CLI from starting.
Multi-reader pattern
config.yml follows the Podman model: one config file, multiple readers.
| Package |
Reads |
Validation |
| terok |
All sections |
Pydantic extra="forbid" — catches typos everywhere |
| terok-sandbox |
paths: only |
Raw YAML — ignores unknown top-level keys |
| terok-executor |
Delegates to sandbox |
No direct config file reading |
This means sandbox and executor never reject terok-only sections (ui:, tui:, hooks:, etc.), while terok catches all typos.