Skip to content
LogoLogo

mppx.session.serveWebSocket

WebSocket session payments

Choose a signing account

Create a server-only wallet.ts module, then import account wherever an example creates a local signing account.

wallet.ts
import { privateKeyToAccount } from 'viem/accounts'

export const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)

Bridges a WebSocket connection to a Tempo Session using the method's configured store and settlement schedule.

Usage

import { , ,  } from 'mppx/server'
import {  } from 'viem/accounts'
 
const  = ['hello', 'world']
const  = .()
 
const  = .({
  : [
    .({
      : ('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'),
      : '0x20c0000000000000000000000000000000000000', // pathUSD on Tempo
      : { : 100 },
      ,
    }),
  ],
})
 
const  = .({ : '0.001', : 'word' })
declare const : <typeof ..>[0]['socket']
 
// In your WebSocket handler:
await ..({
  : async function* () {
    for (const  of ) {
      await .()
      yield 
    }
  },
  ,
  ,
  : 'wss://api.example.com/stream',
})

The helper shares the Store and settlementSchedule configured by tempo.session(). It applies the schedule after every committed WebSocket charge.

Return type

type ReturnType = Promise<void>

Resolves when the WebSocket session completes or the connection closes.

Parameters

amount (optional)

  • Type: string

Expected per-tick amount. When set, Credentials with mismatched amounts are rejected.

generate

  • Type: AsyncIterable<string> | ((stream: SessionController) => AsyncIterable<string>)

Async iterable that produces application messages. When passed as a function, receives a SessionController with a charge() method for requesting payment before yielding each value. Each yielded string is sent to the client as an application message frame.

pollIntervalMs (optional)

  • Type: number
  • Default: 100

Polling interval in milliseconds for voucher balance checks.

route

  • Type: Parameters<typeof mppx.session.serveWebSocket>[0]['route']

Session route handler. Receives synthetic POST requests constructed from in-band authorization frames. The synthetic request carries only the Authorization header—no cookies, bodies, query parameters, or other headers from the original WebSocket upgrade request.

import { ,  } from 'mppx/server'
import {  } from 'viem/accounts'
 
const  = .({
  : [
    .({
      : ('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'),
    }),
  ],
})
 
const : <typeof ..>[0]['route'] =
  .({ : '0.001', : 'word' })

socket

  • Type: Parameters<typeof mppx.session.serveWebSocket>[0]['socket']

WebSocket instance. Accepts a browser WebSocket, a ws library socket, or any object implementing send/close with either addEventListener/removeEventListener or on/off.

url

  • Type: string

URL used for constructing synthetic route requests. ws:// and wss:// schemes are normalized to http:// and https://.

Advanced options

Use tempo.Ws.serve() when a custom integration manages its own store or post-charge behavior. Pass onChargeCommitted to run a hook after each nonzero stream charge commits. The legacy settleScheduled option remains as a deprecated alias.

import { , ,  } from 'mppx/server'
import {  } from 'viem/accounts'
 
const  = .()
const  = .({
  : [
    .({
      : ('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'),
      : { : 100 },
      ,
    }),
  ],
})
 
const  = .({ : '0.001', : 'word' })
declare const : <typeof ..>[0]['socket']
 
await ..({
  : async function* () {
    await .()
    yield 'hello'
    await .()
    yield 'world'
  },
  : ..,
  ,
  ,
  ,
  : 'wss://api.example.com/stream',
})

Credential verification routes each in-band authorization frame through route as a synthetic POST request carrying only the Authorization header. It doesn't include cookies, bodies, query parameters, or other headers from the original WebSocket upgrade request.

Helper functions

tempo.Ws.parseMessage

Parses a raw WebSocket message string into a typed Message.

const message = tempo.Ws.parseMessage(raw)

tempo.Ws.formatAuthorizationMessage

Formats an authorization string into a message frame.

const frame = tempo.Ws.formatAuthorizationMessage(authorization)

tempo.Ws.formatApplicationMessage

Formats application data into a message frame.

const frame = tempo.Ws.formatApplicationMessage(data)

tempo.Ws.formatCloseRequestMessage

Formats a close request message frame.

const frame = tempo.Ws.formatCloseRequestMessage()

tempo.Ws.formatReceiptMessage

Formats a Receipt into a message frame.

const frame = tempo.Ws.formatReceiptMessage(receipt)

tempo.Ws.formatErrorMessage

Formats an error into a message frame.

const frame = tempo.Ws.formatErrorMessage({ message, status })

Types

Message

type Message =
  | { mpp: 'authorization'; authorization: string }
  | { mpp: 'message'; data: string }
  | { mpp: 'payment-close-request' }
  | { mpp: 'payment-close-ready'; data: SessionReceipt }
  | { mpp: 'payment-error'; status: number; message: string }
  | { mpp: 'payment-need-voucher'; data: NeedVoucherEvent }
  | { mpp: 'payment-receipt'; data: SessionReceipt }

Socket

type Socket = {
  close(code?: number, reason?: string): unknown
  send(data: string): unknown
  addEventListener?: (type: string, listener: (event: any) => void) => unknown
  removeEventListener?: (type: string, listener: (event: any) => void) => unknown
  on?: (type: string, listener: (...args: any[]) => void) => unknown
  off?: (type: string, listener: (...args: any[]) => void) => unknown
}

SessionRoute

type SessionRouteResult =
  | { status: 402; challenge: Response }
  | { status: 200; withReceipt(response?: Response): Response }
 
type SessionRoute = (request: Request) => Promise<SessionRouteResult>