Skip to main content
pikopod reads pikopod.yaml from the working directory unless --config says otherwise. --config is accepted by every command. Run pikopod init to scaffold one. Unknown keys are startup errors, not silent no-ops. A typo’d or removed knob must never let you believe something is configured. If pikopod starts, every key you wrote is a key it understands.

A minimal working file

Environment variables override the file. The full set is in Environment variables.

Top-level keys

upstreams

Each named upstream has a route on the agent and a target it forwards to. The target alone decides posture: a real provider means watch mode, a vendor sandbox means staging mode, the internal sandbox means scenario mode.
Health checks and KYC document uploads are the usual reasons for record.exclude: the first is noise, the second must never reach disk in any form. A pattern that does not compile is a configuration error, exit 2. See What never reaches disk at all. Upstream names may only contain letters, digits, _ and -, because the name becomes a file path component under data_dir.

listen and tokens

Both servers bind loopback by default. Binding a non-loopback address requires a token. The agent sits in a production request path, and an unauthenticated listener on 0.0.0.0 is a way to lose data, so pikopod refuses to start rather than let it happen. Supply the token via PIKOPOD_TOKEN or token_file, never on the command line. The environment wins over the file. When a token is set, every request to either server must carry it in the X-Pikopod-Token header. Tokenless agent listeners additionally reject requests carrying a foreign Host header, which blocks DNS rebinding from a browser on the same machine.

tls

Serves both ports over HTTPS. Both files or neither. Self-signed certificates are fine; pikopod’s own CLI clients trust the configured certificate file directly and nothing else. To generate a pair:

data_dir

Recordings, baselines, alert state, imported contracts, scenario packs, the event log and the sandbox store all live here. Back it up like state, not like a cache. Its layout is in Data directory.

sampling

rate thins what recordings persist, never what pikopod learns from. Every record still feeds the learner and the differ; the rate only gates the disk write. Error responses, drift-bearing records and pre-warmup traffic are always kept. 0 keeps guaranteed classes only, and is distinguishable from unset (1.0).

retention

Ages recordings and the event log out of disk. A record is kept at least that long and deleted no later than roughly twice that age. 0 (default) keeps size-based rotation only. scenario from-drift and reproduce read the event log, so fingerprints older than the window can no longer be pinned. Every incident row therefore carries reproducible until <time> and an export: hint. See Incident bundles.

slack

Alert behaviour itself is a fixed contract, not a set of keys. See Alerts.

warmup

pikopod watches an endpoint until it has seen min_samples responses across at least min_hours, then freezes a reference. Both are overridable for evaluation. Lowering them shortens the blind window and raises false positives. min_hours: 0 is explicitly distinguishable from unset. See Warmup and baselines.

spec_watch

Armed per upstream by spec_source. Fetches are ETag-gated and the pin never advances on its own; accepting a declared change is an explicit pikopod import <name> --update.

refine

Off by default. When enabled, observed traffic accumulates an overlay beside the spec-derived contract, and matured observations join the effective contract. prefer_spec flips type-conflict precedence back to spec-wins; the default is traffic-wins once the sustain gates clear. Inspect the result with pikopod agent contract <sandbox>. See Contract.

behaviour

Off by default. When enabled, the agent learns the provider’s observed state machine from traffic. See State machine.

llm

Bring your own key. pikopod never ships a key and never proxies your requests through anyone else. Set provider to the provider the key belongs to (openai, anthropic or openrouter), and the key in api_key or in PIKOPOD_LLM_KEY. The provider’s own standard key variable is also honoured, so an existing environment keeps working. When a key is stored in pikopod.yaml, the file must be mode 0600. Environment-provided keys do not make the file secret. The key is optional. Exactly three things use it: plain-English scenario authoring (scenario create), pikopod fix, and importing from a documentation URL as the last resort after the deterministic rungs fail. Everything else works with no key at all.

quotas

Sandbox resource limits are fixed rather than configured: Exceeding them returns 413 or 507 rather than degrading silently. Reset a sandbox’s state with pikopod sandbox reset <name>.

Sandboxes

Sandboxes are not a pikopod.yaml key. They are registered by pikopod import and stored in <data_dir>/sandboxes.json with their id, seed, spec source, origin, linked upstream, webhook URL and recordings mode. See Importing and Precedence.

The init template

pikopod init writes this file with mode 0600 and refuses to overwrite an existing one:

Validation errors

Every configuration error names the key, the reason and the fix, and exits 2. The ones you are most likely to meet: pikopod doctor checks the file, the data directory, the salt, the ports and every upstream’s reachability in one pass. See Troubleshooting.