tapedb/worker
Alles hier is stabiel tenzij het als experimenteel is gemarkeerd; zie API-stabiliteit.
Functions
Section titled “Functions”defineTapeDB
Section titled “defineTapeDB”declare function defineTapeDB(options: DefineTapeDBOptions): TapeDBWorker;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);}Interfaces
Section titled “Interfaces”ClaimIdentity
Section titled “ClaimIdentity”The two methods a deployment implements, and an optional rate limit. 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>; /** * Optional per-caller limit for the claim route, checked before anything * else. `key` is `claim:<cf-connecting-ip>`. Return false to refuse with * 429 `rate_limited`; a throw refuses with 500 `rate_limit_unavailable`. * Back it with a Cloudflare rate-limit binding when the route is public. */ allowRequest?(key: string): Promise<boolean>; /** * Built-in limits, on by default: exact, global token buckets per caller * IP (before the session lookup) and per subject (after it). Defaults: 300 * requests per IP and 30 per subject, refilling over 60 seconds. Set a * limit to `false`, or `limits: false`, to turn it off. */ limits?: ClaimLimits | false;}ClaimLimit
Section titled “ClaimLimit”Built-in limits for the claim route: exact token buckets, global per key.
Each bucket lives in one of LIMITER_SHARDS Durable Objects of the
workspace class, addressed by a name that is not a workspace id, so a
deployment needs no extra binding or migration. A Durable Object is one
place in the world and runs one request at a time, so a count is exact and
shared by every location, unlike Cloudflare’s rate-limit binding, which
counts per location and approximately.
Keys are HMACs of the caller’s IP or subject under the claim secret: the limiter never stores an address or a user id. A bucket that has refilled is indistinguishable from none and is deleted.
interface ClaimLimit { /** Requests a caller may make in a burst; the bucket's size. */ requests: number; /** Seconds the bucket takes to refill from empty. */ perSeconds: number;}ClaimLimits
Section titled “ClaimLimits”Limits on POST /_tapedb/claim. ip is checked before the session lookup
(it protects your identity system from floods), subject after it.
false turns one off.
interface ClaimLimits { ip?: ClaimLimit | false; subject?: ClaimLimit | false;}DefineTapeDBOptions
Section titled “DefineTapeDBOptions”experimental topics
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. * * @experimental May change in a minor release. */ topics?: { authorize?: TopicAuthorizer; }; /** * How long deleted or overwritten values may remain in the workspace's * history (R2 log segments, older snapshots, the local replay log). * Defaults to 30 days; at least 2. */ retention?: RetentionOptions;}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; /** * Restricts every workspace's Durable Object to a Cloudflare jurisdiction * ("eu" or "fedramp"): it runs and stores data only there. Set it before * the first deploy: an object's id depends on its jurisdiction, so changing * it later addresses new, empty workspaces. `tapedb deploy` checks that the * R2 buckets are declared in the same jurisdiction. */ TAPEDB_JURISDICTION?: 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; /** Requires this token (x-tapedb-dev-token) on the development claim route; for deployed test Workers. */ TAPEDB_DEV_CLAIMS_TOKEN?: string;}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 experimental
Section titled “TopicAction experimental”type TopicAction = "join" | "send";TopicAuthorizer experimental
Section titled “TopicAuthorizer experimental”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”The most one upload request carries: a whole small file in one PUT, or one part of a multipart upload. A file of any size goes up as parts (R2 keeps objects up to 5 TB), so this bounds a request, not a file; it stays well inside the request body size every Workers plan accepts.
declare const MAX_UPLOAD_BYTES: number;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;};