Skip to content

Packages

Typed RPC

Schema-backed procedures and inferred clients over the existing P2P connection.

Verified 16 October 2026

Experimental alpha API

The optional /rpc entry targets the 0.6.0-alpha.2 source candidate; it is absent from published alpha.1. Existing plain packs, peer.call, peer.stream and peer.api remain available. No tRPC or validator package is required.

Define a capability once and share its runtime contract with clients. Query and mutation methods return ordinary promises; stream methods return async iterables. TypeScript infers inputs and results while runtime schemas check peer data.

ts
import { defineCapability, procedure } from '@unstoppable-stack/p2p/rpc'

const noteId = (value: unknown): { id: string } => {
  if (typeof (value as { id?: unknown })?.id !== 'string') throw new Error('Expected id')
  return { id: (value as { id: string }).id }
}
const notes = defineCapability({
  name: 'notes', version: 1,
  methods: {
    get: procedure.input(noteId).query(({ input }) => ({ id: input.id, text: 'Hello' }))
  },
  defaultGrants: ['get']
})
// Host: createNode({ storage, capabilities: [notes] })
// Client: obtain a paired peer and use the shared contract.
const api = peer.client(notes.contract)
const note = await api.get({ id: 'n1' })
  • Use procedure.input(schema).output(schema).query(handler), .mutation(handler) or .stream(handler). Validators can be parser functions, parse() objects or Standard Schema implementations.
  • Input transformations run once on the host. Output schemas must be idempotent JSON wire validators; stripped fields are removed before transmission and clients validate received output again.
  • Return JSON values: dates, maps, undefined, cycles and nonfinite numbers are rejected. Use null for an action with no result data.
  • Binding checks service version, method presence and kind. Partial grants are valid; ungranted calls still fail with NOT_GRANTED. Type inference does not authorize a peer.
  • Calls accept signal and timeout. Cancellation is cooperative; no mutation or stream is replayed automatically after reconnect. Obtain the replacement peer and bind a fresh client.

Keep contracts and validators in a shared application module. Bind the P2P client in the worker and expose app-owned IPC to the renderer; validator functions cannot be sent as JSON. Streams retain finite request and output budgets and do not implement durable subscriptions.

Need help? Report an issue with your CLI version, platform, command and first relevant error.