Every request is one signed, attributable event.
Successes, failures, streams, blocked denials, and admin mutations — each produces exactly one denormalized, HMAC-signed row in an always-on event log. Nothing happens in Trifrost without leaving a verifiable trace.
One request. One row. No joins.
Model, provider, alias, actor, and org are stamped directly onto each row in a weekly-partitioned table. Queries never join across tables — and never guess at attribution after the fact.
Inference events
Every successful call, every upstream failure, every stream — token counts, cost, latency, TTFT, and the full attempt trail when failover kicked in.
Blocked events
Every pre-hook rejection — 401 / 402 / 403 / 429 / 400 — writes its own row with the denial reason. A blocked request is never invisible.
Admin mutations
Every POST / PUT / PATCH / DELETE on the admin API is audited with the resource, action, and actor. Configuration changes answer to the same log.
HMAC-signed per row. Verified on read.
Each event row is signed with HMAC-SHA256 as it's written, and the signature is re-verified when the row is read back. The audit log doesn't ask you to trust it — it proves itself on every query.
- Signed off the hot path — HMAC computed in the batch workers, not in the request
- Verified on read — every served row is checked against its signature
- Partitioned weekly — retention-ready layout, never silently dropped
Mutate one byte and verification fails.
Change a cost, a token count, an actor — any field in a stored row — and its signature no longer matches. The next read flags the row as tampered. For an auditor, that is the difference between a log and evidence.
The UI never lies about why a body is missing.
Every event carries a provenance stamp recording why a request or response body was — or wasn't — captured: the winning policy's ID and name, the effective toggles, and the skip reason when a wanted capture came up empty.
{ "policy": { "id": "7f3a…", "name": "EU dept full capture" }, "capture_prompt": true, "capture_response": true, "response_partial": "final_chunk_only", "response_skip_reason": null }
No stamp, no guessing: when a stream is stored as final_chunk_only, or a capture is skipped with stream_truncated_no_body, the event says so — you will never see a false "no policy applied" claim.
Baseline always. Bodies by policy.
Tier 1 — baseline, no policy needed
On every request, Trifrost stores redacted request headers and, for any response with status ≥ 400, the error body. Debugging a failed upstream call never depends on someone having enabled a policy first.
Tier 2 — policy-gated full capture
The request_body_logging control captures full prompts and responses with independent capture_prompt / capture_response toggles, an optional capture_streaming_full_body mode, and a max_body_bytes ceiling — scoped per org, department, user, or key.
Live in-flight requests
GET /api/v1/requests/in-flight shows what's moving right now — phase (pre_pipeline → routing → upstream → streaming), model, provider, key, and age in ms. In-memory, zero DB hits, safe to poll at high cadence.
TTFT & tokens/sec
Time-to-first-token and decode rate are recorded on every stream. An hourly rollup table powers provider-performance comparison without scanning the event log.
Request IDs end-to-end
X-Request-Id is minted or propagated on every request, echoed in the response, and searchable in the event log. Give support one ID and they find the one row.
Compare providers on your traffic, not their marketing.
The hourly rollup aggregates counts, latency, TTFT percentiles, and tokens/sec per customer × provider × model. When two providers can serve the same alias, you can see which one actually serves it better — on your workload.
Pair it with alias-group load balancing →
SIEM-ready exports
The event log exports as JSON, JSONL, or Syslog RFC 5424 — stream the same signed events into your existing security pipeline without a custom collector.
Multi-tenant isolation is absolute.
Every customer-scoped read is filtered by the session's active customer. Organization A cannot read organization B's events — even read-only, even for a platform admin. Cross-org access returns a 404, as if the data didn't exist. The only exception is migration mode, and it is explicit.