@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 – A store-and-forward messaging system backed by a "message box" server. It supports authenticated sending, listing, and acknowledging (deleting) messages with a mutual-auth approach.
- PeerPayClient – A higher-level payment client built on top of
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.
MessageBoxClient
implements a store-and-forward architecture for P2P messages:
- Store-and-forward: Messages are posted to a central MessageBoxServer under a named "message box" (like an inbox).
- Ephemeral storage: Once the recipient acknowledges the messages, they are removed from the server.
- Mutual authentication: Ensures only authorized peers can read or post messages, using AuthFetch and AuthSocketClient.
- Flexible transport: Supports both WebSockets (for live, push-style delivery) and HTTP (for polling or fallback).
- Extensible: Can be the foundation for more advanced workflows (e.g., token-based messaging, invoice/ticket systems, etc.).
- Secure by default using Auth libraries for signing/verification.
- Real-time or delayed delivery with sockets or HTTP.
- Easy integration with higher-level protocols and services.
PeerPayClient
builds on MessageBoxClient
to enable peer-to-peer Bitcoin payments:
- Secure Payment Delivery: Utilizes the same store-and-forward or live WebSocket approach for delivering payment instructions.
- Derivation & Signing: Creates a unique output script for each payment, derived from sender + recipient keys.
- Live or Delayed: Works with web sockets for immediate notifications, or HTTP for an asynchronous flow.
- Wallet Integration: Accept or reject incoming payments. If accepted, the payment is “internalized” into your BRC-100 compatible wallet automatically.
- Deterministic derivation of payment information using the SPV-compliant BRC-29 protocol.
- Secure transaction passing using the
MessageBoxClient
infrastructure. - Live or offline support for receiving payments.
- Easy acceptance/refunds with built-in methods.
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
})
// (Optional) Initialize a WebSocket connection (for real-time listening)
await msgBoxClient.initializeConnection()
// 3) Send a message to John's "demo_inbox"
await msgBoxClient.sendMessage({
recipient: johnSmithKey,
messageBox: 'demo_inbox',
body: 'Hello John! This is a test message.'
})
// 4) 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."
// 5) Acknowledge (and delete) them from the server
await msgBoxClient.acknowledgeMessage({
messageIds: messages.map(msg => msg.messageId.toString())
})
}
main().catch(console.error)
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.
import { MessageBoxClient } from '@bsv/p2p'
new MessageBoxClient({
host?: string,
walletClient: WalletClient
})
- host: (Optional) Base URL of the MessageBoxServer. Defaults to
https://messagebox.babbage.systems
. - walletClient: A WalletClient instance for identity key and signing.
await msgBoxClient.initializeConnection()
- Establishes a WebSocket connection to
host
. - Authenticates with your wallet’s identity key.
await msgBoxClient.listenForLiveMessages({
messageBox: 'demo_inbox',
onMessage: (msg) => {
console.log('New message in "demo_inbox":', msg)
}
})
- Joins a WebSocket "room" for the specified
messageBox
. - Executes
onMessage
callback whenever a new message arrives.
const result = await msgBoxClient.sendLiveMessage({
recipient: johnSmithKey,
messageBox: 'demo_inbox',
body: 'Hello in real-time!'
})
- Sends a message via WebSockets (falls back to HTTP if the socket is not connected).
- recipient: Hex-encoded public key of the recipient.
- messageBox: Name of the box (e.g.,
"demo_inbox"
). - body: Message payload (string or object).
Returns a SendMessageResponse
with { status: 'success', messageId }
on success.
const response = await msgBoxClient.sendMessage({
recipient: johnSmithKey,
messageBox: 'demo_inbox',
body: 'Hello via HTTP!'
})
- Sends the message via HTTP only.
- recipient: Recipient's identity key.
- messageBox: Name of the box.
- body: Message content (string or object).
Returns { status: 'success', messageId }
on success.
const messages = await msgBoxClient.listMessages({ messageBox: 'demo_inbox' })
- Lists messages in the specified
messageBox
. - Returns an array of
PeerMessage
.
interface PeerMessage {
messageId: number;
body: string;
sender: string;
created_at: string;
updated_at: string;
acknowledged?: boolean;
}
await msgBoxClient.acknowledgeMessage({
messageIds: ['1234', '5678']
})
- Acknowledges (and deletes) the specified messages from the server.
messageIds
: Array of message IDs (as strings).
import { PeerPayClient } from '@bsv/p2p'
new PeerPayClient({
walletClient: WalletClient,
messageBoxHost?: string,
enableLogging?: boolean
})
- walletClient: (Required) Your identity/signing wallet.
- messageBoxHost: (Optional) Base URL of the MessageBoxServer. Defaults to
https://messagebox.babbage.systems
. - enableLogging: (Optional) Enables verbose debug output.
await peerPay.sendPayment({
recipient: '0277a2b...',
amount: 10000
})
- Sends a payment using HTTP.
- Internally derives a public key for the recipient and builds a transaction.
await peerPay.sendLivePayment({
recipient: '0277a2b...',
amount: 15000
})
- Sends a payment using WebSockets, falling back to HTTP if needed.
await peerPay.listenForLivePayments({
onPayment: (payment) => {
console.log('New live payment:', payment)
}
})
- Subscribes to live payments in the
payment_inbox
. - Invokes
onPayment
callback with anIncomingPayment
object:
interface IncomingPayment {
messageId: number;
sender: string;
token: {
customInstructions: {
derivationPrefix: string;
derivationSuffix: string;
};
transaction: any; // typically your BSV transaction format
amount: number;
};
}
await peerPay.acceptPayment(payment)
- Accepts (and "internalizes") the payment into your wallet.
- Acknowledges the message, removing it from the inbox.
await peerPay.rejectPayment(payment)
- Rejects the payment, returning a refund to the sender (minus a small fee, e.g. 1000 sats).
- If the amount is too small to refund, the payment is simply acknowledged and dropped.
const payments = await peerPay.listIncomingPayments()
- Lists all incoming payments in the
payment_inbox
. - Returns an array of
IncomingPayment
objects.
- Clone this repository.
- Install dependencies with
npm install
. - Make your changes, write tests, and open a PR.
We welcome bug reports, feature requests, and community contributions!
This code is licensed under the Open BSV License. See LICENSE for details.