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.
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.