@darksol/agent-proofchain
v0.1.2
Published
Tamper-evident action journal for AI agents with hash-linked proofs.
Maintainers
Readme
@darksol/agent-proofchain
Tamper-evident action journals for AI agents — hash-linked, Ed25519-signed, replay-verifiable.
Agent systems perform high-impact actions — tool calls, API writes, blockchain transactions — but most logs are mutable and impossible to audit. @darksol/agent-proofchain produces append-only, hash-chained action journals where every entry links to the previous one via SHA-256, and the entire run can be cryptographically signed and replay-verified. Zero dependencies beyond Node.js built-ins.
Install
npm install @darksol/agent-proofchainQuick Start
1. Create a journal and append actions
import { createJournal } from "@darksol/agent-proofchain";
import { createToolCallAction } from "@darksol/agent-proofchain/adapters/tool-call";
const journal = createJournal();
journal.append(
createToolCallAction({
toolName: "search",
args: { q: "agent-proofchain" },
success: true
})
);
console.log(journal.entries()); // hash-linked entries2. Verify chain integrity
const result = journal.verify();
console.log(result.ok); // true
console.log(result.journalHash); // SHA-256 over the full chain3. Sign a run manifest with Ed25519
import { createEd25519KeyPair, signManifest, verifyManifestSignature } from "@darksol/agent-proofchain";
const keys = createEd25519KeyPair();
const manifest = signManifest(
{
runId: "run-123",
agentId: "agent-alpha",
startedAt: new Date().toISOString(),
journalHash: journal.journalHash()!
},
keys.secretKey,
keys.publicKey
);
console.log(verifyManifestSignature(manifest)); // true4. Replay-verify exported entries
import { replayVerify } from "@darksol/agent-proofchain";
const exported = journal.export();
const replay = replayVerify(exported.entries);
if (!replay.ok) {
console.error("tamper detected:", replay.invalidIndex, replay.error);
}5. Log blockchain transactions
import { createBlockchainTxAction } from "@darksol/agent-proofchain/adapters/blockchain-tx";
journal.append(
createBlockchainTxAction({
chainId: 1,
txHash: "0xabc...",
from: "0xfrom",
to: "0xto",
value: "1000000000000000000",
status: "confirmed"
})
);Hash-Linked Journal
Every journal entry contains the hash of the previous entry, forming an append-only chain. Tamper with any entry and the chain breaks.
Entry 0: prevHash=null → hash=sha256(payload₀)
Entry 1: prevHash=hash₀ → hash=sha256(payload₁)
Entry 2: prevHash=hash₁ → hash=sha256(payload₂)Each payload is canonicalized to deterministic JSON before hashing — identical actions always produce identical hashes regardless of key ordering.
import { canonicalize, hashJson } from "@darksol/agent-proofchain";
// Deterministic canonical JSON
canonicalize({ b: 2, a: 1 }); // '{"a":1,"b":2}'
// SHA-256 over canonical JSON
hashJson({ b: 2, a: 1 }); // consistent hex digestPolicy Snapshots
Bind journal entries to a declared policy state. If an agent's rules change mid-run, verification catches it.
const journal = createJournal({
policySnapshot: {
id: "policy-1",
version: "2026-03-01",
capturedAt: new Date().toISOString(),
rules: { allowTools: ["search", "fetch"], maxCost: 100 }
}
});
// Every appended entry gets the policy hash automatically
journal.append(createToolCallAction({ toolName: "search", args: {}, success: true }));
// Verify policy consistency across the chain
const result = journal.verify({ requireConsistentPolicyHash: true });
console.log(result.ok); // true — all entries share the same policy hashYou can also check against a specific expected policy hash:
journal.verify({ expectedPolicyHash: "abc123..." });Ed25519 Manifest Signing
Sign a run manifest that binds agent identity, run metadata, and the journal hash into a single verifiable artifact.
import {
createEd25519KeyPair,
signManifest,
verifyManifestSignature,
manifestSigningPayload
} from "@darksol/agent-proofchain";
const keys = createEd25519KeyPair();
const manifest = signManifest(
{
runId: "run-456",
agentId: "agent-beta",
startedAt: "2026-03-01T00:00:00Z",
endedAt: "2026-03-01T00:05:00Z",
journalHash: journal.journalHash()!,
policyHash: journal.policySnapshot()?.hash,
metadata: { model: "gpt-4o", tokens: 1200 }
},
keys.secretKey,
keys.publicKey
);
// Verify the signature
verifyManifestSignature(manifest); // true
// Inspect the signing payload
manifestSigningPayload(manifest); // canonical JSON string that was signedAction Adapters
Type-safe factories for common agent action types. Each adapter validates input and produces a well-formed ActionRecord.
Tool Call Adapter
import { createToolCallAction } from "@darksol/agent-proofchain/adapters/tool-call";
createToolCallAction({
toolName: "web-search",
args: { query: "solana price" },
result: { price: 142.50 },
success: true,
actor: "agent-alpha",
timestamp: new Date().toISOString() // optional, defaults to now
});
// → { type: "tool.call", timestamp, data: { toolName, args, result, success } }Blockchain Transaction Adapter
import { createBlockchainTxAction } from "@darksol/agent-proofchain/adapters/blockchain-tx";
createBlockchainTxAction({
chainId: 8453, // Base
txHash: "0xdef...",
from: "0xagent",
to: "0xcontract",
value: "0",
status: "confirmed", // "pending" | "confirmed" | "failed"
actor: "agent-alpha"
});
// → { type: "blockchain.tx", timestamp, data: { chainId, txHash, from, to, value, status } }Import / Export
Serialize a journal for storage or transport, then reimport with automatic chain verification.
// Export with optional manifest
const exported = journal.export(manifest);
// Store it (JSON-serializable)
const json = JSON.stringify(exported);
// Reimport — chain integrity verified by default
const journal2 = createJournal();
journal2.import(JSON.parse(json));
// Skip verification on import if you've already validated
journal2.import(JSON.parse(json), { skipVerify: true });
// Pass custom verification options
journal2.import(JSON.parse(json), {
verifyOptions: { requireConsistentPolicyHash: true }
});Runtime Validation
Every type has a corresponding assertion function that validates structure at runtime. These are used internally and exported for your own validation needs.
import {
assertActionRecord,
assertJournalEntry,
assertPolicySnapshot,
assertRunManifest,
assertExportedJournal
} from "@darksol/agent-proofchain";
// Throws ValidationError with descriptive message on invalid input
assertActionRecord(untrustedData);
assertRunManifest(untrustedManifest);Verification Options
| Option | Type | Default | Description |
|---|---|---|---|
| requireConsistentPolicyHash | boolean | false | Fail verification if entries were produced under different policy snapshots |
| expectedPolicyHash | string | — | Fail if any entry's policy hash doesn't match this exact value |
Architecture
Action → Canonical JSON → SHA-256 → Append with prevHash → Chain
↓
Export → Sign Manifest (Ed25519)
↓
Replay-Verify → ✅ or 🚫 tamper detected- Normalize action payload into deterministic canonical JSON
- Hash payload + linkage metadata with SHA-256
- Append entry with
prevHashlinking to previous entry - Export immutable sequence for storage/transport
- Replay-verify the full chain with optional policy constraints
- Sign run manifest binding journal hash to agent identity
Security Notes
- This library helps detect tampering — it does not prevent unauthorized storage-layer mutation
- Protect Ed25519 private keys outside application code (KMS / HSM / secret manager)
- Verification guarantees integrity of what was recorded, not truth of external claims
- Zero runtime dependencies — only Node.js built-in
cryptomodule
Development
npm ci
npm run build
npm run test
npm run lint
npm run typecheckDual CJS/ESM build. Subpath exports for adapters:
@darksol/agent-proofchain— core journal, crypto, validation@darksol/agent-proofchain/adapters/tool-call— tool call action factory@darksol/agent-proofchain/adapters/blockchain-tx— blockchain tx action factory
License
MIT
Built with teeth. 🌑
