Self-hosted sidecar (Enterprise)
If your model runs on your own GPUs, you don't have to route prompts through ours. Run the trace worker beside your model; it reads the canary neurons locally, signs the readings with a key you hold, and sends Provenance only the readings and a prompt hash. Your canary policies then read them with canary_source: "sidecar".
1. Generate a key — keep the private half
openssl ecparam -name prime256v1 -genkey -noout -out sidecar.key.pem
openssl ec -in sidecar.key.pem -pubout -out sidecar.pub.pem
2. Register the public half
curl $API/v1/trace/worker-keys -H "Authorization: Bearer $PRV_ENTERPRISE_KEY" -H "Content-Type: application/json" \
-d "{\"name\":\"gpu-pod-eu-1\",\"public_key_pem\":$(jq -Rs . sidecar.pub.pem)}"
# → 201 {"worker_key_id":"wk_…", …}
Only EC P-256 public keys are accepted (400 otherwise). Revoke with DELETE /v1/trace/worker-keys/wk_… — readings signed by a revoked key are rejected from then on. The dashboard's Trace → Sidecar keys page does the same.
3. Run the worker in sidecar mode
TRACE_WORKER_BACKEND=hf TRACE_MODEL=<your HF model> \
TRACE_SIDECAR=1 TRACE_SIGNING_KEY_PEM="$(cat sidecar.key.pem)" TRACE_SIGNING_KEY_ID=wk_… \
PROVENANCE_API_URL=$API TRACE_WORKER_TOKEN=… \
provenance-trace-worker serve --port 8090
Your agent (or gateway) calls the worker before acting:
curl localhost:8090/v1/sidecar/read -H "Authorization: Bearer $TRACE_WORKER_TOKEN" -H "Content-Type: application/json" \
-d '{"model_version_id":"…","messages":[…],"canary_sets":[…]}'
# → {"trace_id":"trc_…","readings":{…},"posted":{"canary_hash":"…"}}
then passes that trace_id to POST /v1/intent/verify (or authorize). The model must be registered — a registry build of your model, with your canary sets.
Stop your model during generation
Add kill_switch (and the generation settings) to /v1/sidecar/read, and the worker generates the completion itself, checking the canaries on every token. Your model stops on the step a family reaches its threshold, on your hardware.
curl localhost:8090/v1/sidecar/read -H "Authorization: Bearer $TRACE_WORKER_TOKEN" -H "Content-Type: application/json" \
-d '{"model_version_id":"…","messages":[…],"canary_sets":[…],
"kill_switch":{"mode":"block","thresholds":{"prompt_injection":0.8}},"max_tokens":256,"temperature":0}'
# → {"trace_id":"trc_…","completion":"…","finish_reason":"canary_tripped","completion_tokens":31,
# "trip":{"family":"prompt_injection","phase":"generation","token_index":30,"score":0.84},
# "readings":{…},"generation_readings":{…},"posted":{"canary_hash":"…","trip":{…}}}
The completion stays with you. The signed reading adds a trip claim when a family tripped, and Provenance stores it with the signed token. flag records the trip point and lets the model finish. See Stop during generation for the fields.
The reading format
POST /v1/trace/readings {"token": "<JWS>"} — no API key; the signature authenticates it.
| Header | Claims |
| --- | --- |
| alg: ES256, typ: provenance-trace-reading+jwt, kid: wk_… | trace_id (trc_ + 32 hex, yours to mint), model_version_id, prompt_hash (sha256 of canonical {model, messages}), readings ({family: {score, tripped, threshold}}), iat, and trip ({family, phase, token_index, score}) only when the in-model kill switch fired |
iatmust be withinTRACE_READING_MAX_AGE_S(15 min by default) and not more than 60 s in the future.- Provenance computes
canary_hashitself from the claims. - A
trace_idcan be ingested once per account — a replay is409trace_reading_replayed. A bad signature, unknown or revoked key is401trace_reading_signature_invalid. - Each accepted reading meters
trace_canary_read(Enterprise: ledgered for your contract, never sent to Stripe).
What a sidecar reading proves
That a key you registered signed these readings for this prompt hash at about this time. It cannot prove which hardware or which weights produced them — that trust is yours, by construction, because you run the model. Prompts and completions never reach Provenance.