walletflow· 04 console
Chapter 04 · The operating console

One cockpit, every movement.

The console at /ops is the operating engine for the wallet core: every Fincra flow that touches a wallet or the ledger — pay-in, transfer, payout, FX, OTC, stablecoin, GPS, rolling reserve, corrections, treasury funding — executed from one server-rendered UI, through the exact same idempotent use cases the API calls. Nothing in it bypasses the ledger's guards.

mount/ops (in-process)
authtoken · Bearer · Google
stackhono/jsx · htmx 4
commands17 · all idempotent
Fig. 1 · the console, in miniature

Console map

A working replica of the /ops navigation. Click a section to see what it shows, what an operator can do there, and which use cases it writes through.

walletflow · ops
/ops

04.1

What it is

Not a separate app. The console is mounted inside the walletflow service itself, so it calls CoreWalletService, CoaService and ReportingService in-process — the same code path as the public API, with the same validation, flags and idempotency.

Pages are server-rendered hono/jsx; htmx 4 submits forms and swaps result fragments; styling is a Tailwind v4 design system built at deploy time — a dark terminal default with a light "ledger paper" theme, toggled from the user menu. The assets are vendored and served same-origin — the console works with no CDN and no client framework. The seventeen command forms are not hand-written: each is generated from src/application/commands/registry.ts, the same declarative entry that mounts the JSON API route, so a new operation is one service method plus one registry entry.

Every money-moving form carries a hidden, auto-generated idempotency key. Submitting twice replays the first result instead of double-posting; after each success the key rotates so the next submission is a fresh command. Server-side validation errors come back as inline field errors; ledger rejections (insufficient funds, currency unknown, flag denied) render as red result cards with the exact reason.

Same guards as production traffic

The console cannot overdraw a wallet or skip a gate: commands run the same Numscript templates, the same fail_closed flag checks, and the same (provider, reference) webhook dedupe as API traffic. An operator mistake degrades into a readable error, not a broken book.

04.2

Operating it

Sign in at /ops/login — operator token or Google — and work the loop: check the overview, keep the four gates green, clear manual review, watch the outbox.

Getting in

The console accepts three kinds of session. The operator token: the login form exchanges AUTH_TOKEN for an HttpOnly cookie (12 h), and scripts can send the same token as a Bearer header instead. Or Sign in with Google (better-auth), active when GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET are set: the account's email must be on the allowlist — seeded by OPS_ALLOWED_EMAILS / OPS_ADMIN_EMAILS, overridden by the lists saved on /ops/settings — and is re-checked on every request, so tightening the list locks existing sessions out within seconds. Admins may view and modify Controls and Settings; operators get everything else; token and Bearer sessions are always admin. Empty allowlists are open in dev but fail closed in production. The console is fully open only when neither AUTH_TOKEN nor Google OAuth is configured (local dev); the REST surface stays Bearer-gated either way.

# local
bun run dev                  # migrate + build assets + serve
open http://localhost:3000/ops

# first boot of a fresh environment (Ledger page has buttons for both)
POST /ops/ledger/rebuild-templates    # load the 18 Numscript templates
POST /ops/ledger/rebuild-coa          # seed COA + GL mappings

The operating loop

Overview first: Formance health, posted/failed counts, outbox pending age, drift and template-mismatch counters. Then Reconciliation — the four gates from chapter 03 run live on the page, with ALM views beneath. Then Payouts: anything in manual_review is yours; anything long-reserved deserves a look. Every result card echoes status, reference, walletflow tx id, Formance tx ids, and the post-command balances — read it before moving on.

04.3

Playbooks

The handful of situations operators actually face, and the console path through each.

A payout is stuck in manual review

The saga exhausted its requeries and parked the hold. Verify the real provider state out-of-band, then expand the hold's row on Payouts: the prefilled Settle (provider paid) or Release (provider failed) form closes it. Money is never moved on a guess — that rule survives the UI.

A reconciliation gate is red

Holds drift → compare reserved rows against ledger hold balances in the gate detail. Wallet drift → Rebuild statement read model first (projection lag is the common cause), re-run, and only then consider a Correction — credit or debit with a mandatory reason and an optional bi-temporal effectiveAt.

An offramp or GPS payout fails with INSUFFICIENT_FUND

By design: the source bank or pool isn't funded. Record the treasury wire with Bank funding (or GPS pool top-up), confirm the balance on Overview, and re-run the original command.

Freeze a merchant — or everything

Controls → Set feature flag: disable core_wallet_enabled for a merchant, currency or provider scope. With CORE_WALLET_FLAG_MODE=fail_closed only enabled scopes transact. Saga completions still record by default — set CORE_WALLET_COMPLETION_GATING=gate for a true hard freeze (chapter 03 explains why that's the exception).

Chargebacks & reversals

A delivered-then-disputed collection is a Correction (debit, reason chargeback …). A transaction posted in error is a Reversal by walletflow tx id — every leg reversed atomically. Don't hand-craft the opposite entries.

04.4

The full surface

Everything the console can do, and the use case each action writes through. If a flow in the Fincra product touches a wallet or the ledger, it is on this table.

PageYou canWrites through
/opswatch health, counters, recent ledger transactionsread-only
/ops/walletscreate wallets · live balances · statementscreateWallet
/ops/operationspay-in · transfer · reserve payout · FX quote/deal · OTCpostPayin · transfer · reservePayout · bookFxDeal · bookCounterpartyTrade
stablecoin onramp/offramp · GPS top-up & cross-currencystablecoinOnramp/Offramp · topUpGpsPool · gpsCrossCurrencyPayout
bank funding · opening balance · rolling reserve · correction · reversal fundBankAccount · postOpeningBalance · hold/releaseRollingReserve · postCorrection · reverse
/ops/payoutsfilter holds · settle · release · dead-letter to manual reviewsettleHold · releaseHold · markPayoutManualReview
/ops/ledgerbrowse COA/GL/templates · rebuild · drift check · CSV exportrebuildTemplates · rebuildFromSeed
/ops/reconrun all four gates · ALM views · rebuild statementsrebuildStatementReadModel
/ops/controlsset/list flags · outbox monitor · provider-event logsetFeatureFlag
/ops/settingsconsole access: allowed/admin email lists (admin only)saveAccessLists (ops_auth.settings)