Hanzo
Hanzo Skills Reference

Hanzo Ledger - Programmable Double-Entry Financial Ledger

Hanzo Ledger is a programmable double-entry financial ledger written in Go. It provides atomic multi-posting transactions, account-based modeling, and a scriptable DSL called Numscript for expressi...

Overview

Hanzo Ledger is a programmable double-entry financial ledger written in Go. It provides atomic multi-posting transactions, account-based modeling, and a scriptable DSL called Numscript for expressing complex money movements. Fork of Formance Ledger (Go module still github.com/formancehq/ledger).

Why Hanzo Ledger?

  • Double-Entry -- Every transaction balances. No money created or destroyed.
  • Multi-Posting -- Atomic transactions across unlimited accounts in a single operation
  • Numscript DSL -- Model complex splits, fees, and routing with a purpose-built language
  • Immutable Audit Trail -- Append-only ledger with full transaction history
  • Multi-Currency -- Native support for any asset type with arbitrary precision (big.Int)
  • Multi-Ledger -- Run multiple isolated ledgers from a single instance
  • Chart of Accounts -- Schema enforcement with variable segments and regex patterns
  • Replication -- Built-in data export pipelines with gRPC worker support

Architecture

hanzo/commerce    Storefront, catalog, orders
       |
hanzo/payments    Payment routing (50+ processors)
       |
hanzo/treasury    Ledger, reconciliation, wallets   <-- this
       |
lux/treasury      On-chain treasury, MPC/KMS wallets

When to use

  • Recording financial transactions with strict double-entry guarantees
  • Multi-party fee splits and revenue sharing
  • E-commerce order payments, refunds, and wallet balances
  • Internal transfer accounting between services
  • Multi-currency or multi-asset tracking (fiat, crypto, credits, points)
  • Audit-compliant financial record keeping

Hard requirements

  1. PostgreSQL (16+ recommended) for transaction storage
  2. Go 1.24+ for building from source
  3. Docker for local development (compose includes Postgres, Prometheus, Jaeger, OTEL collector)

Quick reference

ItemValue
LanguageGo
Go Modulegithub.com/formancehq/ledger
APIHTTP REST (chi router)
Port3068
DatabasePostgreSQL (via uptrace/bun ORM)
API Versionsv1 (legacy), v2 (current), v3 (OpenAPI spec exists)
CLI Commandsserve, worker, buckets upgrade, version, docs
Event BusWatermill (NATS, Kafka, HTTP)
ObservabilityOpenTelemetry (traces + metrics), Prometheus, Jaeger
Repogithub.com/hanzoai/ledger
LicenseMIT

API Reference

Create a Transaction

POST /v2/ledger/{name}/transactions
Content-Type: application/json

{
  "postings": [{
    "source": "world",
    "destination": "users:001",
    "amount": 10000,
    "asset": "USD/2"
  }]
}

Create Transaction with Numscript

POST /v2/ledger/{name}/transactions
Content-Type: application/json

{
  "script": {
    "plain": "send [USD/2 10000] (\n  source = @users:001\n  destination = {\n    90% to @merchants:042\n    10% to @platform:fees\n  }\n)"
  }
}

List Transactions

GET /v2/ledger/{name}/transactions

Get Account Balance

GET /v2/ledger/{name}/accounts/{address}

Set Account Metadata

POST /v2/ledger/{name}/accounts/{address}/metadata
Content-Type: application/json

{
  "role": "merchant",
  "tier": "gold"
}

Revert a Transaction

POST /v2/ledger/{name}/transactions/{id}/revert

Bulk Operations

POST /v2/ledger/{name}/bulk
Content-Type: application/json

[
  {"action": "CREATE_TRANSACTION", "data": {"postings": [...]}},
  {"action": "ADD_METADATA", "data": {"targetType": "ACCOUNT", "targetID": "users:001", "metadata": {...}}}
]

Health and Info

GET /_healthcheck
GET /_/info
GET /_/metrics

Numscript DSL

Numscript is a purpose-built language for expressing money movements.

// Simple transfer
send [USD/2 10000] (
  source = @users:001
  destination = @merchants:042
)

// Multi-party fee split
send [USD/2 10000] (
  source = @users:001
  destination = {
    90% to @merchants:042
    10% to {
      50% to @platform:fees
      50% to @platform:reserve
    }
  }
)
  • @world is a special unlimited source (money creation)
  • Assets use format CURRENCY/precision (e.g., USD/2 = cents)
  • Parsed via ANTLR4 grammar, executed by internal machine

Core Concepts

Accounts

Hierarchical addresses separated by colons: users:001, merchants:042:fees. The special account world is an unlimited source.

Postings

Each posting moves an amount of an asset from source to destination:

type Posting struct {
    Source      string   // source account address
    Destination string   // destination account address
    Amount      *big.Int // amount (arbitrary precision)
    Asset       string   // asset identifier (e.g., "USD/2")
}

Volumes

Every account tracks input/output volumes per asset. Balance = Input - Output.

Ledgers and Buckets

Multiple ledgers can share a single database (bucket). Each ledger has independent accounts, transactions, and metadata. Bucket names and ledger names must match ^[0-9a-zA-Z_-]{1,63}$.

Chart of Accounts

Optional schema enforcement. Define allowed account structures with fixed segments, variable segments ($variable), and regex patterns.

Features

Per-ledger configurable features:

FeatureOptionsDefault
MOVES_HISTORYON, OFFON
MOVES_HISTORY_POST_COMMIT_EFFECTIVE_VOLUMESSYNC, DISABLEDSYNC
HASH_LOGSSYNC, ASYNC, DISABLEDSYNC
ACCOUNT_METADATA_HISTORYSYNC, DISABLEDSYNC
TRANSACTION_METADATA_HISTORYSYNC, DISABLEDSYNC

Development

Build and Test

go build ./...
go test ./...

# With integration tests
go test -tags it ./...

# Full test with coverage
just tests

Local Development with Docker

docker compose up -d
# Ledger available at http://localhost:3068

Docker compose includes: PostgreSQL 16, Prometheus, Jaeger, OTEL collector, ledger server, and worker.

Task Runner (Justfile)

just              # list available commands
just lint         # golangci-lint
just tidy         # go mod tidy
just generate     # go generate
just tests        # full test suite with coverage
just openapi      # regenerate OpenAPI spec
just generate-client  # regenerate Go client SDK (requires SPEAKEASY_API_KEY)
just pre-commit   # tidy + generate + lint + openapi (run before commits)

Project Structure

ledger/
  cmd/              CLI commands (serve, worker, buckets, docs, version)
  internal/
    api/            HTTP API (v1, v2 routers, bulking)
    bus/            Event publishing (Watermill)
    controller/     Business logic (ledger + system controllers)
    machine/        Numscript execution engine
    queries/        Query building
    replication/    Data export pipelines (gRPC, drivers)
    storage/        PostgreSQL storage layer (bun ORM)
    tracing/        OpenTelemetry instrumentation
  pkg/
    accounts/       Account address validation
    assets/         Asset format validation
    client/         Generated Go client SDK (Speakeasy)
    events/         Event type definitions
    features/       Feature flag definitions
  openapi/          OpenAPI specs (v1, v2, v3)
  deployments/      Docker Compose + Pulumi configs
  examples/         Example setups (standalone, publisher-http, publisher-kafka)
  docs/             Generated documentation
  tools/            Build tooling

Configuration

Environment Variables

VariableDescriptionDefault
POSTGRES_URIPostgreSQL connection stringrequired
POSTGRES_MAX_OPEN_CONNSMax open connections--
POSTGRES_MAX_IDLE_CONNSMax idle connections--
POSTGRES_CONN_MAX_IDLE_TIMEConnection max idle time--
AUTO_UPGRADEAuto-upgrade schemas on startfalse
EXPERIMENTAL_FEATURESEnable feature configurabilityfalse
BULK_PARALLELBulk operation parallelism10
DEBUGEnable debug modefalse

CLI Flags (serve command)

FlagDescriptionDefault
--bindAPI bind address0.0.0.0:3068
--auto-upgradeAuto-upgrade schemasfalse
--numscript-cache-max-countNumscript cache size1024
--bulk-max-sizeMax bulk operation size100
--bulk-parallelBulk parallelism10
--default-page-sizeDefault pagination size15
--max-page-sizeMax pagination size100
--workerEnable embedded workerfalse
--schema-enforcement-modeSchema enforcement (audit, strict)audit

Key Dependencies

PackagePurpose
github.com/formancehq/numscriptNumscript DSL parser/interpreter
github.com/formancehq/go-libs/v4Shared library (auth, ORM, health, OTLP, publishing)
github.com/go-chi/chi/v5HTTP router
github.com/uptrace/bunPostgreSQL ORM
github.com/jackc/pgx/v5PostgreSQL driver
github.com/ThreeDotsLabs/watermillEvent bus (NATS, Kafka, HTTP)
github.com/spf13/cobraCLI framework
go.uber.org/fxDependency injection
go.opentelemetry.io/otelObservability
github.com/antlr/antlr4ANTLR parser runtime (Numscript grammar)

Troubleshooting

IssueCauseSolution
Connection refused on 3068Server not runningdocker compose up -d or go run main.go serve
Schema not foundLedger not created or not upgradedSet AUTO_UPGRADE=true or run ledger buckets upgrade
Insufficient fundsSource account balance too lowUse @world as source or fund the account first
Invalid postingNegative amount or invalid account addressAmounts must be >= 0, addresses match ^[a-zA-Z0-9_:]+$
Duplicate referenceTransaction reference already usedUse a unique reference field or omit it
Numscript parse errorInvalid Numscript syntaxCheck DSL syntax, ensure assets use ASSET/precision format
  • hanzo/hanzo-vault.md - Card tokenization (PCI-compliant, used alongside ledger for payment flows)
  • hanzo/hanzo-commerce-api.md - Commerce API (uses ledger for order accounting)
  • hanzo/hanzo-commerce.md - Commerce storefront (drives transactions into ledger)
  • hanzo/hanzo-database.md - PostgreSQL infrastructure (ledger's storage backend)

How is this guide?

Last updated on

On this page