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 walletsWhen 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
- PostgreSQL (16+ recommended) for transaction storage
- Go 1.24+ for building from source
- Docker for local development (compose includes Postgres, Prometheus, Jaeger, OTEL collector)
Quick reference
| Item | Value |
|---|---|
| Language | Go |
| Go Module | github.com/formancehq/ledger |
| API | HTTP REST (chi router) |
| Port | 3068 |
| Database | PostgreSQL (via uptrace/bun ORM) |
| API Versions | v1 (legacy), v2 (current), v3 (OpenAPI spec exists) |
| CLI Commands | serve, worker, buckets upgrade, version, docs |
| Event Bus | Watermill (NATS, Kafka, HTTP) |
| Observability | OpenTelemetry (traces + metrics), Prometheus, Jaeger |
| Repo | github.com/hanzoai/ledger |
| License | MIT |
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}/transactionsGet 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}/revertBulk 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 /_/metricsNumscript 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
}
}
)@worldis 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:
| Feature | Options | Default |
|---|---|---|
MOVES_HISTORY | ON, OFF | ON |
MOVES_HISTORY_POST_COMMIT_EFFECTIVE_VOLUMES | SYNC, DISABLED | SYNC |
HASH_LOGS | SYNC, ASYNC, DISABLED | SYNC |
ACCOUNT_METADATA_HISTORY | SYNC, DISABLED | SYNC |
TRANSACTION_METADATA_HISTORY | SYNC, DISABLED | SYNC |
Development
Build and Test
go build ./...
go test ./...
# With integration tests
go test -tags it ./...
# Full test with coverage
just testsLocal Development with Docker
docker compose up -d
# Ledger available at http://localhost:3068Docker 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 toolingConfiguration
Environment Variables
| Variable | Description | Default |
|---|---|---|
POSTGRES_URI | PostgreSQL connection string | required |
POSTGRES_MAX_OPEN_CONNS | Max open connections | -- |
POSTGRES_MAX_IDLE_CONNS | Max idle connections | -- |
POSTGRES_CONN_MAX_IDLE_TIME | Connection max idle time | -- |
AUTO_UPGRADE | Auto-upgrade schemas on start | false |
EXPERIMENTAL_FEATURES | Enable feature configurability | false |
BULK_PARALLEL | Bulk operation parallelism | 10 |
DEBUG | Enable debug mode | false |
CLI Flags (serve command)
| Flag | Description | Default |
|---|---|---|
--bind | API bind address | 0.0.0.0:3068 |
--auto-upgrade | Auto-upgrade schemas | false |
--numscript-cache-max-count | Numscript cache size | 1024 |
--bulk-max-size | Max bulk operation size | 100 |
--bulk-parallel | Bulk parallelism | 10 |
--default-page-size | Default pagination size | 15 |
--max-page-size | Max pagination size | 100 |
--worker | Enable embedded worker | false |
--schema-enforcement-mode | Schema enforcement (audit, strict) | audit |
Key Dependencies
| Package | Purpose |
|---|---|
github.com/formancehq/numscript | Numscript DSL parser/interpreter |
github.com/formancehq/go-libs/v4 | Shared library (auth, ORM, health, OTLP, publishing) |
github.com/go-chi/chi/v5 | HTTP router |
github.com/uptrace/bun | PostgreSQL ORM |
github.com/jackc/pgx/v5 | PostgreSQL driver |
github.com/ThreeDotsLabs/watermill | Event bus (NATS, Kafka, HTTP) |
github.com/spf13/cobra | CLI framework |
go.uber.org/fx | Dependency injection |
go.opentelemetry.io/otel | Observability |
github.com/antlr/antlr4 | ANTLR parser runtime (Numscript grammar) |
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| Connection refused on 3068 | Server not running | docker compose up -d or go run main.go serve |
| Schema not found | Ledger not created or not upgraded | Set AUTO_UPGRADE=true or run ledger buckets upgrade |
| Insufficient funds | Source account balance too low | Use @world as source or fund the account first |
| Invalid posting | Negative amount or invalid account address | Amounts must be >= 0, addresses match ^[a-zA-Z0-9_:]+$ |
| Duplicate reference | Transaction reference already used | Use a unique reference field or omit it |
| Numscript parse error | Invalid Numscript syntax | Check DSL syntax, ensure assets use ASSET/precision format |
Related Skills
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
Hanzo GoChimp3
GoChimp3 is a Go client library for the MailChimp API v3.0. It provides typed Go structs and methods for managing audiences (lists), members, campaigns, automations, e-commerce data, templates, seg...
Hanzo Numscript
Numscript is a domain-specific language for modeling complex financial transactions. It provides declarative syntax for multi-party fund transfers with percentage splits, ordered funding sources, o...