@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 shardROOT_DO_SIZE_WARN_BYTES: soft cap above which the root DO logs a warningSESSION_DO_TTL_DEFAULT: default session TTL (SessionDO)