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.