<>ts-stack
Get StartedArchitecturePackagesSpecsGuides
⌘K
Reference
Home
Get StartedOverviewInstallChoose your stackKey concepts
ArchitectureOverviewStack layersBEEF (BRC-62)BRC-100 Wallet InterfaceIdentity & AuthConformance pipeline
PackagesOverviewSDKWalletNetworkOverlaysMessaging
@bsv/message-box-client@bsv/authsocket@bsv/authsocket-client@bsv/paymail
MiddlewareHelpers
InfrastructureOverviewmessage-box-serveroverlay-serveruhrp-server-basicuhrp-server-cloud-bucketwabwallet-infrachaintracks-server
SpecsOverviewBRC-100 Wallet InterfaceOverlay HTTPMessage-box HTTPAuthsocket (WebSocket)BRC-31 Auth HandshakeBRC-29 Peer PaymentBRC-121 / HTTP 402ARC BroadcastMerkle ServiceStorage AdapterGASP SyncUHRPAir-Gap Optical (BRC-141)
ConformanceOverviewVector catalogTS runnerContributing vectors
GuidesOverviewIdentity, DIDs and credentialsIdentity migrationBuild a wallet-aware appRun an overlay nodePeer-to-peer messagingHTTP 402 payments
ReferenceOverviewBRC indexRepository health
AboutVersioningContributingDoc agentDocumentation sources
npmhttps://www.npmjs.com/package/@bsv/message-box-clientv2.6.0API reference (TypeDoc) ↗
Loading…
Edit this page on GitHub
© 2026 BSV Blockchain. ts-stack is open-source.
GitHubContributingVersioning

@bsv/message-box-client

This source candidate declares SDK peer ^2.8.6 || ^3.0.0. SDK3 remains a coordinated proposal; see the qualification and migration limits before adopting it.

Browser- and Node-compatible authenticated store-and-forward messaging, live WebSockets, peer payments, token settlement, permissions, quotes, and push-device registration.

The 2.6.0 source candidate adds optional paymentOutcome and payment fields to the PeerMessage values returned by listMessages() and listMessagesLite(), so a payment the wallet did not store is no longer lost when its message is acknowledged. See Payment acceptance and acknowledgment ordering.

The 2.5.4 release raised the @bsv/sdk peer floor to ^2.8.6: SDK 2.8.0 through 2.8.5 reject every valid incoming BRC-29 payment in PeerPayClient.acceptPayment() and rejectPayment() because the recipient key is derived without forSelf. That release changed peer metadata without changing source.

The 2.5.3 release fixes the CommonJS build (new MessageBoxClient() failed with LookupResolver.default is not a constructor under require()). sendMessage() HTTP failures now append the server's well-formed failure code, for example Message Box send failed with HTTP 400 (ERR_DUPLICATE_MESSAGE).; free-text server descriptions are never copied into errors. That 2.5.3 release required ^2.8.0, the first SDK release providing modules this package already required.

Install

bash
npm install @bsv/message-box-client @bsv/sdk

@bsv/sdk is a required peer. Node.js 22 or newer is supported. The current candidate requires ^2.8.6 || ^3.0.0; use SDK2.8.6 or newer within the SDK2 family, or the proposed SDK3 candidate after the linked qualification. The package imports SDK validation modules first shipped in 2.8.0, and SDK 2.8.6 fixes BRC-29 payment acceptance. BRC-29 sends accept both historical number[] and binary Wallet Wire Uint8Array transaction results. Payment receipt also recovers pending typed-array tokens serialized through JSON as numeric-key objects.

Quick start

ts
import { MessageBoxClient } from '@bsv/message-box-client'
import { WalletClient } from '@bsv/sdk'

const wallet = new WalletClient()
const client = new MessageBoxClient({
  walletClient: wallet,
  host: 'https://message-box-us-1.bsvb.tech'
})

await client.sendMessage({
  recipient: '025706528f0f6894b2ba505007267ccff1133e004452a1f6b72ac716f246216366',
  messageBox: 'general_inbox',
  body: { text: 'Hello' }
})

const messages = await client.listMessages({ messageBox: 'general_inbox' })
await client.acknowledgeMessage({
  messageIds: messages.map(message => message.messageId)
})

Before acknowledging a message that carried a payment, check its paymentOutcome: unless it is 'internalized', the payment is returned as payment and must be stored first, because the message holds the only copy of its derivation data.

Initialization is automatic. Call init() only when explicit startup control is useful.

What it provides

  • MessageBoxClient — authenticated HTTP polling and live WebSocket delivery, with selectable socket transports
  • encryption through the BRC-100 wallet protocol, enabled by default
  • overlay host advertisement and public-HTTPS discovery
  • sender-specific and box-wide permissions with fee quotes
  • Firebase push-device registration
  • PeerPayClient — BRC-29 payments, requests, responses, and refunds
  • PeerTokenClient — token transport through pluggable settlement adapters
  • RemittanceAdapter — SDK remittance communication integration

Security and interoperability

Configured hosts require HTTPS except on loopback and must be exact absolute URLs without whitespace, credentials, query strings, fragments, or control characters. Every HTTP result must carry a canonical server identity proven by BRC-103 mutual authentication; ordinary AuthFetch fallback is rejected. The first authenticated identity remains authoritative per origin for the client instance, or applications can supply durable, independently validated serverIdentityKeysByHost pins.

Untrusted overlay destinations require public HTTPS and cannot target local, private, link-local, reserved, or documentation-only hosts. Returned BEEF, output indexes, canonical PushDrop fields, the requested identity, and the advertisement signature are independently bounded and verified. A lookup provider proposes routing candidates but is not itself routing authority.

The client snapshots outgoing send authority before its first asynchronous operation. Recipients must be canonical public keys; message-box names, caller-supplied IDs, bodies, recipient arrays, and JSON object graphs are bounded and accessor-free. Authenticated send results must correlate their recipient and message ID to the request.

Delivery quotes are untrusted financial input even though their transport is authenticated. Each requested recipient and message-box name must appear exactly once, fee/status/block fields must agree, and only bounded safe-integer amounts enter arithmetic. maximumPayment is an optional independent ceiling for sendMessage and shared plaintext batches. Before returning a payment envelope, the client parses the wallet's Atomic BEEF and binds every requested script and satoshi amount at the exact remittance output index. The Message Box states its price; the wallet remains the final spending authority.

Message Box remains accessible from arbitrary deployed browser origins by default. Server-side exact-origin allowlists or disabled CORS are operator opt-ins. CORS and CSP do not replace BRC-103 identity authentication, recipient-owned boxes, permissions, payment checks, quotas, or end-to-end message encryption.

Distribution and verification

The package publishes ESM, CommonJS, declarations, source maps, and a UMD browser bundle. Exact-tarball validation exercises clean ESM/CommonJS consumers, strict declaration resolution, publint, browser bundling through Vite and esbuild, and bundle budgets. Source and tests are not published.

Related

  • Package README
  • Message Box HTTP API
  • Peer-to-peer messaging guide
  • Message Box Server
  • npm

Payment acceptance and acknowledgment ordering

acknowledgeNotification checks the original notification envelope, internalizes a present recipient payment with the configured originator, and acknowledges only after the wallet returns accepted: true. A notification without a payment can be acknowledged immediately. Failures, incomplete envelopes and payments with no wallet-payment outputs remain queued. Its boolean return contract is unchanged: false can mean no payment or a retained failed payment.

Incoming payment envelopes accept Atomic BEEF up to 32 MiB and at most 101 payment outputs, matching the existing 100-recipient batch plus delivery-fee output contract. The client snapshots those fields independently so a valid large transaction is not rejected by the smaller generic JSON-graph limit; sparse arrays, accessors, non-byte values, and oversized fields still fail closed before wallet work.

acceptPayment also requires affirmative wallet acceptance before acknowledgment. For refundable amounts, rejectPayment internalizes first, sends the refund, and then acknowledges. A failed internalization prevents both refund and acknowledgment; a failed refund send leaves the message queued. The existing small-payment policy is unchanged.

listMessages and listMessagesLite set paymentOutcome on each message that carried a payment: 'internalized' (the wallet accepted it), 'failed' (internalizing threw, the wallet refused it, or the payment was malformed), 'skipped' (not attempted: acceptPayments: false, or always for listMessagesLite) or 'no-wallet-outputs'. Unless the outcome is 'internalized', the raw, unvalidated envelope payment is kept on the message as payment; validate and store it before acknowledging the message. Messages without a payment carry neither field.

This is the ordering and list-outcome remediation for issue #503. It does not provide a durable refund journal or exactly-once delivery. Reconcile uncertain refund-send outcomes before retrying, since a send may have completed before its response was lost. Basket-insertion policy, mixed-output failures and resumable refund semantics remain open. Use the original envelope for notification payment processing; a plain acknowledgment is not evidence that a payment was accepted.

Payment and token request fulfillment is not cross-process or cross-crash exactly once. Persist each authenticated request ID and sender together with every attempted or completed settlement transaction ID. Before fulfillment or retry, reject a transaction ID already associated with a different request and reject a different transaction ID for a request whose outcome is still uncertain. Reconcile the wallet and peer first; in-memory serialization does not prevent another service process from replaying the same request.