Remote Actors

Run an actor in a separate OS process, bridged over two named pipes. One import defines both ends of the wire.

defineRemoteActor

import { defineRemoteActor } from "posipaki/remote";

const { actor, runRemoteRoot, isRemoteRoot } = defineRemoteActor(
  echoActor,
  import.meta.url,
);

Wraps a normal defineActor definition and returns:

One file, both ends

The same module works as host and child. defineRemoteActor detects the child via a --remote=<hash> argument in process.argv and starts the remote side automatically. Otherwise you are the host and spawn as usual:

import { defineActor, defineMessages } from "posipaki";
import { defineRemoteActor } from "posipaki/remote";

const echoActor = defineActor({
  name: "echo",
  inMessages: defineMessages<{ type: "PING"; count: number }>(),
  outMessages: defineMessages<{ type: "PONG"; count: number }>(),
  setup: () => ({ pings: 0 }),
  handlers: {
    PING(msg) {
      this.state.pings++;
      this.emit({ type: "PONG", count: msg.count });
    },
  },
});

const { actor: remoteEcho, isRemoteRoot } = defineRemoteActor(echoActor, import.meta.url);

if (!isRemoteRoot) {
  const proc = await remoteEcho.spawn({});
  await proc.ready();
  proc.send({ type: "PING", count: 1 });
  await proc.wait();
}

When the child's actor exits, the host's proc.wait() resolves — the same shutdown story as a local spawn.

Live state

The child streams state updates over the wire, so proc.state on the host tracks the child's current state exactly like a local process.

Connectors

defineRemoteActor(actor, url, { connector }) picks how the child is launched:

import { defineRemoteActor, nodeConnector } from "posipaki/remote";

const { actor } = defineRemoteActor(echoActor, import.meta.url, {
  connector: nodeConnector,
});

RemoteProxy

The bridge between host and child. defineRemoteActor builds one for you; you can also write your own Connector that returns one:

Wire protocol

Host and child speak NDJSON — one JSON object per line — over two named fifos. The frames are:

direction frame payload
child → host $proto protocol version (ndjson.v1)
child → host $state state snapshot / update
child → host $msg out-message { fromName, body }
child → host $exit { code, state }
host → child $init init args + parent identity
host → child $msg in-message { fromName, body }

Notes