Actors/Packages/Cloudflare
@sigx/actors-cloudflare · Preview

Cloudflare#

Cloudflare already guarantees a single instance of a Durable Object globally and serializes requests to it. That is the virtual-actor contract — so the platform is the cluster.

v0.7.0 MIT

Installation#

Terminal
pnpm add @sigx/actors-cloudflare

Why it is small#

This package needs none of the machinery @sigx/actors/cluster uses to rebuild that contract: no membership heartbeats, no activation directory, no HMAC-authenticated host-to-host mount.

There is no HMAC because a Durable Object stub is not network-reachable — holding the binding is the capability grant, and guards run once at the public edge. And there is no 421 retry because ref → object id is a pure function, so a mismatch is a config bug rather than a race.

Storage#

TypeScript
const storage = durableObjectStorage(state.storage);

DO storage is strongly consistent and single-threaded per object, so the runtime's etag compare-and-set holds without a transaction.

Reminders#

TypeScript
const reminders = durableObjectReminders({
    storage: state.storage,
    alarms: state.storage,
    blockConcurrencyWhile: (fn) => state.blockConcurrencyWhile(fn),
});

export class ActorHost {
    async alarm() {
        await reminders.onAlarm();   // fire what is due, re-arm the rest
    }
}

The default shardedReminders() splits one table into fixed hash shards and polls it, because a host holds many actors and has to find whose reminder is due. A DO holds exactly one, so there is nothing to search and nothing to poll — reminders live in the object's own storage and the platform wakes it at the earliest due time.

The visible consequence: an alarm fires at the due time, where shardedReminders() promises only "at or after nextDue".

Client sockets#

Browsers can reach actors over a WebSocket here, terminating in either half:

TypeScript
createWorkerHandler({ ..., socket: {} });                        // in the Worker
createWorkerHandler({ ..., socket: { terminate: 'object' } });   // in the object

Worker-terminated gives one multiplexed socket per client, reaching every actor — the right shape for a dashboard. Object-terminated gives one socket per actor, accepted with the hibernation API inside the object that owns it — the room pattern, and the mode where a disconnect actually releases the activation, because teardown happens locally instead of dying at the stub.fetch boundary.

Pair either with socketTransport() on the client. The deployment guide has the choice in full, plus the hibernation contract.

The getting-started path#

Most apps do not touch the two seams directly — createHostDurableObject() and createWorkerHandler() assemble them. See Cloudflare Workers for the full entry, the wrangler.jsonc and the three gotchas that are each worth their own callout.

Next steps#