m2-market/ledger
2026-07-02 04:12:46 +02:00
..
src/m2_ledger wedge(T034): snapshot commits to registry audit/ via Forgejo contents API 2026-07-02 04:12:46 +02:00
tests wedge: adapt catalog clients to REAL memory-api (store/search, agent_id partitions) 2026-07-02 04:09:33 +02:00
Dockerfile T014: ledger Dockerfile + deploy notes 2026-07-02 03:14:18 +02:00
pyproject.toml wedge(T001-T003): monorepo skeleton, uv workspaces, ruff+pytest config 2026-07-02 02:19:47 +02:00
README.md T014: ledger Dockerfile + deploy notes 2026-07-02 03:14:18 +02:00

m2-ledger

Append-only credits ledger — transactions {ts, from, to, amount, reason, ref}, balances always derived by folding the tx log (never stored authoritatively). See specs/001-market-first-wedge/contracts/ledger-api.md (frozen v1 contract) and docs/config.md (env var / key reference) for the full spec.

Run locally

cd ledger
uv sync
LEDGER_DB_PATH=./ledger.db \
LEDGER_SERVICE_KEYS=svc_devkey \
LEDGER_ADMIN_KEYS=admin_devkey \
  uv run uvicorn m2_ledger.api:app --reload --port 8000

GET http://localhost:8000/health should return {"status":"healthy"}. All other endpoints require an X-API-Key header (service or admin class — see docs/config.md §4).

Deploy as a Coolify app

m2-ledger is the one sanctioned new service in the first wedge (constitution I); it deploys as a plain Coolify app on the coolify Docker network, not baked into any image.

  1. Build: point the Coolify app at this repo/subpath (ledger/) with ledger/Dockerfile — no build args, no secrets at build time (constitution VI: no secrets in repos or images).
  2. Network + alias: attach the app to the coolify network with alias m2-ledger, so callers use http://m2-ledger:8000 per the frozen contract's base URL.
  3. Env vars (inject at runtime via Coolify's environment panel — never commit real values, see docs/config.md §5):
    • LEDGER_DB_PATH=/data/ledger.db (already set as the image default; override only if the persistent volume is mounted elsewhere)
    • LEDGER_SERVICE_KEYS — comma-separated service-class keys (CLI/Store/indexer)
    • LEDGER_ADMIN_KEYS — comma-separated admin-class keys (grants, snapshots); keep this set small
    • LEDGER_PLATFORM_PCT (default 0.10, UNCONFIRMED) and LEDGER_STARTER_GRANT (default 100, UNCONFIRMED) — optional, override the code defaults
    • REGISTRY_REPO_URL + FORGEJO_TOKEN — required once /snapshot actually commits to m2/market-registry (see TODO below)
  4. Persistent volume: mount a Coolify persistent volume at /data (the image declares VOLUME /data) so ledger.db survives redeploys. This is the single source of truth for the tx log — never point it at ephemeral container storage.
  5. Health check: the image's HEALTHCHECK hits GET /health (no auth). Coolify should also be configured to poll the same endpoint for its own readiness gate.
  6. Canary-first (constitution VI): roll out to one environment before any fleet-wide dependency on m2-ledger — there is no fleet-wide blast for this service since it's a single shared instance, but verify /health and one /balance/{operator_id} call with a real service key before pointing the CLI's ledger_url at it in ~/.m2-market/config.toml.

Snapshot cron (TODO T034)

POST /snapshot (admin) currently computes audit/YYYY-MM-DD.json balances and returns the JSON body but does not commit it anywhere — see the TODO(T034) in src/m2_ledger/api.py. Once T034 lands (Forgejo commit via REGISTRY_REPO_URL + FORGEJO_TOKEN), add a daily Coolify scheduled task (or cron container) that calls POST /snapshot with an admin key once every 24h, per constitution V ("Daily balance snapshots MUST be committed to Forgejo for audit") and the contract's "admin, also daily cron" note. Not wired up yet.