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.
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 scenario 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 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 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-fallback flag. See Importing.

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.