Skip to content
Product

A ledger core that is exact, verifiable and safe to hand to an agent

LedgerForge records every movement of money as a balanced double entry in integer units, keeps the evidence to prove its history, and enforces spending limits on the keys your services and agents use. All of it is in the open-source core.

Exact money

Every transfer debits one balance and credits another by the same integer amount

Postings and balance counters use integer units. You send the amount with an explicit precision, the ledger rejects inputs it can't represent exactly, and split allocation conserves every unit.
Integer postings
precise_amount: 1250 at precision: 100 is 12.50, on both sides.
Precision is a multiplier
Not a count of decimal places. Keep one multiplier per currency and send it with each transaction.
Deterministic splits
Leftover attribution never depends on floating-point sums. Mixed or malformed distributions are rejected.
Large integers preserved
Decimal request tokens are kept exactly; clients must not decode amounts through floating point.
SourceUSD · precision 100
debit_balance0 → 1250
credit_balance0 → 0
balance-1250
1250 units
DestinationUSD · precision 100
debit_balance0 → 0
credit_balance0 → 1250
balance1250
precise_amount: 1250 at precision: 100 is 12.50 USD.total debits = total credits = 1250
InputResultWhy
precise_amount: 1250, precision: 1001250 units (12.50)Exact. Preferred for integrations.
amount: 1.005, precision: 100101 unitsCompatibility field, rounded to the nearest unit, ties away from zero.
amount: 0.001, precision: 100RejectedA positive amount that rounds to zero units.
precision: 9007199254740993RejectedThe token can't be decoded without changing its value.
Durable lifecycles

Holds, refunds and retries that keep their meaning across queues and workers

Accepted transactions are processed by workers. Read the stored status rather than assuming settlement, and correct postings with new transactions, never edits.
QUEUED
Accepted for worker processing. Not evidence of settlement.
SCHEDULED
Deferred until a scheduled execution time.
INFLIGHT
Funds reserved pending commit, void or expiry.
APPLIED
Posted to committed balances.
VOID
An inflight reservation was released.
REJECTED
Execution failed validation or another processing constraint.
POST /transactions (same reference)409 Conflict
{  "error": "reference has already been used",  "error_detail": {    "code": "TXN_DUPLICATE_REFERENCE",    "message": "reference has already been used"  }}

Inflight holds

PUT /transactions/inflight/:txID

Commit all or part of a hold, or void the remainder. Expiry and commit dates are handled by workers.

Linked refunds

POST /refund-transaction/:id

Reverses an APPLIED original for its exact units. A durable claim prevents duplicate reversals.

Previews

dry_run: true

Projects balance effects without posting, consuming the reference or sending a webhook.

POST /transactionsRequest
{  "source": "bln_e0f81b15-bd0a-43c5-9e84-7fd4394192e8",  "destination": "bln_df56a248-7d7e-4c0c-8fbf-a5215c87b603",  "currency": "USD",  "precise_amount": 1250,  "precision": 100,  "reference": "order-1042",  "description": "Order 1042",  "skip_queue": true,  "allow_overdraft": true,  "overdraft_limit": 12.5}
Response201 Created
{  "rate": 0,  "precise_amount": 1250,  "amount": 12.5,  "amount_string": "12.5",  "precision": 100,  "overdraft_limit": 12.5,  "transaction_id": "txn_c493a7e3-ac39-4456-90c3-ac43f00c5e0d",  "parent_transaction": "",  "source": "bln_e0f81b15-bd0a-43c5-9e84-7fd4394192e8",  "destination": "bln_df56a248-7d7e-4c0c-8fbf-a5215c87b603",  "reference": "order-1042",  "currency": "USD",  "description": "Order 1042",  "status": "APPLIED",  "hash": "9b7d7ad924e2e626055e3b33edcf194142e3fcd562de1807dd738d9afd7c1f87",  "allow_overdraft": true,  "inflight": false,  "skip_queue": true,  "atomic": false,  "created_at": "2026-10-10T05:54:45.691597464Z",  "effective_date": "2026-10-10T05:54:45.691597464Z",  "scheduled_for": "0001-01-01T00:00:00Z",  "inflight_expiry_date": "0001-01-01T00:00:00Z",  "inflight_commit_date": "0001-01-01T00:00:00Z",  "meta_data": {    "allow_overdraft": true  }}
A verifiable ledger

Check the books yourself, and keep evidence the database can't rewrite

Two read-only checks ship in the binary. Replay recomputes every balance from its postings. The audit chain seals transactions into versioned hashes and checkpoints you can export, sign and compare later.
Replay checker
ledgerforge verify reads one REPEATABLE READ snapshot and compares replayed postings with stored counters. It never repairs data.
Versioned hash chain
Canonical v3 hashes commit amounts, precision, status, references, overdraft caps and execution controls. Enable sealing in the server config.
Checkpoints and anchors
audit export prints checkpoint roots. Store them independently; audit verify --anchor reports any root the chain no longer matches.
Pending isn't tampering
Unsealed rows are reported as pending coverage, separately from tampering. Neither check proves a real-world payment happened; reconcile for that.
ledgerforgeCLI
$ ledgerforge verifyChecked 6 balances and 4 transactions: 0 issues$ ledgerforge audit verifyChain: 4 sealed, head ff10247395ae53c4580005ac9c6cfb6cc7f79cbf5aeed252850e619b82ea2821, 0 pending, 0 tampering findings$ ledgerforge audit export > checkpoints.json$ ledgerforge audit verify --anchor checkpoints.json --json{"verified":true,"last_sequence":4,"head_hash":"ff10247395ae53c4580005ac9c6cfb6cc7f79cbf5aeed252850e619b82ea2821","pending":0,"tampering":[]}

Hash chain (canonical v3, SHA-256)

  1. seq 1h1 = H(h0, txn 1)
  2. seq 2h2 = H(h1, txn 2)
  3. seq 3h3 = H(h2, txn 3)
  4. seq 4h4 = H(h3, txn 4)

Checkpoints

  • seq 1count 19f52ae4fe574506a5dbbc60fa563764579e97c3a818e6bb84f9ffc43ef53d081
  • seq 3count 33e859daaffd0aefeb952ad9d9f00f154d64f82f8e4d6ad0cbb3c80f5baca73ae
  • seq 4count 4ff10247395ae53c4580005ac9c6cfb6cc7f79cbf5aeed252850e619b82ea2821

External anchor

  1. 1audit export writes checkpoint roots as JSON
  2. 2Sign the exact bytes with a separate identity
  3. 3Store them outside the database administrators' reach
  4. 4audit verify --anchor compares them with the live chain
Agent guardrails

Give an agent its own key, a spending policy and someone else to approve large payments

Policies are enforced by the server on the same admission path for HTTP and identity-bound MCP requests. Usage is reserved in PostgreSQL before asynchronous work, so prompts and client-side checks aren't what stands between an agent and your balances.
Per-key spending policies
Currency and precision rules, per-transaction caps, UTC daily limits, approval thresholds and ledger and balance allowlists.
Shared delegated budgets
A child key inherits its parent's restrictions and draws on the same daily budget.
Independent approvals
The initiating key and its delegation family can't approve or reject its own hold.
MCP bound to a key
ledgerforge-mcp is read-only unless started with --allow-write, and carries the bound key's policy on every call.
PUT /policies/:api_key_idRequest
{  "rules": [    {      "currency": "USD",      "precision": 100,      "max_transaction": "5000",      "daily_limit": "10000",      "approval_above": "2000"    }  ],  "ledger_ids": [    "ldg_e04e431f-8a27-4b19-8744-eb1867ed7b23"  ],  "balance_ids": [    "bln_df56a248-7d7e-4c0c-8fbf-a5215c87b603",    "bln_173d086e-57cb-4690-9abe-1cf060fafb13"  ]}
Agent key submitsPOST /transactions or an MCP write tool
Server-side admissionCurrency and precision rule, ledger and balance allowlists, per-transaction cap and the shared UTC daily budget. Usage is reserved in PostgreSQL.
Within limits2000 or less: admitted without review. In the example, 1000 settles as APPLIED.
Above approval thresholdForced inflight hold with a pending approval. 3000 returns INFLIGHT.
Outside policyOver the cap, budget or allowlist: denied. 5001 is refused.
Initiator tries to approveDenied. The initiating key, its descendants and its siblings cannot approve or reject its hold.
Independent approver decidesA separate key with approvals:write calls approve (settles) or reject (releases). Retrying a decision does not settle twice.
A hardened API

Scoped keys, honest errors and webhooks your receivers can verify

Keys can only hand out what they already hold, revocation is checked against PostgreSQL rather than a cache, and outbound webhooks are signed with their own secret.
Constrained delegation
A non-master key creates keys only for its own owner, with scopes it already has and no longer expiry than its own.
Authoritative revocation
Every request checks key hash, scope, expiry and revocation in PostgreSQL. A stale cache can't restore a revoked key.
Signed webhooks
A dedicated signing secret, URL validation, optional private-destination blocking, no redirects and bounded responses.
Sanitised errors
Stable error codes and deliberate validation messages. SQL text and driver errors stay in operator logs.
Webhook delivery headersHMAC-SHA256
X-LedgerForge-Timestamp: <unix seconds>X-LedgerForge-Signature: hex(HMAC-SHA256(secret, timestamp + "." + raw_body))
Operational workflows

Everything else a production ledger needs

All of this is in the open-source repository. Search, metrics and tracing need their own services and are off unless you configure them.
  • Split and bulk transfers

    Several sources or destinations per transfer, and bulk requests with explicit atomic and asynchronous options.

  • Reconciliation

    Upload external records, define matching rules and run batch or instant reconciliation against ledger data.

  • Fund lineage

    Balances can track where funds came from, with lineage queries for balances and transactions.

  • Snapshots and history

    Take balance snapshots and read a balance as it stood at a point in time.

  • Identities

    Link people and organisations to balances, and tokenise sensitive identity fields.

  • Metadata and webhooks

    Attach your own metadata, receive signed events when it changes and register transaction hooks.

  • Search

    Optional Typesense indexes for searching ledger records. PostgreSQL stays the source of truth.

  • Metrics and tracing

    Prometheus metrics behind a bearer token, and optional OpenTelemetry export.

Deploy

Run it yourself, anywhere PostgreSQL and Redis run

Self-host the open-source core on your own infrastructure, or have the LedgerForge team operate it with LedgerForge Cloud. Read the deployment guide before exposing a service.

Try it locally

Builds your checkout, generates a random master key, runs migrations and binds the API to 127.0.0.1:5001.

Docker Compose quickstart
$ git clone https://github.com/devaccuracy/ledgerforge.git$ cd ledgerforge$ ./examples/quickstart/start.sh$ ./examples/quickstart/transfer.shsource: -1250 unitsdestination: 1250 unitstotal debits = total credits = 1250 unitsreference retry: balances unchanged

Run the published image

The compose file runs the server, workers, PostgreSQL and Redis. Pin ghcr.io/devaccuracy/ledgerforge:1.0.0 in production.

From a checkout
$ cp ledgerforge.example.json ledgerforge.json# Replace server.secret_key first, for example with: openssl rand -hex 32$ LEDGERFORGE_IMAGE=ghcr.io/devaccuracy/ledgerforge:1.0.0 docker compose up -d

Download a release binary

Archives for Linux, macOS and Windows on amd64 and arm64, checked against checksums.txt.

Release archive
$ curl -LO https://github.com/devaccuracy/ledgerforge/releases/download/v1.0.0/ledgerforge_v1.0.0_linux_amd64.tar.gz$ curl -LO https://github.com/devaccuracy/ledgerforge/releases/download/v1.0.0/checksums.txt$ sha256sum -c checksums.txt --ignore-missingledgerforge_v1.0.0_linux_amd64.tar.gz: OK$ tar -xzf ledgerforge_v1.0.0_linux_amd64.tar.gz$ ./ledgerforge_v1.0.0_linux_amd64/ledgerforge versionledgerforge 1.0.0 (commit 64c7aec, built 2026-10-10T05:53:57+00:00)

Build with Go

Install both commands from source with Go 1.26.9 or later.

go install
$ go install github.com/devaccuracy/ledgerforge/cmd/ledgerforge@v1.0.0$ go install github.com/devaccuracy/ledgerforge/cmd/ledgerforge-mcp@v1.0.0

Put a provable ledger under your money movement

Start with the open-source core, or let the LedgerForge team run it for you on dedicated infrastructure.