# @disko-game/room

> The Node room-host API, generated from the package declarations.

`@disko-game/room` exports the classes `Room`, `Player` and `DiskoError` and
the types below. It runs on Node.js 22 or later, as ESM only, with a prebuilt
native addon for seven platforms. The guides start at
[`Room.launch` & options](/rooms/launch).

```sh
npm install @disko-game/room
```

## Classes

### Room

A hosted room. Launch one with `Room.launch`. Properties are read-only and always current: they are updated before the event that reports a change is delivered. Every method returns a Promise that settles when the engine applied the command; commands on one room apply in call order.

```ts
export declare class Room {
    #private;
    readonly permissions: Policy["permissions"];
    readonly actions: Policy["actions"];
    readonly options: Policy["options"];
    readonly bans: Policy["bans"];
    private constructor();
    static launch(options: LaunchOptions): Promise<Room>;
    get id(): string;
    get url(): string;
    get link(): string;
    get state(): RoomState;
    get game(): LoadedGame;
    get players(): readonly Player[];
    get name(): string;
    get description(): string;
    get public(): boolean;
    get guests(): boolean;
    get location(): Location | null;
    get capacity(): number;
    on<E extends keyof RoomEvents>(event: E, handler: Handler<RoomEvents[E]>): this;
    on(event: GameEventName, handler: Handler<JsonValue>): this;
    off<E extends keyof RoomEvents>(event: E, handler: Handler<RoomEvents[E]>): this;
    off(event: GameEventName, handler: Handler<JsonValue>): this;
    load(game: GameSource, options?: LoadOptions): Promise<GameInfo>;
    start(): Promise<void>;
    stop(): Promise<void>;
    pause(): Promise<void>;
    resume(): Promise<void>;
    send(message: string | Message): Promise<{
        id: number;
    }>;
    emit(name: string, payload?: unknown): Promise<void>;
    setName(text: string): Promise<void>;
    setDescription(text: string): Promise<void>;
    setPublic(value: boolean): Promise<void>;
    setGuests(value: boolean): Promise<void>;
    setLocation(value: Location | null): Promise<void>;
    close(options?: CloseOptions): Promise<void>;
}
```

| Member | Description |
| --- | --- |
| `permissions` | `room.permissions`: resolvers of the game's permissions. |
| `actions` | `room.actions`: Room management player and room actions. |
| `options` | `room.options`: Room management option rows. |
| `bans` | `room.bans`: the room's bans. |
| `static launch()` | Launches a room: validates the options, binds the WebTransport endpoint, loads the initial game into Ready, fetches the master's ticket verification key, and registers the hosting lease. Any failure rejects with a [`DiskoError`](#diskoerror) and releases every resource. A room never appears in discovery without a loaded game. |
| `id` | 32 lowercase hex digits. |
| `url` | The public WebTransport URL. |
| `link` | `<play>/?room=<id>`, shareable. |
| `state` | Lifecycle state. |
| `game` | The loaded game and the permissions it declares. |
| `players` | Connected players, including spectators, in join order. |
| `name` | The advertised name. |
| `description` | The advertised description. |
| `public` | Listed in the directory. |
| `guests` | Admits players without an account. |
| `location` | Declared coordinates, or `null`. |
| `capacity` | Admitted players. |
| `on()` | Subscribes to a built-in event or a game event `game:<name>`. Handlers run on the main thread in subscription order. Unknown built-in names throw a `TypeError`. |
| `off()` | Removes one subscription. |
| `load()` | Replaces the game. Resolves once the new game is Ready; players stay connected and the new game starts fresh. On failure the previous game keeps running. |
| `start()` | Starts a fresh game instance. Resolves when Running. |
| `stop()` | Stops the game. Resolves when Ready. A faulted game recovers fresh. |
| `pause()` | Holds the running game at its current boundary. |
| `resume()` | Continues the paused game. |
| `send()` | Sends a visible message: text from the room, or `{ author?, content, sound? }`. Resolves with the message identity. |
| `emit()` | Sends an event to the game, delivered on its next eligible turn. Resolves when queued. `payload` must be JSON-serializable. |
| `setName()` | Replaces the advertised name. |
| `setDescription()` | Replaces the advertised description; `""` clears it. |
| `setPublic()` | Lists or unlists the room. Direct links keep working. |
| `setGuests()` | Admits or refuses new guests. Connected guests stay. |
| `setLocation()` | Declares coordinates, or clears them with `null`. |
| `close()` | Closes the room: stops accepting players and disconnects everyone with the reason, releases the directory lease, stops the engine, then emits `close`. Idempotent; never rejects. |

### DiskoError

Every rejection of the room-host API. `code` is stable; the message is human text that is not a stable contract.

```ts
export declare class DiskoError extends Error {
    readonly code: DiskoErrorCode;
    readonly problems?: readonly Problem[];
    constructor(code: DiskoErrorCode, message: string, options?: DiskoErrorOptions);
}
```

| Member | Description |
| --- | --- |
| `code` | Stable error code. |
| `problems` | Problems, for `game_invalid` and `game_build_failed`. |

### Player

A player connected to the room, including spectators. A player object belongs to one connection: after leaving, `connected` is `false` and a returning person is a new player.

```ts
export declare class Player {
    #private;
    readonly id: number;
    readonly account: string | null;
    readonly isOwner: boolean;
    get name(): string;
    get connected(): boolean;
    play(): Promise<void>;
    spectate(): Promise<void>;
    setPresentation(presentation: Presentation): Promise<void>;
    kick(reason?: string): Promise<void>;
    ban(options?: BanOptions): Promise<void>;
}
```

| Member | Description |
| --- | --- |
| `id` | Stable for the connection. |
| `account` | An opaque account id, or `null` for guests. |
| `isOwner` | Signed in with the account that owns the room. Guests never are. |
| `name` | Resolved under the loaded game's name policy. |
| `connected` | `false` after the player left or was disconnected. |
| `play()` | Asks the game to make this player a participant. |
| `spectate()` | Moves this player to the spectators. |
| `setPresentation()` | Sets how clients render this player's name: in the player list, the player detail view, chat and `ui.playerName`. |
| `kick()` | Disconnects this player, showing them the reason. They may rejoin. |
| `ban()` | Bans this player for the room's lifetime and disconnects them. Signed-in players are banned by account, guests by an engine-held network identity the room never sees. |

## Room policy

### room.permissions

`room.permissions`: resolvers of the game's permissions.

```ts
readonly permissions: {
    resolve: (first: string | PermissionResolver, second?: PermissionResolver) => Promise<void>;
    remove: (name?: string) => Promise<void>;
    refresh: (options?: {
        player?: Player;
        permission?: string;
    }) => Promise<void>;
}
```

| Member | Description |
| --- | --- |
| `resolve` | Registers a resolver for one permission name, or, without a name, for every permission the loaded game declares. A named resolver takes precedence. Registering again replaces it. |
| `remove` | Removes the resolver of one name, or the catch-all without a name. |
| `refresh` | Marks decisions stale so resolvers are asked again, after the room's own facts changed. Omitted fields mean every player or permission. |

### room.actions

`room.actions`: Room management player and room actions.

```ts
readonly actions: {
    player: (id: string, definition: PlayerActionDefinition) => Promise<void>;
    room: (id: string, definition: RoomActionDefinition) => Promise<void>;
    remove: (scope: "player" | "room", id: string) => Promise<void>;
    refresh: (options?: ActionRefresh) => Promise<void>;
}
```

| Member | Description |
| --- | --- |
| `player` | Defines or replaces a player action. |
| `room` | Defines or replaces a room action. |
| `remove` | Removes an action. |
| `refresh` | Asks `visible` again after the room's facts changed. |

### room.options

`room.options`: Room management option rows.

```ts
readonly options: {
    toggle: (id: string, definition: OptionDefinition<boolean>) => Promise<void>;
    choice: (id: string, definition: ChoiceOptionDefinition) => Promise<void>;
    number: (id: string, definition: NumberOptionDefinition) => Promise<void>;
    text: (id: string, definition: TextOptionDefinition) => Promise<void>;
    set: (id: string, value: unknown) => Promise<void>;
    remove: (id: string) => Promise<void>;
}
```

| Member | Description |
| --- | --- |
| `toggle` | A label and a toggle. |
| `choice` | A label and one of a closed set of choices. |
| `number` | A label and a bounded number. |
| `text` | A label and bounded text. |
| `set` | Sets an option's value; every viewer sees it live. |
| `remove` | Removes an option. |

### room.bans

`room.bans`: the room's bans.

```ts
readonly bans: {
    list: () => Promise<readonly BanEntry[]>;
    import: (sealed: string) => Promise<void>;
}
```

| Member | Description |
| --- | --- |
| `list` | The current bans. |
| `import` | Restores a ban from the `sealed` export of this room. |

## Types

### ChatRequestEvent

Payload of `chat-request`. Nothing is shown unless the room sends it.

```ts
export interface ChatRequestEvent {
    readonly id: number;
    readonly player: Player;
    readonly text: string;
}
```

| Member | Description |
| --- | --- |
| `id` | Room-assigned request identity. |
| `player` | The sender. |
| `text` | Plain text. |

### CloseEvent

Payload of `close`.

```ts
export interface CloseEvent {
    readonly reason: string;
}
```

| Member | Description |
| --- | --- |
| `reason` | The given reason, `"controller_overloaded"` when the event queue filled, or `"engine_error"` after a native engine failure. |

### CloseOptions

Options of `Room.close`.

```ts
export interface CloseOptions {
    readonly reason?: string;
}
```

| Member | Description |
| --- | --- |
| `reason` | Shown to players and reported in `close`. Default `"closed"`. |

### DirectoryEvent

Payload of `directory`.

```ts
export interface DirectoryEvent {
    readonly status: "registered" | "retrying" | "rejected";
    readonly error?: string;
}
```

| Member | Description |
| --- | --- |
| `status` | After `"rejected"` the room keeps running but is no longer advertised or joinable through the directory. |
| `error` | Why a publication failed. |

### GameEventName

A game event: `game:<name>` from `game.emit(<name>, payload)`.

```ts
export type GameEventName = `game:${string}`;
```

### GameFaultEvent

Payload of `game-fault`. The room keeps serving players and chat.

```ts
export interface GameFaultEvent {
    readonly message: string;
    readonly stack: string;
    readonly phase: string;
}
```

| Member | Description |
| --- | --- |
| `message` | The error message. |
| `stack` | The stack, in package paths. |
| `phase` | `"start"`, `"tick"` or `"event"`. |

### GameInfo

The loaded game.

```ts
export interface GameInfo {
    readonly reference: string | null;
    readonly path: string | null;
    readonly digest: string;
}
```

| Member | Description |
| --- | --- |
| `reference` | `"ns/slug@version"` of a registry release, or `null`. |
| `path` | The absolute path of a local game, or `null`. |
| `digest` | `blake3:<hex>`: exactly what the room runs. |

### GameLoadedEvent

Payload of `game-loaded`.

```ts
export interface GameLoadedEvent extends GameInfo {
    readonly deprecation: {
        readonly message: string;
    } | null;
}
```

| Member | Description |
| --- | --- |
| `deprecation` | A deprecated release's notice, or `null`. |

### Handler

An event handler. It may be `async`; the room never waits for it, and it never catches its errors.

```ts
export type Handler<T> = (payload: T) => unknown;
```

### JsonValue

A JSON value, frozen.

```ts
export type JsonValue = null | boolean | number | string | readonly JsonValue[] | {
    readonly [key: string]: JsonValue;
};
```

### LoadedGame

The loaded game and the permissions it declares.

```ts
export interface LoadedGame extends GameInfo {
    readonly permissions: readonly PermissionDeclaration[];
}
```

| Member | Description |
| --- | --- |
| `permissions` | Permissions the game declares, in declaration order. |

### LoadOptions

Options of `Room.load`.

```ts
export interface LoadOptions {
    readonly players?: PlayersOptions;
}
```

| Member | Description |
| --- | --- |
| `players` | The new game's name policy. |

### Message

A structured room message.

```ts
export interface Message {
    readonly author?: Player;
    readonly content: string | readonly MessageSpan[];
    readonly sound?: MessageSound;
}
```

| Member | Description |
| --- | --- |
| `author` | A connected player, or omitted for the room. |
| `content` | Text, or 1-32 spans; at most 2048 bytes of text. |
| `sound` | Default `"normal"`. |

### PlayerEvent

Payload of `player-join` and `player-leave`.

```ts
export interface PlayerEvent {
    readonly player: Player;
}
```

### RoomEvents

Built-in events and their payloads.

```ts
export interface RoomEvents {
    "player-join": PlayerEvent;
    "player-leave": PlayerEvent;
    "chat-request": ChatRequestEvent;
    state: StateEvent;
    "game-fault": GameFaultEvent;
    "game-loaded": GameLoadedEvent;
    directory: DirectoryEvent;
    close: CloseEvent;
}
```

### RoomState

Lifecycle state of a room.

```ts
export type RoomState = "ready" | "running" | "paused" | "faulted" | "closed";
```

### StateEvent

Payload of `state`.

```ts
export interface StateEvent {
    readonly state: RoomState;
    readonly previous: RoomState;
    readonly reason?: "finished" | "stopped" | "fault";
}
```

| Member | Description |
| --- | --- |
| `reason` | `"finished"`, `"stopped"` or `"fault"` where it applies. |

### DiskoErrorCode

Stable error codes of the room-host API.

- `invalid_option`: a bad `launch` option or setter value; the message names it. - `invalid_state`: a lifecycle method called in the wrong state. - `invalid_message`: a bad `send` argument. - `invalid_event`: a bad `emit` argument. - `unauthorized`: disko-master rejected the room id or token. - `game_not_found`: an unknown release, or a private release without access. - `game_removed`: a release that was taken down. - `game_invalid`: the game failed the release checks; see `problems`. - `game_build_failed`: a local project failed to build; see `problems`. - `game_start_failed`: the game script threw during top-level evaluation or exceeded its startup limits; the message includes the stack. - `registry_unavailable`: the registry or disko-master could not be reached, or another instance's hosting lease stayed live for `leaseSeconds` plus a margin while `launch` waited for it. - `listen_failed`: the address is in use or not bindable. - `certificate_invalid`: the certificate or key did not parse or match. - `closed`: the room is closed.

```ts
export type DiskoErrorCode = "invalid_option" | "invalid_state" | "invalid_message" | "invalid_event" | "unauthorized" | "game_not_found" | "game_removed" | "game_invalid" | "game_build_failed" | "game_start_failed" | "registry_unavailable" | "listen_failed" | "certificate_invalid" | "closed";
```

### DiskoErrorOptions

Options of the [`DiskoError`](#diskoerror) constructor.

```ts
export interface DiskoErrorOptions {
    readonly problems?: readonly Problem[];
    readonly cause?: unknown;
}
```

| Member | Description |
| --- | --- |
| `problems` | Release-check or build problems. |
| `cause` | The underlying error. |

### Problem

One build or validation problem, in the shape shared by the CLI, the registry and room hosts.

`code` is stable. `message` is human text that states the fix; it is not a stable contract. `line` and `column` are 1-based and present only for source positions (columns count UTF-16 code units).

```ts
export interface Problem {
    readonly code: string;
    readonly path?: string;
    readonly line?: number;
    readonly column?: number;
    readonly message: string;
    readonly docs: string;
}
```

| Member | Description |
| --- | --- |
| `code` | Stable machine-readable code, for example `import_unresolved`. |
| `path` | Project or package path the problem concerns. |
| `line` | 1-based line. |
| `column` | 1-based column in UTF-16 code units. |
| `message` | What is wrong and how to fix it. |
| `docs` | The documentation page for `code`. |

### BanOptions

Options of `Player.ban`.

```ts
export interface BanOptions {
    readonly reason?: string;
}
```

| Member | Description |
| --- | --- |
| `reason` | Shown to the player, at most 256 UTF-8 bytes. |

### PlayerColor

Palette roles a room may give a player's name.

```ts
export type PlayerColor = "accent" | "success" | "warning" | "danger" | "muted";
```

### Presentation

Room-set presentation of a player's name. `null` clears a field.

```ts
export interface Presentation {
    readonly color?: PlayerColor | null;
    readonly badge?: string | null;
}
```

| Member | Description |
| --- | --- |
| `color` | A client palette role, never an arbitrary color. |
| `badge` | One short label, at most 12 characters, for example `"Admin"`. |

### ActionConfirm

A confirmation before an action runs: `true` asks to confirm, and `{ reason: true }` also offers one optional text field, such as "Reason".

```ts
export type ActionConfirm = boolean | {
    readonly reason?: boolean;
};
```

### ActionRefresh

Options of `room.actions.refresh`.

```ts
export interface ActionRefresh {
    readonly viewer?: Player;
}
```

| Member | Description |
| --- | --- |
| `viewer` | Only this viewer; omitted, every viewer. |

### BanEntry

An opaque ban entry. It never contains an address or an account.

```ts
export interface BanEntry {
    readonly id: number;
    readonly kind: "account" | "guest";
    readonly name: string;
    readonly reason: string | null;
    readonly sealed: string | null;
    lift(): Promise<void>;
}
```

| Member | Description |
| --- | --- |
| `id` | Identity for this room's lifetime. |
| `kind` | `"account"` or `"guest"`. |
| `name` | The player's name at ban time. |
| `reason` | The ban reason. |
| `sealed` | A sealed export: store it and pass it to `room.bans.import` after a restart of the same room to restore the ban. |
| `lift()` | Lifts this ban. |

### ChoiceOptionDefinition

A choice option row.

```ts
export interface ChoiceOptionDefinition extends OptionDefinition<string> {
    readonly choices: readonly OptionChoice[];
}
```

| Member | Description |
| --- | --- |
| `choices` | 1-32 distinct choices. |

### NumberOptionDefinition

A number option row.

```ts
export interface NumberOptionDefinition extends OptionDefinition<number> {
    readonly min: number;
    readonly max: number;
    readonly step?: number;
}
```

| Member | Description |
| --- | --- |
| `min` | Inclusive minimum. |
| `max` | Inclusive maximum. |
| `step` | Optional positive step from the minimum. |

### OptionChoice

A choice of a choice option.

```ts
export interface OptionChoice {
    readonly value: string;
    readonly label: string;
}
```

| Member | Description |
| --- | --- |
| `value` | Submitted value, 1-32 UTF-8 bytes. |
| `label` | Displayed label. |

### OptionContext

What `visible` and `editable` of an option receive.

```ts
export interface OptionContext {
    readonly viewer: Player;
}
```

| Member | Description |
| --- | --- |
| `viewer` | The player viewing the Room management modal. |

### OptionDefinition

Fields every option row shares.

```ts
export interface OptionDefinition<T> {
    readonly label: string;
    readonly value: T;
    readonly visible?: (context: OptionContext) => boolean | Promise<boolean>;
    readonly editable?: (context: OptionContext) => boolean | Promise<boolean>;
    readonly change?: (context: OptionContext & {
        readonly value: T;
    }) => unknown;
}
```

| Member | Description |
| --- | --- |
| `label` | Row label, 1-48 UTF-8 bytes. |
| `value` | The current value. |
| `visible` | Which viewers see the row. Omitted: everyone. |
| `editable` | Which viewers may change the value. Omitted: nobody; a visible row that is not editable shows its value read-only. |
| `change` | Accepts a viewer's change after `visible` and `editable` are checked again. Throwing rejects it: the row keeps its value and the viewer sees the error message. Every viewer sees an accepted value live. |

### PermissionDeclaration

A permission a game declares.

```ts
export interface PermissionDeclaration {
    readonly name: string;
    readonly label: string;
    readonly default: "everyone" | "restricted" | "owner";
}
```

| Member | Description |
| --- | --- |
| `name` | The game's name for it, for example `"manage-match"`. |
| `label` | A human label for room owners. |
| `default` | The game's decision when the room makes none. |

### PermissionRequest

What a permission resolver receives.

```ts
export interface PermissionRequest {
    readonly player: Player;
    readonly permission: PermissionDeclaration;
    readonly game: {
        readonly permissions: readonly PermissionDeclaration[];
    };
}
```

| Member | Description |
| --- | --- |
| `player` | The player the decision is for. |
| `permission` | The permission being decided. |
| `game` | Every permission the loaded game declares. |

### PermissionResolver

Decides one permission for one player: `true` grants it, `false` denies it, and `undefined` keeps the game's default. It may be `async`. Errors are not caught (they reach Node's `uncaughtException`), and the game's default applies to that decision.

```ts
export type PermissionResolver = (request: PermissionRequest) => boolean | undefined | Promise<boolean | undefined>;
```

### PlayerActionContext

What `visible` of a player action receives.

```ts
export interface PlayerActionContext {
    readonly viewer: Player;
    readonly target: Player;
}
```

| Member | Description |
| --- | --- |
| `viewer` | The player viewing the Room management modal. |
| `target` | The player whose detail view lists the action. |

### PlayerActionDefinition

A player action, shown in a player's detail view.

```ts
export interface PlayerActionDefinition {
    readonly label: string;
    readonly confirm?: ActionConfirm;
    readonly visible?: (context: PlayerActionContext) => boolean | Promise<boolean>;
    readonly run: (context: PlayerActionContext & {
        readonly reason: string | undefined;
    }) => unknown;
}
```

| Member | Description |
| --- | --- |
| `label` | Button label, 1-48 UTF-8 bytes. |
| `confirm` | Optional confirmation. |
| `visible` | Which viewers see the action for which targets. Omitted: everyone. |
| `run` | Runs the action after `visible` is checked again. Throwing rejects it, and the error message is shown to the viewer. |

### RoomActionContext

What `visible` of a room action receives.

```ts
export interface RoomActionContext {
    readonly viewer: Player;
}
```

| Member | Description |
| --- | --- |
| `viewer` | The player viewing the Room management modal. |

### RoomActionDefinition

A room action, shown in the Actions section.

```ts
export interface RoomActionDefinition {
    readonly label: string;
    readonly confirm?: ActionConfirm;
    readonly visible?: (context: RoomActionContext) => boolean | Promise<boolean>;
    readonly run: (context: RoomActionContext & {
        readonly reason: string | undefined;
    }) => unknown;
}
```

| Member | Description |
| --- | --- |
| `label` | Button label, 1-48 UTF-8 bytes. |
| `confirm` | Optional confirmation. |
| `visible` | Which viewers see the action. Omitted: everyone. |
| `run` | Runs the action after `visible` is checked again. Throwing rejects it, and the error message is shown to the viewer. |

### TextOptionDefinition

A text option row.

```ts
export interface TextOptionDefinition extends OptionDefinition<string> {
    readonly maxLength: number;
    readonly secret?: boolean;
}
```

| Member | Description |
| --- | --- |
| `maxLength` | Maximum UTF-8 bytes, at most 128. |
| `secret` | The value is never delivered to viewers. |

### MessageSound

Whole-message sound category the client plays.

```ts
export type MessageSound = "none" | "normal" | "notification";
```

### MessageSpan

One styled substring of a room message.

```ts
export interface MessageSpan {
    readonly text: string;
    readonly color?: string;
    readonly weight?: "normal" | "bold";
    readonly style?: "normal" | "italic";
}
```

| Member | Description |
| --- | --- |
| `text` | Non-empty text. |
| `color` | `#rrggbb` or `#rrggbbaa`. |
| `weight` | Suggested weight. Default `"normal"`. |
| `style` | Suggested style. Default `"normal"`. |

### Certificate

A certificate identity as PEM strings, not file paths.

```ts
export interface Certificate {
    readonly certificatePem: string;
    readonly privateKeyPem: string;
}
```

| Member | Description |
| --- | --- |
| `certificatePem` | The certificate chain, leaf first. |
| `privateKeyPem` | The PKCS#8 private key of the leaf certificate. |

### Endpoints

Base URLs of the disko services. Local development passes `*.localhost`.

```ts
export interface Endpoints {
    readonly api?: string;
    readonly registry?: string;
    readonly play?: string;
}
```

| Member | Description |
| --- | --- |
| `api` | disko-master API host. Default `https://api.disko.ooo`. |
| `registry` | Registry host. Default `https://registry.disko.ooo`. |
| `play` | Web client, for room links. Default `https://play.disko.ooo`. |

### GameSource

A game to load: a release reference, or a local directory or `.tgz`.

```ts
export type GameSource = string | {
    readonly path: string;
};
```

### HostOptions

The launch options a host function receives from `disko dev` or its own entry point: credentials, the game, and the network options. The host function passes them through to `Room.launch`.

```ts
export type HostOptions = Pick<LaunchOptions, "id" | "token" | "game" | "listen" | "publicUrl" | "endpoints">;
```

### LaunchOptions

Options of `Room.launch`. Unknown keys reject with `invalid_option`.

```ts
export interface LaunchOptions {
    readonly id: string;
    readonly token: string;
    readonly name: string;
    readonly game: GameSource;
    readonly players?: PlayersOptions;
    readonly description?: string;
    readonly public?: boolean;
    readonly guests?: boolean;
    readonly location?: Location | null;
    readonly password?: string | null;
    readonly capacity?: number;
    readonly listen?: string;
    readonly publicUrl?: string;
    readonly transportPath?: string;
    readonly certificate?: Certificate;
    readonly endpoints?: Endpoints;
    readonly cacheDir?: string;
    readonly leaseSeconds?: number;
    readonly logging?: LoggingOptions;
    readonly limits?: Limits;
}
```

| Member | Description |
| --- | --- |
| `id` | 32 lowercase hex digits: a room identity from the Creator Portal, or a test room. |
| `token` | The room's hosting token. It is never logged, never exposed on the `Room`, and sent only to disko-master. |
| `name` | 1-96 UTF-8 bytes, not blank, no control characters. |
| `game` | `"ns/slug@version"`, or `{ path }` of a local directory or `.tgz`. |
| `players` | The name policy of the initial game. |
| `description` | At most 1024 UTF-8 bytes. Default `""`. |
| `public` | Listed in the directory. Default `true`. Test rooms are never listed. |
| `guests` | Admit players without an account. Default `true`. |
| `location` | Declared coordinates, or `null`. Default `null`. |
| `password` | 1-4096 UTF-8 bytes, or `null` for no password. Default `null`. |
| `capacity` | Admitted players, 1-65535. Default 64. |
| `listen` | UDP/HTTP3 bind address. Default `"0.0.0.0:4433"`. Only when omitted, a taken port falls back to the next free one up to 4532. An explicit port is used exactly; port 0 picks a random free port. |
| `publicUrl` | The WebTransport URL players connect to. Derived when omitted: from the bound address when `listen` names a host, otherwise from the address disko-master observes for this host. |
| `transportPath` | WebTransport request path. Default `"/room"`. |
| `certificate` | A certificate chain and key. Without it the room uses a self-signed ECDSA P-256 certificate valid for 13 days, rotated every 6 days. |
| `endpoints` | Service base URLs. Default: the production hosts. |
| `cacheDir` | The release cache, keyed by digest. Default: the OS cache directory + `/disko-game/releases`. |
| `leaseSeconds` | Directory lease duration, 5-300 seconds. Default 30. |
| `logging` | Native engine logging. |
| `limits` | Resource limit overrides. |

### Limits

Resource limits; omitted fields keep the server defaults.

```ts
export interface Limits {
    readonly pendingHandshakes?: number;
    readonly streamBootstrapTimeoutMs?: number;
    readonly mailbox?: number;
    readonly inboundQueueFrames?: number;
    readonly inboundFramesPerSecond?: number;
    readonly inboundFrameBurst?: number;
    readonly resumeRetained?: number;
    readonly resumeGraceMs?: number;
    readonly checkpointChunkBytes?: number;
    readonly eventQueue?: number;
}
```

| Member | Description |
| --- | --- |
| `pendingHandshakes` | Connections allowed while admission is pending. Default 64. |
| `streamBootstrapTimeoutMs` | Deadline for a player's first control stream. Default 5000. |
| `mailbox` | Room mailbox entries. Default 256. |
| `inboundQueueFrames` | Decoded frames staged per player. Default 128. |
| `inboundFramesPerSecond` | Sustained frames per second per player. Default 240. |
| `inboundFrameBurst` | Frame burst per player. Default 480. |
| `resumeRetained` | Interrupted sessions kept for resume. Default 64. |
| `resumeGraceMs` | Resume window. Default 15000. |
| `checkpointChunkBytes` | Checkpoint chunk payload bytes. Default 16384. |
| `eventQueue` | Events waiting for the main thread. When full, chat requests get a retryable backpressure answer and any other event closes the room with `controller_overloaded`. Default 4096. |

### Location

Declared coordinates, used only for directory distance sorting.

```ts
export interface Location {
    readonly latitude: number;
    readonly longitude: number;
}
```

| Member | Description |
| --- | --- |
| `latitude` | Degrees, within ±90. |
| `longitude` | Degrees, within ±180. |

### LoggingOptions

Native engine logging.

```ts
export interface LoggingOptions {
    readonly filter?: string;
    readonly format?: "pretty" | "json";
}
```

| Member | Description |
| --- | --- |
| `filter` | A tracing filter. `disko` names every engine target. Default `"disko=info"`. Logging is process-wide: the first room to launch installs it. |
| `format` | `"pretty"` (the default) or `"json"`. Records go to stderr. |

### PlayersOptions

How a game names players.

```ts
export interface PlayersOptions {
    readonly name?: "display" | "username";
}
```

| Member | Description |
| --- | --- |
| `name` | `"display"` (the default) uses the account's display name, or a guest's chosen name. `"username"` uses the verified username; guests keep their chosen name. |
