Portal
Documentation: all sections

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": true to 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: only sha256(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.

identicalRecorded samplingReading
nullThe replay produced no output, so there was nothing to compare. See error_class.
truetemperature: 0The model, routing and policy behind this request reproduce.
falsetemperature: 0Something 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.
trueabove 0Byte-identical anyway, which is a strong signal that nothing changed.
falseabove 0Carries 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.

Honesty box

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_fp

Limits

BoundValue
Recorded request body256 KiB of serialized JSON. A capsule is a regression test, not an archive.
Caller-supplied output for fingerprinting1 MiB. Hashed and dropped in the same frame, so this bounds the request body and nothing at rest.
Label120 characters. Readable at rest — never put the prompt in it.
Replays per capsule100
TTLSABLE_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

MethodPathRoleNotes
POST/v1/replay/capsulesMemberRecord one request. Requires confirm_seal.
GET/v1/replay/capsulesViewerContent-free listing. ?limit=, ?run_id=.
GET/v1/replay/capsules/{id}ViewerOne capsule and its replay history, content-free.
POST/v1/replay/capsules/{id}/replayMemberRe-execute and diff. Body optional: {api_key_id?, include_output?, run_id?}.
DELETE/v1/replay/capsules/{id}MemberDestroy 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.