> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blnkfinance.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Transaction integrity and hashing

> How Blnk uses per-row hashes and an optional global hash chain to keep transaction history tamper-evident.

export const CtaCallout = props => {
  const {title, buttonLabel, href, trackingEvent, buttonTarget, rel = "noopener noreferrer", children} = props;
  const handleCtaClick = () => {
    if (typeof window === "undefined" || !trackingEvent) {
      return;
    }
    try {
      window.dispatchEvent(new CustomEvent("blnk:docs-cta", {
        detail: {
          name: trackingEvent,
          href
        }
      }));
    } catch {}
    try {
      window.posthog?.capture?.(trackingEvent, {
        href
      });
    } catch {}
    const gaPayload = {
      cta_href: href
    };
    try {
      window.gtag?.("event", trackingEvent, gaPayload);
    } catch {}
    try {
      window.dataLayer = window.dataLayer || [];
      window.dataLayer.push({
        event: trackingEvent,
        ...gaPayload
      });
    } catch {}
  };
  const isExternal = typeof href === "string" && (/^https?:\/\//i).test(href);
  const target = buttonTarget ?? (isExternal ? "_blank" : undefined);
  const linkRel = isExternal ? rel : undefined;
  return <section className="cta-callout not-prose relative my-8 w-full min-w-0 overflow-hidden rounded-xl border border-zinc-200 p-5 dark:border-white/10">
      <div className="cta-callout-noise" aria-hidden="true" />
      <div className="cta-callout-layout">
        {title ? <div className="cta-callout-title-row">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" width="14" height="14" className="cta-callout-icon shrink-0 text-zinc-800 dark:text-zinc-200" aria-hidden="true">
              <g fill="none" fillRule="nonzero">
                <path d="M28 0v28H0V0h28ZM14.691833333333335 27.134333333333334l-0.012833333333333334 0.0023333333333333335 -0.08283333333333333 0.04083333333333334 -0.023333333333333334 0.004666666666666667 -0.016333333333333335 -0.004666666666666667 -0.08283333333333333 -0.04083333333333334c-0.011666666666666667 -0.004666666666666667 -0.022166666666666668 -0.0011666666666666668 -0.028000000000000004 0.005833333333333334l-0.004666666666666667 0.011666666666666667 -0.019833333333333335 0.49933333333333335 0.005833333333333334 0.023333333333333334 0.011666666666666667 0.015166666666666667 0.12133333333333333 0.08633333333333333 0.0175 0.004666666666666667 0.014000000000000002 -0.004666666666666667 0.12133333333333333 -0.08633333333333333 0.014000000000000002 -0.018666666666666668 0.004666666666666667 -0.019833333333333335 -0.019833333333333335 -0.4981666666666667c-0.0023333333333333335 -0.011666666666666667 -0.0105 -0.019833333333333335 -0.019833333333333335 -0.021Zm0.3091666666666667 -0.13183333333333336 -0.015166666666666667 0.0023333333333333335 -0.21583333333333335 0.1085 -0.011666666666666667 0.011666666666666667 -0.0035000000000000005 0.012833333333333334 0.021 0.5016666666666667 0.005833333333333334 0.014000000000000002 0.009333333333333334 0.008166666666666668 0.23450000000000004 0.1085c0.014000000000000002 0.004666666666666667 0.026833333333333334 0 0.03383333333333334 -0.009333333333333334l0.004666666666666667 -0.016333333333333335 -0.03966666666666667 -0.7163333333333334c-0.0035000000000000005 -0.014000000000000002 -0.011666666666666667 -0.023333333333333334 -0.023333333333333334 -0.025666666666666667Zm-0.8341666666666667 0.0023333333333333335a0.026833333333333334 0.026833333333334334 0 0 0 -0.0315 0.007000000000000001l-0.007000000000000001 0.016333333333333335 -0.03966666666666667 0.7163333333333334c0 0.014000000000000002 0.008166666666666668 0.023333333333333334 0.019833333333333335 0.028000000000000004l0.0175 -0.0023333333333333335 0.23450000000000004 -0.1085 0.011666666666666667 -0.009333333333333334 0.004666666666666667 -0.012833333333333334 0.019833333333333335 -0.5016666666666667 -0.0035000000000000005 -0.014000000000000002 -0.011666666666666667 -0.011666666666666667 -0.21466666666666667 -0.10733333333333334Z" strokeWidth="1.1667" />
                <path fill="currentColor" d="M14 2.916666666666667A1.75 1.75 0 0 1 15.750000000000002 4.666666666666667v6.302333333333334L21.207666666666668 7.816666666666667a1.75 1.75 0 0 1 1.75 3.031L17.5 14l5.457666666666667 3.151166666666667a1.75 1.75 0 0 1 -1.75 3.031l-5.457666666666667 -3.1500000000000004V23.333333333333336a1.75 1.75 0 0 1 -3.5 0v-6.302333333333334L6.792333333333334 20.183333333333337a1.75 1.75 0 1 1 -1.75 -3.031L10.5 14 5.042333333333334 10.848833333333333a1.75 1.75 0 0 1 1.75 -3.031l5.457666666666667 3.1500000000000004V4.666666666666667A1.75 1.75 0 0 1 14 2.916666666666667Z" strokeWidth="1.1667" />
              </g>
            </svg>
            <p className="cta-callout-title min-w-0 font-semibold text-zinc-800 dark:text-zinc-200">
              {title}
            </p>
          </div> : null}
        <div className={`cta-callout-body text-sm leading-normal text-zinc-800 dark:text-zinc-200${title ? " cta-callout-body--indented" : ""}`}>
          {children}
        </div>
        <a href={href} target={target} rel={linkRel} onClick={handleCtaClick} data-docs-cta={trackingEvent || undefined} className="cta-callout-button inline-flex items-center justify-center gap-1 rounded-full bg-white px-3 py-1.5 text-sm font-semibold transition hover:bg-zinc-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white/50 dark:bg-white dark:hover:bg-zinc-200">
          {buttonLabel}
          <span className="cta-callout-button-arrow" aria-hidden="true">
            →
          </span>
        </a>
      </div>
    </section>;
};

Blnk makes your transaction history <Tooltip tip="You can detect if protected fields were changed after recording. Any change leaves a visible sign in the hash or chain.">tamper-evident</Tooltip> 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.

| Layer                 | What it protects                       | Where you see it                         | When it's on                  |
| :-------------------- | :------------------------------------- | :--------------------------------------- | :---------------------------- |
| **Per-row `hash`**    | A single transaction's core details    | Transaction API responses (`hash` field) | Always                        |
| **Global hash chain** | Your full transaction history in order | Verify with `blnk verify-chain`          | Optional, disabled by default |

<Info>
  The global hash chain is available in Blnk Core **0.15.0** and later. It is **disabled by default**.
</Info>

***

## 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 <Tooltip tip="You can detect if protected fields were changed after recording. Any change leaves a visible sign in the hash or chain.">tamper-evident</Tooltip>:

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.

<Frame caption="Global hash chain linking sealed transactions in order, where each seal depends on the previous chain head">
  <img src="https://mintcdn.com/blnk/9MJwGfwCYUdbRSsI/images/hash-chain.png?fit=max&auto=format&n=9MJwGfwCYUdbRSsI&q=85&s=c0287a73f8f37e4c884d3823e30f03bb" alt="Diagram showing transactions sealed into an ordered hash chain from genesis to chain head" width="3840" height="2400" data-path="images/hash-chain.png" />
</Frame>

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

<Steps>
  <Step title="Enable the hash chain">
    Set on the `server` service, then restart.

    <CodeGroup>
      ```bash blnk.env theme={"system"}
      BLNK_TRANSACTION_HASHCHAIN_ENABLED=true
      ```

      ```json blnk.json theme={"system"}
      {
        "transaction": {
          "hash_chain": {
            "enabled": true
          }
        }
      }
      ```
    </CodeGroup>

    See [Hash chain settings](/advanced/configuration/transactions#hash-chain-settings) for all options.
  </Step>

  <Step title="Wait for sealing">
    Existing transaction rows are sealed automatically. New ones seal after the trailing delay, `default: 30s`.
  </Step>

  <Step title="Verify the chain">
    To verify the chain, run in the same directory as your `docker-compose.yaml`:

    ```bash theme={"system"}
    docker compose run --rm server blnk verify-chain
    ```

    Exit code `0` means the chain is intact.

    ```text Success wrap theme={"system"}
    chain verified: 153 transactions sealed, head 18526b69d80e7c0b200da69cfc0f9a4e661a55c724b4f984ebf4ebd9b32e92f9 at seq 153
    ```

    <Note>
      `verify-chain` is a Core CLI command, not an HTTP API. The one-off container uses the same Postgres connection and config as your server.
    </Note>
  </Step>

  <Step title="Monitor sealing progress">
    Track backlog and sealing delay over time through [Monitoring in Blnk](/advanced/monitoring). When the hash chain is enabled, these gauges appear on the metrics endpoint:

    | Metric                   | Description                                     |
    | :----------------------- | :---------------------------------------------- |
    | `blnk_chain_backlog`     | Transactions not yet sealed into the hash chain |
    | `blnk_chain_head_seq`    | Sequence number of the hash-chain head          |
    | `blnk_chain_lag_seconds` | Seconds since the hash chain last advanced      |

    See [Metrics reference](/advanced/metrics-reference) for attribute details and other Core metrics.
  </Step>
</Steps>

***

## 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`.

<Note>
  The per-row hash uses `amount` (the display float), not `precise_amount`. Fields such as `description`, `status`, and `meta_data` are not included.
</Note>

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.

<Frame caption="Broken hash chain where a tampered or reordered transaction breaks verification at the first invalid link">
  <img src="https://mintcdn.com/blnk/9MJwGfwCYUdbRSsI/images/hash-chain-broken.png?fit=max&auto=format&n=9MJwGfwCYUdbRSsI&q=85&s=5909cc931f6de6b0b6f34761e9866eb5" alt="Diagram showing a hash chain with a broken link where verification fails" width="3840" height="2400" data-path="images/hash-chain-broken.png" />
</Frame>

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.

<Note>
  Fields such as `description` and `meta_data` are not integrity-protected by the chain. Changing them does not fail verification. Protected transaction fields are also guarded by the database immutability trigger.
</Note>

Chain seals are not returned in transaction API or [search](/search/typesense/introduction) 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.

| Result   | What it means                                                                              |
| :------- | :----------------------------------------------------------------------------------------- |
| **Pass** | The full transaction history is intact. Output states how many transactions are sealed.    |
| **Fail** | History was tampered with or broken. Output names the position where verification stopped. |

<Tabs>
  <Tab title="Pass">
    When verification passes, output looks like this:

    ```text Success wrap theme={"system"}
    chain verified: 153 transactions sealed, head 18526b69d80e7c0b200da69cfc0f9a4e661a55c724b4f984ebf4ebd9b32e92f9 at seq 153
    ```
  </Tab>

  <Tab title="Fail">
    Verification fails when one of these conditions is true:

    | Condition               | What happened                                                 |
    | :---------------------- | :------------------------------------------------------------ |
    | Protected field changed | A sealed transaction detail was altered after sealing         |
    | Chain link altered      | A seal in the ordered chain no longer matches its transaction |
    | Order disrupted         | Transactions are no longer in the expected sequence           |

    The output looks like this:

    ```text Failure wrap theme={"system"}
    chain BROKEN at seq 6 (txn txn_c1dfaf42-93c1-4371-8918-4d3d6ed2d2be): non-contiguous chain_seq: expected 5, got 6
    ```
  </Tab>
</Tabs>

***

## Related docs

<CardGroup cols={2}>
  <Card title="Hash chain settings" icon="gear" href="/advanced/configuration/transactions#hash-chain-settings">
    Full config and env vars.
  </Card>

  <Card title="Metrics reference" icon="chart-line" href="/advanced/metrics-reference">
    Chain backlog, head sequence, and lag gauges.
  </Card>

  <Card title="About transactions" icon="arrow-left-right" href="/transactions/introduction">
    Immutability and recording basics.
  </Card>
</CardGroup>

## Need help?

We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool.

To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx).

<CtaCallout title="Connect your ledger to Blnk Cloud" href="https://cloud.blnkfinance.com/auth/sign-up?utm_source=blnk_docs&utm_medium=documentation&utm_campaign=need-help" buttonLabel="Open Blnk Cloud" trackingEvent="clicked_cloud_signup">
  Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard.
</CtaCallout>
