Shared

Handlers aren't expected to touch disk, but a single instance serves many connections and several handlers often need to see the same data. The broker offers an in-memory shared store on every ctxctx.shared — with a namespaced key/value API and a publish/subscribe channel. Proposed the surface below.

Key/value

CallReturnsMeaning
shared.get(key)Promise<value | undefined> Read a key. undefined if unset.
shared.set(key, value)Promise<void> Write a key. value must be structured-cloneable (below).
shared.delete(key)Promise<void> Remove a key.

The API is async throughout so the same code works whether the store is a local heap or reached over a socket (see the note below). Keys are strings; namespace them with a prefix ("echo:count") to avoid collisions between handlers.

Publish / subscribe

Beyond point reads and writes, the store carries a channel so one handler (or one connection) can notify others of an event without polling.

CallMeaning
shared.subscribe(channel, fn)Register fn(message) for a channel. Returns an unsubscribe function.
shared.publish(channel, message)Deliver message to every current subscriber of the channel. message must be structured-cloneable.

Example

A chat relay: each connection joins a channel, publishes lines it receives, and writes out lines others publish. Presence lives in the key/value store; the fan-out is pub/sub.

// relay.tcp.4000.port.js
export async function handle(conn, ctx) {
  const n = ((await ctx.shared.get("relay:peers")) ?? 0) + 1;
  await ctx.shared.set("relay:peers", n);

  const off = ctx.shared.subscribe("relay:line", (line) => conn.write(line));
  conn.on("data", (buf) => ctx.shared.publish("relay:line", buf));

  conn.on("close", async () => {
    off();
    await ctx.shared.set("relay:peers", (await ctx.shared.get("relay:peers")) - 1);
  });
}

Value constraints

Stored values and published messages must be structured-cloneable: primitives, plain objects and arrays, Map, Set, Date, ArrayBuffer and typed arrays. Not cloneable — and so not storable — are functions, class instances with behaviour, sockets, and streams. This is what lets the store move a value between processes unchanged if handlers are ever isolated.

Cross-process semantics

Today handlers run in the broker's process, so ctx.shared is a shared heap and reads are effectively instant. The API is deliberately async and clone-constrained anyway, so that the same handler code keeps working if handlers later move into isolated processes and the store is served by the broker over a local socket. Write to the API, not to the current implementation.

The store is in memory and does not survive a broker restart. When state must be durable, back it with datahoster rather than having the handler write to disk itself.

See also