Tamper-Proof Audit Logs Without the Blockchain
The auditor asks: “How do we know these logs weren’t modified after the fact?”
The blockchain crowd says: decentralized consensus, immutable ledger, distributed trust.
The practical answer: hash chains and digital signatures. Same cryptographic guarantees. No tokens, no consensus mechanisms, no blockchain.
The Problem
Audit logs need three properties:
- Integrity: Entries can’t be modified without detection
- Ordering: The sequence of events is provable
- Attribution: You can prove when entries were created
Traditional logging gives you none of this. Someone with database access can update records. Timestamps can be backdated. Logs can be deleted and rewritten.
For compliance (SOC 2, HIPAA, financial regulations), you need to prove logs haven’t been tampered with. “Trust us, we didn’t change them” isn’t an audit-friendly answer.
Hash Chains: Integrity Through Linking
Each log entry includes a hash of the previous entry. Modify any entry, and all subsequent hashes break.
Entry 1: { data: "...", hash: SHA256(data + "genesis") }
Entry 2: { data: "...", hash: SHA256(data + hash1) }
Entry 3: { data: "...", hash: SHA256(data + hash2) }
To change Entry 1, you’d need to recalculate Entry 2’s hash. But that changes Entry 2, so you’d need to recalculate Entry 3. And so on, through every subsequent entry.
For a chain with 10,000 entries, tampering with entry #1 means recalculating 9,999 hashes. It’s not computationally impossible—it’s just detectable. Anyone can verify the chain by recomputing the hashes.
Digital Signatures: Attribution and Timing
Hashes prove integrity. Signatures prove authorship and timing.
Each entry gets signed with a private key. The signature includes a timestamp. The corresponding public key is published. Anyone can verify that:
- The entry was signed by the key holder
- The timestamp was part of what was signed
- The content hasn’t changed since signing
const receipt = {
data_hash: 'abc123...',
timestamp: 1709251200,
sequence_id: 42,
previous_hash: 'def456...',
new_hash: 'ghi789...',
signature: sign(privateKey, `${timestamp}.${new_hash}`)
}
An attacker can’t forge a signature without the private key. They can’t backdate a timestamp because it’s part of the signed payload.
Why Not Blockchain?
Blockchains add decentralized consensus. Multiple parties validate each block. This is useful when you don’t trust any single party.
But for audit logs:
- You’re the party generating logs
- Auditors verify using your public key
- You don’t need trustless consensus—you need cryptographic proof
Blockchain overhead:
- Transaction fees
- Confirmation delays
- Node infrastructure
- Smart contract complexity
Hash chain overhead:
- None of that
Same cryptographic guarantees. Simpler implementation. Lower cost.
The Witness Approach
Witness is a digital notary API. POST a hash, get back a signed receipt.
# Hash your data locally
DATA_HASH=$(echo -n '{"event":"user.deleted","user_id":"123"}' | sha256sum | cut -d' ' -f1)
# Get it witnessed
curl -X POST https://witness.solenoid.systems/v1/witness/sign \
-H "Authorization: Bearer sm_your_api_key" \
-d "{\"hash\": \"$DATA_HASH\"}"
Response:
{
"chain_id": "your_chain_abc",
"sequence_id": 1042,
"input_hash": "a1b2c3...",
"previous_hash": "d4e5f6...",
"new_hash": "g7h8i9...",
"timestamp": 1709251200,
"signature": "MEUCIQD..."
}
Store this receipt alongside your log entry. The signature proves when it was created. The chain proves ordering.
Verification
Anyone with your public key can verify receipts:
# Get the public key
curl https://witness.solenoid.systems/v1/witness/pubkey \
-H "Authorization: Bearer sm_your_api_key"
# Verify locally
openssl dgst -sha256 -verify pubkey.pem -signature receipt.sig receipt.data
No API call needed for verification. No dependency on Witness being online. The cryptographic proof stands alone.
Practical Integration
Async is fine. Witness calls don’t need to block your main flow. Log the event, queue a witnessing request, store the receipt when it arrives.
async function logAuditEvent(event: AuditEvent) {
// Write to your database immediately
const entry = await db.auditLog.create({ data: event })
// Queue witnessing in background
await queue.add('witness', {
entryId: entry.id,
hash: sha256(JSON.stringify(event))
})
}
// Worker processes queue
async function processWitness(job) {
const receipt = await witness.sign(job.hash)
await db.auditLog.update({
where: { id: job.entryId },
data: { witnessReceipt: receipt }
})
}
Batch for efficiency. If you’re logging thousands of events per hour, hash them in batches. Witness a Merkle root instead of individual entries.
const batch = events.map(e => sha256(JSON.stringify(e)))
const merkleRoot = computeMerkleRoot(batch)
const receipt = await witness.sign(merkleRoot)
// Store receipt + merkle tree for later verification
Keep receipts forever. Receipts are small (< 1KB). Store them. They’re your proof for auditors.
Use Cases
Financial compliance: Transaction logs that prove trades happened when claimed.
Healthcare (HIPAA): Access logs showing who viewed patient records, with tamper-evident timestamps.
Legal hold: Document chains proving when evidence was collected and that it hasn’t changed.
SaaS audit trails: Show enterprise customers their data access logs are cryptographically secured.
Supply chain: Provenance records showing chain of custody through manufacturing.
The Auditor Conversation
Before: “We store logs in Postgres with timestamps.” Auditor: “How do we know they weren’t modified?” You: “We have access controls.” Auditor: “Access controls can be bypassed. DBAs can modify records.”
After: “Each log entry is hashed and signed. Here’s the verification procedure.” Auditor: “Show me.” You: [Run verification script] Auditor: “Great, these receipts demonstrate integrity. Next topic.”
The receipts are evidence. The cryptography is the proof. The auditor doesn’t need to trust your processes—they verify the math.
Getting Started
Free tier: 10,000 API calls to evaluate. Scale tier ($149.99/mo): unlimited Witness usage for teams with serious compliance needs.
# Create a test witness
curl -X POST https://witness.solenoid.systems/v1/witness/sign \
-H "Authorization: Bearer sm_your_api_key" \
-d '{"hash": "test_hash_abc123"}'
Tamper-proof logs. No blockchain required.