134 lines
6.9 KiB
Markdown
134 lines
6.9 KiB
Markdown
# Standalone General Ledger
|
|
|
|
GL's journal API and explorer require only GL's own PostgreSQL database.
|
|
Stellar, Kuknos, Wallet, and an OpenTelemetry collector are not startup or
|
|
posting dependencies. Blockchain references on journals are optional.
|
|
|
|
## Run independently
|
|
|
|
From the GL directory, set two runtime secrets and launch:
|
|
|
|
```bash
|
|
export DARANO_GL_DB_PASSWORD='<database password>'
|
|
export DARANO_GL_ADMIN_TOKEN='<administrative retry token>'
|
|
docker compose -f deployment/compose.standalone.yaml up --build -d --wait
|
|
```
|
|
|
|
The gRPC API is on `127.0.0.1:8600`; the read-only dashboard is on
|
|
`http://127.0.0.1:8601`. Images must be built/downloaded before an outage.
|
|
The runtime network is internal and has no external network access.
|
|
Settlement submission and telemetry export are explicitly disabled. The
|
|
settlement queue remains persisted and queryable; disabling its worker does
|
|
not confirm queued blockchain transactions.
|
|
|
|
The named `ledger-data` volume retains journals across container restarts and
|
|
ordinary `docker compose down`. Do not use `down -v` on a ledger containing
|
|
financial records.
|
|
|
|
This launch creates a **new, empty ledger**. Before treating it as the source
|
|
of truth, restore the existing authoritative GL database, or import approved,
|
|
balanced opening journals and replay all subsequent committed movements.
|
|
Running a second empty GL does not provide failover for the first.
|
|
|
|
## What remains available during a blockchain outage
|
|
|
|
- Atomic, balanced, append-only journal posting with idempotent retries.
|
|
- Journal lookup, history, lifecycle events, and balances rebuilt from entries.
|
|
- Read-only exploration of the same database.
|
|
- Durable storage of settlement requests already carrying signed XDR.
|
|
|
|
GL readiness reflects its database. Settlement backlog or submission failures
|
|
do not prevent ledger reads or writes. Database loss or failed journal commits
|
|
must prevent a caller from declaring an unrecorded financial effect successful.
|
|
|
|
## Financial authority versus settlement
|
|
|
|
The committed journal is the source of book balances. A blockchain hash is
|
|
settlement evidence, not the journal identity. Do not modify a journal to add
|
|
later confirmation; retain it as separate settlement/lifecycle evidence.
|
|
|
|
The persisted `GL_ONLY` operating mode makes GL the execution authority.
|
|
`PostInternalJournal` commits balanced internal effects synchronously, checks
|
|
that affected available/frozen accounts do not become negative, and serializes
|
|
concurrent spending inside PostgreSQL. An identical retry returns the original
|
|
journal even after the mode changes. In `GL_ONLY`, new effects must use the
|
|
authenticated internal posting API; ordinary ingestion rejects new journals
|
|
while still accepting identical retries. PostgreSQL enforces the funds checks
|
|
and rejects external blockchain movements at sealing. Business admission still
|
|
owns user authorization, asset eligibility, fees, and exact counterparty legs.
|
|
|
|
GL starts in `NORMAL`; mode is stored in its database and survives restarts.
|
|
Disabling the settlement worker in TOML is separate from choosing `GL_ONLY`.
|
|
Read the current revision, then use the authenticated `SetOperatingMode` API
|
|
to change mode. Each change retains actor, reason, revision, and time in an
|
|
immutable audit table. Stale revisions fail. Enabling `GL_ONLY` requires no
|
|
in-flight settlement claims and no negative user account balances. Drain the
|
|
Wallet outbox, pause and drain in-flight Wallet processing, and confirm the
|
|
ledger includes every committed effect before switching.
|
|
|
|
The existing AdminPanel controls GL mode through its GL journals →
|
|
GL operating mode page. Configure `GL_ADMIN_GRPC_ADDRESS` and
|
|
`GL_ADMIN_GRPC_TOKEN` (matching GL's `DARANO_GL_ADMIN_TOKEN`), and grant
|
|
`ledgerops.set_operating_mode` to designated operators. AdminPanel never
|
|
updates GL tables directly; mode changes go through GL's API.
|
|
|
|
Wallet remains the public entry point in both modes: the future GL-only route
|
|
must preserve the existing Wallet endpoints, response formats, and UI. In
|
|
normal operation, admitted requests continue through GL settlement to
|
|
Stellar/Kuknos. With GL-only routing implemented and enabled, Wallet will
|
|
instead submit internal effects directly to GL and serve GL balances through
|
|
those same Wallet endpoints. See [the future Wallet routing contract](AUTHORITY_API.md#future-wallet-routing-contract).
|
|
|
|
That Wallet routing is not implemented yet; keep `NORMAL` selected for the
|
|
current deployment. The existing Wallet configuration toggle still selects read-only behavior.
|
|
Wallet consumers must route internal execution to `PostInternalJournal`;
|
|
the AdminPanel change does not replace Wallet's blockchain-dependent workflows.
|
|
GL keeps its own readiness healthy but reports settlement as paused, so existing
|
|
normal-mode Wallet clients fail closed until their internal routes are integrated.
|
|
A committed GL journal is the financial
|
|
success result; Wallet state becomes an idempotent projection of it. Do not
|
|
also send the same effect through the normal Wallet outbox under a different
|
|
key: that would duplicate the movement. Existing normal-mode committed Wallet
|
|
effects continue using their durable outbox.
|
|
|
|
Internal transfers, freezes, releases, trades, and clearing movements are
|
|
represented by balanced entries with no blockchain reference. An operation
|
|
that actually sends money outside the internal books cannot become externally
|
|
settled during an outage. External blockchain account entries and settlement
|
|
submission are rejected in `GL_ONLY`; store any future external intent
|
|
separately and keep its settlement pending.
|
|
|
|
After blockchain recovery, reconcile journals against external evidence before
|
|
resuming normal settlement. Returning to `NORMAL` requires an operator to set
|
|
`reconciliation_confirmed=true`; this is an explicit assertion, not an automated
|
|
proof. While `GL_ONLY` is enabled, the worker leaves the existing settlement
|
|
queue untouched. Mode changes reject in-flight claims so a toggle cannot race
|
|
an already claimed submission. The existing queue requires signed XDR; it does not
|
|
turn arbitrary journals into blockchain transactions. Sequence numbers,
|
|
transaction expiry, and exhausted retries may require operator review and
|
|
reconstruction. Never blindly resubmit all journals or mark them settled.
|
|
|
|
## Verify and preserve the ledger
|
|
|
|
The existing write/read load test can exercise a **disposable** standalone
|
|
database without any blockchain endpoint:
|
|
|
|
```bash
|
|
go run ./cmd/gl-loadtest -duration 5s -concurrency 1 -wallets 10
|
|
```
|
|
|
|
This writes synthetic journals and must not run against an authoritative
|
|
financial database. Restart GL and confirm the same journal IDs and balances
|
|
remain queryable.
|
|
|
|
Keep PostgreSQL backups and WAL recovery independent of the blockchain
|
|
services, and periodically verify a restore. For example, create a logical
|
|
backup without deleting or changing ledger records:
|
|
|
|
```bash
|
|
docker compose -f deployment/compose.standalone.yaml exec -T postgres \
|
|
pg_dump -U gl -d gl_db -Fc > gl.dump
|
|
```
|
|
|
|
A named volume provides persistence on one host, not disaster recovery.
|