Receipts & audit
Every action a Fibric operator proposes leaves a receipt: an immutable record of what was proposed, which policy rule decided it, the idempotency key, and the outcome. Receipts are what make the system explainable after the fact. An action that ran, an action that was escalated, an action that was refused, and an action that deduplicated all leave one. If you cannot account for something, it did not happen.
The receipt store and its query API are part of the hosted platform, v1.0 preview. The kernel's shipped seed of a receipt is the ActionResult the deterministic executor returns for every action — action, decision, ok, result or error. The fields documented below mirror the kernel's ActionResult and envelope types; where the hosted surface goes beyond them, it is labeled preview.
What a receipt records
A receipt binds together the three halves of one governed action: the context it ran in (tenant, envelope, operator), the decision made about it (policy rule, disposition), and what actually happened (outcome, timestamps). Each field exists so a specific question can be answered later without reconstructing anything.
| Field | Description |
|---|---|
receipt_id |
The stable identifier for this record, for example rc_5b21. Referenced by corrections, exports, and the CLI. |
tenant_id, reseller_id |
The tenancy scope the action ran under, carried from the envelope. A receipt belongs to exactly one tenant and is visible only inside it. See Tenancy & isolation. |
operator |
The agent_id of the operator that proposed the action. Every receipt traces back to a named worker; there is no anonymous actor. |
envelope |
The event_id and correlation_id of the event envelope that triggered the run. This is how you walk from a receipt back to the observation that caused it, and across to every other receipt in the same causal chain. |
action |
The proposal as the operator made it: connector, tool, and args, verbatim from the PlannedAction. The receipt records what was asked for, not a paraphrase. |
decision |
The disposition the executor reached: ALLOW, ALERT, BLOCK, or DEDUP. See Trust tiers and Single-flight & idempotency for how each arises. |
ok / outcome |
Whether the action completed as intended, and the outcome detail: applied, failed with an error, refused for a block, deduplicated for a repeat. |
policy |
The rule that matched and produced the decision. For a BLOCK with no match, the rule is recorded as the fail-closed default. |
idempotency_key |
The dedup key the action carried, for example ship-risk:SO-10884:hold. This is the field that lets you prove a side effect applied exactly once. |
proposed_at, decided_at, completed_at |
Timestamps for the three moments of the action's life: when the operator proposed it, when the executor disposed of it, and when the connector call finished. The gaps between them are your latency audit. |
undo (preview) |
A pointer to the compensating action when the connector declares one — orders.hold pairs with orders.release. An undo is a new governed action with its own receipt, never a rollback of this one. |
An example receipt
This is the full record behind the rc_5b21 receipt shown in the quickstart, expanded with the envelope reference and timestamps.
{
"receipt_id": "rc_5b21",
"tenant_id": "t_8f2a…c901",
"reseller_id": null,
"operator": "ship-risk",
"envelope": {
"event_id": "evt_9c41…77a2",
"correlation_id": "cor_1fd0…3e58"
},
"action": {
"connector": "magento",
"tool": "orders.hold",
"args": { "order": "SO-10884", "reason": "ship-risk-review" }
},
"decision": "ALLOW",
"ok": true,
"outcome": "applied",
"policy": { "decision": "ALLOW", "rule": "magento/orders.hold maxValue=500" },
"idempotency_key": "ship-risk:SO-10884:hold",
"proposed_at": "2026-06-14T09:02:10Z",
"decided_at": "2026-06-14T09:02:10Z",
"completed_at": "2026-06-14T09:02:11Z",
"undo": { "tool": "orders.release", "status": "available" }
}
Read the record from the outside in. The tenancy fields say whose world this happened in. The envelope reference says why the operator was running at all. The action block says what the model wanted, verbatim. The decision, policy, and outcome say what the executor did about it. And the idempotency key is the standing proof that proposing this hold again changes nothing.
Following a correlation
The correlation_id is the thread that ties one causal chain together. An inbound observation, the operator run it triggered, every action that run proposed, and any follow-up runs the outcome caused all share it. Filtering receipts by correlation reconstructs the whole story of one incident in order, across operators and connectors, without guessing at timestamps.
# every receipt in the causal chain of one event
fibric receipts tail --correlation cor_1fd0…3e58
Immutability
Receipts are append-only. Once written, a receipt is never edited, never overwritten, and never deleted by normal operation. A correction — a wrong outcome recorded because a vendor returned a misleading response, an undo that compensated an action — is a new receipt referencing the old one, so the record of what was believed at the time survives alongside the record of what turned out to be true.
Receipts also survive the operator that produced them. Uninstalling an operator, rebinding its capabilities to a different connector, or deleting its definition leaves every receipt it ever generated intact and queryable. The audit trail is a property of the workspace, not of any component in it.
Undo pointers (preview)
When a connector declares a compensating action for a tool — orders.hold pairs with orders.release — the receipt carries an undo pointer. Triggering it does not rewind the original receipt. It proposes the compensating action through the same governed pipeline: trust evaluation, single-flight, its own idempotency key, its own receipt referencing the one it compensates. History gains an entry; it never loses one.
An audit trail you can edit is a narrative, not evidence. Append-only receipts mean the answer to "what did the system do on June 14" is the same whoever asks and whenever they ask, including after the configuration that produced the behavior is long gone.
The veto trail
BLOCK and DEDUP produce receipts too. A refused action is still accounted for: the receipt records the proposal exactly as the operator made it, the decision BLOCK, the fail-closed or explicit rule that produced it, and the error blocked by trust policy. A deduplicated action records the decision DEDUP and the key that collapsed it.
This is what fail-closed auditing means in practice. Most systems log what they did. Fibric also logs what it declined to do, which is where the interesting questions usually live: what was the model trying to do that policy would not let it, how often does an operator propose over its value cap, which duplicates did the 657-flood lock absorb this week. The veto trail turns policy from a silent gate into a measurable one, and it is the evidence base for promoting an action from ALERT to ALLOW.
Querying receipts
The CLI is the day-to-day surface. fibric receipts tail streams receipts as they land; fibric receipts show retrieves one record in full. Filters compose: --since bounds the window, --decision selects a disposition, --connector narrows to one system.
# stream receipts from one operator as they land
fibric receipts tail --operator ship-risk
# one full record
fibric receipts show rc_5b21 --json
# everything policy refused on the Magento connector this week
fibric receipts tail --since 7d --decision BLOCK --connector magento
The same data is available over REST at the Receipts API (preview), with the same filters as query parameters, for wiring receipts into your own dashboards and alerting.
Useful standing queries: --decision BLOCK --since 7d shows what policy is refusing and whether the fail-closed default is catching proposals you should write a policy for; --decision DEDUP counts the duplicates the lock absorbed; --decision ALERT is the review queue's history, the evidence for promoting an action to unattended.
Export
The hosted console exports any filtered receipt view as CSV or JSONL, suitable for a spreadsheet review or for loading into your own warehouse. Exports carry every field in the table above; JSONL preserves the nested action and envelope objects, CSV flattens them.
# pipe a JSON receipt into your own tooling
fibric receipts show rc_5b21 --json | jq '.policy.rule'
Scheduled exports — a recurring JSONL drop of the workspace's receipts to your own S3 bucket — are an Enterprise feature, for teams whose compliance posture requires the audit trail to live in infrastructure they control. See Tenancy & isolation for what the Enterprise tier adds.
Retention
During early access, receipts are retained for the life of the workspace: nothing is aged out, and the full trail remains queryable from the first action the workspace ever ran. Contractual retention windows — guaranteed minimums, scheduled destruction, and legal-hold semantics — are an Enterprise feature.
Keep going
- Single-flight & idempotency: where the
DEDUPdisposition and the idempotency key on every receipt come from. - Trust tiers: the decisions receipts record, and the policies that produce them.
- Governance & trust: the full propose-then-dispose loop a receipt is the final step of.
- Tenancy & isolation: why a receipt is visible in exactly one tenant, and what Enterprise adds.