Time Machine
A capsule records one request so you can run it again later and find out whether the answer moved. Model repointed under you, policy edited, provider swapped an engine, guardrail rule set tightened: a replay tells you that something behind a call changed, on your own traffic, with a receipt at each end.
The Time Machine is built and tested. It is not on the production deployment
yet, so the calls below describe a surface you cannot reach at
api.buildsable.com today.
Start with the constraint
Two rules shape everything else on this page, and they are limits rather than details.
The recorded request is never returned by any endpoint, including to you. It is opened in one frame, handed to the chat handler, and dropped. An endpoint that read a capsule back out would turn a regression-testing store into a prompt archive with an API in front of it, and the privacy amendment that licenses the storage does not license that.
The recorded output is not stored at all. A capsule keeps
sha256(utf8(assistant message)) and nothing else of it. So:
A diff can prove that the output changed, and can never show what changed.
That trade is deliberate. The obvious implementation stores the old completion — which is exactly the thing §3 says the gateway never does, for every other caller, on every other route. Storing it “just for capsules” would mean the sentence Sable never persists completions acquires an asterisk, and an asterisk on that sentence is worth more than this feature.
A hash of a paragraph does not yield the paragraph, and it is entirely
sufficient for the question actually being asked: same or not. If you need the
text of the new output, ask for it on the replay call with
include_output: true — it is returned once and discarded, exactly as a chat
completion is.
Recording is never implicit
A capsule exists only because you asked for one and confirmed, per capsule,
that Sable may hold the request. Omit confirm_seal and the call is refused
with the disclosure spelled out verbatim, so it is impossible to have not been
shown:
set
"confirm_seal": trueto record this capsule. Doing so stores the request body you supplied — messages, system prompt and any declared context — AES-GCM-sealed with Sable's master key, until you delete the capsule or it reaches its TTL. That is ciphertext at rest under Sable's key, not end-to-end encryption. The output is NOT stored: onlysha256(utf8(assistant message))is kept, which is enough to tell a later replay apart and not enough to reconstruct a completion.
Ciphertext is destroyed the instant you DELETE the capsule, and by the
sweeper at a hard TTL — 7 days by default, operator-configurable up to 90.
The content-free row and the replay history survive as your record; nothing in
them is content.
What a diff means
identical is deliberately nullable, and an unknown is not a regression.
identical | Recorded sampling | Reading |
|---|---|---|
null | — | The replay produced no output, so there was nothing to compare. See error_class. |
true | temperature: 0 | The model, routing and policy behind this request reproduce. |
false | temperature: 0 | Something moved — the engine, the routing, the policy, or the provider itself. Evidence to investigate, not proof of a fault: no provider guarantees bit-reproducibility even at temperature 0, because batching, expert routing and floating-point reduction order all shift. |
true | above 0 | Byte-identical anyway, which is a strong signal that nothing changed. |
false | above 0 | Carries no information. The sampler alone changes the bytes almost every time. Re-record at temperature 0 if you want the diff to mean something. |
Only temperature: 0 counts as deterministic. An omitted temperature is the
provider's default — 1.0 almost everywhere — which is emphatically not
deterministic, so a capsule that never set one is scored in the bottom two rows.
Every replay response carries deterministic and a diff_meaning sentence
naming which reading applies, so the verdict never travels without it.
Proven: that the request recorded in this capsule, re-run under the recorded key's scope, produced a completion whose sha256 does or does not equal the one observed at recording time.
Not proven: which of the two outputs was correct. A diff can prove the output changed; it can never show what changed, and it never establishes fault. Above temperature 0 a difference is the expected result and is not evidence of anything.
Scope binding
A capsule names the API key it was recorded under, and a replay runs under that key by default: the caps, the attached policy and the guardrail rule set are part of the experiment, not incidental to it.
You may override the key explicitly — a rotated key would otherwise strand
every capsule recorded under the old one — and the response then reports the
configuration the replay actually ran under beside the recorded one, plus
config_changed. A diff taken under changed rules is still useful; silently
pretending the configuration was the same would not be.
A replay also mirrors chat's own status code. If the underlying completion is refused for credit, scope, rate or a guardrail, the replay is refused the same way, so an agent's error handling does not have to learn a second vocabulary. The attempt is recorded either way, so a refused replay is visible in the capsule's history rather than lost with the response.
Quickstart
# Omit "output" and the gateway runs the request once itself and
# fingerprints what comes back — a metered, receipted completion like
# any other.
curl -s https://api.buildsable.com/v1/replay/capsules \
-H "Authorization: Bearer $SESSION" \
-H "Content-Type: application/json" \
-d '{
"api_key_id": "<key id>",
"confirm_seal": true,
"label": "ticket classifier, golden case 4",
"request": {
"model": "sable-llama-3.3-70b",
"temperature": 0,
"messages": [{"role":"user","content":"Classify: my card was charged twice."}]
}
}'
# {
# "id": "cap_9f3c…", "status": "sealed", "recording": "gateway-observed",
# "model": "sable-llama-3.3-70b",
# "output_fp": "c1d4…", "input_fp": "7b2a…", "input_bytes": 142,
# "config": {"model":"…","temperature":0.0,"deterministic":true,…},
# "recording_cost_micro_usd": 648, "recording_receipt_id": "req_…",
# "expires_at": "2026-09-20T12:00:00Z", "ttl_days": 7,
# "fingerprint_layout": "output_fp = sha256(utf8(assistant message content)), hex, no salt and no prefix",
# "note": "The sealed request is never returned by any endpoint, including to you."
# }Recompute the fingerprint
The layout is published on every response and is the simplest one you can recompute yourself — no salt, no prefix, no canonicalisation — so anyone holding the output they observed can check that a capsule really is a capsule of their run:
output_fp = sha256(utf8(assistant message content)), hex
printf '%s' "$OUTPUT" | shasum -a 256
# -> must equal the capsule's output_fpLimits
| Bound | Value |
|---|---|
| Recorded request body | 256 KiB of serialized JSON. A capsule is a regression test, not an archive. |
| Caller-supplied output for fingerprinting | 1 MiB. Hashed and dropped in the same frame, so this bounds the request body and nothing at rest. |
| Label | 120 characters. Readable at rest — never put the prompt in it. |
| Replays per capsule | 100 |
| TTL | SABLE_REPLAY_TTL_DAYS, default 7, ceiling 90. A capsule is short-lived evidence. |
Capsules holding ciphertext are counted per account, and DELETE frees a slot.
Expiry frees one on its own.
Endpoints
| Method | Path | Role | Notes |
|---|---|---|---|
POST | /v1/replay/capsules | Member | Record one request. Requires confirm_seal. |
GET | /v1/replay/capsules | Viewer | Content-free listing. ?limit=, ?run_id=. |
GET | /v1/replay/capsules/{id} | Viewer | One capsule and its replay history, content-free. |
POST | /v1/replay/capsules/{id}/replay | Member | Re-execute and diff. Body optional: {api_key_id?, include_output?, run_id?}. |
DELETE | /v1/replay/capsules/{id} | Member | Destroy the sealed request immediately. |
Session-authed (sess_…). Recording and replaying spend the account's
credit, which is why they are Member rather than Viewer.
Privacy posture
The recorded request body is stored — you cannot re-run what you did not keep — AES-GCM-sealed with the master key, the same posture as hosted agents, Relay, Legacy and Sealed Calls. This is sealing at rest under Sable's key, not end-to-end encryption.
Everything else on the row is content-free by construction: config_json holds
the model, sampling parameters, tier, policy id and rules hash, and guardrail
rule set id — none of it derived from your messages — and context_root is the
same context.root the inference receipt
already carries. Error classes stored on a replay result are derived from the
error variant, never from its message, because an upstream's own error
text can echo a prompt.
Related
- The privacy contract — the rule this amends, and exactly how.
- Verifiable receipts — every recording and every replay mints one.
- Evals — assertions over declared cases, where the Time Machine compares one real request against its own past.
- Autopilot — the other half of “did this change break anything”, on cost.