A reference implementation of BSV wallet infrastructure for secure UTXO storage and management. Provides HTTP JSON-RPC endpoints for wallet clients to store/retrieve transaction outputs, track spent/unspent states, manage baskets and labels, and store certificate data.
The Wallet Infrastructure Server implements JSON-RPC 2.0 endpoints backed by MySQL via Knex, extending @bsv/wallet-toolbox base classes. Clients POST JSON-RPC method calls (walletUtxoStorage_getHeight, walletUtxoStorage_listOutputs, walletUtxoStorage_insertOutput, walletUtxoStorage_updateOutput, walletUtxoStorage_listBaskets, walletUtxoStorage_createBasket, etc.) to a single / endpoint. The server enforces mutual authentication via BRC-103 auth middleware, optionally enforces micropayment pricing via @bsv/payment-express-middleware, and manages UTXO state in MySQL with indexes on identity_key, output_hash, and blockchain_height.
Clients connect with identity-based auth headers, manage UTXOs, baskets, labels, and certificates via standardized JSON-RPC interface compatible with @bsv/wallet-toolbox WalletClient.
| Type | Requirement |
|---|---|
| Database | MySQL 8.0 via Knex + mysql2 driver (other Knex-supported DBs can be substituted) |
| External services | Arc/Taal-compatible services for transaction broadcasting and proof lookup; optional Arcade/Chaintracks support through wallet-toolbox service options |
| ts-stack packages | @bsv/wallet-toolbox, @bsv/sdk, @bsv/auth-express-middleware, @bsv/payment-express-middleware |
| Method | Path | Purpose |
|---|---|---|
| POST | / | JSON-RPC 2.0 endpoint (all wallet operations) |
| PUT | /action-batch/:batchId/blob/:digest | Authenticated bounded binary blob upload |
| GET | /, /robots.txt | Public service metadata |
| GET | /healthz | Public process/storage health |
JSON-RPC methods: walletUtxoStorage_getHeight, walletUtxoStorage_listOutputs, walletUtxoStorage_insertOutput, walletUtxoStorage_updateOutput, walletUtxoStorage_listBaskets, walletUtxoStorage_createBasket, walletUtxoStorage_getBasket, walletUtxoStorage_listLabels, walletUtxoStorage_upsertLabel, walletUtxoStorage_dropLabels, walletUtxoStorage_listCertificates, walletUtxoStorage_insertCertificate (see @bsv/wallet-toolbox docs for full list).
None; HTTP JSON-RPC only.
| Variable | Required | Description |
|---|---|---|
| NODE_ENV | No | development or production |
| HTTP_PORT | No | Express server port (default: 8081, use 8081 if nginx enabled on 8080) |
| ENABLE_NGINX | No | Set to 'true' to start nginx reverse proxy on port 8080 (default: true) |
| WALLET_STORAGE_BIND_HOST | No | Application listener host (default 127.0.0.1 behind nginx, otherwise 0.0.0.0) |
| BSV_NETWORK | No | Target blockchain network (main, test, ttn, tstn, or mock); historical monitor alias CHAIN is accepted |
| SERVER_PRIVATE_KEY | Yes | 256-bit hex private key for server identity |
| KNEX_DB_CONNECTION | Yes | Knex database connection JSON string; historical monitor aliases MAIN_KNEX_DB_CONNECTION and TEST_KNEX_DB_CONNECTION are selected by network |
| COMMISSION_FEE | No | Optional commission fee in satoshis per request (default: 0) |
| COMMISSION_PUBLIC_KEY | No | Public key to receive commission payments (if COMMISSION_FEE > 0) |
| FEE_MODEL | No | Fee calculation model as JSON (default: {"model":"sat/kb","value":1}) |
| TAAL_API_KEY | No | API key used by the default Arc/Taal service configuration (optional) |
| TSTN_ARCADE_URL | tstn only | Private Arcade (broadcast + merkle proofs) endpoint for the tstn network. Not public; supplied per-deployment. Also used as the default ChainTracks host. |
| TSTN_CHAINTRACKS_URL | No | Private ChainTracks endpoint for tstn. Defaults to ${TSTN_ARCADE_URL}/chaintracks/v1 when omitted. |
| WALLET_STORAGE_CORS_MODE | No | public (default), allowlist, or disabled |
| WALLET_STORAGE_CORS_ALLOWED_ORIGINS | No | Exact comma-separated origins in allowlist mode |
| WALLET_STORAGE_CORS_ALLOWED_HEADERS | No | Strict comma-separated browser request-header allowlist; omit for additive compatibility |
| WALLET_STORAGE_JSON_MAX_BODY_BYTES | No | JSON-RPC body ceiling (default 31457280) |
| WALLET_STORAGE_BINARY_MAX_BODY_BYTES | No | Blob body ceiling (default 8388608) |
| WALLET_STORAGE_RPC_DEFAULT_LIST_LIMIT | No | Row limit inserted for list/find RPCs that omit one (standard default 1000; -1/unlimited is an explicit operator opt-out) |
| WALLET_STORAGE_RPC_MAX_LIST_LIMIT | No | Largest explicit list/find page (standard default 1000; legacy 10,000-row clients require a measured 10000 compatibility override) |
| WALLET_STORAGE_RPC_MAX_ARRAY_ITEMS | No | Maximum items in any decoded RPC request array (standard default 1000000) |
| WALLET_STORAGE_RPC_MAX_RESPONSE_BYTES | No | Serialized JSON-RPC response ceiling (standard default 8388608) |
| WALLET_STORAGE_TRUST_PROXY_HOPS | No | Exact trusted reverse-proxy hop count, 0–10 (default 0/direct exposure) |
| WALLET_STORAGE_MONITOR_START_TASKS | No | Enable monitor work for all/monitor roles (default true; historical alias supported) |
| WALLET_STORAGE_MONITOR_STARTUP_TASK_MODE | No | default, multiuser, alltoother, or none (default default; historical alias supported) |
| WALLET_STORAGE_MONITOR_ADMIN_ENABLED | No | Enable the private authenticated monitor operator listener on an all or monitor singleton (default false) |
| WALLET_STORAGE_MONITOR_ADMIN_HOST | No | Operator listener bind host (default 127.0.0.1) |
| WALLET_STORAGE_MONITOR_ADMIN_PORT | No | Operator listener port (default 8082; must not collide with the storage/nginx listener) |
| WALLET_STORAGE_MONITOR_ADMIN_PRIVATE_KEY | No | Stable operator-service identity key; secret; defaults to SERVER_PRIVATE_KEY |
| WALLET_STORAGE_ADMIN_IDENTITY_KEYS | Admin | Comma-separated compressed public keys allowed to use storage and monitor admin APIs |
See Public Service Edge Security for the authentication, rate, timeout, logging, CORS/CSP, admin, and nginx contracts.
tstn(Teranode Scaling Test Net) runs only Arcade and ChainTracks — it has no WhatsOnChain / block-explorer service. Its endpoints are private and are read from the environment at runtime rather than hardcoded, soTSTN_ARCADE_URL(and optionallyTSTN_CHAINTRACKS_URL) must be set wheneverBSV_NETWORK=tstn.
The reference entrypoint exposes TAAL, WhatsOnChain, Bitails, Arcade,
GorillaPool ARC, exchange-rate, callback-token, monitor-profile, and database
pool settings through the documented WALLET_STORAGE_* variables. Historical
provider and monitor variable names remain accepted where documented so an
operator can adopt the official image without rebuilding it.
Wallets should keep explicit list pages within
WALLET_STORAGE_RPC_MAX_LIST_LIMIT. Balance displays should use
wallet-toolbox's database-side balance special operation rather than loading
and summing every output in the client. A temporary 10,000-row maximum can
support historical BRC-100 clients, but only after production-shaped memory
testing and with the response-byte and concurrency ceilings still enabled.
# Install dependencies
npm install
# Development with ts-node
npm run dev
# Requires MySQL running
docker compose up -d mysql
# Build TypeScript
npm run build
# Run production build
npm start# Multi-stage Docker build: pinned Node 24 alpine → production
docker build -t wallet-infra:latest .
# Run with MySQL backend
docker run -d \
-e NODE_ENV=production \
-e ENABLE_NGINX=false \
-e HTTP_PORT=8081 \
-e WALLET_STORAGE_BIND_HOST=0.0.0.0 \
-e BSV_NETWORK=main \
-e SERVER_PRIVATE_KEY=<256-bit-hex> \
-e KNEX_DB_CONNECTION='{"port":3306,"host":"mysql","user":"root","password":"rootPass","database":"wallet_storage"}' \
-e COMMISSION_FEE=1000 \
-e COMMISSION_PUBLIC_KEY=<pubkey> \
-p 8081:8081 \
wallet-infra:latest
# With optional nginx reverse proxy on port 8080
docker run -d \
-e ENABLE_NGINX=true \
... (other env vars)
-p 8080:8080 \
-p 8081:8081 \
wallet-infra:latest
# Or via docker-compose (includes MySQL)
docker compose up -dDockerfile uses a digest-pinned Node 24 multi-stage build. Optional nginx.conf reverse proxy (if ENABLE_NGINX=true) on 8080 proxying to app on 8081.
Auto-run on startup via Knex. Creates tables: outputs, baskets, labels, certificates, metadata with indexes on identity_key, output_hash, blockchain_height for query performance.
The process starts both the JSON-RPC storage server and a wallet-toolbox
Monitor:
Monitor.createDefaultWalletMonitorOptions(chain, storage, services, ..., startupTaskMode) builds the selected task profile for real networks.monitor.startTasks() runs the background task loop when the role and
WALLET_STORAGE_MONITOR_START_TASKS permit it.The default task set handles:
For mock, the reference server uses MockServices with shorter task timing so
local integration tests complete quickly.
The storage listener exposes /healthz. The image health check probes nginx on
port 8080 when it is enabled and otherwise probes the configured application
listener, so a healthy loopback upstream cannot mask a failed public proxy.
When the optional monitor operator
listener is enabled, it exposes its own /healthz plus the static /admin
bootstrap page; authenticated and allowlisted BRC-100 clients can use
/admin/api. Monitor:
walletUtxoStorage_getHeight.monitor.start with outcome=ok.If this component is deployed behind nginx, separately monitor the upstream app
port (HTTP_PORT, default 8081) for diagnostics; public readiness should use
the nginx listener.