Api
WebSocket Activity Stream
Authenticated real-time WebSocket events for droplit activity.
WebSocket Activity Stream
Use the activity stream endpoint to receive real-time droplit events such as taps, pushes, deposits, and sync activity.
Endpoint
GET /faucet/{faucetName}/activity-stream
Replace {faucetName} with your droplit name.
Authentication
The HTTP request upgrades to a WebSocket, then the SDK's Peer performs the
BRC-103/104 handshake over that connection. Every protected route uses this
mutual-authentication protocol.
Event types
After authentication, the server sends:
connected: ownership is verified and the stream is readyping: keep-alive eventfaucet_activity: primary activity payloaderror: authentication, authorization, or stream failure
Payload shape
type ActivityStreamMessage =
| { type: "connected"; data: { message: string } }
| { type: "ping"; data: { time: string } }
| { type: "faucet_activity"; data: FaucetActivityItem }
| { type: "error"; status: number; error: string };Application messages arrive as signed Peer general-message payloads.
Browser transport
The SDK defines the transport interface while Peer owns all signing, nonce,
certificate, and session logic:
import {
Peer,
type AuthMessage,
type Transport,
type WalletInterface,
} from "@bsv/sdk";
class WebSocketTransport implements Transport {
private pending: Array<{
message: string;
resolve: () => void;
}> = [];
constructor(private socket: WebSocket) {
socket.addEventListener("open", () => {
for (const item of this.pending.splice(0)) {
socket.send(item.message);
item.resolve();
}
});
}
async send(message: AuthMessage): Promise<void> {
const serialized = JSON.stringify(message);
if (this.socket.readyState === WebSocket.OPEN) {
this.socket.send(serialized);
return;
}
await new Promise<void>((resolve) => {
this.pending.push({ message: serialized, resolve });
});
}
async onData(
callback: (message: AuthMessage) => Promise<void>,
): Promise<void> {
this.socket.addEventListener("message", (event) => {
void callback(JSON.parse(String(event.data)) as AuthMessage);
});
}
}
export async function connectActivityStream(
apiBaseUrl: string,
faucetName: string,
wallet: WalletInterface,
onEvent: (event: ActivityStreamMessage) => void,
): Promise<WebSocket> {
const url = new URL(`/faucet/${faucetName}/activity-stream`, apiBaseUrl);
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
const socket = new WebSocket(url);
const peer = new Peer(wallet, new WebSocketTransport(socket));
peer.listenForGeneralMessages((_serverIdentityKey, payload) => {
const json = new TextDecoder().decode(new Uint8Array(payload));
onEvent(JSON.parse(json) as ActivityStreamMessage);
});
await peer.ready;
await peer.getAuthenticatedSession();
return socket;
}