From 6c2ce6c2d36093da10ba5fdd2ade0a9bd9496e0b Mon Sep 17 00:00:00 2001 From: Kjartan Date: Mon, 24 Aug 2026 16:11:53 +0200 Subject: [PATCH 1/3] Add new managerOptions optional param on MessageBoxClient + release notes --- governance/package-release-notes.json | 6 +-- .../messaging/message-box-client/CHANGELOG.md | 7 ++++ .../messaging/message-box-client/package.json | 2 +- .../src/MessageBoxClient.ts | 9 +++- .../src/__tests/MessageBoxClient.test.ts | 42 +++++++++++++++++++ .../messaging/message-box-client/src/types.ts | 9 ++++ 6 files changed, 69 insertions(+), 6 deletions(-) diff --git a/governance/package-release-notes.json b/governance/package-release-notes.json index 393ed8dea..8883cc482 100644 --- a/governance/package-release-notes.json +++ b/governance/package-release-notes.json @@ -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 managerOptions client option that forwards Socket.IO manager and socket settings to AuthSocketClient, letting callers select transports such as websocket-only against deployments whose proxy rejects Engine.IO HTTP polling. 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. managerOptions is optional and omitted by default, so socket transport negotiation and every HTTP code path are unchanged for existing callers. 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", diff --git a/packages/messaging/message-box-client/CHANGELOG.md b/packages/messaging/message-box-client/CHANGELOG.md index ddf8763fb..def328ddb 100644 --- a/packages/messaging/message-box-client/CHANGELOG.md +++ b/packages/messaging/message-box-client/CHANGELOG.md @@ -15,6 +15,13 @@ All notable changes to this project will be documented in this file. The format ### Added +- Added an optional `managerOptions` client option, forwarded to + `AuthSocketClient` when the live socket is created. Callers can now select + Socket.IO transports (for example `{ transports: ['websocket'] }`) to reach + deployments whose proxy rejects Engine.IO's HTTP polling transport. The + option is omitted entirely 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. diff --git a/packages/messaging/message-box-client/package.json b/packages/messaging/message-box-client/package.json index c6ed729eb..4cdc5af73 100644 --- a/packages/messaging/message-box-client/package.json +++ b/packages/messaging/message-box-client/package.json @@ -1,6 +1,6 @@ { "name": "@bsv/message-box-client", - "version": "2.4.1", + "version": "2.5.0", "sideEffects": false, "engines": { "node": ">=22" diff --git a/packages/messaging/message-box-client/src/MessageBoxClient.ts b/packages/messaging/message-box-client/src/MessageBoxClient.ts index cd22ab2d2..c9df67be2 100644 --- a/packages/messaging/message-box-client/src/MessageBoxClient.ts +++ b/packages/messaging/message-box-client/src/MessageBoxClient.ts @@ -211,6 +211,7 @@ export class MessageBoxClient { private socketAuthenticated = false private connectionInitPromise?: Promise protected originator?: OriginatorDomainNameStringUnder250Bytes + private readonly managerOptions?: MessageBoxClientOptions['managerOptions'] /** * @constructor * @param {Object} options - Initialization options for the MessageBoxClient. @@ -218,6 +219,7 @@ export class MessageBoxClient { * @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 {Partial} [options.managerOptions] - Socket.IO manager/socket options forwarded to the underlying AuthSocketClient, e.g. `{ transports: ['websocket'] }`. Omitted when unset. * * @description * Constructs a new MessageBoxClient. @@ -241,7 +243,8 @@ export class MessageBoxClient { walletClient, enableLogging = false, networkPreset = 'mainnet', - originator = undefined + originator = undefined, + managerOptions = undefined } = options if (networkPreset === 'teratestnet' && host == null) { @@ -257,6 +260,7 @@ export class MessageBoxClient { this.host = normalizeMessageBoxHost(host ?? defaultHost) this.originator = originator + this.managerOptions = managerOptions this.walletClient = walletClient ?? new WalletClient('auto', originator) this.authFetch = new AuthFetch(this.walletClient, undefined, undefined, originator) this.networkPreset = networkPreset @@ -436,7 +440,8 @@ export class MessageBoxClient { const targetHost = normalizeMessageBoxHost(overrideHost ?? this.host) this.socket = AuthSocketClient(targetHost, { wallet: this.walletClient, - originator: this.originator + originator: this.originator, + ...(this.managerOptions !== undefined && { managerOptions: this.managerOptions }) }) this.socket.on('connect', () => { diff --git a/packages/messaging/message-box-client/src/__tests/MessageBoxClient.test.ts b/packages/messaging/message-box-client/src/__tests/MessageBoxClient.test.ts index f1d7dd5b4..0e16ae99c 100644 --- a/packages/messaging/message-box-client/src/__tests/MessageBoxClient.test.ts +++ b/packages/messaging/message-box-client/src/__tests/MessageBoxClient.test.ts @@ -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])) @@ -166,6 +169,45 @@ describe('MessageBoxClient', () => { await expect(connection).resolves.toBeUndefined() }, 10000) + it('Forwards managerOptions to AuthSocketClient when configured', async () => { + const messageBoxClient = new MessageBoxClient({ + walletClient: mockWalletClient, + host: 'https://message-box-us-1.bsvb.tech', + managerOptions: { transports: ['websocket'] } + }) + + 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'] } }) + ) + }, 10000) + + it('Omits managerOptions from AuthSocketClient when not configured', 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 + expect(Object.keys(options)).not.toContain('managerOptions') + }, 10000) + it('Falls back to HTTP when WebSocket is not initialized', async () => { const messageBoxClient = new MessageBoxClient({ walletClient: mockWalletClient, diff --git a/packages/messaging/message-box-client/src/types.ts b/packages/messaging/message-box-client/src/types.ts index f29565b97..2c8e1da7a 100644 --- a/packages/messaging/message-box-client/src/types.ts +++ b/packages/messaging/message-box-client/src/types.ts @@ -14,6 +14,7 @@ import { WalletInterface, type LookupNetworkPreset } from '@bsv/sdk' +import type { AuthSocketClientOptions } from '@bsv/authsocket-client' /** * Configuration options for initializing a MessageBoxClient. @@ -48,6 +49,14 @@ export interface MessageBoxClientOptions { * Originator of the message box client. */ originator?: string + + /** + * Socket.IO manager/socket options forwarded to the underlying AuthSocketClient. + * Use this to override transport selection, e.g. `{ transports: ['websocket'] }` + * against a deployment whose proxy rejects Engine.IO's HTTP polling transport. + * Only affects the socket path; HTTP requests are unchanged. + */ + managerOptions?: AuthSocketClientOptions['managerOptions'] } /** From 3edfda4499fe4855b343ebb76c8a0c5542d76519 Mon Sep 17 00:00:00 2001 From: Kjartan Date: Mon, 24 Aug 2026 17:23:21 +0200 Subject: [PATCH 2/3] CI fix: baselines.json now matches package.json version, updated message-box-client doc --- docs/packages/messaging/message-box-client.md | 9 +++++---- governance/repository-health/baselines.json | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/packages/messaging/message-box-client.md b/docs/packages/messaging/message-box-client.md index 412b48b35..f11ac0d84 100644 --- a/docs/packages/messaging/message-box-client.md +++ b/docs/packages/messaging/message-box-client.md @@ -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' @@ -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 diff --git a/governance/repository-health/baselines.json b/governance/repository-health/baselines.json index dedb65461..7b27c4635 100644 --- a/governance/repository-health/baselines.json +++ b/governance/repository-health/baselines.json @@ -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", From 5a46e3f6ca481d7d5ed261c36c04ec6d33bab642 Mon Sep 17 00:00:00 2001 From: Kjartan Date: Tue, 25 Aug 2026 18:58:25 +0200 Subject: [PATCH 3/3] Change managerOptions to full socketOptions support --- governance/package-release-notes.json | 4 +- .../messaging/message-box-client/CHANGELOG.md | 15 ++++--- .../src/MessageBoxClient.ts | 12 +++--- .../src/__tests/MessageBoxClient.test.ts | 42 ++++++++++++++++--- .../messaging/message-box-client/src/types.ts | 15 ++++--- 5 files changed, 64 insertions(+), 24 deletions(-) diff --git a/governance/package-release-notes.json b/governance/package-release-notes.json index 8883cc482..b99ab6394 100644 --- a/governance/package-release-notes.json +++ b/governance/package-release-notes.json @@ -98,8 +98,8 @@ "name": "@bsv/message-box-client", "publishedVersion": "2.4.0", "releaseType": "minor", - "summary": "Adds an optional managerOptions client option that forwards Socket.IO manager and socket settings to AuthSocketClient, letting callers select transports such as websocket-only against deployments whose proxy rejects Engine.IO HTTP polling. 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. managerOptions is optional and omitted by default, so socket transport negotiation and every HTTP code path are unchanged for existing callers. 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." + "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", diff --git a/packages/messaging/message-box-client/CHANGELOG.md b/packages/messaging/message-box-client/CHANGELOG.md index def328ddb..344bfdac8 100644 --- a/packages/messaging/message-box-client/CHANGELOG.md +++ b/packages/messaging/message-box-client/CHANGELOG.md @@ -15,12 +15,15 @@ All notable changes to this project will be documented in this file. The format ### Added -- Added an optional `managerOptions` client option, forwarded to - `AuthSocketClient` when the live socket is created. Callers can now select - Socket.IO transports (for example `{ transports: ['websocket'] }`) to reach - deployments whose proxy rejects Engine.IO's HTTP polling transport. The - option is omitted entirely when unset, so default transport negotiation and - all HTTP code paths are unchanged. +- 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 diff --git a/packages/messaging/message-box-client/src/MessageBoxClient.ts b/packages/messaging/message-box-client/src/MessageBoxClient.ts index c9df67be2..fb81121a2 100644 --- a/packages/messaging/message-box-client/src/MessageBoxClient.ts +++ b/packages/messaging/message-box-client/src/MessageBoxClient.ts @@ -211,7 +211,7 @@ export class MessageBoxClient { private socketAuthenticated = false private connectionInitPromise?: Promise protected originator?: OriginatorDomainNameStringUnder250Bytes - private readonly managerOptions?: MessageBoxClientOptions['managerOptions'] + private readonly socketOptions?: MessageBoxClientOptions['socketOptions'] /** * @constructor * @param {Object} options - Initialization options for the MessageBoxClient. @@ -219,7 +219,7 @@ export class MessageBoxClient { * @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 {Partial} [options.managerOptions] - Socket.IO manager/socket options forwarded to the underlying AuthSocketClient, e.g. `{ transports: ['websocket'] }`. Omitted when unset. + * @param {Omit} [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. @@ -244,7 +244,7 @@ export class MessageBoxClient { enableLogging = false, networkPreset = 'mainnet', originator = undefined, - managerOptions = undefined + socketOptions = undefined } = options if (networkPreset === 'teratestnet' && host == null) { @@ -260,7 +260,7 @@ export class MessageBoxClient { this.host = normalizeMessageBoxHost(host ?? defaultHost) this.originator = originator - this.managerOptions = managerOptions + this.socketOptions = socketOptions this.walletClient = walletClient ?? new WalletClient('auto', originator) this.authFetch = new AuthFetch(this.walletClient, undefined, undefined, originator) this.networkPreset = networkPreset @@ -439,9 +439,9 @@ export class MessageBoxClient { if (this.socket == null) { const targetHost = normalizeMessageBoxHost(overrideHost ?? this.host) this.socket = AuthSocketClient(targetHost, { + ...this.socketOptions, wallet: this.walletClient, - originator: this.originator, - ...(this.managerOptions !== undefined && { managerOptions: this.managerOptions }) + originator: this.originator }) this.socket.on('connect', () => { diff --git a/packages/messaging/message-box-client/src/__tests/MessageBoxClient.test.ts b/packages/messaging/message-box-client/src/__tests/MessageBoxClient.test.ts index 0e16ae99c..6275fda58 100644 --- a/packages/messaging/message-box-client/src/__tests/MessageBoxClient.test.ts +++ b/packages/messaging/message-box-client/src/__tests/MessageBoxClient.test.ts @@ -169,11 +169,14 @@ describe('MessageBoxClient', () => { await expect(connection).resolves.toBeUndefined() }, 10000) - it('Forwards managerOptions to AuthSocketClient when configured', async () => { + it('Forwards socketOptions to AuthSocketClient when configured', async () => { const messageBoxClient = new MessageBoxClient({ walletClient: mockWalletClient, host: 'https://message-box-us-1.bsvb.tech', - managerOptions: { transports: ['websocket'] } + socketOptions: { + managerOptions: { transports: ['websocket'] }, + maxPendingAuthMessages: 8 + } }) await messageBoxClient.init() @@ -186,11 +189,14 @@ describe('MessageBoxClient', () => { expect(authSocketClientMock).toHaveBeenCalledWith( 'https://message-box-us-1.bsvb.tech', - expect.objectContaining({ managerOptions: { transports: ['websocket'] } }) + expect.objectContaining({ + managerOptions: { transports: ['websocket'] }, + maxPendingAuthMessages: 8 + }) ) }, 10000) - it('Omits managerOptions from AuthSocketClient when not configured', async () => { + 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' @@ -205,7 +211,33 @@ describe('MessageBoxClient', () => { await connection const options = authSocketClientMock.mock.calls[0][1] as Record - expect(Object.keys(options)).not.toContain('managerOptions') + 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 + expect(options.originator).toBe('client.example') + expect(options.wallet).not.toBe(foreignWallet) }, 10000) it('Falls back to HTTP when WebSocket is not initialized', async () => { diff --git a/packages/messaging/message-box-client/src/types.ts b/packages/messaging/message-box-client/src/types.ts index 2c8e1da7a..eea0a92b6 100644 --- a/packages/messaging/message-box-client/src/types.ts +++ b/packages/messaging/message-box-client/src/types.ts @@ -51,12 +51,17 @@ export interface MessageBoxClientOptions { originator?: string /** - * Socket.IO manager/socket options forwarded to the underlying AuthSocketClient. - * Use this to override transport selection, e.g. `{ transports: ['websocket'] }` - * against a deployment whose proxy rejects Engine.IO's HTTP polling transport. - * Only affects the socket path; HTTP requests are unchanged. + * 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. */ - managerOptions?: AuthSocketClientOptions['managerOptions'] + socketOptions?: Omit } /**