Skip to main content

Lumenize Mesh

Cloudflare's Durable Objects are powerful — each one is a stateful, single-threaded actor with transactional storage. But out of the box, they're isolated. Workers RPC connects server-side components, but doesn't reach browsers. There's no built-in authentication, no access control framework, and no way for a browser client to be a first-class participant in the system.

Lumenize Mesh solves this. It extends Cloudflare's actor model into a true mesh network where DOs, Workers, and browser clients are all equal peers. Every node — whether it's a Durable Object managing state, a Worker doing stateless computation, or a browser client — uses the same API, the same patterns, and the same security model. Authentication and fine-grained access control are required, not optional. Rich types (cycles, Date, Map, Set, Error with cause chains) work seamlessly all the way to the browser.

Node Types

Every participant in the mesh extends one of these base classes:

Node TypeRuns InStorageUse Case
LumenizeDOCloudflare DOSQL/KVStateful server-side logic
LumenizeWorkerCloudflare WorkerNoneStateless server-side logic
LumenizeClientBrowser/Node.js/BunLocalClient access to mesh

All three mesh node types, have access to the same APIs/utilities, are coded in the same way, and act as full peers, able to both make and receive calls. Yes, even LumenizeClients can receive calls from any other mesh node.

Supporting infrastructure:

LumenizeClientGateway is a zero-storage Cloudflare DO that bridges LumenizeClients into the mesh.

Additionally, it's common to use @lumenize/auth, @lumenize/routing (particularly routeDORequest), @lumenize/testing, and @lumenize/debug with Lumenize Mesh.

Illustration

Core Concepts

All mesh nodes have access to this.lmz — the unified API for identity and communication (see full reference below).

Automatic Identity Propagation

Durable Objects don't inherently know their own binding name or instance name. However, any caller must know this information to make a call. Lumenize leverages this by including callee identity in every call envelope. Callees store this information on first contact, enabling them to provide return addresses for callbacks, tracing, etc.

Call Context

Every mesh call carries context that propagates through the entire call chain:

Client (alice) → DocumentDO → SpellCheckWorker
↓ ↓
callContext has callContext has
alice's auth alice's auth (propagated!)

Access context in any mesh node via this.lmz.callContext:

@mesh()
updateDocument(changes: DocumentChange) {
const { origin, callChain, originAuth } = this.lmz.callContext;
const sub = originAuth?.sub;
const caller = callChain.at(-1) ?? origin; // Immediate caller
// Make decisions based on who's calling
}

Hibernation-safe (for DOs): For long-running remote calls, capture context explicitly by passing it as continuation parameters. See Managing Context for the full guide.

Continuations

Continuations describe work to be done in another place or time. They enable type-safe, serializable method chains:

// Describe what to call on a remote DO
const remote = this.ctn<DocumentDO>().getContent();

// Describe what to do locally when the result arrives
const handler = this.ctn().handleContent(this.ctn().$result);

// Make the call
this.lmz.call('DOCUMENT_DO', 'draft-1', remote, handler);

Continuations:

  • Are serializable — can be stored, sent over the wire, stored/restored
  • Are type-safe — TypeScript checks method names and signatures
  • Carry context — pass what you need as parameters
  • Reduce race condition risk — designed to work with Durable Objects concurrency model

See Continuations for the full guide.

Zero Trust Security

Every node is responsible for its own fine-grained access control. Lumenize provides secure-by-default defense-in-depth:

LayerMechanismPurpose
Class-wideonBeforeCall() hookWHO can call (authentication)
Entry Point@mesh() decoratorWHAT is exposed (method allowlist)
Method-level@mesh(guard)Fine-grained per-method permissions

See Security for complete documentation with examples.

Rich Type Support All the Way to the Browser

Parameters and return values support all Workers RPC types (except streams): objects with cycles, aliases, Date, Error with cause chains, Map, Set, ArrayBuffer, Uint8Array, and more.

Unlike Workers RPC, this capability extends all the way to the browser — the same rich types work seamlessly between LumenizeDO, LumenizeWorker, and LumenizeClient.

See @lumenize/structured-clone for the complete type support table.

The this.lmz API

Every mesh node has access to this.lmz — the unified API for identity and communication.

Identity

this.lmz.type           // 'LumenizeDO' | 'LumenizeWorker' | 'LumenizeClient'
this.lmz.bindingName // e.g., 'DOCUMENT_DO' (auto-propagated)
this.lmz.instanceName // e.g., 'draft-1' (auto-propagated, undefined for LumenizeWorker)
this.lmz.callContext // Current request's context (during handler execution)

Making Calls

// Fire-and-forget
this.lmz.call('DOCUMENT_DO', 'draft-1', this.ctn<DocumentDO>().update(changes));

// With response handler
this.lmz.call(
'SPELLCHECK_WORKER',
undefined,
this.ctn<SpellCheckWorker>().check(content),
this.ctn().handleResult(this.ctn().$result)
);

See Making Calls for all patterns including cost optimization and error handling.

See Mesh API for the complete API reference including CallContext, @mesh() decorator, and continuations.

Getting Started

Ready to build? See the Getting Started Guide for a hands-on tutorial building a collaborative document editor.

Reference Documentation

Concept Deep Dives