LogoPear Docs

Structured RPC and schema-first design

Layering typed request/response calls on top of a raw IPC duplex stream with bare-rpc and hrpc, then generating the wire format from one schema with hyperschema, hyperdb, and hyperdispatch.

Raw Inter-Process Communication (IPC) carries bytes, not typed messages. For a handful of one-off signals, length-preserving writes or newline-delimited JSON may be enough (see Compact encoding for binary framing). As a protocol between two processes grows, manually pairing requests and responses becomes error-prone.

This page assumes two Bare (or Bare-and-host) processes talking over an IPC duplex stream—for example a Pear worker and its host. The same code applies to any process pair that shares a duplex stream.

Structured RPC over IPC

bare-rpc adds a thin Remote Procedure Call (RPC) layer on top of a duplex stream. Each message has a numeric command id and a payload; handlers dispatch on req.command:

import RPC from 'bare-rpc'

export const RPC_MESSAGE = 1

const rpc = new RPC(Bare.IPC, (req) => {
  if (req.command === RPC_MESSAGE) {
    console.log(req.data.toString())
  }
})

const req = rpc.request(RPC_MESSAGE)
req.send(Buffer.from('Hello from worker'))

Share command constants between both sides (a shared commands.mjs module works well). bare-rpc scales to moderate protocols but still leaves schema, versioning, and method naming to you.

hrpc generates typed client and server stubs from a schema. Define request and response types (often via hyperschema), register methods, run the code generator, and import the result:

import HRPC from './spec/hrpc/index.js'

const rpc = new HRPC(Bare.IPC)

rpc.onHello(({ world }) => ({
  message: `Hello ${world}, from worker`
}))

await rpc.hello({ world: 'host' })

The other side constructs new HRPC(IPC) with the same generated module. Method names and encodings stay in sync because both sides compile from one definition.

ApproachBest for
Raw IPCPrototypes, single-channel streaming
bare-rpcSmall fixed command sets, mobile worklets
HRPCMany methods, evolving schemas, shared types

Stay on raw IPC until message pairing hurts; adopt HRPC when the protocol is large enough that hand-maintained command ids become a maintenance burden.

Schema-first design

Larger apps push this further: instead of hand-writing encoders, they declare every data shape once and generate the byte-level machinery from that single definition. In a peer-to-peer app this is not just ergonomics—it's a correctness requirement, for two reasons a client–server app doesn't face:

No server normalizes the wire format

Peers replicate raw bytes directly to each other, and any two peers may be running different builds. If one peer encodes a message differently than another decodes it, replication produces garbage—there's no central authority to reconcile them. Every peer has to agree on the format ahead of time.

Append-only logs are immutable and permanent

Blocks written to a Hypercore or Autobase are signed and replicated forever; you can't migrate them later. Today's encoding must still decode in next year's build, so the format has to evolve in a backward-compatible way (add optional fields, never renumber existing ones).

The schema-first toolchain solves both by deriving everything from one declaration. A single schema file (run via a build step like npm run build:db) typically generates:

  • hyperschema—the canonical field definitions every other generator consumes, using stable field numbering so additions stay compatible.
  • hyperdb—typed, compactly-encoded collections for what's stored on disk.
  • hyperdispatch—typed encoders for the payloads appended to an Autobase.
  • hrpc—the typed RPC stubs from the section above.

Because storage, replication, and the IPC contract are all generated from the same source, they can't drift out of sync, and the generated code is the part you don't edit by hand. This is the role a database schema and API contract play in a client–server stack—but pushed down to the wire and disk format so every peer and every version shares it. A production Autobase-backed chat room walks a concrete schema.js and its generated spec/ directory.

See also

On this page