@txfence/provenance
Transaction-level provenance chains with cryptographic integrity. Each record includes the hash of the previous record — modifying any entry invalidates all subsequent records.
Installation
npm install @txfence/provenancecreateMemoryProvenanceChain
For testing:
import { createMemoryProvenanceChain } from '@txfence/provenance'
const chain = createMemoryProvenanceChain()createFileProvenanceChain
JSONL file backend with atomic appends:
import { createFileProvenanceChain } from '@txfence/provenance'
const chain = createFileProvenanceChain('./provenance.jsonl')Appending records
chain.append() takes a ProvenanceRecordInput — everything in ProvenanceRecord except id, previousHash, and entryHash (those are computed for you).
await chain.append({
timestamp: Date.now(),
agentId: 'agent-0x123',
policyVersionId: 'abc123...',
action,
simulationResult, // optional
approvalDecision: 'not_required', // 'approved' | 'rejected' | 'not_required'
submittedAtBlock: 21000000, // plain number, not bigint
outcome: { // ProvenanceOutcome discriminated union
status: 'success',
txHash: '0x...',
confirmedAtBlock: 21000001,
gasUsed: '21000',
},
receipt, // optional
})Verifying integrity
const result = await chain.verify()
// result.valid: boolean
// result.entryCount: number
// result.firstHash / result.lastHash / result.merkleRoot: string
// result.verifiedAt: number
// result.violations: ChainViolation[]
// each: { entryId, entryHash, violation: 'hash_mismatch'|'chain_broken'|'invalid_hash', details }Merkle proofs
const root = await chain.getMerkleRoot() // async
const proof = await chain.generateProof(entryHash) // async — returns MerkleProof | null
const valid = chain.verifyProof(proof) // synchronous — no awaitCompact proof that a specific record exists without revealing others.
Wiring into the pipeline
Pass as the optional provenanceChain parameter (15th) to runPipeline or createAgent. Records all 6 outcome types automatically.
CLI
txfence provenance verify --chain ./provenance.jsonl
txfence provenance proof --chain ./provenance.jsonl --hash <entryHash>Exit 0 when valid, 1 when invalid — CI-friendly.
Low-level utilities
If you’re building custom storage or verification on top of provenance records, the underlying primitives are exported directly:
import {
computeEntryHash,
GENESIS_HASH,
buildMerkleTree,
getMerkleRoot,
generateMerkleProof,
verifyMerkleProof,
} from '@txfence/provenance'
// Hash chaining
const hash = computeEntryHash(previousHash, record) // SHA-256 of canonical JSON
console.log(GENESIS_HASH) // '0'.repeat(64) — the first link
// Merkle tree
const tree = buildMerkleTree(leafHashes) // standard binary tree, odd-leaf duplication
const root = getMerkleRoot(leafHashes) // commits to all entries
const proof = generateMerkleProof(leafHashes, targetHash) // O(log n) inclusion proof
const valid = verifyMerkleProof(proof) // synchronous booleanCanonical JSON sorts keys and stringifies bigints so the hash is deterministic across language implementations.