sqlstore

package
v1.801.307 Latest Latest
Warning

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

Go to latest
Published: Jul 28, 2026 License: Apache-2.0 Imports: 15 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 one fixed file — the pre-existing {DataDir}/treasury.db, kept verbatim so live reserve capital is never orphaned by a path change.
  • a CUSTOMER ledger — one isolated file per tenant at {DataDir}/finance/{slug}.db, opened on first use and cached.

It CONSUMES the treasury's canonical Base opener (Open, over github.com/hanzoai/ sqlite — the ONE Hanzo Base driver) rather than importing the ledger's per-tenant opener: that opener is a test-only helper deliberately kept unexported because it registers modernc's database/sql "sqlite" driver, which would COLLIDE with the hanzoai/sqlite registration this store already carries. the ledger's own contract is "production callers supply their own *bun.DB to New" — which Open does — so the per-tenant file selection lives HERE, over Open, and the slug guard matches the ledger's per-tenant guard exactly.

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

View Source
const HouseSlug = "house"

HouseSlug names the platform's OWN reserve/house ledger — the single (single-writer, overdraw-guarded) book behind every backed payout. It is RESERVED: tenantSlug routes a literal "house" tenant to a hashed slug, so a caller can never open the house fund by naming its org "house".

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 tenant. It is safe for concurrent use. Each distinct tenant maps to a distinct file; the mapping 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 collide with the reserved house file.

func NewManager

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

NewManager roots the per-tenant treasury stores under dataDir: the house ledger at {dataDir}/treasury.db (preserved) and customer ledgers under {dataDir}/finance/. The finance dir (and thus dataDir) is created if absent.

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(tenant string) (*Store, error)

Get resolves the caller's OWN per-tenant store from the validated tenant key. The tenant is turned into a safe, injective, path-guarded slug FIRST, so a caller can only ever open its own file — never another tenant's, never the house fund, never a path outside the finance dir.

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(path string) (*Store, error)

Open opens (creating + migrating) the ledger database at path.

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.Entry, error)

func (*Store) Policy

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

func (*Store) SetPolicy

func (s *Store) SetPolicy(ctx context.Context, p ledger.Policy) 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