Skip to content

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.ymlproject.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.yml
project:
  # 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:

  1. $TEROK_CONFIG_FILE (explicit override)
  2. ${XDG_CONFIG_HOME:-~/.config}/terok/config.yml
  3. sys.prefix/etc/terok/config.yml
  4. /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.ymlproject.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.yml
credentials:
  # 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 validatedsecurity_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.