Skip to Content
Audit & ComplianceHash Chain Verification

Hash Chain Verification

AiQarus uses SHA-256 hash chaining to create tamper-evident audit trails that can prove no records have been modified after the fact.

How Hash Chaining Works

Each trace in the audit trail contains a cryptographic hash that includes the previous trace’s hash:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ SHA-256 Hash Chain β”‚ β”‚ β”‚ β”‚ Trace 1 (Genesis) Trace 2 Trace 3 Trace 4 β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”‚ β”‚ β”‚ event: start β”‚ β”‚ event: think β”‚ β”‚ event: act β”‚ β”‚ ... β”‚ β”‚ β”‚ data: {...} β”‚ β”‚ data: {...} β”‚ β”‚ data: {...} β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ prev: null │───▢│ prev: Hash_1 │─▢│ prev: Hash_2 │─▢│ prev β”‚ β”‚ β”‚ hash: Hash_1 β”‚ β”‚ hash: Hash_2 β”‚ β”‚ hash: Hash_3 β”‚ β”‚ hash β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ └──────│ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Hash Computation

Each hash is computed from all trace data plus the previous hash:

CurrentHash = SHA256( OrgID + RunID + StepID + EventType + Payload (JSON) + SequenceNum + PreviousHash + Timestamp )

This creates an unbreakable chain where modifying any trace invalidates all subsequent hashes.

Verification Process

Step 1: Load Traces

Retrieve all traces for a run, ordered by sequence number:

query GetRunTraces($runId: ID!) { runTraces(runId: $runId, orderBy: SEQUENCE_ASC) { id eventType payload sequenceNum previousHash currentHash timestamp } }

Step 2: Verify Genesis

The first trace (genesis) must have:

  • sequenceNum: 1
  • previousHash: null

For each subsequent trace:

  1. Recompute the hash from trace data
  2. Compare computed hash to stored currentHash
  3. Verify previousHash matches prior trace’s currentHash
function verifyChain(traces) { for (let i = 0; i < traces.length; i++) { const trace = traces[i]; // Recompute hash const computed = sha256( trace.orgId + trace.runId + trace.stepId + trace.eventType + JSON.stringify(trace.payload) + trace.sequenceNum + trace.previousHash + trace.timestamp ); // Verify current hash if (computed !== trace.currentHash) { return { valid: false, error: `Hash mismatch at trace ${i}` }; } // Verify chain link (except genesis) if (i > 0 && trace.previousHash !== traces[i-1].currentHash) { return { valid: false, error: `Chain broken at trace ${i}` }; } } return { valid: true }; }

Verification via UI

Click Verify Chain on any run to validate:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Chain Verification β”‚ β”‚ β”‚ β”‚ βœ“ Genesis trace valid (no previous hash) β”‚ β”‚ βœ“ Trace 2: Hash verified, chain linked β”‚ β”‚ βœ“ Trace 3: Hash verified, chain linked β”‚ β”‚ βœ“ Trace 4: Hash verified, chain linked β”‚ β”‚ ... β”‚ β”‚ βœ“ Trace 47: Hash verified, chain linked β”‚ β”‚ β”‚ β”‚ ═══════════════════════════════════════════════════════════════ β”‚ β”‚ β”‚ β”‚ Result: CHAIN VALID β”‚ β”‚ Total traces: 47 β”‚ β”‚ Verification time: 0.3s β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Verification via API

query VerifyChain($runId: ID!) { verifyTraceChain(runId: $runId) { isValid totalTraces verifiedCount genesisHash finalHash errors { traceId sequenceNum expectedHash actualHash errorType } } }

What Tampering Looks Like

If someone modifies a trace:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Chain Verification - FAILED β”‚ β”‚ β”‚ β”‚ βœ“ Trace 1: Valid β”‚ β”‚ βœ“ Trace 2: Valid β”‚ β”‚ βœ— Trace 3: HASH MISMATCH β”‚ β”‚ Expected: sha256:a1b2c3d4e5f6... β”‚ β”‚ Actual: sha256:x9y8z7w6v5u4... β”‚ β”‚ β”‚ β”‚ βœ— Trace 4: CHAIN BROKEN β”‚ β”‚ Previous hash doesn't match trace 3 β”‚ β”‚ β”‚ β”‚ βœ— All subsequent traces invalid β”‚ β”‚ β”‚ β”‚ ═══════════════════════════════════════════════════════════════ β”‚ β”‚ β”‚ β”‚ Result: CHAIN INVALID - POSSIBLE TAMPERING DETECTED β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Exporting Verification Proofs

Generate cryptographic proofs for auditors:

{ "verification_report": { "run_id": "run_123", "verified_at": "2026-01-15T16:00:00Z", "verified_by": "system", "chain_status": "valid", "total_traces": 47, "genesis": { "trace_id": "trace_001", "hash": "sha256:abc123...", "timestamp": "2026-01-15T14:29:55Z" }, "final": { "trace_id": "trace_047", "hash": "sha256:xyz789...", "timestamp": "2026-01-15T14:35:22Z" }, "signature": "base64:verification_signature..." } }

Database Enforcement

The database enforces chain integrity:

-- Traces are INSERT-only (no UPDATE, no DELETE) CREATE TABLE traces ( id UUID PRIMARY KEY, -- ... fields ... current_hash TEXT NOT NULL, -- Prevent modifications CONSTRAINT no_update CHECK (true) -- Enforced at application level ); -- Trigger to prevent updates CREATE TRIGGER prevent_trace_update BEFORE UPDATE ON traces FOR EACH ROW EXECUTE FUNCTION raise_exception('Traces are immutable'); -- Trigger to prevent deletes CREATE TRIGGER prevent_trace_delete BEFORE DELETE ON traces FOR EACH ROW EXECUTE FUNCTION raise_exception('Traces are immutable');

Best Practices

Regular Verification

Schedule automated verification:

  • After each run completes
  • Daily batch verification
  • Before compliance audits

Secure Hash Storage

Consider additional protections:

  • Replicate hashes to separate system
  • Periodic hash snapshots
  • Third-party verification service

Incident Response

If verification fails:

  1. Isolate the affected run
  2. Investigate the discrepancy
  3. Check for system issues vs tampering
  4. Document findings
  5. Report to security team