Skip to main content
Every request a sandbox answers goes through the same order, after auth, faults, the route and any rules:
  1. A resource the sandbox itself stored. Reads, lists, modifies and deletes of something the sandbox created or seeded are answered from its store. What you created is what you read back.
  2. A recording of the linked upstream. When the agent has recorded the real provider answering the same method and path template with the same request shape, that recorded response is the answer. The provider’s ids, envelopes, pagination fields and error bodies come from the provider.
  3. The spec. Only when neither holds does the sandbox synthesise a response from the spec, in the source order that page describes.
Recordings answer first as soon as they exist for the upstream the sandbox is linked to. Nothing is on silently: the mode is set at import, printed, and shown by sandbox list.

The three modes

pikopod import <name> --update re-evaluates the default, so a sandbox imported on day one switches to first the first time you refresh it after traffic has been recorded. Pass --recordings off to keep it off on purpose.

A create keeps the store consistent

A create always runs the operation, so what it stores can be read back. When a recording matches the create, the recorded response is the template the stored resource is completed from: the provider’s fields and envelope, with the id rewritten to the store key. Fields the request sent win over the recording, the way they win over the spec.
livemode is not in the spec. The provider sends it, so the sandbox now does too.

Matching is hierarchical

One clever hash would miss on real payment traffic where every request carries different amounts and references, so matching degrades in three steps:
  1. exact: method, path, and the normalized request body hash.
  2. shape: method, path template, and the set of body fields, values ignored.
  3. sequence: the next unserved recording for that method and template.
Before hashing, a curated list of request fields and headers that churn by nature (idempotency keys, trace ids, signatures, timestamps) is stripped. A recorded 4xx or 5xx is served only on an exact match. A different body may be the reason the provider refused, so a shape-only match never inherits a refusal.

Every response says where it came from

requests --explain notes every field a recording supplied with the source recorded:

What it needs

An agent upstream with the same name, or one linked with --upstream, and recordings under <data_dir>/recordings/<upstream>.ndjson. When the mode is first or fallback but no recordings load, pikopod up says so and the spec answers until traffic is recorded. Recordings are the redacted ones. Identifiers are tokens and unclassified strings were dropped before anything reached disk, and a replayed body serves those tokens as they are stored. They are never un-redacted. See Redaction.

Watch the number move

The same recordings score the sandbox. On the first run the sandbox answers from the spec alone; after recordings answer first, the same command says how much of the provider’s traffic the sandbox now reproduces. See Truthfulness.