A secure peer-to-peer message routing server for the Bitcoin SV ecosystem. Provides identity-based message delivery, real-time WebSocket communication, and full mutual authentication using BRC-103 signatures.
The Message Box Server implements encrypted, store-and-forward messaging with support for both HTTP and WebSocket transports. Messages are identified by sender identity keys and recipient message boxes, encrypted end-to-end, and stored in MySQL until acknowledged by the recipient. The server speaks HTTP and WebSocket protocols, maintains connection state via @bsv/authsocket rooms, and can emit Firebase push notifications to registered devices.
Clients connect with identity-based authentication, send and receive messages through authenticated endpoints, and can opt into real-time WebSocket delivery for immediate notifications.
| Type | Requirement |
|---|---|
| Database | MySQL 8.0 via mysql2 |
| External services | Wallet Storage (WALLET_STORAGE_URL), Firebase Admin SDK (optional) |
| ts-stack packages | @bsv/sdk, @bsv/auth-express-middleware, @bsv/authsocket, @bsv/payment-express-middleware, @bsv/wallet-toolbox |
| Method | Path | Purpose |
|---|---|---|
| POST | /sendMessage | Send encrypted message to recipient (authenticated) |
| POST | /listMessages | Page unacknowledged messages with limit and offset/skip |
| POST | /acknowledgeMessage | Mark messages as read/delete them (authenticated) |
| POST | /registerDevice | Register a push-notification device for the authenticated identity |
| GET | /devices | List the authenticated identity's devices with redacted tokens |
| GET | /permissions/get | Read a recipient permission |
| GET | /permissions/list | List recipient permissions |
| GET | /permissions/quote | Quote up to 100 recipients |
| POST | /permissions/set | Set an authenticated recipient permission |
| GET | /health | Public process liveness |
| GET | /ready | Public database readiness |
| GET | /docs, /openapi.json | Public API documentation |
The omitted page size is 1,000 in the standard profile and is operator
configurable. Responses include limit, offset, nextOffset, and hasMore.
See Service Resource Profiles for
all limits, shared state, BRC-105 pricing, memory evidence, and scaling guidance.
{identityKey}-{messageBox} formatauthenticated, joinRoom, sendMessage, leaveRoom| Variable | Required | Description |
|---|---|---|
| NODE_ENV | No | development, staging, or production |
| PORT | No | HTTP/WebSocket port (default 8080; takes precedence) |
| HTTP_PORT | No | Compatibility port fallback |
| HOSTING_DOMAIN | No | Public domain for overlay advertisement (e.g., http://localhost:8080) |
| SERVER_PRIVATE_KEY | Yes | 256-bit hex private key for server identity and auth signing |
| ROUTING_PREFIX | No | Optional path prefix for all routes (e.g., /api) |
| ENABLE_WEBSOCKETS | No | Set to 'true' to enable real-time messaging (default true) |
| LOGGING_ENABLED | No | Set to 'true' for verbose debug logging |
| WALLET_STORAGE_URL | No | URL of wallet storage service (e.g., https://store-us-1.bsvb.tech) |
| KNEX_DB_CLIENT | No | Database client (default: mysql) |
| KNEX_DB_CONNECTION | Yes | JSON connection config: {"host":"localhost","port":3306,"user":"root","password":"...","database":"messagebox-backend"} |
| MIGRATE_KEY | No | Optional key to authorize migration operations |
| ENABLE_FIREBASE | No | Set to 'true' to enable Firebase push notifications |
| FIREBASE_PROJECT_ID | No | GCP project ID for Firebase |
| FIREBASE_SERVICE_ACCOUNT_JSON | No | Firebase service account JSON (inline) |
| FIREBASE_SERVICE_ACCOUNT_PATH | No | Path to Firebase service account JSON file |
| MESSAGE_BOX_CORS_MODE | No | public (default), allowlist, or disabled |
| MESSAGE_BOX_CORS_ALLOWED_ORIGINS | No | Exact comma-separated origins in allowlist mode |
| MESSAGE_BOX_CORS_ALLOWED_HEADERS | No | Strict comma-separated browser request-header allowlist; omit for additive compatibility |
| MESSAGE_BOX_MAX_BODY_BYTES | No | HTTP JSON ceiling (default 4194304) |
| MESSAGE_BOX_WEBSOCKET_MAX_BODY_BYTES | No | WebSocket payload ceiling (default 1048576) |
See Public Service Edge Security for rate, timeout, WebSocket authorization, CORS/CSP, and error controls.
# Install dependencies
npm install
# Development with hot-reload
npm run dev
# Database setup (MySQL 8.0 running)
docker compose up -d mysql
# Build for production
npm run build
# Run production build
npm start# Build Docker image
docker build -t messagebox:latest .
# Run the service directly on port 8080
docker run -d \
-e NODE_ENV=production \
-e PORT=8080 \
-e SERVER_PRIVATE_KEY=<hex-key> \
-e KNEX_DB_CONNECTION='{"host":"mysql","port":3306,"user":"root","password":"...","database":"messagebox-backend"}' \
-e ENABLE_WEBSOCKETS=true \
-p 8080:8080 \
messagebox:latest
# Or use docker-compose
docker compose up -dThe multi-stage Dockerfile compiles TypeScript for a minimal production runtime. The Node process serves HTTP and WebSocket traffic directly; deploy a trusted platform ingress or load balancer in front when required.
Knex migrations run automatically at server startup. After building, they can also be run manually:
npx --no-install knex --knexfile out/knexfile.js migrate:latest --env productionMigrations tracked in src/migrations/:
2022-12-28-001-initial-migration.ts – Core messages table with identity keys2023-01-17-messages-update.ts – Payload storage updates2024-03-05-001-messageID-upgrade.ts – MessageID uniqueness constraints2025-01-31-001-notification-permissions.ts – Firebase notification permissions2025-01-31-002-device-registrations.ts – Device registration tracking2026-07-26-001-message-permission-scope.ts – Enforce one box-wide or sender-specific permission per scope2026-07-26-002-list-query-indexes.ts – Add indexes for bounded inbox and sender-list queries2026-08-04-001-resource-safety.ts – Add retained-state accounting and resource-safety fieldsGET /health reports process liveness without authentication.GET /ready verifies database connectivity and returns a non-sensitive 503
response while dependencies are unavailable.@bsv/message-box-client@bsv/payment-express-middleware/ready.The service package is private and is distributed as a container, not as a public npm package.