tapedb/worker
Generated from the public tapedb/worker entry point’s TypeScript declarations. Signatures and comments are the API contract for this preview.
Functions
Section titled “Functions”authenticateEngineRequest
Section titled “authenticateEngineRequest”Extracts and verifies the claim attached to an engine request.
declare function authenticateEngineRequest(request: Request, env: ClaimVerificationEnv, workspaceId: string): Promise<RoleClaim>;defineTapeDB
Section titled “defineTapeDB”declare function defineTapeDB(options: DefineTapeDBOptions): TapeDBWorker;encodeTrustedRoleClaim
Section titled “encodeTrustedRoleClaim”Serialises the verified claim for the Durable Object. Only the fields the engine needs are forwarded; signature material is never re-sent.
declare function encodeTrustedRoleClaim(claim: RoleClaim): string;extractClaimToken
Section titled “extractClaimToken”Reads a claim token from the request.
Authorization: Bearer <jwt>is the preferred transport.?claim=<jwt>is a temporary fallback for WebSocket upgrades, where browser clients cannot set anAuthorizationheader. The mint endpoint should prefer a header wherever the client supports it.
A present-but-non-Bearer Authorization header returns null instead of
falling back to the query string, so the two transports cannot be confused.
declare function extractClaimToken(request: Request): string | null;mintClaim
Section titled “mintClaim”POST /_tapedb/claim: session to workspace role to a short-lived claim.
Rate limiting is the deployment’s call. If you expose this on the public
internet, back allowRequest with a Cloudflare rate-limit binding.
declare function mintClaim(request: Request, deps: MintClaimDependencies): Promise<Response>;mintDevClaim
Section titled “mintDevClaim”Development-only issuer. Mints an OWNER claim for any workspace without a
session so tapedb dev works with zero configuration.
The caller is responsible for refusing this in production; defineTapeDB
enforces that before this function is reachable.
declare function mintDevClaim(workspaceId: string, secret: string): Promise<MintedClaim>;signRoleClaim
Section titled “signRoleClaim”Signs the compact HS256 claim the engine verifier accepts.
declare function signRoleClaim(secret: string, input: MintClaimInput, now?: number): Promise<MintedClaim>;verifyRoleClaim
Section titled “verifyRoleClaim”Verifies a compact JWS against the frozen RoleClaim contract.
Throws ClaimError for malformed tokens, bad signatures, expiry, workspace
mismatches and invalid claim values. A missing server secret throws a plain
Error: that is a deployment problem, not a client failure.
declare function verifyRoleClaim(token: string, env: ClaimVerificationEnv, expectedWorkspaceId: string): Promise<RoleClaim>;Classes
Section titled “Classes”ClaimError
Section titled “ClaimError”Typed verification failure; code is safe to return to the caller.
declare class ClaimError extends Error { readonly code: ClaimErrorCode; constructor(code: ClaimErrorCode, message: string);}WorkspaceRoom
Section titled “WorkspaceRoom”One workspace engine, addressed by idFromName(workspaceId).
Owns its SQLite schema, applies bundled Atlas migrations on boot, commits transactions, and flushes/snapshots to R2 on a 30-second alarm.
declare class WorkspaceRoom extends DurableObject<Env> { /** * Last emitted result per socket client id and subscription id. This is a * cache, not a source of truth: hibernation clears it and the next pass * falls back to a full `query:data` payload. */ private readonly subscriptionResults; /** Namespaces touched since the current debounce window opened. */ private pendingInvalidationNamespaces; private invalidationTimer; constructor(ctx: DurableObjectState, env: Env); private get workspaceId(); /** * Applies pending migrations before the object serves any request. Each * migration and its `_migrations` row commit in the same transaction, so a * failed boot cannot leave a half-migrated schema, and each runs with * foreign keys off and checked before commit (see * `applyMigrationWithoutCascades`). */ private applyMigrations; private applyMigration; fetch(request: Request): Promise<Response>; private route; private transact; /** * Runs a query document against this workspace. The claim is read from the * Worker-forwarded header; `executeQuery` requires `view` on every requested * and traversed namespace before touching SQL. */ private query; private handleWebSocket; webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): Promise<void>; webSocketClose(ws: WebSocket, code: number, reason: string): void; webSocketError(ws: WebSocket): void; private handlePing; private topicError; private handleTopicJoin; private handleTopicLeave; /** * Delivers to every other socket joined to the topic. The sender need not * be joined; it never receives its own message. */ private handleTopicSend; private handlePresenceSet; private handlePresenceClear; private handleCursorSet; private handleDragSet; private handleDragClear; private broadcastPresence; private broadcastToTarget; private presencePeers; private socketsForTarget; private removePresence; private handleSubscribe; private handleUnsubscribe; private send; private closeSocket; private resultsFor; private dropSubscriptionResults; private scheduleInvalidation; private flushInvalidations; /** One subscription's re-run for this flush: its result, or why it failed. */ private runSubscription; /** * Runs the durability pass (flush then snapshot) and schedules the next * pass only while there is work left. The 30s delay lets transactions * accumulate into fewer, larger log segments. */ alarm(): Promise<void>; /** * Schedules an alarm for the next due durability pass. An existing alarm is * never pushed later: a write must not delay a flush that is already due. */ private scheduleNextDurabilityAlarm; private scheduleDurabilityAlarm; /** * File metadata rows. Bytes live in R2 and never enter the transaction log; * this table is the queryable index over them. */ private listFiles; private recordFile; private forgetFile; /** File-index entries are log rows, so they ship to R2 like any write. */ private scheduleFileFlush; /** Registry snapshot for tooling: API names, attribute types and links. */ private schemaDocument; private status;}Interfaces
Section titled “Interfaces”ClaimIdentity
Section titled “ClaimIdentity”The two methods a deployment implements. Everything else about identity (passwords, magic codes, OAuth, cookies) stays in the host application.
interface ClaimIdentity { /** Resolve the caller's subject from the request. Null means "not signed in". */ getSubject(headers: Headers): Promise<{ sub: string; } | null>; /** Resolve the caller's role in a workspace. Null means "not a member". */ getRole(workspaceId: string, sub: string): Promise<WorkspaceRole | null>;}DefineTapeDBOptions
Section titled “DefineTapeDBOptions”interface DefineTapeDBOptions { schema: TapeDBSchema; /** * The app's committed migrations, from `tapedb/migrations.gen.ts`. Omit * only before the first `tapedb migrate dev`: the schema's generated SQL * is used, which cannot evolve. */ migrations?: readonly AppMigration[]; /** * Identity adapter. Implement `getSubject` and `getRole` to serve * `POST /_tapedb/claim`. Without it that route returns 501 and the consumer * must supply tokens to the client itself. */ identity?: ClaimIdentity; /** * Development-only issuer: mints an OWNER claim for any workspace with no * session. Always refused when `ENVIRONMENT` is `production`. */ devClaims?: boolean; /** * Broadcast topics. `authorize` decides whether a caller may join a topic * or send to it; without it any member of the workspace may, as with * presence. */ topics?: { authorize?: TopicAuthorizer; };}Worker bindings and vars. tapedb init writes exactly these.
Three resources: one Durable Object namespace, two R2 buckets. There is no D1 and no identity database: the engine consumes a verified claim and never reads a session.
interface Env { /** The database room. One instance per workspace. */ WORKSPACE: DurableObjectNamespace<WorkspaceRoom>; /** Transaction log segments and snapshots. */ DATA: R2Bucket; /** Uploaded files. Optional unless `db.$files` is used. */ FILES?: R2Bucket; ENVIRONMENT?: string; SERVICE_VERSION?: string; ENABLE_SMOKE?: string; ALLOWED_ORIGINS?: string; /** Shared HS256 secret used to verify role claims. */ TAPEDB_CLAIM_SECRET: string; /** Optional expected `iss` claim; checked only when set. */ TAPEDB_CLAIM_ISSUER?: string; /** Optional expected `aud` claim; checked only when set. */ TAPEDB_CLAIM_AUDIENCE?: string; /** * Enables the development claim issuer. Set to `"true"` by `tapedb dev` on * the command line and never written to `wrangler.jsonc`, so a plain * `wrangler deploy` has no dev claim route. */ TAPEDB_DEV_CLAIMS?: string;}MintClaimDependencies
Section titled “MintClaimDependencies”interface MintClaimDependencies { identity: ClaimIdentity; secret: string; issuer?: string; audience?: string; ttlSeconds?: number; now?: () => number; /** Optional per-caller limiter; return false to reject with 429. */ allowRequest?: (key: string) => Promise<boolean>;}TapeDBWorker
Section titled “TapeDBWorker”interface TapeDBWorker { /** The database room class. Export it from the Worker entry. */ WorkspaceRoom: typeof WorkspaceRoom; worker: { fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response>; };}AppMigration
Section titled “AppMigration”One app migration, as tapedb migrate bundles it.
type AppMigration = BundledWorkspaceMigration;TopicAction
Section titled “TopicAction”Broadcast topics: fire-and-forget messages between the sockets of one workspace, on the socket they already hold.
A socket joins topics; a message sent to a topic reaches every other socket joined to it, with the sender’s client id and subject. Nothing is stored or replayed: a socket that is not joined when a message is sent never sees it. Joined topics live in the socket attachment, so they survive hibernation, and the client rejoins them after a reconnect.
Who may join or send is the deployment’s call (defineTapeDB({ topics }));
by default any member of the workspace may, as with presence.
type TopicAction = "join" | "send";TopicAuthorizer
Section titled “TopicAuthorizer”Decides whether a caller may join a topic or send to it. Return false (or
throw) to refuse; the socket gets a topic:error and nothing else happens.
type TopicAuthorizer = (context: { auth: { sub: string; role: number; workspaceId: string; }; topic: string; action: TopicAction;}) => boolean;Constants
Section titled “Constants”MAX_UPLOAD_BYTES
Section titled “MAX_UPLOAD_BYTES”Worker entry.
defineTapeDB() installs the deployment schema and returns the Durable
Object class plus the fetch handler. The consumer’s Worker is then two
exports and nothing else:
const tapedb = defineTapeDB({ schema }); export const WorkspaceRoom = tapedb.WorkspaceRoom; export default tapedb.worker;
declare const MAX_UPLOAD_BYTES: number;TRUSTED_CLAIM_HEADER
Section titled “TRUSTED_CLAIM_HEADER”Trusted header forwarded to the Durable Object after verification.
declare const TRUSTED_CLAIM_HEADER = "x-tapedb-claims";WORKSPACE_ROLE_LEVELS
Section titled “WORKSPACE_ROLE_LEVELS”Shared role-claim contract between the Worker (JWT verification) and the workspace engine (permission checks). consumes this shape.
declare const WORKSPACE_ROLE_LEVELS: { readonly GUEST: 0; readonly USER: 1; readonly MEMBER: 2; readonly EDITOR: 3; readonly MODERATOR: 4; readonly ADMIN: 5; readonly OWNER: 6; readonly FOUNDER: 7; readonly DEVELOPER: 8;};