Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions docs/packages/messaging/message-box-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ id: pkg-message-box-client
title: '@bsv/message-box-client'
kind: package
domain: messaging
version: '2.4.1'
version: '2.5.0'
source_repo: 'bsv-blockchain/ts-stack'
last_updated: '2026-08-14'
last_verified: '2026-08-14'
last_updated: '2026-08-24'
last_verified: '2026-08-24'
review_cadence_days: 30
npm: 'https://www.npmjs.com/package/@bsv/message-box-client'
repo: 'https://github.com/bsv-blockchain/ts-stack/tree/main/packages/messaging/message-box-client'
Expand Down Expand Up @@ -61,7 +61,8 @@ is useful.

## What it provides

- `MessageBoxClient` — authenticated HTTP polling and live WebSocket delivery
- `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
Expand Down
6 changes: 3 additions & 3 deletions governance/package-release-notes.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@
{
"name": "@bsv/message-box-client",
"publishedVersion": "2.4.0",
"releaseType": "patch",
"summary": "Preserves BRC-29 payments, paid messages, remittances, and peer tokens across binary Wallet Wire results and historical numeric-key JSON payloads, with strict rejection of sparse or invalid byte records.",
"migration": "No API migration is required. Upgrade @bsv/sdk and @bsv/message-box-client together; historical number-array wallets, current Uint8Array substrates, and already-pending numeric-key messages interoperate through the same portable transaction form."
"releaseType": "minor",
"summary": "Adds an optional socketOptions client option that forwards the AuthSocketClient options other than wallet and originator, letting callers select Socket.IO transports such as websocket-only against deployments that do not carry Engine.IO HTTP polling, and reach certificate requests, session management, auth-message concurrency, and error reporting. Also preserves BRC-29 payments, paid messages, remittances, and peer tokens across binary Wallet Wire results and historical numeric-key JSON payloads, with strict rejection of sparse or invalid byte records.",
"migration": "No API migration is required. socketOptions is optional and nothing is forwarded by default, so socket transport negotiation and every HTTP code path are unchanged for existing callers; wallet and originator stay client-owned and cannot be overridden through it. Upgrade @bsv/sdk and @bsv/message-box-client together; historical number-array wallets, current Uint8Array substrates, and already-pending numeric-key messages interoperate through the same portable transaction form."
},
{
"name": "@bsv/overlay",
Expand Down
2 changes: 1 addition & 1 deletion governance/repository-health/baselines.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@
"@bsv/templates": "1.10.0",
"@bsv/authsocket": "2.1.6",
"@bsv/authsocket-client": "2.1.5",
"@bsv/message-box-client": "2.4.1",
"@bsv/message-box-client": "2.5.0",
"@bsv/paymail": "2.4.6",
"@bsv/402-pay": "0.2.4",
"@bsv/auth": "0.1.3",
Expand Down
10 changes: 10 additions & 0 deletions packages/messaging/message-box-client/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ All notable changes to this project will be documented in this file. The format

### Added

- Added an optional `socketOptions` client option, forwarded to
`AuthSocketClient` when the live socket is created. It carries the
`AuthSocketClient` options other than `wallet` and `originator`, which the
client owns, so callers can select Socket.IO transports (for example
`{ managerOptions: { transports: ['websocket'] } }`) to reach deployments that
do not carry Engine.IO's HTTP polling transport, and can also supply
`requestedCertificates`, `sessionManager`, `maxPendingAuthMessages`, and
`onError`. Nothing is forwarded when unset, so default transport negotiation
and all HTTP code paths are unchanged.

- Added the `teratestnet` overlay preset. TTN clients must provide an explicit
Message Box host until a dedicated TTN deployment is available, preventing
accidental use of the existing testnet staging service.
Expand Down
2 changes: 1 addition & 1 deletion packages/messaging/message-box-client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@bsv/message-box-client",
"version": "2.4.1",
"version": "2.5.0",
"sideEffects": false,
"engines": {
"node": ">=22"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,13 +211,15 @@
private socketAuthenticated = false
private connectionInitPromise?: Promise<void>
protected originator?: OriginatorDomainNameStringUnder250Bytes
private readonly socketOptions?: MessageBoxClientOptions['socketOptions']

Check warning on line 214 in packages/messaging/message-box-client/src/MessageBoxClient.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Consider removing 'undefined' type or '?' specifier, one of them is redundant.

See more on https://sonarcloud.io/project/issues?id=bsv-blockchain_ts-stack&issues=AaA55PhjEOpv-Iua3MJb&open=AaA55PhjEOpv-Iua3MJb&pullRequest=490
/**
* @constructor
* @param {Object} options - Initialization options for the MessageBoxClient.
* @param {string} [options.host] - The base URL of the MessageBox server. Required for TerraTestNet until a dedicated TTN deployment is available.
* @param {WalletInterface} options.walletClient - Wallet instance used for authentication, signing, and encryption.
* @param {boolean} [options.enableLogging=false] - Whether to enable detailed debug logging to the console.
* @param {'local' | 'mainnet' | 'testnet' | 'teratestnet'} [options.networkPreset='mainnet'] - Overlay network preset used for routing and advertisement lookup.
* @param {Omit<AuthSocketClientOptions, 'wallet' | 'originator'>} [options.socketOptions] - Options forwarded to the underlying AuthSocketClient, e.g. `{ managerOptions: { transports: ['websocket'] } }`. The client's own wallet and originator always win.
*
* @description
* Constructs a new MessageBoxClient.
Expand All @@ -241,7 +243,8 @@
walletClient,
enableLogging = false,
networkPreset = 'mainnet',
originator = undefined
originator = undefined,
socketOptions = undefined
} = options

if (networkPreset === 'teratestnet' && host == null) {
Expand All @@ -257,6 +260,7 @@

this.host = normalizeMessageBoxHost(host ?? defaultHost)
this.originator = originator
this.socketOptions = socketOptions
this.walletClient = walletClient ?? new WalletClient('auto', originator)
this.authFetch = new AuthFetch(this.walletClient, undefined, undefined, originator)
this.networkPreset = networkPreset
Expand Down Expand Up @@ -435,6 +439,7 @@
if (this.socket == null) {
const targetHost = normalizeMessageBoxHost(overrideHost ?? this.host)
this.socket = AuthSocketClient(targetHost, {
...this.socketOptions,
wallet: this.walletClient,
originator: this.originator
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ jest.unstable_mockModule('@bsv/authsocket-client', () => ({

const { MessageBoxClient } = await import('../MessageBoxClient.js')

const { AuthSocketClient } = await import('@bsv/authsocket-client')
const authSocketClientMock = AuthSocketClient as unknown as jest.Mock

// MOCK: WalletClient methods globally
jest.spyOn(WalletClient.prototype, 'createHmac').mockResolvedValue({
hmac: Array.from(new Uint8Array([1, 2, 3]))
Expand Down Expand Up @@ -166,6 +169,77 @@ describe('MessageBoxClient', () => {
await expect(connection).resolves.toBeUndefined()
}, 10000)

it('Forwards socketOptions to AuthSocketClient when configured', async () => {
const messageBoxClient = new MessageBoxClient({
walletClient: mockWalletClient,
host: 'https://message-box-us-1.bsvb.tech',
socketOptions: {
managerOptions: { transports: ['websocket'] },
maxPendingAuthMessages: 8
}
})

await messageBoxClient.init()

const connection = messageBoxClient.initializeConnection()
setTimeout(() => {
socketOnMap.authenticationSuccess?.({ status: 'ok' })
}, 100)
await connection

expect(authSocketClientMock).toHaveBeenCalledWith(
'https://message-box-us-1.bsvb.tech',
expect.objectContaining({
managerOptions: { transports: ['websocket'] },
maxPendingAuthMessages: 8
})
)
}, 10000)

it('Passes only client-owned options to AuthSocketClient when socketOptions is omitted', async () => {
const messageBoxClient = new MessageBoxClient({
walletClient: mockWalletClient,
host: 'https://message-box-us-1.bsvb.tech'
})

await messageBoxClient.init()

const connection = messageBoxClient.initializeConnection()
setTimeout(() => {
socketOnMap.authenticationSuccess?.({ status: 'ok' })
}, 100)
await connection

const options = authSocketClientMock.mock.calls[0][1] as Record<string, unknown>
expect(Object.keys(options).sort()).toEqual(['originator', 'wallet'])
}, 10000)

it('Keeps client-owned wallet and originator ahead of socketOptions', async () => {
const foreignWallet = { id: 'foreign-wallet' }
const messageBoxClient = new MessageBoxClient({
walletClient: mockWalletClient,
host: 'https://message-box-us-1.bsvb.tech',
originator: 'client.example',
// A JS caller can still smuggle these past the Omit<> type.
socketOptions: {
wallet: foreignWallet,
originator: 'socket.example'
} as any
})

await messageBoxClient.init()

const connection = messageBoxClient.initializeConnection()
setTimeout(() => {
socketOnMap.authenticationSuccess?.({ status: 'ok' })
}, 100)
await connection

const options = authSocketClientMock.mock.calls[0][1] as Record<string, unknown>
expect(options.originator).toBe('client.example')
expect(options.wallet).not.toBe(foreignWallet)
}, 10000)

it('Falls back to HTTP when WebSocket is not initialized', async () => {
const messageBoxClient = new MessageBoxClient({
walletClient: mockWalletClient,
Expand Down
14 changes: 14 additions & 0 deletions packages/messaging/message-box-client/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
WalletInterface,
type LookupNetworkPreset
} from '@bsv/sdk'
import type { AuthSocketClientOptions } from '@bsv/authsocket-client'

/**
* Configuration options for initializing a MessageBoxClient.
Expand Down Expand Up @@ -48,6 +49,19 @@ export interface MessageBoxClientOptions {
* Originator of the message box client.
*/
originator?: string

/**
* Options forwarded to the underlying AuthSocketClient when the live socket is
* created. Covers socket.io transport selection via `managerOptions` (e.g.
* `{ managerOptions: { transports: ['websocket'] } }` for a deployment that does
* not carry Engine.IO HTTP polling), plus certificate requests, session
* management, auth-message concurrency, and error reporting.
*
* `wallet` and `originator` are excluded because the client owns both; the
* client's own values always win. Only affects the socket path; HTTP requests
* are unchanged.
*/
socketOptions?: Omit<AuthSocketClientOptions, 'wallet' | 'originator'>
}

/**
Expand Down
Loading