Skip to content
DocspackagesDocumentation

@lunora/do

The Durable Object base classes — ShardDO and SessionDO.

PackagesDo

@lunora/do ships the Durable Object base classes. ShardDO is the one the runtime drives on every request; SessionDO is a standalone building block (see the caveat below). You only touch them in src/server/*.ts when you need to add a custom RPC method or wire bespoke storage logic. The defaults are enough for most apps.

ShardDO

Base class for every shard. Owns the per-shard SQLite storage (one DB per DO instance), broadcasts subscription deltas to connected WebSockets, and implements the RPC fan-in.

import { ShardDO } from "lunorash/do";

export class MyShardDO extends ShardDO {
    // Optional: extend with custom RPC methods.
    public async customMethod(input: { foo: string }): Promise<{ ok: true }> {
        return { ok: true };
    }
}

The root shard (when you haven't called .shardBy(...) yet) routes to the DO named ROOT_SHARD_NAME. Storage above ROOT_DO_SIZE_WARN_BYTES emits a console warning so you remember to call .shardBy() before the DO hits its single-instance ceiling.

SessionDO

A TTL'd session store in a Durable Object: records expire after SESSION_DO_TTL_DEFAULT seconds (7 days) unless a shorter TTL is requested, a GC alarm sweeps daily, and instance count stays bounded by keying on the token prefix (idFromName(token.slice(0, 16))). Every request must present the SESSION_DO_SECRET shared secret in the x-lunora-session-do-secret header; when the secret is unset the DO rejects all calls with 401.

@lunora/auth does not use this class. It never has: grep -rn "SessionDO" packages/auth/src/ finds nothing, and no commit in that package's history references it. So exporting SessionDO, binding it as SESSION, and setting SESSION_DO_SECRET gets you a correctly-configured object that nothing ever calls; sessions still live in the auth database.

For auth on Durable Object storage, use LunoraAuthDO / .auth({ namespace }) from @lunora/auth. That path puts the whole better-auth schema, sessions included, in an object with real transactions (which is what @better-auth/scim requires). SessionDO remains usable on its own terms as a TTL'd token store; it is just not wired into auth.

import { SessionDO } from "lunorash/do";

export class MySessionDO extends SessionDO {}

SessionRecord

Type-only export. The shape of an entry in the session DO's storage:

interface SessionRecord {
    userId: string;
    createdAt: number;
    expiresAt: number;
}

Hibernation

ShardDO runs with WebSocket hibernation enabled: inactive sockets cost zero CPU until a message arrives. Exposed via the HibernatableWebSocket type for unit tests. (SessionDO has no WebSocket surface at all: it is HTTP-only, so hibernation does not apply to it.)

ShardDOState / SocketAttachment / MutationDelta / RpcRequest / SubscriptionEnvelope / SubscriptionQuery

Internal serialization shapes. Stable across patch releases so add-ons can type their broadcast payloads.

Constants

  • ROOT_SHARD_NAME: the DO instance name used by the root shard
  • ROOT_DO_SIZE_WARN_BYTES: soft cap above which the root DO logs a warning
  • SESSION_DO_TTL_DEFAULT: default session TTL (SessionDO)