Skip to main content
An agent’s configuration lives in a single settings.yaml per agent, with one optional section per concern. Host-level knobs that bound the whole application (not a single persona) live one level up, in a layer-root settings.yaml. Ziro reads both across a fixed chain of layers, most specific to least, and falls back to built-in defaults when nothing supplies a value.
The older layout of one split file per concern (shell_policy.yaml, permissions.yaml, …) still works: Ziro dual-reads a section from settings.yaml or the matching split file at each layer. The bundled agents now ship as consolidated settings.yaml; run ziro migrate-config to fold your own split files into one file.

The per-agent folder

An agent is a directory whose name is the agent id. Its settings.yaml holds the sections it sets; a missing section falls back to the built-in default.
Each section key is the old file name minus its _policy / _servers / _config suffix: shell_policy.yamlshell, mcp_servers.yamlmcp, agent_config.yamlpersona, plugin_config.yamlplugins. registry.yaml is deliberately tiny: it names the default agent and nothing else. Whether an agent is selectable is its own meta.enabled flag, not a central list.

The resolution chain

Config resolves across three layers, most specific first:
1

Project layer

<project>/.ziro/agents/<id>/: this repo, this agent.
2

User layer

~/.ziro/agents/<id>/: this machine, this agent. How you configure a pip-installed Ziro without editing the package.
3

Bundled package

The settings.yaml shipped inside the ziro package.
4

Built-in defaults

No layer defines a section: the loader constructs the config object from its model defaults.
Two consequences worth internalising:
  • Per section, deep-merged across layers. For each section, every layer that defines it contributes, and the layers deep-merge most-specific-last: a project settings.yaml that sets only shell.timeout_s inherits the rest of the bundled shell section. This is safer than a wholesale replace: you cannot silently drop a guardrail by omitting it.
  • Section or split file, per layer. At each layer the contribution is that layer’s settings.yaml section if present, else its legacy split file. So a project split shell_policy.yaml still overrides a bundled shell section.
A missing section is normal and safe: it degrades to built-in defaults. The one deliberate exception is the plugins section: an agent no layer supplies it for surfaces zero plugins (absence is opt-out). See Plugins.

Trust gate

The permissions, mcp, hooks, and plugins sections can widen authority or launch a subprocess, so they are ignored from an untrusted project layer (they fall through to the user / bundled layers). The first interactive run in a new project prompts to trust it. See Permissions.

The top-level resource tier

Some knobs bound host resources, not a persona: how many subagents fan out, the background job pool, scratch and background-shell garbage collection. Because a thread can switch agents mid-conversation, these do not belong on any one agent. They live in a layer-root settings.yaml (~/.ziro/settings.yaml, <project>/.ziro/settings.yaml, or bundled):
resources values are ceilings: a project layer may lower them but never raise them above the user layer, and the whole block is ignored from an untrusted project. security.fs_deny_paths is the symmetric floor: it only ever adds to the non-removable shipped secret list, so a project (or an agent’s own fs section) can tighten what is denied but never open a hole in it. Where the top-level tier is unset, each knob falls back to the per-agent value (subagents, queue, fs sections), so existing configs keep working.

The flavour overlay

A flavour is a top-level container selected once at launch. Beyond scoping which agents and threads you see, it can overlay a policy baseline onto its member agents, merged in memory, before the config object is built: So the full order for any one config value is:
With no active flavour the overlay is empty and behaviour is byte-identical to having no flavours at all. ziro init --flavour <name> writes a flavour’s overrides into the scaffolded project agent’s settings.yaml sections.

Registration gates

Several sections gate registration entirely: when shell, websearch, or webfetch has enabled: false (or is absent, for the default-off features), the tool is never registered. It is invisible to search_tools and to the subagent permission view. The absence is the disable switch.

Migrating split files

Folding is safe: an agent resolves identically before and after. Originals are kept as *.migrated backups (use --delete to remove them).

The three state files

Config files describe intent and are yours to edit and commit. State files record what happened and are written by Ziro. Do not hand-edit them.
layered env file
API keys, the default model, the LLM provider, Langfuse credentials, and DATABASE_URL. Written by the setup wizard (ziro setup) and by /settings. Read as layers, so a project-level .env can override it.
MRU registry
The list of known user ids, last_user, and the last flavour each user chose. Drives the launch-time user picker and the /user command.
per-workspace index
Thread metadata: user, agent, flavour, title, timestamps, and any per-thread LLM or plugin overrides. Checkpoints remain the source of truth for conversation content. This file is the index over them, and is rebuildable.
Two more per-project areas are keyed outside the repo by a stable workspace key (<basename>-<hash>): project memory under ~/.ziro/projects/<key>/memory/ and the per-thread scratchpad under your system temp directory. See Memory.

Runtime mode

One environment variable decides the whole storage backend:

Inspecting and editing

In chat, /settings edits the API key, model, Langfuse, and database entries in ~/.ziro/.env, queuing a live rebind only for the section you actually changed.

Next steps

Agent file reference

Every section of settings.yaml.

Environment variables

Every variable Ziro reads and where it is written.

Providers and models

Pinning a provider and model per agent.