Appearance
Authority and adapters
The engine host is an extension runtime. Application authority, transport, storage and rendering remain explicit adapters with separate owners.
Project before the boundary
TableView contains only an already audience-projected view:
ts
import type { TableView } from '@talerook/sdk';
const view: TableView = {
tableId: 'table-1',
campaignId: 'campaign-1',
revision: 4,
scene: {
id: 'scene-1',
name: 'Courtyard',
width: 1600,
height: 1000,
gridSize: 80,
},
tokens: [{ id: 'piece-1', name: 'Scout', x: 240, y: 320, controlled: true }],
};Tokens have id, name, x, y and optional color/controlled fields. These are rendering and interaction hints, not authority grants. An omitted secret cannot be restored by a layer, panel, tooltip, event or error payload. Apply the same audience policy to snapshots, subscriptions, replay and recovery before mapping domain projections into this view.
The runtime's own types may be richer. Map them at the adapter rather than adding renderer or concrete-system dependencies to domain contracts.
Dispatch a durable request
createExtensionHost({ dispatch }) accepts a function with this shape:
ts
import type { CommandRequest, JsonValue } from '@talerook/sdk';
type Dispatch = (request: CommandRequest, signal: AbortSignal) => Promise<JsonValue>;CommandRequest contains only type and JSON payload. The application adapter maps it to the runtime-owned envelope, including campaign/table routing, command identity and expected revision. Preserve retry identity for the same intent. The current runtime enforces membership, piece control, expected versions and bounded foundation capacities on the server, including retry and commit-time authority checks. Commercial entitlement/billing policy is not implemented by this foundation. Future plan capabilities and quotas must be enforced server-side as a separate policy; SDK grants cannot replace it.
SDK grants never grant GM privileges or access to another campaign. A command contribution can ask authority to act; it cannot commit by mutating a browser projection.
The beacon's BEACON_IDS.rally command is a concrete consumer of this boundary. Its panel selects the first controlled projected piece and captures that view's scene.id, then requests { type: 'token.move', payload: { sceneId, tokenId, x, y } } through the host. The browser adapter validates/maps that request to the runtime intent and authority envelope. The marker stays local; accepted piece movement is durable.
Retain the captured target in the runtime envelope:
ts
const capturedSceneId = view.scene.id;
const envelope = {
commandId, // Retain this identity and the same body on an uncertain retry.
expectedVersion: view.revision,
intent: { type: 'token.move', sceneId: capturedSceneId, tokenId, x, y },
};Form this intent from the view that produced the action. Do not fill in a newer current scene during dispatch or retry. Player moves require an explicit scene target; the server still checks that it is the active scene and that the current seat controls the piece. A scene change or version conflict requires fresh authoritative state, not silently retargeting the retained action.
Native backgrounds and campaign covers use reviewed .set requests with separate table/campaign versions. Images and resources documents that bridge and the authenticated loader's lifetime.
Connect a renderer
The adapter subscribes to host invalidation, calls host.renderLayers(view) and renders the returned world-coordinate primitives. It normalizes pointer input through host.pointer(toolId, input) and maps panel actions to host.execute(commandId, payload).
Keep temporary gesture state and presentation separate from durable state. Release renderer-owned resources when switching tables. Each table's host must be disposed when the table session ends; do not reuse a GM host or cached projection for a player audience.
Production-shaped local development
Local development must use real Docker-backed adapters. SDK examples can exercise local, non-durable effects without a server, but those examples do not prove persistence, authorization or recovery. Use the runtime owner's actual driver and two-role workflow checks for that evidence. This milestone performs no AWS, DNS or deployment setup.