sqlstore

package
v1.801.450 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 5, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Overview

manager.go is the PER-TENANT selector over the treasury Store: it resolves each request to its OWN Hanzo Base (SQLite) file instead of a process-wide singleton, so one tenant's finance/ledger writes can NEVER appear in another tenant's reads. This is the storage side of the standing Hanzo rule — Postgres stays a supported option (Formance, one layer up), but every tenant's books run live on its own Base file.

TWO file classes, one opener:

  • the HOUSE ledger — the platform's OWN reserve fund (fund:reserve, revenue:*, payout:*): a SINGLE single-writer, overdraw-guarded file. It CANNOT be split per tenant (the reserve overdraw guard is one atomic balance), so it is the deployment's own book, in the system namespace.
  • a CUSTOMER ledger — one isolated file per tenant, in that org's namespace, opened on first use and cached.

The house fund is UNREACHABLE by naming a tenant, and no longer because a slug is reserved: the system namespace is a different KIND from every org namespace, so a tenant string cannot render to it however it is spelled. That is why the reserved-slug guard, the hash escape hatch and the third physical layout this file used to carry are gone — hanzoai/namespace already answers "which file does this entity's ledger live in", injectively, and answering it a second time here is how two answers start.

Package sqlstore is the Hanzo Base (HIP-0105 per-tenant SQLite) adapter for the ledger core: it implements ledger.Store + ledger.Tx over one SQLite file, and nothing more. It carries the storage concern the core deliberately does not — the SAME single-connection + WAL pattern every clients/* store uses (referrals, crm, prompts), so it is Base-compatible and drops into the unified binary unchanged.

It imports the core (ledger) and the one Hanzo SQLite driver — never cloud, zip, or IAM. When the core is lifted to hanzoai/finance this adapter travels with it as the default backend; the driver import is the only thing a different Base backend would swap.

MONEY IS EXACT AND BIG. Amounts are 18-decimal USD (1e-18, the EVM/uint256 unit) held as big.Int money.Amount — a value exceeds SQLite's 64-bit INTEGER past ~$9.20, so amount columns are TEXT (the signed 18-decimal integer string) and an account's balance is a maintained running total (treasury_accounts.balance), NOT a SQL SUM (you cannot SUM a decimal-string column, and a busy wallet's million usage postings must not be re-summed on every gate read). The running balance is updated inside the same transaction as each posting, so it can never drift from the journal.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager opens and caches one *Store per namespace. It is safe for concurrent use. Each distinct tenant maps to a distinct file; the mapping is namespace's, so it is injective (it never folds "acme" and "ACME" into one bucket — that would itself be a cross-tenant break) and can never traverse the path or reach the house fund.

func NewManager

func NewManager(dataDir string) (*Manager, error)

NewManager roots every ledger under dataDir.

func (*Manager) Close

func (m *Manager) Close() error

Close closes every open store (house + tenants). Idempotent; returns the first close error, if any.

func (*Manager) Get

func (m *Manager) Get(ns namespace.Namespace) (*Store, error)

Get resolves a tenant's OWN ledger. It takes the NAME and not the tenant string it was folded from: this package sits below cloud, so it cannot reach cloud's one door for turning a principal into a name, and a second fold here would be a second answer to which file a tenant's money is in. Handed the name, it cannot open another tenant's file, cannot reach the house fund (a different KIND), and cannot leave the data directory.

func (*Manager) House

func (m *Manager) House() (*Store, error)

House opens (once, then cached) the platform's reserve/house ledger — the single file the ledger-of-record binds to.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store is the SQLite-backed ledger persistence. ONE file holds the whole chart of accounts, journal and policy for a deployment (the platform's own books — not per-org, unlike a tenant product store). Serialized on a single connection so the engine's balance-guard read-then-write is atomic under load.

func Open

func Open(ns namespace.Namespace, subsystem, dir string) (*Store, error)

Open opens (creating + migrating) the ledger database subsystem names for ns, under dir.

The namespace is a PARAMETER because these stores do not share one: a customer's ledger belongs to that org, the house book belongs to the deployment. It is also what keys the file, so an opener cannot name one entity's ledger and unlock it with another's key — that pairing is made once, inside cek, from this one value.

func (*Store) Balance

func (s *Store) Balance(ctx context.Context, account string) (money.Amount, error)

func (*Store) BalancesWithPrefix

func (s *Store) BalancesWithPrefix(ctx context.Context, prefix string) (map[string]money.Amount, error)

func (*Store) Close

func (s *Store) Close() error

Close closes the underlying database.

func (*Store) Entries

func (s *Store) Entries(ctx context.Context, limit int) ([]ledger.JournalEntry, error)

func (*Store) Policy

func (s *Store) Policy(ctx context.Context) (ledger.SharePolicy, error)

func (*Store) SetPolicy

func (s *Store) SetPolicy(ctx context.Context, p ledger.SharePolicy) error

func (*Store) SumByKindSince

func (s *Store) SumByKindSince(ctx context.Context, kind string, since int64) (money.Amount, error)

SumByKindSince sums the entry amounts of ONE kind created at/after `since` (unix seconds) — a read-only aggregate over the indexed created_at, no schema change. Amounts are 18-decimal TEXT (SQLite INTEGER overflows past ~$9.20), so the fold is in Go. This is the usage-cap's period-spend source: sum kind "finance.usage" for the org store since the start of the UTC month, so a cap enforces on real ledger spend rather than a bounded, truncatable journal scan.

func (*Store) Tx

func (s *Store) Tx(ctx context.Context, fn func(ledger.Tx) error) error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL