Admin controls
docs/ADMIN_CONTROLS.md · the same file GitHub renders ·Edit on GitHub ·Markdown
Every Mockingbird HTTP emulator answers the same controls: isolate state by key, reset it, snapshot and restore it, move the clock, inject faults, latency and timeouts, and replay all of it from a seed. They are the same on every emulator, and the same whether you set them at boot, call the admin API, or send a request header.
Find a control
Each control has its own page. That page is the interface: every route, body field, header, boot option, in-process method, admin screen and error.
Save state and put it back: Snapshot and restore. Take one with POST /__admin/snapshots. Restore it with POST /__admin/snapshots/<id>/restore. On the admin page the sidebar item is Snapshot & restore (/__admin/ui#snapshots) and Restore is the button on a saved snapshot. The Restore button on Checkpoints checks out a checkpoint. That is a different control.
State
| I want to | Page | Interface |
|---|---|---|
| Give each test or worker its own state | Namespaces | x-mockingbird-namespace: <key> |
| Wipe state between tests | Reset | POST /__admin/reset |
| Save state and put it back | Snapshot and restore | POST /__admin/snapshots · POST /__admin/snapshots/<id>/restore |
Time
| I want to | Page | Interface |
|---|---|---|
| Set, freeze or advance time | Clock | POST /__admin/clock |
| Read old state, or try a different path from it | Checkpoints and branches | x-mockingbird-at: <checkpoint> |
Failure
| I want to | Page | Interface |
|---|---|---|
| Make requests fail | Faults | POST /__admin/faults |
| Make requests slow, or hang | Latency and timeouts | PUT /__admin/latency · x-mockingbird-timeout: 30s |
Set them
| I want to | Page | Interface |
|---|---|---|
| Get the same ids and outcomes every run | Seed and determinism | --seed 7 |
| Set several controls in one call | Control document | PUT /__admin/controls |
| Start an emulator already configured | Boot | adminControls · serve --clock … --fault … |
| Control one request without any setup | Request headers | x-mockingbird-fault: 503 |
Inspect
| I want to | Page | Interface |
|---|---|---|
| See what the emulator received and holds | Journal, metrics and state | GET /__admin/requests · GET /__admin/state |
| Look up a route, a header, an error or an admin screen | Reference | GET /__admin |
One document, three carriers
A control is described once, as JSON:
{
"seed": 7,
"clock": { "now": "2030-01-01T00:00:00Z", "frozen": true, "autoAdvance": "1s" },
"virtualTime": true,
"latency": { "delay": "40ms", "jitter": "10ms" },
"faults": [{ "preset": "rate_limited", "count": 1 }]
}The same document applies in three places. Control document is its schema.
At boot. Pass it as adminControls, or as flags. See Boot.
import { createRuntime } from "@crvouga/mockingbird-service-stripe"
const stripe = createRuntime({
seed: 7,
adminControls: {
clock: { now: "2030-01-01T00:00:00Z", frozen: true },
latency: "40ms",
faults: [{ preset: "rate_limited", count: 1 }],
},
})npx mockingbird-stripe serve --seed 7 --clock 2030-01-01T00:00:00Z --freeze \
--latency 40ms --fault rate_limitedOver the admin API. Every control has routes under /__admin. See Reference for all of them.
curl -X POST localhost:8787/__admin/snapshots -d '{"name": "baseline"}'
curl -X POST localhost:8787/__admin/clock -d '{"advance": "15m"}'
curl -X POST localhost:8787/__admin/faults -d '{"preset": "rate_limited", "count": 1}'
curl -X POST localhost:8787/__admin/snapshots/baseline/restoreIn a request header. A request carries its own controls. See Request headers.
curl -X POST localhost:8787/v1/charges \
-H 'authorization: Bearer sk_test_mockingbird' \
-H 'x-mockingbird-namespace: worker-3' \
-H 'x-mockingbird-time: 2030-01-01T00:00:00Z' \
-H 'x-mockingbird-fault: status=503; delay=2s'In process, the runtime every emulator returns has the same controls as methods:
| Method | Control |
|---|---|
runtime.reset(key?) |
Reset |
runtime.snapshots |
Snapshot and restore |
runtime.namespaceClock(key?) · runtime.clock |
Clock |
runtime.checkpoint() · runtime.branch() · runtime.checkout() · runtime.timeline() |
Checkpoints and branches |
runtime.faults · runtime.applyPreset() |
Faults |
runtime.latency |
Latency and timeouts |
runtime.controls(key?) · runtime.configure(document, key?) |
Control document |
runtime.headers(input) |
Request headers |
runtime.journal · runtime.metrics · runtime.state(key?) |
Journal, metrics and state |
runtime.namespaces() · runtime.dropNamespace(key) |
Namespaces |
What holds everywhere
- Every control belongs to a namespace. A request that names none uses
default. One namespace's records, clock, faults, latency, journal and snapshots are invisible to every other. - Refused means unchanged. A document, body or header that cannot be read answers
400and changes nothing. Unknown keys are errors, never ignored. - Durations are milliseconds or a string:
"250ms","2s","15m","1h","3d". Instants are epoch milliseconds or ISO-8601. - Errors are always
{"error": {"type": "mockingbird_admin", "message": "…"}}, never a vendor's error shape. - Same seed, same requests, same bytes. See Seed and determinism.
- A running emulator describes itself.
GET /__adminlists every route and header with a summary and a link to its page, so a script or an agent can discover the controls without these docs.
The examples in this guide assume an emulator on localhost:8787 (npx mockingbird-stripe serve --port 8787). Every curl to /__admin and every JSON document shown is run by the test suite.