Proof Chain
Ed25519-signed, hash-chained audit trail: how proofs are created, verified, and why tamper detection works.
Proof Chain
The Proof Chain is the immutable audit trail of every governance decision in the Vorion stack. Every ALLOW, DENY, ESCALATE, and DEGRADE decision produces a proof record that is hash-chained and cryptographically signed.
The proof chain is the complement to the Live Agent Anchor. The anchor is mutable (current state). The proof chain is immutable (complete history). Together they provide real-time governance with cryptographic accountability.
Implemented in @vorionsys/proof-plane.
How It Works
Each proof event contains three cryptographic primitives:
- SHA-256 content hash -- A hash of the event's contents
- SHA3-256 integrity anchor -- A second hash algorithm for defense in depth
- Ed25519 digital signature -- Cryptographic proof of authorship
And one chain linkage:
- Previous hash -- The SHA-256 hash of the preceding event
graph LR
subgraph "Event 1 (genesis)"
E1H[eventHash: sha256:abc...]
E1P[previousHash: null]
E1S[signature: Ed25519]
end
subgraph "Event 2"
E2H[eventHash: sha256:def...]
E2P[previousHash: sha256:abc...]
E2S[signature: Ed25519]
end
subgraph "Event 3"
E3H[eventHash: sha256:ghi...]
E3P[previousHash: sha256:def...]
E3S[signature: Ed25519]
end
E1H -->|referenced by| E2P
E2H -->|referenced by| E3P
The first event in a chain has previousHash: null. Every subsequent event
references the prior event's hash. Modifying any event invalidates every
event that follows it.
Creating a Proof
import { createProofService } from '@vorionsys/atsf-core';
const proofService = createProofService();
const proof = await proofService.create({
intent: {
parsedAction: 'write_external_api',
riskLevel: 'MEDIUM',
capabilities: ['write_data', 'external_network'],
},
decision: 'ALLOW',
outcome: 'SUCCESS',
agentId: 'data-sync-bot',
riskLevel: 'MEDIUM',
inputs: {
endpoint: 'https://partner.example.com/api/customers',
recordCount: 42,
},
outputs: {
statusCode: 200,
recordsUpdated: 42,
},
});
The proof service:
- Serializes the event with deterministic key ordering
- Computes the SHA-256 content hash
- Computes the SHA3-256 integrity anchor
- Retrieves the previous event's hash for chain linkage
- Signs the event with Ed25519
- Stores the complete proof record
Hash Computation
Hashing uses deterministic serialization to ensure the same event always produces the same hash, regardless of property insertion order.
// From packages/proof-plane/src/events/hash-chain.ts
// Fields included in the hash (excludes eventHash itself and recordedAt)
interface HashableEventData {
eventId: string;
eventType: string;
correlationId: string;
agentId?: string;
payload: ProofEventPayload;
previousHash: string | null;
occurredAt: string;
signedBy?: string;
signature?: string;
}
// Recursive key sorting for deterministic JSON
function sortObjectKeys(obj: unknown): unknown {
if (obj === null || typeof obj !== 'object') return obj;
if (Array.isArray(obj)) return obj.map(sortObjectKeys);
const sorted: Record<string, unknown> = {};
for (const key of Object.keys(obj).sort()) {
sorted[key] = sortObjectKeys((obj as Record<string, unknown>)[key]);
}
return sorted;
}
// Hash = SHA-256 of sorted, serialized event data
async function computeEventHash(event): Promise<string> {
const hashable = getHashableData(event);
const sorted = sortObjectKeys(hashable);
const serialized = JSON.stringify(sorted);
return sha256(serialized);
}
The dual-hash approach (SHA-256 primary + SHA3-256 anchor) provides defense in depth. If a vulnerability is found in one hash algorithm, the other still protects chain integrity.
Ed25519 Signatures
Every proof event is signed with Ed25519, providing:
- 128-bit security level
- 64-byte signatures (compact)
- Fast signing and verification
- Deterministic -- no random nonce, same input always produces same signature
Key Management
import { generateSigningKeyPair, createSigningService } from '@vorionsys/proof-plane';
// Generate a key pair for a service
const keyPair = await generateSigningKeyPair('proof-plane-prod');
// keyPair.publicKey -- share with verifiers
// keyPair.privateKey -- keep secret
// Create a signing service
const signingService = createSigningService({
serviceId: 'proof-plane-prod',
privateKey: keyPair.privateKey,
keyId: keyPair.keyId,
trustedKeys: [
{ publicKey: keyPair.publicKey, keyId: keyPair.keyId, owner: 'proof-plane-prod' },
],
});
Signing an Event
// Sign during proof creation
const signature = await signingService.sign(event);
// Returns base64-encoded Ed25519 signature
The signable data includes the event contents and the signedBy field, but
excludes the signature itself and computed fields like eventHash:
interface SignableEventData {
eventId: string;
eventType: string;
correlationId: string;
agentId?: string;
payload: ProofEventPayload;
previousHash: string | null;
occurredAt: string;
signedBy: string;
}
Verification
Chain verification checks three things for every event:
- Hash integrity -- The stored hash matches a recomputed hash of the contents
- Dual-hash integrity -- The SHA3-256 anchor also matches (if present)
- Chain linkage -- The
previousHashmatches the prior event'seventHash
import { verifyChainWithDetails } from '@vorionsys/proof-plane';
// Verify an entire chain
const result = await verifyChainWithDetails(events);
console.log(result);
// {
// valid: true,
// verifiedCount: 1247,
// totalEvents: 1247,
// firstEventId: 'evt-001',
// lastEventId: 'evt-1247',
// }
If any event is tampered with, verification reports exactly where the chain broke:
// Tampered chain
const result = await verifyChainWithDetails(tamperedEvents);
// {
// valid: false,
// verifiedCount: 842,
// totalEvents: 1247,
// brokenAtIndex: 842,
// brokenAtEventId: 'evt-843',
// error: 'Event evt-843 has invalid SHA-256 hash',
// }
Signature Verification
Signatures are verified independently of the hash chain:
const sigResult = await signingService.verify(event);
// {
// valid: true,
// signer: 'proof-plane-prod',
// verifiedAt: '2026-04-02T10:30:00Z',
// }
Batch verification checks all events at once:
import { verifyEventSignatures } from '@vorionsys/proof-plane';
const batchResult = await verifyEventSignatures(events, signingService);
// {
// totalEvents: 1247,
// validCount: 1247,
// invalidCount: 0,
// unsignedCount: 0,
// success: true,
// }
Why Tamper Detection Works
The proof chain is tamper-evident because of three interlocking properties:
1. Hash Chain Integrity
Modifying any event changes its content hash. Since the next event's
previousHash references the original hash, the chain breaks at the
modification point.
Original: E1(hash=abc) -> E2(prev=abc, hash=def) -> E3(prev=def)
Tampered: E1(hash=xyz) -> E2(prev=abc, hash=def) -> E3(prev=def)
^-- broken: prev=abc != xyz
To hide a modification, you would need to recompute every subsequent event's hash -- which requires the signing key for every event.
2. Ed25519 Signatures
Even if an attacker recomputes hashes, they cannot produce valid signatures
without the private signing key. The signature covers the event contents
including the previousHash, so rewriting the chain requires the key.
3. Dual-Hash Defense
SHA-256 and SHA3-256 use different algorithms. A collision attack against one would not affect the other. Both must validate for the chain to be considered intact.
Chain Per Agent
Each agent has its own proof chain. The genesis event (first action by that
agent) has previousHash: null. All subsequent events chain from there.
This means:
- Agent chains are independent -- one agent's chain cannot affect another's
- Verification can target a single agent's history
- Chain length scales with agent activity, not system activity
Proof Bridge
The Proof Bridge connects the local ATSF proof chain with the Cognigate cloud proof chain. Decisions made locally are forwarded to the centralized proof plane for cross-agent and cross-tenant auditability.
Local ATSF Engine -> Proof Bridge -> Cognigate Proof Plane
|
Optional: Polygon anchoring
For highest assurance, proofs can be anchored to the Polygon blockchain, providing cross-system trust verification. This is optional and configurable.
Data Lifecycle
| Stage | Duration | Storage | |-------|----------|---------| | Hot | 90 days | PostgreSQL, fast queries | | Warm | 2 years | TimescaleDB, compressed | | Cold | 7 years | S3/Blob, compliance archive |
Proofs are never deleted -- only moved to cheaper storage. The 7-year retention matches common regulatory requirements (SOC 2, GDPR accountability, EU AI Act).
Next Steps
- Intent to Proof Flow -- See proofs created in context
- Trust Signal Bus -- How proof outcomes propagate
- Live Agent Anchor -- The mutable state the proof chain audits
- Risk-Weighted Formulas -- The math behind trust deltas in proofs