Transactions

Transaction integrity and hashing

Check that recorded transaction history still matches what Blnk sealed.

Blnk makes your transaction history tamper-evidentYou can detect if protected fields were changed after recording. Any change leaves a visible sign in the hash or chain. through hashing.

Every transaction gets a fingerprint you can check on its own; optionally, your full transaction history can also be sealed into one ordered chain you verify on demand.

LayerWhat it protectsWhere you see itWhen it’s on
Per-row hashA single transaction’s core detailsTransaction API responses (hash field)Always
Global hash chainYour full transaction history in orderVerify with blnk verify-chainOptional, disabled by default

Why hashing matters

Transaction records are the audit trail of your ledger. If amount, balance, currency, status, or reference changes after recording, your balances and reports can no longer be trusted.

Blnk uses hashing to make that history tamper-evidentYou can detect if protected fields were changed after recording. Any change leaves a visible sign in the hash or chain.:

  1. Per-row hash - fingerprints important fields on each transaction.
  2. Global hash chain - links sealed transactions together in order.
  3. blnk verify-chain - replays the chain and reports the first broken link if protected data changed.

Diagram showing transactions sealed into an ordered hash chain from genesis to chain head

Global hash chain linking sealed transactions in order, where each seal depends on the previous chain head

Hashing does not replace database security, backups, access controls, or audit logs. It gives operators a way to verify that ledger history still matches what Blnk originally sealed.

Quick start

Enable the hash chain

Set on the server service, then restart.

BLNK_TRANSACTION_HASHCHAIN_ENABLED=true
{
  "transaction": {
    "hash_chain": {
      "enabled": true
    }
  }
}

See Hash chain settings for all options.

Wait for sealing

Existing transaction rows are sealed automatically. New ones seal after the trailing delay, default: 30s.

Verify the chain

To verify the chain, run in the same directory as your docker-compose.yaml:

docker compose run --rm server blnk verify-chain

Exit code 0 means the chain is intact.

chain verified: 153 transactions sealed, head 18526b69d80e7c0b200da69cfc0f9a4e661a55c724b4f984ebf4ebd9b32e92f9 at seq 153

Monitor sealing progress

Track backlog and sealing delay over time through Monitoring in Blnk. When the hash chain is enabled, these gauges appear on the metrics endpoint:

MetricDescription
blnk_chain_backlogTransactions not yet sealed into the hash chain
blnk_chain_head_seqSequence number of the hash-chain head
blnk_chain_lag_secondsSeconds since the hash chain last advanced

See Metrics reference for attribute details and other Core metrics.

Per-row hash

Every transaction in Blnk gets a hash - a SHA-256 fingerprint built from five fields. If any of them change after recording, the fingerprint no longer matches.

Blnk concatenates these values in order, hashes the string with SHA-256, and returns the hex digest as hash: amount, reference, currency, source, destination.

Compare the hash from when you first recorded a transaction with the hash on a later fetch to confirm those fingerprinted fields are unchanged. Built-in integrity verification is handled by the global hash chain and blnk verify-chain.

Global hash chain

When you enable the hash chain, transaction rows are linked in order. Each seal depends on the previous one, so your full history acts as one tamper-evident record.

Diagram showing a hash chain with a broken link where verification fails

Broken hash chain where a tampered or reordered transaction breaks verification at the first invalid link

This is stronger than checking rows independently because every sealed transaction depends on the previous chain head. If a transaction is changed, removed, or reordered, verification fails where the history breaks.

  1. Existing transactions seal automatically when you turn the chain on.
  2. New transactions seal after a short trailing delay so recent writes can finish.
  3. The chain includes all statuses: queued, inflight, applied, voided, rejected, and refund.

The chain checks these fields: transaction_id, source, destination, amount, precise_amount, currency, status, reference, created_at. Change any after sealing and verification fails.

Chain seals are not returned in transaction API or filter responses. Check integrity with blnk verify-chain, not by reading a response field.

Verification

blnk verify-chain tells you whether your full sealed history is intact or where it broke.

Exit code 0 means the chain passed; any other exit code means it failed.

ResultWhat it means
PassThe full transaction history is intact. Output states how many transactions are sealed.
FailHistory was tampered with or broken. Output names the position where verification stopped.

When verification passes, output looks like this:

chain verified: 153 transactions sealed, head 18526b69d80e7c0b200da69cfc0f9a4e661a55c724b4f984ebf4ebd9b32e92f9 at seq 153

Verification fails when one of these conditions is true:

ConditionWhat happened
Protected field changedA sealed transaction detail was altered after sealing
Chain link alteredA seal in the ordered chain no longer matches its transaction
Order disruptedTransactions are no longer in the expected sequence

The output looks like this:

chain BROKEN at seq 6 (txn txn_c1dfaf42-93c1-4371-8918-4d3d6ed2d2be): non-contiguous chain_seq: expected 5, got 6