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_provider string or null — Default LLM endpoint provider the agent routes to (e.g. openrouter)
default_shell 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, timeout, instructions, etc.)

project:

Key Type Default Description
name string or null — Unique project name / slug (lowercase, [a-z0-9_-])
description string or null — Free-text, human-readable project description (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

gate.backups:

Key Type Default Description
enabled boolean true Back up branch tips before destructive gate ops
retention_days integer 30 Days before backups expire (0 = keep forever)

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

gatekeeping.review_lag:

Key Type Default Description
enabled boolean true Warn when a gate branch is ahead of an open MR/PR on the forge
surface_in_tasks boolean true Write the warning into each task's ~/.terok/review-status

run:

Key Type Default Description
shutdown_timeout integer 10 Seconds to wait before SIGKILL on container stop
gpus string or boolean or list or null — GPU passthrough: "all"/true (every vendor detected on the host), a vendor name ("nvidia", "amd", "intel"), or a list of vendor names; 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.
perf boolean false Grant the container the perfmon capability so perf can sample inside it (flips the default seccomp profile's perf_event_open rule to allow). Rootless scope: the task samples its own processes, user-space only — and only if the host's kernel.perf_event_paranoid sysctl is ≤ 2 (hardened kernels ship 4, which disables unprivileged perf entirely).
podman_args list of string [] Freeform extra podman run flags, appended verbatim (expert escape hatch — you own the pieces). Sandbox-managed flags (--network, --cap-add, …), mounts shadowing sandbox targets, and isolation-weakening flags (--privileged, --security-opt) are rejected at parse time.
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
down_on_task_run boolean or null — Take the shield down when the task container is created
on_task_restart Literal or null — Shield policy on container restart: retain or up
sets list or null — Curated egress sets granted to this project's tasks (t40). Unset applies the generous default (every curated set); an empty list disables all curated content. See terok shield sets for the available names
allow list of string [] Extra hosts allowed at egress (shield's t40 project-allow tier)
override list of RawShieldOverride [] Break-glass overrides above the security-deny (shield's t10 tier)

services:

Key Type Default Description
mode Literal or null — Host↔container IPC transport for this project's tasks: tcp or socket; unset inherits the global services.mode

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

credentials:

Key Type Default Description
scope Literal "shared" shared (default) reuses the host-wide credential bucket and the global agent-config mount tree. project carves out a private set under the project's own state directory — agent logins, OAuth tokens, and shared config files live separately from every other project and must be re-authenticated.

Full example

project.yml
project:
  # Unique project name / slug (lowercase, [a-z0-9_-])
  name:
  # Free-text, human-readable project description (display only)
  description:
  # 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:
  backups:
    # Back up branch tips before destructive gate ops
    enabled: true
    # Days before backups expire (0 = keep forever)
    retention_days: 30


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: []

  review_lag:
    # Warn when a gate branch is ahead of an open MR/PR on the forge
    enabled: true
    # Write the warning into each task's ~/.terok/review-status
    surface_in_tasks: true


run:
  # Seconds to wait before SIGKILL on container stop
  shutdown_timeout: 10
  # GPU passthrough: "all"/true (every vendor detected on the host), a vendor name ("nvidia", "amd", "intel"), or a list of vendor names; 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
  # Grant the container the perfmon capability so perf can sample inside it (flips the default seccomp profile's perf_event_open rule to allow).  Rootless scope: the task samples its own processes, user-space only — and only if the host's kernel.perf_event_paranoid sysctl is ≤ 2 (hardened kernels ship 4, which disables unprivileged perf entirely).
  perf: false
  # Freeform extra podman run flags, appended verbatim (expert escape hatch — you own the pieces).  Sandbox-managed flags (--network, --cap-add, …), mounts shadowing sandbox targets, and isolation-weakening flags (--privileged, --security-opt) are rejected at parse time.
  podman_args: []
  # 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:
  # Take the shield down when the task container is created
  down_on_task_run:
  # Shield policy on container restart: retain or up
  on_task_restart:
  # Curated egress sets granted to this project's tasks (t40).  Unset applies the generous default (every curated set); an empty list disables all curated content.  See terok shield sets for the available names
  sets:
  # Extra hosts allowed at egress (shield's t40 project-allow tier)
  allow: []
  # Break-glass overrides above the security-deny (shield's t10 tier)
  override: []

services:
  # Host↔container IPC transport for this project's tasks: tcp or socket; unset inherits the global services.mode
  mode:

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:

credentials:
  # shared (default) reuses the host-wide credential bucket and the global agent-config mount tree.  project carves out a private set under the project's own state directory — agent logins, OAuth tokens, and shared config files live separately from every other project and must be re-authenticated.
  scope: shared

# Default agent provider (e.g. claude, codex)
default_agent:
# Default LLM endpoint provider the agent routes to (e.g. openrouter)
default_provider:
default_shell:
# Shared directory for multi-agent IPC (true = auto-create under tasks root, or absolute path)
shared_dir:
# Agent configuration dict (model, timeout, instructions, 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_provider string or null —
default_shell 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 — REMOVED — the plaintext config tier no longer exists. The validator below rejects any set value with migration directions; the field itself stays so the error names the replacement instead of pydantic's generic extra-key refusal.
use_keyring boolean true The OS keyring tier of the passphrase resolution chain. On by default — an empty keyring simply doesn't resolve; set false to keep the chain away from Secret Service entirely (its ACLs are per-collection, not per-item).
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 below the OS keyring. Canonical headless option for hosts without systemd ≥ 257 — covers a plain secret file (cat /path/to/file), 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
disable_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
dnsmasq_path Path or null — dnsmasq binary to run; found on the host PATH when unset
down_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 list or null — GPU passthrough: "all"/true (every vendor detected on the host), a vendor name ("nvidia", "amd", "intel"), or a list of vendor names; 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.
perf boolean false Grant the container the perfmon capability so perf can sample inside it (flips the default seccomp profile's perf_event_open rule to allow). Rootless scope: the task samples its own processes, user-space only — and only if the host's kernel.perf_event_paranoid sysctl is ≤ 2 (hardened kernels ship 4, which disables unprivileged perf entirely).
podman_args list of string [] Freeform extra podman run flags, appended verbatim (expert escape hatch — you own the pieces). Sandbox-managed flags (--network, --cap-add, …), mounts shadowing sandbox targets, and isolation-weakening flags (--privileged, --security-opt) are rejected at parse time.
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
theme string or null — Textual theme applied at TUI startup (e.g. textual-dark, ansi-dark). Maintained automatically: picking a theme in the TUI's command palette writes the choice here so it persists across sessions. Unset — or a name the installed Textual doesn't know — keeps the default theme.
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.
container_resync_seconds integer 14400 Full container-state resync interval, in seconds (default: 14400 = 4 hours). The task list is driven by events — inotify on task metadata plus a podman event stream — so this periodic resync is only insurance against a missed event, and is deliberately slow (à la a Kubernetes informer resync). Set it low (e.g. 2) on a monitor where inotify can't be trusted (network filesystem, or no podman event stream), trading disk activity for fault tolerance; set 0 to disable the resync entirely and rely purely on events.

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:
  # REMOVED — the plaintext config tier no longer exists.  The validator below rejects any set value with migration directions; the field itself stays so the error names the replacement instead of pydantic's generic extra-key refusal.
  passphrase:
  # The OS keyring tier of the passphrase resolution chain.  On by default — an empty keyring simply doesn't resolve; set false to keep the chain away from Secret Service entirely (its ACLs are per-collection, not per-item).
  use_keyring: true
  # 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 below the OS keyring.  Canonical headless option for hosts without systemd ≥ 257 — covers a plain secret file (cat /path/to/file), 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
  disable_firewall_no_protection: false
  # Named shield profiles for per-project firewall rules
  profiles:
  # Enable shield audit logging
  audit: true
  # dnsmasq binary to run; found on the host PATH when unset
  dnsmasq_path:
  down_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: "all"/true (every vendor detected on the host), a vendor name ("nvidia", "amd", "intel"), or a list of vendor names; 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
  # Grant the container the perfmon capability so perf can sample inside it (flips the default seccomp profile's perf_event_open rule to allow).  Rootless scope: the task samples its own processes, user-space only — and only if the host's kernel.perf_event_paranoid sysctl is ≤ 2 (hardened kernels ship 4, which disables unprivileged perf entirely).
  perf: false
  # Freeform extra podman run flags, appended verbatim (expert escape hatch — you own the pieces).  Sandbox-managed flags (--network, --cap-add, …), mounts shadowing sandbox targets, and isolation-weakening flags (--privileged, --security-opt) are rejected at parse time.
  podman_args: []
  # 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
  # Textual theme applied at TUI startup (e.g. textual-dark, ansi-dark).  Maintained automatically: picking a theme in the TUI's command palette writes the choice here so it persists across sessions.  Unset — or a name the installed Textual doesn't know — keeps the default theme.
  theme:
  # 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
  # Full container-state resync interval, in seconds (default: 14400 = 4 hours).  The task list is driven by events — inotify on task metadata plus a podman event stream — so this periodic resync is only insurance against a missed event, and is deliberately slow (à la a Kubernetes informer resync).  Set it low (e.g. 2) on a monitor where inotify can't be trusted (network filesystem, or no podman event stream), trading disk activity for fault tolerance; set 0 to disable the resync entirely and rely purely on events.
  container_resync_seconds: 14400

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_provider:
default_shell:
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.