Appearance
Quick Facts
This page condenses key sections of the Maintainers Playbook into a quick technical reference for auditing the source code.
Quick Project Facts
- Base Framework: Nuxt 4 + Vue 3 + TypeScript.
- UI: @nuxt/ui components.
- State: Pinia (+ pinia-plugin-persistedstate for settings).
- Rendering mode: client-side (
ssr: false). - Dev server port:
3500(fromnuxt.config.ts). - Main runtime dependency: Bitcoin JSON-RPC via HTTP Basic auth.
High-Level Architecture
Frontend layout and routes:
- App boot:
app/app.vuecallsbitcoinStore.fetchNodeNames()on mount. - Main layout:
app/layouts/default.vuebuilds dynamic nav from node list. - Pages:
app/pages/index.vue-> dashboard cards and combined totalsapp/pages/system/[i].vue-> detailed node infoapp/pages/mempool/[i].vue-> mempool summary + visualizerapp/pages/peers/[i].vue-> peer table + map + geo lookupapp/pages/ban/[i].vue-> ban list and removalapp/pages/settings.vue-> polling intervals persisted to localStorage
Backend API pattern:
- Thin Nuxt server endpoints in
server/api/**. - Each endpoint validates input with Zod and/or shared schema.
- Node RPC access through
server/utils/bitcoinRpcClient.ts. - Errors normalized by
sendErrorResponseinserver/utils/errors.ts.
Data Flow
app/app.vueloads node names (/api/getNodeNames).- Sidebar/menu renders from
bitcoinStore.nodeNames. - Dashboard (
index.vue) polls/api/getDashboardper node. - Dashboard updates each node's
isIbdstate in the store. - Menu and polling behavior respond to
isIbd:- IBD nodes are marked/limited in nav.
- IBD nodes are polled on a slower interval.
- Node-specific pages use route param
[i]asnodeIndexfor API calls.
Store Responsibilities
app/stores/bitcoin.ts
nodeNames: source of truth for node list and status (isError,isIbd).nodeCount: derived from fetched node names.cachedNodes: dashboard cache used for IBD nodes.
app/stores/appSettings.ts
- Poll intervals for dashboard/mempool.
- Persisted to localStorage via pinia-plugin-persistedstate.
API Surface Map
Core endpoints:
GET /api/getNodeNamesPOST /api/getDashboardbody:{ nodeIndex }POST /api/getNodeInfobody:{ nodeIndex }GET /api/getPeerInfo?nodeIndex=...GET /api/getTransaction?nodeIndex=...&txid=...POST /api/getVisualizerbody:{ nodeIndex }
Ban endpoints:
GET /api/ban/listBanned?nodeIndex=...POST /api/ban/setBanbody:{ nodeIndex, ipAddress, banTime }POST /api/ban/removeBanbody:{ nodeIndex, ipAddress }POST /api/ban/clearBannedbody:{ nodeIndex }
Geo endpoints:
GET /api/geoip/:ipPOST /api/geoip/batchbody:{ ips: string[] }
Mempool Visualizer Notes
The visualizer endpoint (/api/getVisualizer) is performance-sensitive and uses in-memory caches:
mempoolCache: per node and block heightcategoriesCache: categorized tx groupsblocksCache: recent blocks
Behavior:
- Cold start/new block: fetch full verbose mempool.
- Warm updates: fetch txid list, then batch fetch only new tx entries.
- High-priority txs are sorted by fee rate and trimmed to
MAX_VIZ_TX.
If editing this path, preserve cache invalidation logic by block height.
Validation and Error Contract
Shared base schema:
AppConstants.BASE_VALIDATION_SCHEMAenforcesnodeIndexin [0, 32].
Error utility:
- Zod errors -> HTTP 400 with readable field messages.
StatusErrorsupports explicit HTTP status mapping.- Unknown errors -> generic
success: falseerror payload.
Rule of thumb:
- Keep API responses in
ApiResponse<T>shape. - Prefer throwing
StatusErrorfor predictable client behavior.