
Security News
How Enterprise Security Is Adapting to AI-Accelerated Threats
Socket CTO Ahmad Nassri discusses why supply chain attacks now target developer machines and what AI means for the future of enterprise security.
@bsv/p2p is a toolkit for peer-to-peer messaging and payments on the BSV blockchain. It leverages a server-side store-and-forward system for message delivery (via MessageBoxClient) and also includes a higher-level peer-to-peer payment flow (via PeerPayClient). Both functionalities build on BRC-103 for mutual authentication and identity key management, allowing secure and authenticated exchanges of data and BSV.
The @bsv/p2p library provides two main tools for peer-to-peer interaction:
MessageBoxClient, enabling real-time, peer-to-peer Bitcoin payments on the BSV blockchain.Both clients use the BRC-103-based authentication model. By integrating with a WalletClient, they can sign and verify messages, ensuring only authorized parties can send and receive.
npm install @bsv/p2p
The package exports both MessageBoxClient and PeerPayClient. You can import them individually in your JavaScript/TypeScript applications.
The P2P integration tests verify live communication between the MessageBoxClient and a running instance of the MessageBox Server. To run them, make sure the following are set up:
Prerequisites
git clone https://github.com/bitcoin-sv/messagebox-server.git
cd messagebox-server
cp .env.example .env
npm install
npm run start # Starts LARS (overlay service on http://localhost:8080)
npm run dev # Starts MessageBox Server (HTTP API on http://localhost:5001)
NODE_ENV=development
BSV_NETWORK=local
ENABLE_WEBSOCKETS=true
and valid values for:
Running the Tests Once the MessageBox Server and overlay service are running:
npm run test:integration
This will execute all integration tests under src/tests/integration/, including HTTP, WebSocket, encryption, and overlay scenarios.
Notes
Quick Summary
MessageBoxClient uses a store-and-forward architecture for Peer-to-Peer messages:
Important:
Starting with version @bsv/p2p@1.2.0, MessageBoxClient automatically initializes itself if needed.
You may still manually call await init() if you want to control when initialization happens.
Example:
const client = new MessageBoxClient({ walletClient, host: 'https://messagebox.babbage.systems' })
// Manual init (optional but supported)
await client.init()
// Or just start sending — init will auto-run if needed
await client.sendMessage({ recipient, messageBox: 'inbox', body: 'Hello' })
Example:
const client = new MessageBoxClient({ walletClient, host: 'https://messagebox.babbage.systems' })
await client.init() // Must always call init() before using the client
await client.sendMessage({ recipient, messageBox: 'inbox', body: 'Hello' })
The init() method will:
Anoint your messagebox host onto the overlay network if necessary.
Initialize the client’s internal identity and network information.
Ensure that your client can receive messages securely from peers.
PeerPayClient builds on MessageBoxClient to enable peer-to-peer Bitcoin payments:
MessageBoxClient infrastructure.Below are two condensed examples: one for basic messaging (MessageBoxClient) and another for peer-to-peer payments (PeerPayClient).
const { WalletClient } = require('@bsv/sdk')
const { MessageBoxClient } = require('@bsv/p2p')
// Example identity key of the recipient (public key in hex)
const johnSmithKey = '022600d2ef37d123fdcac7d25d7a464ada7acd3fb65a0daf85412140ee20884311'
async function main() {
// 1) Create your WalletClient (this obtains your identity key)
const myWallet = new WalletClient()
// 2) Create a MessageBoxClient, pointing to a MessageBoxServer
const msgBoxClient = new MessageBoxClient({
host: 'https://messagebox.babbage.systems',
walletClient: myWallet
})
// 3) (Optional) Initialize the client manually
await msgBoxClient.init()
// (Optional) Initialize a WebSocket connection (for real-time listening)
await msgBoxClient.initializeConnection()
// 4) Send a message to John's "demo_inbox"
await msgBoxClient.sendMessage({
recipient: johnSmithKey,
messageBox: 'demo_inbox',
body: 'Hello John! This is a test message.'
})
// 5) List messages in "demo_inbox"
const messages = await msgBoxClient.listMessages({ messageBox: 'demo_inbox' })
console.log(messages[0].body) // --> "Hello John! This is a test message."
// 6) Acknowledge (and delete) them from the server
await msgBoxClient.acknowledgeMessage({
messageIds: messages.map(msg => msg.messageId.toString())
})
}
main().catch(console.error)
Note:
await init(), the client will automatically initialize itself when you use it.await init() manually if you want explicit control.init() ensures your identity is properly registered and discoverable via overlay advertisement if necessary.Listening for Live Messages
If you want push-style message notifications instead of polling:
await msgBoxClient.listenForLiveMessages({
messageBox: 'demo_inbox',
onMessage: (msg) => {
console.log('Received live message in "demo_inbox":', msg.body)
}
})
import { WalletClient } from '@bsv/sdk'
import { PeerPayClient } from '@bsv/p2p'
async function paymentDemo() {
// 1) Create your wallet instance
const wallet = new WalletClient()
// 2) Create a PeerPayClient
const peerPay = new PeerPayClient({
walletClient: wallet
})
// 3) (Optional) Listen for incoming payments
await peerPay.listenForLivePayments({
onPayment: async (payment) => {
console.log('Received payment:', payment)
// Accept it into the wallet
await peerPay.acceptPayment(payment)
}
})
// 4) Send a payment of 50,000 sats to the recipient
await peerPay.sendLivePayment({
recipient: '0277a2b...e3f4', // recipient's public key
amount: 50000
})
}
paymentDemo().catch(console.error)
Note: sendLivePayment will try WebSocket first and fall back to HTTP if unavailable.
### 5.1. MessageBoxClient API
<!--#region ts2md-api-merged-here-->
Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes)
##### Interfaces
| |
| --- |
| [AcknowledgeMessageParams](#interface-acknowledgemessageparams) |
| [EncryptedMessage](#interface-encryptedmessage) |
| [ListMessagesParams](#interface-listmessagesparams) |
| [MessageBoxClientOptions](#interface-messageboxclientoptions) |
| [PeerMessage](#interface-peermessage) |
| [SendMessageParams](#interface-sendmessageparams) |
| [SendMessageResponse](#interface-sendmessageresponse) |
Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes)
---
###### Interface: AcknowledgeMessageParams
Defines the structure of a request to acknowledge messages.
Example
```ts
{
messageIds: ["abc123", "def456"]
}
export interface AcknowledgeMessageParams {
messageIds: string[];
}
Links: API, Interfaces, Classes
Encapsulates an AES-256-GCM encrypted message body.
Used when transmitting encrypted payloads to the MessageBox server.
export interface EncryptedMessage {
encryptedMessage: Base64String;
}
Links: API, Interfaces, Classes
Defines the structure of a request to list messages.
Example
{
messageBox: "payment_inbox"
}
export interface ListMessagesParams {
messageBox: string;
}
Links: API, Interfaces, Classes
Configuration options for initializing a MessageBoxClient.
export interface MessageBoxClientOptions {
walletClient?: WalletClient;
host?: string;
enableLogging?: boolean;
networkPreset?: "local" | "mainnet" | "testnet";
}
####### Property enableLogging
If true, enables detailed logging to the console.
enableLogging?: boolean
####### Property host
Base URL of the MessageBox server.
host?: string
####### Property networkPreset
Overlay network preset for routing resolution.
networkPreset?: "local" | "mainnet" | "testnet"
####### Property walletClient
Wallet instance used for auth, identity, and encryption. If not provided, a new WalletClient will be created.
walletClient?: WalletClient
Links: API, Interfaces, Classes
Represents a decrypted message received from a MessageBox. Includes metadata such as sender identity, timestamps, and optional acknowledgment status.
Used in both HTTP and WebSocket message retrieval responses.
export interface PeerMessage {
messageId: string;
body: string;
sender: string;
created_at: string;
updated_at: string;
acknowledged?: boolean;
}
Links: API, Interfaces, Classes
Parameters required to send a message. Message content may be a string or object, and encryption is enabled by default.
Example
{
recipient: "03abc...",
messageBox: "payment_inbox",
body: { type: "ping" },
skipEncryption: false
}
export interface SendMessageParams {
recipient: string;
messageBox: string;
body: string | object;
messageId?: string;
skipEncryption?: boolean;
}
Links: API, Interfaces, Classes
Server response structure for successful message delivery.
Returned by both sendMessage and sendLiveMessage.
export interface SendMessageResponse {
status: string;
messageId: string;
}
Links: API, Interfaces, Classes
| Logger |
| MessageBoxClient |
Links: API, Interfaces, Classes
export class Logger {
static enable(): void
static disable(): void
static log(...args: unknown[]): void
static warn(...args: unknown[]): void
static error(...args: unknown[]): void
}
Links: API, Interfaces, Classes
Example
const client = new MessageBoxClient({ walletClient, enableLogging: true })
// Manual init is optional — client will auto-initialize if needed
await client.init()
await client.sendMessage({ recipient, messageBox: 'payment_inbox', body: 'Hello world' })
export class MessageBoxClient {
public readonly authFetch: AuthFetch;
constructor(options: MessageBoxClientOptions = {})
async init(targetHost: string = this.host): Promise<void>
public getJoinedRooms(): Set<string>
public async getIdentityKey(): Promise<string>
public get testSocket(): ReturnType<typeof AuthSocketClient> | undefined
async initializeConnection(): Promise<void>
async resolveHostForRecipient(identityKey: string): Promise<string>
async joinRoom(messageBox: string): Promise<void>
async listenForLiveMessages({ onMessage, messageBox }: {
onMessage: (message: PeerMessage) => void;
messageBox: string;
}): Promise<void>
async sendLiveMessage({ recipient, messageBox, body, messageId, skipEncryption }: SendMessageParams): Promise<SendMessageResponse>
async leaveRoom(messageBox: string): Promise<void>
async disconnectWebSocket(): Promise<void>
async sendMessage(message: SendMessageParams, overrideHost?: string): Promise<SendMessageResponse>
async anointHost(host: string): Promise<{
txid: string;
}>
async listMessages({ messageBox }: ListMessagesParams): Promise<PeerMessage[]>
async acknowledgeMessage({ messageIds }: AcknowledgeMessageParams): Promise<string>
}
See also: AcknowledgeMessageParams, ListMessagesParams, MessageBoxClientOptions, PeerMessage, SendMessageParams, SendMessageResponse
####### Constructor
constructor(options: MessageBoxClientOptions = {})
See also: MessageBoxClientOptions
Argument Details
Example
const client = new MessageBoxClient({
host: 'https://messagebox.example',
walletClient,
enableLogging: true,
networkPreset: 'testnet'
})
await client.init()
####### Method acknowledgeMessage
async acknowledgeMessage({ messageIds }: AcknowledgeMessageParams): Promise<string>
See also: AcknowledgeMessageParams
Returns
'success'.Argument Details
Throws
If the message ID array is missing or empty, or if the request to the server fails.
Example
await client.acknowledgeMessage({ messageIds: ['msg123', 'msg456'] })
####### Method anointHost
async anointHost(host: string): Promise<{
txid: string;
}>
Returns
Argument Details
Throws
If the URL is invalid, the PushDrop creation fails, or the overlay broadcast does not succeed.
Example
const { txid } = await client.anointHost('https://my-messagebox.io')
####### Method disconnectWebSocket
async disconnectWebSocket(): Promise<void>
Returns
Resolves when the WebSocket connection is successfully closed.
Example
await client.disconnectWebSocket()
####### Method getIdentityKey
public async getIdentityKey(): Promise<string>
Returns
The identity public key of the user
####### Method getJoinedRooms
public getJoinedRooms(): Set<string>
Returns
A set of currently joined WebSocket room IDs
####### Method init
async init(targetHost: string = this.host): Promise<void>
Argument Details
Throws
If no valid host is provided, or anointing fails.
Example
const client = new MessageBoxClient({ host: 'https://mybox.example', walletClient })
await client.init()
await client.sendMessage({ recipient, messageBox: 'inbox', body: 'Hello' })
####### Method initializeConnection
async initializeConnection(): Promise<void>
Throws
If the identity key is unavailable or authentication fails
Example
const mb = new MessageBoxClient({ walletClient })
await mb.initializeConnection()
// WebSocket is now ready for use
####### Method joinRoom
async joinRoom(messageBox: string): Promise<void>
Argument Details
Example
await client.joinRoom('payment_inbox')
// Now listening for real-time messages in room '028d...-payment_inbox'
####### Method leaveRoom
async leaveRoom(messageBox: string): Promise<void>
Argument Details
payment_inbox).Example
await client.leaveRoom('payment_inbox')
####### Method listMessages
async listMessages({ messageBox }: ListMessagesParams): Promise<PeerMessage[]>
See also: ListMessagesParams, PeerMessage
Returns
PeerMessage objects.Argument Details
Throws
If no messageBox is specified, the request fails, or the server returns an error.
Example
const messages = await client.listMessages({ messageBox: 'inbox' })
messages.forEach(msg => console.log(msg.sender, msg.body))
####### Method listenForLiveMessages
async listenForLiveMessages({ onMessage, messageBox }: {
onMessage: (message: PeerMessage) => void;
messageBox: string;
}): Promise<void>
See also: PeerMessage
Argument Details
Example
await client.listenForLiveMessages({
messageBox: 'payment_inbox',
onMessage: (msg) => console.log('Received live message:', msg)
})
####### Method resolveHostForRecipient
async resolveHostForRecipient(identityKey: string): Promise<string>
Returns
Argument Details
Example
const host = await resolveHostForRecipient('028d...') // → returns either overlay host or this.host
####### Method sendLiveMessage
async sendLiveMessage({ recipient, messageBox, body, messageId, skipEncryption }: SendMessageParams): Promise<SendMessageResponse>
See also: SendMessageParams, SendMessageResponse
Returns
A success response with the generated messageId.
Argument Details
Throws
If message validation fails, HMAC generation fails, or both WebSocket and HTTP fail to deliver.
Example
await client.sendLiveMessage({
recipient: '028d...',
messageBox: 'payment_inbox',
body: { amount: 1000 }
})
####### Method sendMessage
async sendMessage(message: SendMessageParams, overrideHost?: string): Promise<SendMessageResponse>
See also: SendMessageParams, SendMessageResponse
Returns
{ status, messageId } on success.Argument Details
Throws
If validation, encryption, HMAC, or network request fails.
Example
await client.sendMessage({
recipient: '03abc...',
messageBox: 'notifications',
body: { type: 'ping' }
})
Links: API, Interfaces, Classes
import { PeerPayClient } from '@bsv/p2p'
new PeerPayClient({
walletClient: WalletClient,
messageBoxHost?: string,
enableLogging?: boolean
})
https://messagebox.babbage.systems.sendPayment({ recipient, amount })await peerPay.sendPayment({
recipient: '0277a2b...',
amount: 10000
})
sendLivePayment({ recipient, amount })await peerPay.sendLivePayment({
recipient: '0277a2b...',
amount: 15000
})
listenForLivePayments({ onPayment })await peerPay.listenForLivePayments({
onPayment: (payment) => {
console.log('New live payment:', payment)
}
})
payment_inbox.onPayment callback with an IncomingPayment object:interface IncomingPayment {
messageId: number;
sender: string;
token: {
customInstructions: {
derivationPrefix: string;
derivationSuffix: string;
};
transaction: any; // typically your BSV transaction format
amount: number;
};
}
acceptPayment(payment)await peerPay.acceptPayment(payment)
rejectPayment(payment)await peerPay.rejectPayment(payment)
listIncomingPayments()const payments = await peerPay.listIncomingPayments()
payment_inbox.IncomingPayment objects.npm install.We welcome bug reports, feature requests, and community contributions!
This code is licensed under the Open BSV License. See LICENSE for details.
FAQs
A client for P2P messaging and payments
We found that @bsv/p2p demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Socket CTO Ahmad Nassri discusses why supply chain attacks now target developer machines and what AI means for the future of enterprise security.

Security News
Learn the essential steps every developer should take to stay secure on npm and reduce exposure to supply chain attacks.

Security News
Experts push back on new claims about AI-driven ransomware, warning that hype and sponsored research are distorting how the threat is understood.