verify_ledger
Verifies the integrity of a hash-chained forensic audit ledger by recomputing every SHA-256 hash from genesis and reporting whether the chain remains intact. Ensures tamper-proof audit trail validation.
Instructions
Verify the integrity of the hash-chained forensic audit ledger. Recomputes every SHA-256 hash from genesis and reports whether the chain is intact. Classification: INFORMATIONAL — read-only, no side effects.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/mcp/tools/verify-ledger.ts:24-66 (handler)Tool handler function for 'verify_ledger'. Registers an MCP tool that calls engine.ledger.verifyChain() to recompute SHA-256 hashes from genesis and verify chain integrity. Returns detailed results including chainIntegrity, entriesVerified, chainHead, firstBrokenLink, breakDetail, and compliance evidence notes.
export function registerVerifyLedgerTool(server: McpServer, engine: GovernanceEngine): void { server.tool( 'verify_ledger', 'Verify the integrity of the hash-chained forensic audit ledger. Recomputes every SHA-256 hash from genesis and reports whether the chain is intact. Classification: INFORMATIONAL — read-only, no side effects.', {}, { title: 'Verify Ledger Integrity', readOnlyHint: true, idempotentHint: true, destructiveHint: false, openWorldHint: false }, async () => { const result = engine.ledger.verifyChain(); // Tool accountability tracking engine.telemetryService.emitToolCall('verify_ledger', `verify-${Date.now().toString(36)}`, 'INFORMATIONAL', true); return { content: [{ type: 'text' as const, text: JSON.stringify({ chainIntegrity: result.valid ? 'INTACT' : 'BROKEN', entriesVerified: result.entriesVerified, totalEntries: engine.ledger.size, chainHead: result.headHash, genesisHash: GENESIS_HASH, hashAlgorithm: HASH_ALGORITHM, chainVersion: CHAIN_VERSION, firstBrokenLink: result.firstBrokenLink, breakDetail: result.breakDetail ?? null, verifiedAt: result.verifiedAt.toISOString(), verificationDurationMs: result.verificationDurationMs, verificationMethod: { type: 'full-chain-recomputation', steps: [ 'Walk append-only ledger from entry 0 through N', 'For each entry: recompute SHA-256(previousHash || canonicalizedEntry)', 'Verify recomputed hash matches stored entryHash', 'Verify each entry.previousHash matches prior entry.entryHash', 'Verify final recomputed hash equals reported chainHead', ], }, complianceNote: result.valid ? 'Hash chain verified — tamper-evident audit trail is intact. Suitable for EU AI Act Article 12 (Record-Keeping) and NIST 800-53 AU (Audit and Accountability) compliance evidence.' : `CHAIN INTEGRITY FAILURE at entry ${result.firstBrokenLink}. Audit trail tamper evidence detected. Investigate immediately.`, }, null, 2) }], }; } ); } - src/mcp/server.ts:97-97 (registration)Tool registration entry in TOOL_REGISTRY array. Maps 'verify_ledger' to registerVerifyLedgerTool with visibility tier 'public'.
{ tier: 'public', register: registerVerifyLedgerTool, description: 'verify_ledger' }, - src/mcp/server.ts:44-44 (registration)Import of registerVerifyLedgerTool from the verify-ledger.ts module.
import { registerVerifyLedgerTool } from './tools/verify-ledger.js'; - src/core/audit/ledger.ts:370-470 (helper)Core verifyChain() method on ForensicLedger class. Incremental O(n) chain verification that recomputes SHA-256 hashes from the last checkpoint. Checks chain index, previous hash, and entry hash at each position. Returns IChainVerificationResult with tamper evidence details.
* Verify the integrity of the entire hash chain. * * Recomputes every hash from genesis and compares against stored hashes. * If any link is broken, returns the index and details of the first break. * * Time complexity: O(n) where n = log.length * This is an INFORMATIONAL operation — read-only, no side effects. */ verifyChain(): IChainVerificationResult { const startTime = Date.now(); if (this.log.length === 0) { return { valid: true, entriesVerified: 0, firstBrokenLink: -1, headHash: GENESIS_HASH, verifiedAt: utcNow(), verificationDurationMs: Date.now() - startTime, }; } // Incremental: start from the last verified checkpoint. // If nothing new since last verify, return instantly. const startIndex = this.verifyCheckpointIndex + 1; if (startIndex >= this.log.length) { return { valid: true, entriesVerified: this.log.length, firstBrokenLink: -1, headHash: this.verifyCheckpointHash, verifiedAt: utcNow(), verificationDurationMs: Date.now() - startTime, }; } let previousHash = startIndex === 0 ? GENESIS_HASH : this.verifyCheckpointHash; for (let i = startIndex; i < this.log.length; i++) { const entry = this.log[i]; if (entry.chainIndex !== i) { this.verifyCheckpointIndex = -1; this.verifyCheckpointHash = GENESIS_HASH; return { valid: false, entriesVerified: i, firstBrokenLink: i, headHash: previousHash, breakDetail: `Chain index mismatch at position ${i}: expected ${i}, found ${entry.chainIndex}`, verifiedAt: utcNow(), verificationDurationMs: Date.now() - startTime, }; } if (entry.previousHash !== previousHash) { this.verifyCheckpointIndex = -1; this.verifyCheckpointHash = GENESIS_HASH; return { valid: false, entriesVerified: i, firstBrokenLink: i, headHash: previousHash, breakDetail: `Previous hash mismatch at position ${i}: expected ${previousHash.substring(0, 16)}..., found ${(entry.previousHash ?? 'undefined').substring(0, 16)}...`, verifiedAt: utcNow(), verificationDurationMs: Date.now() - startTime, }; } const recomputed = computeEntryHash(previousHash, entry); if (entry.entryHash !== recomputed) { this.verifyCheckpointIndex = -1; this.verifyCheckpointHash = GENESIS_HASH; return { valid: false, entriesVerified: i, firstBrokenLink: i, headHash: previousHash, breakDetail: `Entry hash mismatch at position ${i} (id: ${entry.id}, op: ${entry.operation}): stored ${(entry.entryHash ?? 'undefined').substring(0, 16)}..., computed ${recomputed.substring(0, 16)}...`, verifiedAt: utcNow(), verificationDurationMs: Date.now() - startTime, }; } previousHash = recomputed; } // Advance checkpoint to cover all verified entries this.verifyCheckpointIndex = this.log.length - 1; this.verifyCheckpointHash = previousHash; return { valid: true, entriesVerified: this.log.length, firstBrokenLink: -1, headHash: previousHash, verifiedAt: utcNow(), verificationDurationMs: Date.now() - startTime, }; } - src/core/audit/ledger.ts:42-57 (schema)IChainVerificationResult interface defining the return type of verifyChain(): valid boolean, entriesVerified count, firstBrokenLink index, headHash, breakDetail, verifiedAt timestamp, verificationDurationMs.
export interface IChainVerificationResult { /** Whether the entire chain is intact (no tampering detected). */ valid: boolean; /** Total entries verified. */ entriesVerified: number; /** Index of the first broken link, or -1 if chain is valid. */ firstBrokenLink: number; /** Hash of the most recent entry (chain head). */ headHash: string; /** Details of the break if chain is invalid. */ breakDetail?: string; /** Verification timestamp. */ verifiedAt: Date; /** Duration of verification in milliseconds. */ verificationDurationMs: number; } - src/core/audit/telemetry.ts:200-201 (schema)Telemetry classification for verify_ledger: toolClass 'read', riskTier 'low', maiDefault 'INFORMATIONAL', category 'forensics'.
{ toolName: 'verify_ledger', toolClass: 'read', riskTier: 'low', maiDefault: 'INFORMATIONAL', requiresHumanApproval: false, category: 'forensics' }, ]; - src/shared/constants.ts:66-66 (helper)GENESIS_HASH constant used as the cryptographic anchor for the audit ledger chain.
export const GENESIS_HASH = 'a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a';