A TypeScript/Express server that provides presentation-key and Shamir-share recovery workflows for BSV wallet applications, using Twilio verification in production and an explicitly development-only console OTP method.
WAB enables Twilio phone verification and coordinates key/share storage through
SQLite (development) or MySQL (production). A Persona example exists in source
but is not registered as a supported method. The DevConsole method is available
only when explicitly enabled in a development or test runtime and cannot be
activated in production or staging.
Clients authenticate by phone number, recover original presentation keys, optionally receive one-time BSV payments, and can verify a same-or-new phone number to rotate the presentation key. Operators can pin a legacy ambiguous UMP account to one verified outpoint and restore recorded phone associations.
| Type | Requirement |
|---|---|
| Database | SQLite (dev: ./dev.sqlite3) or MySQL (production: DB_CLIENT, DB_USER, DB_PASS, DB_NAME, DB_HOST, DB_PORT) |
| External services | Twilio (if TwilioAuthMethod), Wallet Storage (if faucet enabled), ARC (for transaction broadcasting) |
| ts-stack packages | @bsv/sdk, @bsv/wallet-toolbox |
| Method | Path | Purpose |
|---|---|---|
| GET | /info | Server configuration info |
| POST | /auth/start | Start authentication (methodType, presentationKey, payload) |
| POST | /auth/complete | Complete authentication (methodType, presentationKey, payload) |
| POST | /auth/phone-change/start | Verify current account and send OTP to requested phone |
| POST | /auth/phone-change/complete | Verify OTP and issue a ten-minute change token |
| POST | /auth/phone-change/commit | Stage the verified phone association and replacement key |
| POST | /auth/phone-change/finalize | Promote the key after the wallet publishes its UMP rotation |
| POST | /admin/ump-pin | Set/clear a support UMP outpoint pin (admin bearer required) |
| POST | /admin/phone-change/restore | Restore recorded phone associations (admin bearer required) |
| POST | /user/linkedMethods | List user's linked auth methods (presentationKey) |
| POST | /user/unlinkMethod | Unlink auth method (presentationKey, methodId) |
| POST | /user/delete | Delete user account (presentationKey) |
| POST | /faucet/request | Request faucet payment (presentationKey) |
| POST | /account/delete/start | Start OTP-confirmed account deletion |
| POST | /account/delete/complete | Complete account deletion |
| POST | /share/store | OTP-confirmed Shamir share creation |
| POST | /share/retrieve | OTP-confirmed Shamir share recovery |
| POST | /share/update | OTP-confirmed Shamir share rotation |
| POST | /share/delete | OTP-confirmed share/account deletion |
None.
| Variable | Required | Description |
|---|---|---|
| NODE_ENV | No | development or production |
| PORT | No | HTTP server port (default: 3000) |
| TWILIO_ACCOUNT_SID | No | Twilio account ID (if using TwilioAuthMethod) |
| TWILIO_AUTH_TOKEN | No | Twilio auth token |
| TWILIO_VERIFY_SERVICE_SID | No | Twilio Verify service ID (VAxxxx or VExxxx) |
| SERVER_PRIVATE_KEY | No | 256-bit hex key for faucet transactions |
| STORAGE_URL | No | Overlay services URL for faucet (e.g., wallet storage endpoint) |
| COMMISSION_FEE | No | Commission fee in satoshis per faucet request (default: 0) |
| DB_CLIENT | No | Database client (default: sqlite3; or mysql2) |
| DB_USER | No | Database user (production MySQL) |
| DB_PASS | No | Database password |
| DB_NAME | No | Database name |
| DB_HOST | No | Database host |
| DB_PORT | No | Database port |
| DB_CONNECTION_NAME | No | GCP Cloud SQL connection name (for Cloud SQL with Unix socket) |
| DEV_CONSOLE_AUTH_METHOD_ENABLED | No | Development/test-only explicit console OTP opt-in |
| WAB_CORS_MODE | No | public (default), allowlist, or disabled |
| WAB_CORS_ALLOWED_ORIGINS | No | Exact comma-separated origins for allowlist mode |
| WAB_CORS_ALLOWED_HEADERS | No | Strict comma-separated browser request-header allowlist; omit to accept additive well-formed request headers |
| WAB_MAX_BODY_BYTES | No | JSON body ceiling (default 262144) |
| WAB_MAX_CONCURRENT_REQUESTS | No | Per-process in-flight ceiling (default 200) |
| WAB_ADMIN_TOKEN | No | At least 32 random characters; enables authenticated /admin/* support routes |
| WAB_ADMIN_RATE_LIMIT_MAX | No | Administrative requests per window (default 30) |
| WAB_ADMIN_RATE_LIMIT_WINDOW_MS | No | Administrative rate-limit window (default 900000) |
| TRUST_PROXY_HOPS | No | Exact trusted proxy hop count, 0 through 10 |
See Public Service Edge Security for endpoint rate limits, errors, CORS/CSP behavior, and the threat model.
# Install dependencies
npm install
# Development with auto-restart
npm run dev
# Database migrations
npm run migrate
# Run tests with coverage
npm test
# Build TypeScript
npm run build
# Run production server
npm startUses SQLite by default (./dev.sqlite3); MySQL configured via DB_* env vars.
# Build Docker image
docker build -t wab-server:latest .
# Run with MySQL backend
docker run -d \
-e NODE_ENV=production \
-e DB_CLIENT=mysql2 \
-e DB_HOST=mysql \
-e DB_USER=root \
-e DB_PASS=password \
-e DB_NAME=wab \
-e TWILIO_ACCOUNT_SID=<sid> \
-e TWILIO_AUTH_TOKEN=<token> \
-e TWILIO_VERIFY_SERVICE_SID=<service-id> \
-e SERVER_PRIVATE_KEY=<hex-key> \
-e STORAGE_URL=<overlay-url> \
-p 3000:3000 \
wab-server:latest
# Or with GCP Cloud SQL
docker run -d \
-e DB_CLIENT=mysql2 \
-e DB_CONNECTION_NAME=project:region:instance \
-e DB_USER=root \
-e DB_PASS=password \
-e DB_NAME=wab \
... (other env vars)
# Or via docker-compose with MySQL
docker compose up -dRun Knex migrations for schema initialization:
npm run migrateCreates the core users, auth-method, payment, share, deletion, and abuse-control
tables. The UMP support migration adds nullable users.umpTokenOutpoint plus
phone_change_sessions and phone_change_history. Back up the database before
rollout. Do not remove the history table after users begin changing numbers.
GET /healthz is the liveness endpoint and GET /info is the readiness and
configuration endpoint. Monitor:
npm run migrate to verify)WAB_CORS_MODE=allowlist only when the deployment has a closed caller set/admin/* returns 404 when WAB_ADMIN_TOKEN is absent; a non-empty token shorter than 32 characters fails startupchangeId so a fraudulent transfer can be restored