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: 1previousHash: null
Step 3: Verify Each Link
For each subsequent trace:
- Recompute the hash from trace data
- Compare computed hash to stored
currentHash - Verify
previousHashmatches prior traceβscurrentHash
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:
- Isolate the affected run
- Investigate the discrepancy
- Check for system issues vs tampering
- Document findings
- Report to security team