Skip to Content
GuidesAudit Log

Audit Log

The audit log records every pipeline decision regardless of outcome. Unlike receipt storage which only records successful transactions, the audit log captures policy rejections, simulation failures, approval decisions, and execution outcomes — giving compliance teams a complete trail of agent activity.

Setup

import { createAgent } from '@txfence/core' import { createFileAuditLog } from '@txfence/audit' const auditLog = createFileAuditLog('./audit.jsonl') const agent = createAgent( config, adapters, rpcUrls, executor, undefined, undefined, undefined, // 5–7: capLockProvider, metadataVerifier, approvalProvider receiptStore, // 8: receiptStore auditLog, // 9: auditLog )

Every pipeline run automatically records to the audit log — no additional code required.

Backends

createMemoryAuditLog() — in-memory, for development and testing.

createFileAuditLog(path) — append-only NDJSON file. One entry per line, never rewrites. Production-ready for single-process deployments.

Querying

const rejected = await auditLog.query({ status: 'policy_rejected' }) const recentSwaps = await auditLog.query({ actionKind: 'swap', from: Date.now() - 86_400_000, to: Date.now(), }) const byChain = await auditLog.query({ chain: 'ethereum' })

AuditFilter fields: chain, from, to (ms timestamps), status, actionKind.

AuditEntry structure

type AuditEntry = { id: string timestamp: number action: Action policySnapshot: Policy // immutable deep clone at decision time evaluation: PolicyEvaluation simulation?: SimulationResult // populated when simulation ran approvalRequest?: ApprovalRequest approvalDecision?: ApprovalDecision outcome: AuditOutcome policyVersionId?: string // stable SHA-256 ID when using policy versioning intentId?: string // populated when entry is part of an intent intentStepId?: string }

AuditOutcome

type AuditOutcome = | { status: 'success'; txHash: string; confirmedAtBlock: number; gasUsed: string } | { status: 'policy_rejected'; reason: PolicyRejectionReason | undefined } | { status: 'simulation_failed' } | { status: 'approval_timeout' } | { status: 'execution_failed'; reason: string } | { status: 'dry_run'; stoppedAt: 'policy' | 'simulation' | 'approval' | 'execution' }

Policy snapshot immutability

clonePolicy() is called inside record() — the policy snapshot in each entry is immutable even if you mutate the policy object after recording. Historical entries always reflect the policy that was actually in effect.

Replay

Use replayAuditLog from @txfence/core to test a proposed policy against historical decisions:

import { replayAuditLog } from '@txfence/core' const result = await replayAuditLog(auditLog, proposedPolicy, { onlyChanged: true, actionKind: 'swap', }) console.log(result.summary.newlyRejected)

See the Replay & Backtesting guide for details.

No tamper evidence in the file backend. Use a write-once storage backend (S3 with object lock) for strict compliance requirements. For cryptographic tamper evidence, see Provenance Chains.

Last updated on