pikopod up serves two listeners. Each carries a small control surface next to the traffic it handles. When a token is configured, every request to either listener must carry it in X-Pikopod-Token. On loopback with no token, the sandbox control plane is open, because it controls a fake and never a provider.
All control responses are JSON with content-type: application/json; charset=utf-8.
Sandbox listener (:4600)
Traffic to a sandbox goes to /<name>/.... The control plane lives under /_pikopod/v1/sandboxes/<name>/. The v1 segment is the stable contract the test helpers are written against; every response carries x-pikopod-api: v1. The unversioned paths, /_pikopod/v1/sandboxes/<name>/..., keep working as aliases for one release. A fork’s control plane lives under its own name, /_pikopod/v1/sandboxes/<name>--<token>/, and resolves modes and verification against the parent’s spec and seed.
Rules
PUT replaces the live set at once and does not write the rules file; pikopod rule add and rule drop write the file and then push it here, so the file stays the source of truth across restarts. See Rules.
Seed, snapshot, reset and forks
A fork is served at
/<name>--<token>/ and has its own control plane under /_pikopod/v1/sandboxes/<name>--<token>/. Seeding a fork does not change the parent’s seed file. See Running.
Faults
A fault rule:
kind is one of error, latency, hang, slow_body, rate_limit, connection_reset, empty_response, random_data_then_close, malformed_response, wrong_content_length, duplicate_webhook, drop_webhook, reorder_webhook, delay_webhook. Non-webhook kinds need method and path (the path template as in the spec). rate_limit is armed as an error with status 429; error with no status becomes 500. probability defaults to 1. body (a JSON object or array) and headers are optional and apply to error; without body the fault answers with the spec’s declared example for that status when there is one, else no body. Without method and path the server answers 400 {"message":"method and path are required"}. See Faults.
Requests journal
Each entry carries the sequence number, method, path, matched template, status, and redacted headers, query and body.
evicted counts older entries the bounded journal dropped. scopes.live counts the scopes the sandbox currently keeps, at most 256, and scopes.evicted the ones it forgot to make room. See Request journal.
Mode
DELETE clears every fault on the sandbox and removes the stored mode. A scenario that has no standing state is refused with 400 and the reason. See Modes.
Without
scope, only the requests that carried no scope header are judged. See Scoped, or global.
Webhooks
Only events the spec declares can be emitted. See Webhooks.
Errors
Request bodies are read up to 1 MiB.
Every endpoint, exercised
Each example below is the real request and response from one run of the endpoint, generated by a test in the repository so this section cannot drift from the binary. The sandbox was imported from the example spec with seeddocs; the client traffic between the mode and its verification was three POST /charges under one idempotency key.
Rules: read the live set
200
Rules: replace the live set
200
Faults: arm one
201
Faults: list standing faults
200
Faults: clear matching faults
200
Mode: enter a scenario’s standing state
201
Mode: verify what the client sent
200
Mode: show the standing mode
200
Mode: clear it
200
Requests: the journal
200
Requests: reset the journal
200
Webhooks: deliveries so far
200
Webhooks: emit an event the spec does not declare
400
Seed: store resources
200
Snapshot: mark a point
200
Restore: return to it
200
Reset: back to the seeded state
200
Fork: a fresh copy for one test
201
Forks: the live copies
200
Fork: delete it
200
Agent listener (:4700)
Traffic to an upstream goes to its listen route, /<upstream>/... by default. Anything that matches no upstream answers 404.
Health
GET /healthz is public for liveness. Without a token, or with a token and the header, it returns the full payload:
200 {"status":"ok"} only, so a load balancer can poll it without being handed your upstream inventory. spec_watch appears when a watcher is armed. The divergence_* counters are described under Divergence and the webhooks_* counters under Incidents.
Inbound webhooks
POST /hooks/<upstream> exists for every upstream with a webhooks.receiver. It needs no token and ignores the loopback Host guard, since the provider delivering to it cannot carry either; it forwards to the configured receiver and nothing else.
Acknowledge and accept
agent ack silences a fingerprint until its diff changes. agent accept refreezes the endpoint’s baseline from live traffic and acknowledges, so the drifted behaviour becomes the new normal. Unknown fingerprints answer 404. See Alerts.
Headers the agent handles
X-Pikopod-Token is stripped before anything is forwarded, in every configuration. When an upstream cannot be reached, the agent answers 502 with X-Pikopod-Error: upstream-unreachable and never retries. See Headers.