API index and search · Build metadata
@rindle/room/token
0.0.0 · Public export map; development manifest version (0.0.0).
mintRoomToken
FunctionDeclaration · Source: packages/room/src/token.ts:126 · Supporting declarations
Sign a room lease token. This runs on the API-server side (or a test playing it).
export declare function mintRoomToken(opts: MintRoomTokenOptions): Promise<string>;MintRoomTokenOptions
InterfaceDeclaration · Source: packages/room/src/token.ts:46 · Supporting declarations
export interface MintRoomTokenOptions {
doc: string;
ast: unknown;
/** The subject (user id) this token authorizes — the §4.1 revocation key. */
sub: string;
/** Key id + its secret (utf-8; give every room the same `keys` map). */
kid: string;
key: string;
/** Expiry, ms from `now`. Keep short (minutes) — the §4.1 TTL backstop. */
ttlMs: number;
/** Mint time; defaults to `Date.now()`. Injectable for tests. */
now?: number;
/** The room's {@link scopeSpecsHash} for the profile this lease serves — stamped so the
* shell can flag scope skew (see {@link RoomTokenPayload.scopesHash}). Omit to not stamp. */
scopesHash?: string;
}RoomTokenError
ClassDeclaration · Source: packages/room/src/token.ts:149 · Supporting declarations
Why a token was refused. The reason is terse and safe to echo in a queryError.
export declare class RoomTokenError extends Error {
readonly reason: string;
constructor(reason: string);
}RoomTokenPayload
InterfaceDeclaration · Source: packages/room/src/token.ts:29 · Supporting declarations
The room's self-authorizing signed lease token (RINDLE-REALTIME-DESIGN.md §10.1).
The API server is the authority (§4): it authenticates the user, resolves the named
query to an approved AST, and signs this token. The token then IS the lease — the
room materializes on first presentation, so no pre-placement /materialize control
call ever touches it (the §10.1 inversion; on the DO shell the Worker verifies the
same signature statelessly before get(id)).
Shape: rt1.<base64url(payload)>.<base64url(hmac-sha256(prefix.payload))> with
payload { v: 1, doc, ast, sub, iat, exp, kid }:
doc— the room/document id the token authorizes (a token for one room presented to another is refused);ast— the approved wireAst(the client never composes this; §4's "ASTs never cross the public wire" holds because the token is opaque TO THE CLIENT — it carries it, it cannot mint or alter it);sub— the subject (user id): the revocation key (§4.1);iat/exp— issued-at / expiry, ms epoch. Renewal is re-authorization: clients obtain a fresh token through the API server, never extend this one.iatis what lets a revocation refuse pre-revocation tokens while a genuine re-grant (a newer token) passes immediately;kid— which shared secret signed it (rotation).
HMAC via WebCrypto (crypto.subtle) so the exact same code verifies in Node (the
test shell) and in a Cloudflare Worker/DO (P4) — no node:crypto import.
export interface RoomTokenPayload {
v: 1;
doc: string;
ast: unknown;
sub: string;
iat: number;
exp: number;
kid: string;
/** A short fingerprint of the room's compiled scope specs at mint time
* ({@link scopeSpecsHash}). Advisory, not a credential: the room's §3.3 gate is the
* contract regardless. It lets the shell detect SCOPE SKEW — a room profile edited
* while a room is already live arms the gate with the OLD specs (a one-shot at boot)
* while fresh leases prove against the NEW ones, so every routed write silently
* deopt-loops. Optional so a pre-stamp api-server / older token still verifies. */
scopesHash?: string;
}scopeSpecsHash
FunctionDeclaration · Source: packages/room/src/token.ts:87 · Supporting declarations
A stable short fingerprint of the compiled scope specs — the scope-skew tripwire.
NOT security (the gate re-proves every write): FNV-1a-32 over the {@link canonicalJson}
form, 8 hex chars. Both the api-server (stamping the lease token) and the room shell
(hashing the boot-wire scopes it armed the gate with) run this over the SAME compiler
output, so an unchanged profile ⇒ equal hash and a profile edited under a live room ⇒
mismatch. A collision only costs a missed diagnostic, never correctness. Accepts either
wire's spec array (RoomScopeSpec[] / RoomTableSpec[] — structurally identical).
export declare function scopeSpecsHash(specs: readonly unknown[]): string;verifyRoomToken
FunctionDeclaration · Source: packages/room/src/token.ts:172 · Supporting declarations
Verify a token's signature and claims; returns the payload (with the approved AST)
or throws {@link RoomTokenError}. Signature is checked FIRST — no claim is trusted
(not even kid's existence beyond the key lookup) before the MAC passes.
export declare function verifyRoomToken(token: string, opts: VerifyRoomTokenOptions): Promise<RoomTokenPayload>;VerifyRoomTokenOptions
InterfaceDeclaration · Source: packages/room/src/token.ts:158 · Supporting declarations
export interface VerifyRoomTokenOptions {
/** The room's own doc id — a token for any other doc is refused. */
doc: string;
/** kid → shared secret. Unknown kids are refused (never "try them all"). */
keys: Record<string, string>;
/** Verification time; defaults to `Date.now()`. Injectable for tests. */
now?: number;
}