← Back to Build Log
complianceauditreceiptseu-ai-actsecurity

Building a Tamper-Evident Audit Trail for AI Agents

When an AI agent takes an action in your production system, what record do you have of why it happened? Most teams answer this question with: "Application logs. We log the tool calls." That answer satisfies a developer debugging an incident. It does not satisfy an auditor, a regulator, or a lawyer.

Application logs are mutable. Any engineer with database access can delete or modify them. There is no way to prove the logs have not been tampered with. They typically capture the action but not the context — not the policy version that governed the decision, not the rules that matched, not the reasoning that led to the outcome. And in multi-agent systems, they are scattered across multiple services with no clear chain of custody.

The EU AI Act Article 12 requires that high-risk AI systems "automatically record events" in a way that enables post-hoc verification. Generic application logs do not meet this bar. You need something that is structured, complete, and tamper-evident.


What a Receipt Is

A receipt in Authensor is a structured, immutable record created for every authorization decision. It captures six things:

  1. The intent — the exact tool call that was evaluated, including action name, resource, and arguments
  2. The principal — the verified agent identity that made the call
  3. The policy version — the specific version of the policy that evaluated the intent
  4. The matched rules — which rules fired and what their decisions were
  5. The final decision — ALLOW, DENY, or REVIEW
  6. The hash chain link — a SHA-256 hash that cryptographically links this receipt to the previous one

The last item is what makes the chain tamper-evident.


How Hash Chaining Works

Each receipt includes a prev_receipt_hash field that is the SHA-256 hash of the previous receipt in the chain (or a genesis hash for the first receipt). The receipt hash for the current record is computed over all its fields, including prev_receipt_hash.

// Simplified receipt structure
interface Receipt {
  id: string;
  timestamp: string;
  agentId: string;
  action: string;
  resource: string;
  policyVersion: string;
  matchedRules: string[];
  decision: "ALLOW" | "DENY" | "REVIEW";
  prev_receipt_hash: string;  // SHA-256 of the previous receipt
  receipt_hash: string;       // SHA-256 of this entire receipt
}

The chain verification property is this: if you modify any receipt anywhere in the chain — change a decision, alter an argument, delete a record — the hash chain breaks at that point and every subsequent receipt becomes invalid. You cannot tamper with a past receipt without invalidating all future receipts, which means you cannot silently cover your tracks.

import { verifyReceiptChain } from "@authensor/sdk";

// Verify the entire chain for an agent
const result = await verifyReceiptChain({
  agentId: "agent-finance-prod",
  from: "2026-01-01T00:00:00Z",
  to: "2026-03-15T00:00:00Z",
});

if (!result.valid) {
  console.error(`Chain integrity violation at receipt ${result.brokenAt}`);
  console.error(`Expected hash: ${result.expectedHash}`);
  console.error(`Actual hash: ${result.actualHash}`);
  // Alert security team immediately
}

This is the same principle used in blockchain systems and certificate transparency logs. Each block or entry commits to the history that preceded it. The chain is publicly verifiable and any modification is detectable.


What the Receipt Chain Captures That Logs Do Not

Compare a typical application log entry to an Authensor receipt:

Typical log entry:

2026-03-15T14:23:01Z agent-finance executed create_transfer amount=50000 currency=USD

Authensor receipt:

{
  "id": "rcpt_01HX9K3MZQW1FVBN2YC4T8RSPJ",
  "timestamp": "2026-03-15T14:23:01.847Z",
  "agentId": "agent-finance-prod",
  "action": "create_transfer",
  "resource": "accounts/ACC-001",
  "args": {
    "amount": 50000,
    "currency": "USD",
    "destination": "ACC-002"
  },
  "policyId": "pol_finance_v3",
  "policyVersion": "3.2.1",
  "matchedRules": ["finance-write-review"],
  "decision": "ALLOW",
  "approvedBy": "ops-lead@example.com",
  "approvalTimestamp": "2026-03-15T14:22:47Z",
  "aegisScanResult": "CLEAN",
  "prev_receipt_hash": "sha256:a7f3c2e19b4d...",
  "receipt_hash": "sha256:9e2f1a84c7b3..."
}

The receipt tells you: exactly what happened, which policy governed it, which rule matched, who approved it and when, whether the content was scanned, and cryptographic proof that this record has not been modified.

An auditor reviewing agent behavior can trace every action back to a specific policy rule and the exact inputs that triggered it. A security team investigating an incident can verify that the audit trail has not been tampered with. An automated compliance check can verify the chain programmatically.


Why This Matters for EU AI Act Article 12

Article 12 of the EU AI Act requires high-risk AI systems to maintain logs that enable post-hoc verification. The specific requirements are:

  • Automatic recording of events during the system's operation
  • The period of use of the system
  • The reference database used for verification
  • Traceability of natural persons involved in verification
  • Records sufficient to reconstruct the sequence of events

Authensor's receipt system satisfies all five. Every authorization event is automatically recorded. Timestamps are captured at sub-second precision. Policy versions are recorded with each receipt, creating a reference trail. Human approvers are recorded by identity. The hash chain enables reconstruction of the exact sequence of events.

The August 2, 2026 deadline for high-risk systems is approaching. Teams that have been logging to application databases will need to either upgrade their logging infrastructure to meet these requirements or add an authorization layer that produces compliant records automatically.


Querying the Receipt Chain

Receipts are structured data, not unstructured log strings. You can query them.

import { AuthensorClient } from "@authensor/sdk";

const client = new AuthensorClient({
  apiKey: process.env.AUTHENSOR_KEY,
});

// All REVIEW decisions in the last 30 days
const reviewedActions = await client.queryReceipts({
  decision: "REVIEW",
  from: "2026-02-13T00:00:00Z",
  to: "2026-03-15T00:00:00Z",
});

// All actions by a specific agent
const agentHistory = await client.queryReceipts({
  agentId: "agent-finance-prod",
  limit: 1000,
  orderBy: "timestamp",
});

// All denied actions with their matched rules
const deniedWithRules = await client.queryReceipts({
  decision: "DENY",
  includeMatchedRules: true,
  from: "2026-03-01T00:00:00Z",
});

This makes it practical to produce compliance reports, investigate incidents, and audit agent behavior at any granularity.


Receipt Export for External Auditors

For external audits, receipts can be exported in a format that allows independent verification without access to your production database.

// Export a receipt bundle for an audit period
const bundle = await client.exportReceiptBundle({
  agentId: "agent-finance-prod",
  from: "2026-01-01T00:00:00Z",
  to: "2026-03-15T00:00:00Z",
  includeChainProof: true,  // includes genesis hash and chain verification data
});

// The bundle is a JSON file with all receipts and the data needed to
// verify the hash chain independently, without connecting to the live system.
await fs.writeFile("audit-bundle-q1-2026.json", JSON.stringify(bundle));

An auditor with the exported bundle can verify the chain integrity, confirm that decisions matched the stated policy, and identify any gaps in the record — without needing direct database access.


Getting Started

Receipt generation is built into the Authensor control plane. Every authorization decision automatically produces a receipt. You do not need to add any additional instrumentation. The receipt chain starts the moment you make your first API call.

Run npx create-authensor to scaffold a project with the receipt system preconfigured. The compliance guide that maps receipts to EU AI Act Article 12 requirements is in the docs at authensor.com/docs. The code is open source at github.com/authensor/authensor.