yaml_schema
yaml_schema
¶
Pydantic v2 models mirroring the raw YAML structure of project.yml and config.yml.
These are Tier 1 models: they validate types, enums, and unknown-key typos
(extra="forbid") but do not resolve paths or merge config layers. The
companion modules projects and
config transform these into resolved runtime objects.
Sections owned by lower-level packages live in those packages' own
config_schema modules and are imported here for composition:
terok_sandbox.config_schemaownspaths,credentials,vault,gate_server,services,shield,network,ssh(eight sandbox-consumed sections).terok_executor.config_schemaownsimage.
terok itself owns the remaining four global sections (tui,
logs, tasks-global, git-global) plus every
project.yml-only section. Task-lifecycle hooks (run.hooks)
and the run: section as a whole live in sandbox and inherit
through to both RawProjectYaml and RawGlobalConfig — same
schema both levels, project values override globals.
RawGlobalConfig
inherits from ExecutorConfigView
and flips back to extra="forbid" because terok knows the full
ecosystem section set — a typo at the top level (tuii:) is
caught here.
NameCategories = Annotated[list[str] | None, BeforeValidator(_coerce_name_categories)]
module-attribute
¶
Reusable type: list[str] | str | None coerced to list[str] | None.
RawProjectSection
¶
Bases: BaseModel
The project: section of project.yml.
model_config = ConfigDict(extra='forbid')
class-attribute
instance-attribute
¶
name = Field(default=None, description='Unique project name / slug (lowercase, ``[a-z0-9_-]``)')
class-attribute
instance-attribute
¶
description = Field(default=None, description='Free-text, human-readable project description (display only)')
class-attribute
instance-attribute
¶
security_class = Field(default='gatekeeping', description='Security mode: ``gatekeeping`` (gated mirror, default) or ``online`` (direct push)')
class-attribute
instance-attribute
¶
isolation = Field(default='shared', description='shared (bind mounts) or sealed (no mounts)')
class-attribute
instance-attribute
¶
RawGitSection
¶
Bases: BaseModel
The git: section of project.yml.
model_config = ConfigDict(extra='forbid')
class-attribute
instance-attribute
¶
upstream_url = Field(default=None, description='Repository URL to clone into task containers')
class-attribute
instance-attribute
¶
default_branch = Field(default=None, description='Default branch name (e.g. ``main``)')
class-attribute
instance-attribute
¶
human_name = Field(default=None, description='Human name for git committer identity')
class-attribute
instance-attribute
¶
human_email = Field(default=None, description='Human email for git committer identity')
class-attribute
instance-attribute
¶
authorship = Field(default=None, description='How agent/human map to git author/committer. Values: ``agent-human``, ``human-agent``, ``agent``, ``human``')
class-attribute
instance-attribute
¶
RawGlobalGitSection
¶
Bases: BaseModel
The git: section of global config.yml (identity fields only).
model_config = ConfigDict(extra='forbid')
class-attribute
instance-attribute
¶
human_name = Field(default=None, description='Human name for git committer identity')
class-attribute
instance-attribute
¶
human_email = Field(default=None, description='Human email for git committer identity')
class-attribute
instance-attribute
¶
authorship = Field(default=None, description='How agent/human map to git author/committer. Values: ``agent-human``, ``human-agent``, ``agent``, ``human``')
class-attribute
instance-attribute
¶
RawTasksSection
¶
Bases: BaseModel
The tasks: section of project.yml.
model_config = ConfigDict(extra='forbid')
class-attribute
instance-attribute
¶
root = Field(default=None, description='Override task workspace root directory')
class-attribute
instance-attribute
¶
name_categories = Field(default=None, description='Word categories for auto-generated task names (string or list of strings)')
class-attribute
instance-attribute
¶
RawGateBackups
¶
Bases: BaseModel
Nested gate.backups settings — safety net for destructive gate ops.
Every confirmed destructive branch change (delete, force-update) first
saves the old tip under a hidden backup ref in the gate. enabled
opts a project out entirely; retention_days bounds how long the
backups accumulate (0 keeps them forever).
model_config = ConfigDict(extra='forbid')
class-attribute
instance-attribute
¶
enabled = Field(default=True, description='Back up branch tips before destructive gate ops')
class-attribute
instance-attribute
¶
retention_days = Field(default=30, description='Days before backups expire (0 = keep forever)')
class-attribute
instance-attribute
¶
RawGateSection
¶
Bases: BaseModel
The gate: section of project.yml.
enabled and upstream_url are orthogonal knobs. Four combinations:
enabled=True+ upstream set → host mirrors upstream; container clones from the mirror (the default; current behaviour).enabled=True+ no upstream → host initialises a remoteless bare repo; the container still gets a remote to push to.enabled=False+ upstream set → host never touches the remote; the container fetches directly from upstream. Useful when the host has no path to the upstream but the container does (firewall, corporate proxy), or when the mirror is simply unwanted.enabled=False+ no upstream → no git plumbing; the container starts with an empty workspace.
When upstream is absent the two classes still differ: gatekeeping
clones the workspace from the gate's local mirror, while online —
whose purpose is exposing an upstream — has nothing to expose and the
container starts with an empty workspace.
model_config = ConfigDict(extra='forbid')
class-attribute
instance-attribute
¶
enabled = Field(default=True, description='Enable the host-side git gate mirror for this project')
class-attribute
instance-attribute
¶
path = Field(default=None, description='Override git gate (mirror) path')
class-attribute
instance-attribute
¶
backups = Field(default_factory=lambda: RawGateBackups())
class-attribute
instance-attribute
¶
RawUpstreamPolling
¶
Bases: BaseModel
Nested gatekeeping.upstream_polling settings.
model_config = ConfigDict(extra='forbid')
class-attribute
instance-attribute
¶
enabled = Field(default=True, description='Poll upstream for new commits')
class-attribute
instance-attribute
¶
interval_minutes = Field(default=5, description='Polling interval in minutes')
class-attribute
instance-attribute
¶
RawAutoSync
¶
Bases: BaseModel
Nested gatekeeping.auto_sync settings.
model_config = ConfigDict(extra='forbid')
class-attribute
instance-attribute
¶
enabled = Field(default=False, description='Auto-sync branches from upstream to gate')
class-attribute
instance-attribute
¶
branches = Field(default_factory=list, description='Branch names to auto-sync')
class-attribute
instance-attribute
¶
RawReviewLag
¶
Bases: BaseModel
Nested gatekeeping.review_lag settings.
model_config = ConfigDict(extra='forbid')
class-attribute
instance-attribute
¶
enabled = Field(default=True, description='Warn when a gate branch is ahead of an open MR/PR on the forge')
class-attribute
instance-attribute
¶
surface_in_tasks = Field(default=True, description="Write the warning into each task's ~/.terok/review-status")
class-attribute
instance-attribute
¶
RawGatekeepingSection
¶
Bases: BaseModel
The gatekeeping: section of project.yml.
model_config = ConfigDict(extra='forbid')
class-attribute
instance-attribute
¶
staging_root = Field(default=None, description='Staging directory for gatekeeping builds')
class-attribute
instance-attribute
¶
expose_external_remote = Field(default=False, description='Add upstream URL as ``external`` remote in gatekeeping containers')
class-attribute
instance-attribute
¶
upstream_polling = Field(default_factory=RawUpstreamPolling)
class-attribute
instance-attribute
¶
auto_sync = Field(default_factory=RawAutoSync)
class-attribute
instance-attribute
¶
review_lag = Field(default_factory=RawReviewLag)
class-attribute
instance-attribute
¶
RawShieldOverride
¶
Bases: BaseModel
One shield.override break-glass entry (shield's t10 tier).
A t10 override sits above the security-deny, so it can reach a host the
firewall would otherwise refuse — including private (RFC 1918) addresses.
A host, an IP, or a CIDR: opening the agent to several local services is
a legitimate way to run, so a range is accepted, but shield logs it as a
warning at launch because it widens a whole subnet through the firewall.
A reason is mandatory so the punch-through is auditable.
model_config = ConfigDict(extra='forbid')
class-attribute
instance-attribute
¶
host = Field(description='Host, IP, or CIDR to allow above the deny (a range is logged as a warning)')
class-attribute
instance-attribute
¶
reason = Field(description='Why this break-glass override exists (audit trail)')
class-attribute
instance-attribute
¶
expires = Field(default=None, description='Optional ISO-8601 date after which the override is dropped. Evaluated when the container is launched or restarted — an already-running container keeps its overrides until its next start')
class-attribute
instance-attribute
¶
RawShieldProjectSection
¶
Bases: BaseModel
The shield: section of project.yml.
down_on_task_run / on_task_restart default to None (inherit from
global config.yml); allow / override are additive project layers
that stack on top of the global defaults.
model_config = ConfigDict(extra='forbid')
class-attribute
instance-attribute
¶
down_on_task_run = Field(default=None, description='Take the shield down when the task container is created')
class-attribute
instance-attribute
¶
on_task_restart = Field(default=None, description='Shield policy on container restart: ``retain`` or ``up``')
class-attribute
instance-attribute
¶
sets = Field(default=None, description="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")
class-attribute
instance-attribute
¶
allow = Field(default_factory=list, description="Extra hosts allowed at egress (shield's t40 project-allow tier)")
class-attribute
instance-attribute
¶
override = Field(default_factory=list, description="Break-glass overrides above the security-deny (shield's t10 tier)")
class-attribute
instance-attribute
¶
RawServicesProjectSection
¶
Bases: BaseModel
The services: section of project.yml.
mode defaults to None (inherit the global services.mode
from config.yml); tcp or socket pins the host↔container
IPC transport for this project's tasks regardless of the global
choice. Same vocabulary as the global section sandbox owns, so the
documented opt-out snippet works at either level.
RawCredentialsSection
¶
Bases: BaseModel
The credentials: section of project.yml.
Controls whether the project shares the host-wide credential bucket
(the default — Claude, Codex, gh, etc. logins are reused across every
project) or carves out its own isolated set. Opting in is destructive
for first-run UX: the project starts with no stored credentials and
has to be authenticated from scratch via terok auth --project.
model_config = ConfigDict(extra='forbid')
class-attribute
instance-attribute
¶
scope = Field(default='shared', description="``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.")
class-attribute
instance-attribute
¶
RawProjectYaml
¶
Bases: BaseModel
Validated structure of a project.yml file.
model_config = ConfigDict(extra='forbid')
class-attribute
instance-attribute
¶
project = Field(default_factory=RawProjectSection)
class-attribute
instance-attribute
¶
git = Field(default_factory=RawGitSection)
class-attribute
instance-attribute
¶
ssh = Field(default_factory=RawSSHSection)
class-attribute
instance-attribute
¶
tasks = Field(default_factory=RawTasksSection)
class-attribute
instance-attribute
¶
gate = Field(default_factory=RawGateSection)
class-attribute
instance-attribute
¶
gatekeeping = Field(default_factory=RawGatekeepingSection)
class-attribute
instance-attribute
¶
run = Field(default_factory=RawRunSection)
class-attribute
instance-attribute
¶
shield = Field(default_factory=RawShieldProjectSection)
class-attribute
instance-attribute
¶
services = Field(default_factory=RawServicesProjectSection)
class-attribute
instance-attribute
¶
image = Field(default_factory=RawImageSection)
class-attribute
instance-attribute
¶
credentials = Field(default_factory=RawCredentialsSection)
class-attribute
instance-attribute
¶
default_agent = Field(default=None, description='Default agent provider (e.g. ``claude``, ``codex``)')
class-attribute
instance-attribute
¶
default_provider = Field(default=None, description='Default LLM endpoint provider the agent routes to (e.g. ``openrouter``)')
class-attribute
instance-attribute
¶
default_shell = None
class-attribute
instance-attribute
¶
shared_dir = Field(default=None, description='Shared directory for multi-agent IPC (``true`` = auto-create under tasks root, or absolute path)')
class-attribute
instance-attribute
¶
agent = Field(default_factory=dict, description='Agent configuration dict (model, timeout, instructions, etc.)')
class-attribute
instance-attribute
¶
RawTUISection
¶
Bases: BaseModel
Global tui: section.
model_config = ConfigDict(extra='forbid')
class-attribute
instance-attribute
¶
default_tmux = Field(default=False, description='Default to tmux mode when launching the TUI')
class-attribute
instance-attribute
¶
theme = Field(default=None, description="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.")
class-attribute
instance-attribute
¶
external_editor = Field(default=True, description='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.')
class-attribute
instance-attribute
¶
desktop_entry = Field(default='auto', description='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.')
class-attribute
instance-attribute
¶
container_resync_seconds = Field(default=14400, ge=0, description="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.")
class-attribute
instance-attribute
¶
RawLogsSection
¶
RawTasksGlobalSection
¶
RawGlobalConfig
¶
Bases: ExecutorConfigView
Validated structure of the global config.yml file.
Composed from the ecosystem's per-package schemas:
- Sandbox-owned sections (
paths,credentials,vault,gate_server,services,shield,network,ssh) come fromSandboxConfigViewviaExecutorConfigView. - Executor-owned
imagecomes fromExecutorConfigView. - The
runsection (including the nestedhooks) is inherited transparently from sandbox via the same chain — the same schema applies at both project and global level, with project values overriding globals per the resolver below. - The four terok-owned global sections (
tui,logs,tasks-global,git-global) are added explicitly.
extra="forbid" flips back on at this top-of-stack layer because
terok knows every legitimate section. A typo at the top level
(tuii:) is caught here, even though sandbox / executor would
have tolerated it via their extra="allow" posture.