@eigeninteractive/server
@eigeninteractive/server — everything that deploys: the
createEngine API factory, the GameDO base class, the D1 applier, and
the protocol types.
The D1 and Durable Object table definitions are deliberately NOT exported.
They are engine-owned storage internals that migrate on their own schedule,
and readGameRow already returns the whole game row typed. Exporting the
drizzle tables would turn a private layout into a compatibility surface.
Classes
AuthError
Defined in: eigen-server/packages/server/src/auth/firebase.ts:12
Verification failure — always the caller's fault; the app maps it to 401.
Extends
Error
Constructors
Constructor
new AuthError(message?): AuthError;
Defined in: eigen-web/node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1080
Parameters
| Parameter | Type |
|---|---|
message? | string |
Returns
Inherited from
Error.constructor
Constructor
new AuthError(message?, options?): AuthError;
Defined in: eigen-web/node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1080
Parameters
| Parameter | Type |
|---|---|
message? | string |
options? | ErrorOptions |
Returns
Inherited from
Error.constructor
abstract BaseGameDO
Defined in: eigen-server/packages/server/src/do/game-do.ts:104
Durable Object base class that owns one authoritative game session.
A game Worker subclasses this once to supply its gameModule and D1 binding. Do not override command, socket, alarm, or persistence behavior: the base class owns the serialized game loop and applies engine migrations on activation.
Example
export class GameDO extends BaseGameDO<Env> {
protected readonly gameModule = gameModule;
protected d1(env: Env) {
return env.GAME_DB;
}
}
Extends
unknown<TEnv>
Type Parameters
| Type Parameter |
|---|
TEnv |
Implements
GameStub
Constructors
Constructor
new BaseGameDO<TEnv>(ctx, env): BaseGameDO<TEnv>;
Defined in: eigen-server/packages/server/src/do/game-do.ts:118
Parameters
| Parameter | Type |
|---|---|
ctx | DurableObjectState |
env | TEnv |
Returns
BaseGameDO<TEnv>
Overrides
DurableObject<TEnv>.constructor
Properties
gameModule
abstract protected readonly gameModule: GameModule;
Defined in: eigen-server/packages/server/src/do/game-do.ts:106
The implementor's game — the versions map the engine dispatches on.
Methods
abort()
abort(gameId): Promise<void>;
Defined in: eigen-server/packages/server/src/do/game-do.ts:262
Unconditional teardown (cron reap): mark the game aborted in D1 and
drop the DO's storage — no creator gate, no init requirement. A
never-touched lobby's DO has no meta row, so the caller passes the
gameId. Idempotent: a re-run re-aborts a game whose storage is already
gone. Used by the cron; cancel shares the teardown for its live path.
Parameters
| Parameter | Type |
|---|---|
gameId | string |
Returns
Promise<void>
Implementation of
GameStub.abort
alarm()
alarm(): Promise<void>;
Defined in: eigen-server/packages/server/src/do/game-do.ts:708
Returns
Promise<void>
d1()
abstract protected d1(env): D1Database;
Defined in: eigen-server/packages/server/src/do/game-do.ts:109
The EngineConfig seam: the engine never assumes binding names — the subclass picks the D1 database off its own Env.
Parameters
| Parameter | Type |
|---|---|
env | TEnv |
Returns
D1Database
fetch()
fetch(request): Promise<Response>;
Defined in: eigen-server/packages/server/src/do/game-do.ts:732
The worker routes the upgrade here after authenticating; the principal header is worker-set (never client-supplied — the worker strips inbound headers when forwarding). One socket serves the game's whole lifetime: unversioned roster snapshots pre-game, versioned frames from v0. A not-yet-seated user's socket simply receives no frames until the roster contains them.
Parameters
| Parameter | Type |
|---|---|
request | Request |
Returns
Promise<Response>
Implementation of
GameStub.fetch
firebaseAdmin()
protected firebaseAdmin(env): FirebaseAdminEffects;
Defined in: eigen-server/packages/server/src/do/game-do.ts:112
Required Firebase Admin effects. Tests override this with the explicit
fake exported by @eigeninteractive/server/testing.
Parameters
| Parameter | Type |
|---|---|
env | TEnv |
Returns
frames()
frames(args): Promise<FrameMessage[]>;
Defined in: eigen-server/packages/server/src/do/game-do.ts:832
Project a version range for one seat (null = public viewer, replay only). Live rows serve the stored frame; compacted/ratings rows re-project. Raw state never leaves the DO.
Parameters
| Parameter | Type |
|---|---|
args | { from: number; isReplay?: boolean; seat: number | null; to: number; } |
args.from | number |
args.isReplay? | boolean |
args.seat | number | null |
args.to | number |
Returns
Promise<FrameMessage[]>
Implementation of
GameStub.frames
handle()
handle(cmd): Promise<CommandResult>;
Defined in: eigen-server/packages/server/src/do/game-do.ts:131
Parameters
| Parameter | Type |
|---|---|
cmd | Command |
Returns
Promise<CommandResult>
Implementation of
GameStub.handle
repokeFinish()
repokeFinish(): Promise<boolean>;
Defined in: eigen-server/packages/server/src/do/game-do.ts:696
The gated admin re-poke (step 4): re-runs the D1 apply for a finish whose effects never landed. Idempotent end to end — finish_id dedupes the apply, and the outbox row exists iff the ratings transition hasn't been committed. Returns false when there is nothing to do.
Returns
Promise<boolean>
Implementation of
GameStub.repokeFinish
webSocketClose()
webSocketClose(): Promise<void>;
Defined in: eigen-server/packages/server/src/do/game-do.ts:766
Returns
Promise<void>
webSocketError()
webSocketError(_ws, error): Promise<void>;
Defined in: eigen-server/packages/server/src/do/game-do.ts:772
Parameters
| Parameter | Type |
|---|---|
_ws | WebSocket |
error | unknown |
Returns
Promise<void>
webSocketMessage()
webSocketMessage(): Promise<void>;
Defined in: eigen-server/packages/server/src/do/game-do.ts:761
Returns
Promise<void>
HttpError
Defined in: eigen-server/packages/server/src/http.ts:38
Extends
Error
Constructors
Constructor
new HttpError(
status,
message,
code?,
retryAfterSeconds?): HttpError;
Defined in: eigen-server/packages/server/src/http.ts:46
Parameters
| Parameter | Type |
|---|---|
status | 400 | 401 | 403 | 404 | 409 | 413 | 415 | 422 | 429 | 500 | 502 |
message | string |
code? | ErrorCode |
retryAfterSeconds? | number |
Returns
Overrides
Error.constructor
Properties
code
readonly code: ErrorCode | undefined;
Defined in: eigen-server/packages/server/src/http.ts:40
retryAfterSeconds
readonly retryAfterSeconds: number | undefined;
Defined in: eigen-server/packages/server/src/http.ts:44
Seconds the caller should wait before retrying — rendered as the
Retry-After header. Set only on a 429 (see ErrorCode.rateLimited);
undefined everywhere else.
status
readonly status: 400 | 401 | 403 | 404 | 409 | 413 | 415 | 422 | 429 | 500 | 502;
Defined in: eigen-server/packages/server/src/http.ts:39
Interfaces
AuthClaims
Defined in: eigen-server/packages/server/src/auth/firebase.ts:18
What a verified ID token asserts. isAnonymous (the
firebase.sign_in_provider === 'anonymous' claim) drives every guest gate;
the profile claims seed user provisioning (Google supplies name/picture,
Apple usually only email, guests none).
Properties
email
email: string | null;
Defined in: eigen-server/packages/server/src/auth/firebase.ts:21
isAnonymous
isAnonymous: boolean;
Defined in: eigen-server/packages/server/src/auth/firebase.ts:20
name
name: string | null;
Defined in: eigen-server/packages/server/src/auth/firebase.ts:22
picture
picture: string | null;
Defined in: eigen-server/packages/server/src/auth/firebase.ts:23
uid
uid: string;
Defined in: eigen-server/packages/server/src/auth/firebase.ts:19
CreateGameInput
Defined in: eigen-server/packages/server/src/d1/apply.ts:292
The worker-direct create, engine-owned so implementors never touch the D1 schema: seats already validated by worker policy.
Properties
access
access: GameAccess;
Defined in: eigen-server/packages/server/src/d1/apply.ts:296
budgetSeconds
budgetSeconds: number | null;
Defined in: eigen-server/packages/server/src/d1/apply.ts:300
config
config: JsonObject;
Defined in: eigen-server/packages/server/src/d1/apply.ts:298
createdBy
createdBy: string | null;
Defined in: eigen-server/packages/server/src/d1/apply.ts:294
gameId
gameId: string;
Defined in: eigen-server/packages/server/src/d1/apply.ts:293
incrementSeconds
incrementSeconds: number | null;
Defined in: eigen-server/packages/server/src/d1/apply.ts:301
maxPlayers
maxPlayers: number;
Defined in: eigen-server/packages/server/src/d1/apply.ts:305
minPlayers
minPlayers: number;
Defined in: eigen-server/packages/server/src/d1/apply.ts:304
now
now: number;
Defined in: eigen-server/packages/server/src/d1/apply.ts:308
rated
rated: boolean;
Defined in: eigen-server/packages/server/src/d1/apply.ts:302
ratingPool
ratingPool: string | null;
Defined in: eigen-server/packages/server/src/d1/apply.ts:303
schemaVersion
schemaVersion: number;
Defined in: eigen-server/packages/server/src/d1/apply.ts:297
seats
seats: Seat[];
Defined in: eigen-server/packages/server/src/d1/apply.ts:307
shortCode
shortCode: string;
Defined in: eigen-server/packages/server/src/d1/apply.ts:306
status
status: "waiting" | "ready";
Defined in: eigen-server/packages/server/src/d1/apply.ts:295
turnSeconds
turnSeconds: number | null;
Defined in: eigen-server/packages/server/src/d1/apply.ts:299
EngineConfig
Defined in: eigen-server/packages/server/src/engine.ts:99
The EngineConfig seam: the engine never assumes binding names — the
implementor picks bindings off their own Env. Annotate the accessors' env
parameter and both type arguments infer.
Type Parameters
| Type Parameter |
|---|
TEnv |
TDO extends BaseGameDO<TEnv> |
Properties
appName
appName: string;
Defined in: eigen-server/packages/server/src/engine.ts:106
The whitelabel app's display name — the single source of truth for the
engine's own identity (share metadata and public-page titles today;
FCM titles and share copy later). Deliberately top-level, not nested under
deepLink, so there is one place to set it regardless of which optional
feature blocks are enabled.
avatars?
optional avatars?: AvatarsConfig<TEnv>;
Defined in: eigen-server/packages/server/src/engine.ts:128
Opt-in avatar uploads. Omit → not mounted.
clientOrigins?
optional clientOrigins?: readonly string[] | ((env) => readonly string[]);
Defined in: eigen-server/packages/server/src/engine.ts:124
Browser origins allowed to call the engine from a different origin.
Same-origin requests always work. When omitted, the engine trusts the
exact origin from the conventional WEB_APP_ORIGIN var when it is set.
Supply this option to replace that default for multiple or otherwise
non-standard browser origins. Paths and wildcards are intentionally
unsupported. The list also protects browser WebSocket upgrades, whose
Origin header is not governed by CORS.
Set an empty list to disable the WEB_APP_ORIGIN default.
deepLink?
optional deepLink?: DeepLinkConfig;
Defined in: eigen-server/packages/server/src/engine.ts:126
Native deep-link verification and store links. Omit for web-only.
gameModule
gameModule: GameModule;
Defined in: eigen-server/packages/server/src/engine.ts:100
lifecycle?
optional lifecycle?: LifecycleOptions;
Defined in: eigen-server/packages/server/src/engine.ts:135
Cron-backstop tuning — guest-purge/reap windows and batch caps.
Omit for the defaults (LIFECYCLE_DEFAULTS); set any subset to
override just those.
site?
optional site?: SiteConfig;
Defined in: eigen-server/packages/server/src/engine.ts:131
The public web surface — download page, legal documents, crawler files. Omit → not mounted (the worker is API-only).
testing?
optional testing?: {
auth: TokenVerifier;
firebaseAdmin: FirebaseAdminEffects;
};
Defined in: eigen-server/packages/server/src/engine.ts:140
Explicit test-only replacements for Firebase verification and Admin effects. Supplying them together prevents a fake verifier from accidentally turning missing production credentials into a nullable runtime path. Leave unset in production.
auth
auth: TokenVerifier;
firebaseAdmin()
firebaseAdmin(env): FirebaseAdminEffects;
Parameters
| Parameter | Type |
|---|---|
env | TEnv |
Returns
Methods
d1()
d1(env): D1Database;
Defined in: eigen-server/packages/server/src/engine.ts:108
The engine's D1 database (engine-private).
Parameters
| Parameter | Type |
|---|---|
env | TEnv |
Returns
D1Database
firebaseProjectId()?
optional firebaseProjectId(env): string;
Defined in: eigen-server/packages/server/src/engine.ts:113
Firebase project id for token verification; defaults to the
FIREBASE_PROJECT_ID var (the only secret verification needs).
Parameters
| Parameter | Type |
|---|---|
env | TEnv |
Returns
string
gameDO()
gameDO(env): DurableObjectNamespace<TDO>;
Defined in: eigen-server/packages/server/src/engine.ts:110
The GameDO namespace binding.
Parameters
| Parameter | Type |
|---|---|
env | TEnv |
Returns
DurableObjectNamespace<TDO>
FinishApplyInput
Defined in: eigen-server/packages/server/src/d1/apply.ts:30
Properties
finishId
finishId: string;
Defined in: eigen-server/packages/server/src/d1/apply.ts:34
The DO-minted idempotency key — the apply is a no-op replay when the games row already carries it.
gameId
gameId: string;
Defined in: eigen-server/packages/server/src/d1/apply.ts:31
now
now: number;
Defined in: eigen-server/packages/server/src/d1/apply.ts:39
outcomes
outcomes: OutcomeEntry[];
Defined in: eigen-server/packages/server/src/d1/apply.ts:35
rated
rated: boolean;
Defined in: eigen-server/packages/server/src/d1/apply.ts:37
ratingPool
ratingPool: string | null;
Defined in: eigen-server/packages/server/src/d1/apply.ts:38
roster
roster: Seat[];
Defined in: eigen-server/packages/server/src/d1/apply.ts:36
FirebaseAdminEffects
Defined in: eigen-server/packages/server/src/firebase/admin-effects.ts:15
The Firebase Admin effects used by authenticated engine paths.
Methods
deleteAccount()
deleteAccount(userId): Promise<void>;
Defined in: eigen-server/packages/server/src/firebase/admin-effects.ts:19
Permanently delete one Firebase Authentication account.
Parameters
| Parameter | Type |
|---|---|
userId | string |
Returns
Promise<void>
notifyUser()
notifyUser(
d1,
userId,
message): Promise<void>;
Defined in: eigen-server/packages/server/src/firebase/admin-effects.ts:17
Send one notification through the engine's registered-device store.
Parameters
| Parameter | Type |
|---|---|
d1 | D1Database |
userId | string |
message | NotificationMessage |
Returns
Promise<void>
FrameMessage
Defined in: eigen-server/packages/server/src/protocol.ts:91
One seat's versioned frame on the wire — the socket fan-out payload, and
(for the acting seat) the command-response ride-along. ratings appears
only on the post-finish ratings transition.
Properties
data
data: JsonObject;
Defined in: eigen-server/packages/server/src/protocol.ts:94
deadline
deadline: number | null;
Defined in: eigen-server/packages/server/src/protocol.ts:97
The true client-facing deadline (grace is display-only there).
outcomes?
optional outcomes?: OutcomeEntry[];
Defined in: eigen-server/packages/server/src/protocol.ts:99
pendingPlayers
pendingPlayers: number[];
Defined in: eigen-server/packages/server/src/protocol.ts:95
playerTimes
playerTimes: number[] | null;
Defined in: eigen-server/packages/server/src/protocol.ts:98
ratings?
optional ratings?: RatingDelta[];
Defined in: eigen-server/packages/server/src/protocol.ts:100
type
type: "frame";
Defined in: eigen-server/packages/server/src/protocol.ts:92
version
version: number;
Defined in: eigen-server/packages/server/src/protocol.ts:93
LegalConfig
Defined in: eigen-server/packages/server/src/site/config.ts:29
Legal document overrides. Each is an HTML fragment — body content only, no document wrapper; the engine supplies the shell, styling and footer. Omitted documents fall back to the engine's generic templates.
A fragment is inserted as-is, so it is the implementor's own trusted markup with their own values already written in. There are no placeholders to fill: the engine's defaults take an OperatorConfig as typed props, which is what a template's tokens used to stand in for.
Properties
deleteAccount?
optional deleteAccount?: string;
Defined in: eigen-server/packages/server/src/site/config.ts:32
privacy?
optional privacy?: string;
Defined in: eigen-server/packages/server/src/site/config.ts:31
terms?
optional terms?: string;
Defined in: eigen-server/packages/server/src/site/config.ts:30
OperatorConfig
Defined in: eigen-server/packages/server/src/site/config.ts:9
The legal entity publishing the game. Required whenever site is present:
the default legal documents take it as a prop and cannot render without it.
Properties
contactEmail
contactEmail: string;
Defined in: eigen-server/packages/server/src/site/config.ts:15
Support and privacy contact address.
effectiveDate
effectiveDate: string;
Defined in: eigen-server/packages/server/src/site/config.ts:18
Effective date of the legal documents, as displayed. A plain string, not a Date — it is prose, and its format is the operator's choice.
jurisdiction
jurisdiction: string;
Defined in: eigen-server/packages/server/src/site/config.ts:13
Governing jurisdiction, e.g. India.
name
name: string;
Defined in: eigen-server/packages/server/src/site/config.ts:11
Legal entity name. Also the page footers' copyright holder.
Principal
Defined in: eigen-server/packages/server/src/protocol.ts:16
Who a command acts as, resolved at the edge. Exactly one id is set.
Properties
botId
botId: string | null;
Defined in: eigen-server/packages/server/src/protocol.ts:18
userId
userId: string | null;
Defined in: eigen-server/packages/server/src/protocol.ts:17
RatingDelta
Defined in: eigen-server/packages/kernel/dist/index.d.ts:171
One rated identity's before → after, exactly the rating_history row minus
store keys. Computed by the D1 applier inside the rating CAS and delivered
on the post-finish ratings transition (the kind: "ratings" action).
Properties
displayAfter
displayAfter: number;
Defined in: eigen-server/packages/kernel/dist/index.d.ts:179
displayBefore
displayBefore: number;
Defined in: eigen-server/packages/kernel/dist/index.d.ts:176
displayChange
displayChange: number;
Defined in: eigen-server/packages/kernel/dist/index.d.ts:180
identity
identity: RatingIdentity;
Defined in: eigen-server/packages/kernel/dist/index.d.ts:172
muAfter
muAfter: number;
Defined in: eigen-server/packages/kernel/dist/index.d.ts:177
muBefore
muBefore: number;
Defined in: eigen-server/packages/kernel/dist/index.d.ts:174
pool
pool: string;
Defined in: eigen-server/packages/kernel/dist/index.d.ts:173
sigmaAfter
sigmaAfter: number;
Defined in: eigen-server/packages/kernel/dist/index.d.ts:178
sigmaBefore
sigmaBefore: number;
Defined in: eigen-server/packages/kernel/dist/index.d.ts:175
RetryOptions
Defined in: eigen-server/packages/server/src/d1/retry.ts:59
Properties
attempts?
optional attempts?: number;
Defined in: eigen-server/packages/server/src/d1/retry.ts:61
Total attempts including the first. Default 4.
baseDelayMs?
optional baseDelayMs?: number;
Defined in: eigen-server/packages/server/src/d1/retry.ts:63
First backoff, doubling each retry. Default 50ms.
maxDelayMs?
optional maxDelayMs?: number;
Defined in: eigen-server/packages/server/src/d1/retry.ts:65
Backoff ceiling. Default 2000ms.
onRetry?
optional onRetry?: (error, attempt) => void;
Defined in: eigen-server/packages/server/src/d1/retry.ts:69
Observe each retry (logging); never throws into the loop.
Parameters
| Parameter | Type |
|---|---|
error | unknown |
attempt | number |
Returns
void
shouldRetry?
optional shouldRetry?: (error) => boolean;
Defined in: eigen-server/packages/server/src/d1/retry.ts:67
Which failures are worth retrying. Default isTransientD1Error.
Parameters
| Parameter | Type |
|---|---|
error | unknown |
Returns
boolean
sleep?
optional sleep?: (ms) => Promise<void>;
Defined in: eigen-server/packages/server/src/d1/retry.ts:71
Delay primitive, injectable so tests run without real timers.
Parameters
| Parameter | Type |
|---|---|
ms | number |
Returns
Promise<void>
RosterSnapshot
Defined in: eigen-server/packages/server/src/protocol.ts:82
The unversioned pre-game snapshot: pushed to every socket on any roster change, idempotent — a reconnect just gets the current one. Also the response body of an accepted waiting-room command.
Properties
players
players: Seat[];
Defined in: eigen-server/packages/server/src/protocol.ts:85
status
status: GameStatus;
Defined in: eigen-server/packages/server/src/protocol.ts:84
type
type: "roster";
Defined in: eigen-server/packages/server/src/protocol.ts:83
SiteConfig
Defined in: eigen-server/packages/server/src/site/config.ts:41
The public web surface a deployed game serves on its own host: download page, legal documents, and the crawler files. Absent → none of it is mounted and the worker stays API-only.
The scaffold reserves these paths for the Worker with Static Assets'
run_worker_first; customize legal prose through this typed config.
Properties
description?
optional description?: string;
Defined in: eigen-server/packages/server/src/site/config.ts:47
Longer download-page prose. Defaults to tagline.
legal?
optional legal?: LegalConfig;
Defined in: eigen-server/packages/server/src/site/config.ts:59
name?
optional name?: string;
Defined in: eigen-server/packages/server/src/site/config.ts:43
Public game name in titles and OG tags. Defaults to appName.
ogImage?
optional ogImage?: string;
Defined in: eigen-server/packages/server/src/site/config.ts:57
Path under public/ to the 1200x630 OG image. Defaults to
/og-image.png, the name the
branding guide
prescribes for the Flutter app's own share card — one image, both
surfaces. The engine never generates images.
operator
operator: OperatorConfig;
Defined in: eigen-server/packages/server/src/site/config.ts:58
primaryColor
primaryColor: string;
Defined in: eigen-server/packages/server/src/site/config.ts:49
Hex accent colour, e.g. #1a237e. Also the theme-color.
screenshots?
optional screenshots?: string[];
Defined in: eigen-server/packages/server/src/site/config.ts:51
Filenames under public/screenshots/, shown as a scrolling strip.
tagline
tagline: string;
Defined in: eigen-server/packages/server/src/site/config.ts:45
One-sentence hook. The meta description and OG description.
TokenVerifier
Defined in: eigen-server/packages/server/src/auth/firebase.ts:29
The seam createEngine consumes. Production is
createFirebaseVerifier with the default remote JWKS; tests inject a
local JWKS and mint their own RS256 tokens.
Methods
verify()
verify(token): Promise<AuthClaims>;
Defined in: eigen-server/packages/server/src/auth/firebase.ts:31
Resolve a bearer token to claims, or throw AuthError.
Parameters
| Parameter | Type |
|---|---|
token | string |
Returns
Promise<AuthClaims>
Type Aliases
Command
type Command =
| {
actor: Principal;
commandId: string;
gameId: string;
kind: "join" | "leave";
}
| {
actor: Principal;
commandId: string;
gameId: string;
kind: "cancel";
}
| {
actor: Principal;
commandId: string;
gameId: string;
kind: "start";
}
| {
actor: Principal;
botId: string;
commandId: string;
gameId: string;
kind: "add-bot";
}
| {
actor: Principal;
commandId: string;
data: unknown;
expectedVersion: number;
gameId: string;
kind: "action";
seat: number;
}
| {
actor: Principal | null;
commandId: string;
gameId: string;
kind: "lifecycle";
seat?: number;
type: LifecycleType;
};
Defined in: eigen-server/packages/server/src/protocol.ts:23
Everything that crosses the worker → DO boundary after creation ( create itself is a worker-direct D1 write; the DO does not exist yet).
Union Members
Type Literal
{
actor: Principal;
commandId: string;
gameId: string;
kind: "join" | "leave";
}
Type Literal
{
actor: Principal;
commandId: string;
gameId: string;
kind: "cancel";
}
Type Literal
{
actor: Principal;
commandId: string;
gameId: string;
kind: "start";
}
Type Literal
{
actor: Principal;
botId: string;
commandId: string;
gameId: string;
kind: "add-bot";
}
Type Literal
{
actor: Principal;
commandId: string;
data: unknown;
expectedVersion: number;
gameId: string;
kind: "action";
seat: number;
}
actor
actor: Principal;
commandId
commandId: string;
data
data: unknown;
expectedVersion
expectedVersion: number;
The version the client computed the move against — a lower value is arbitrated by the same-view rule.
gameId
gameId: string;
kind
kind: "action";
seat
seat: number;
The acting seat — carried uniformly by humans and bots. The DO verifies it belongs to the actor (user id from the token, bot id from the HMAC claim) against its own roster and rejects otherwise, so a client can never act on a seat it does not hold. Required because one bot id may hold several seats, and uniform for one code path.
Type Literal
{
actor: Principal | null;
commandId: string;
gameId: string;
kind: "lifecycle";
seat?: number;
type: LifecycleType;
}
actor
actor: Principal | null;
Null for identity-less system lifecycles (timeout, autoForfeit).
commandId
commandId: string;
gameId
gameId: string;
kind
kind: "lifecycle";
seat?
optional seat?: number;
The affected seat: forfeit carries the resigning seat (verified
against the actor, like an action); autoForfeit the purged seat;
timeout carries none (it resolves all pending).
type
type: LifecycleType;
CommandResult
type CommandResult =
| {
frame: FrameMessage | null;
ok: true;
version: number;
}
| {
ok: true;
roster: RosterSnapshot;
}
| {
code: | RejectCode
| LobbyRejectCode;
message: string;
ok: false;
};
Defined in: eigen-server/packages/server/src/protocol.ts:124
What GameDO.handle() returns; accepted results are stored for commandId
dedupe and replayed verbatim to a retry. Rejections are computed
fresh each time — re-evaluating one is always sound. State-transitioning
commands answer with a version (+ the acting seat's frame); waiting-room
commands answer with the post-commit roster snapshot.
LobbyRejectCode
type LobbyRejectCode =
| "unknownGame"
| "notJoinable"
| "gameFull"
| "alreadyJoined"
| "notParticipant"
| "notCreator"
| "creatorCannotLeave";
Defined in: eigen-server/packages/server/src/protocol.ts:62
Why the DO refused a waiting-room command — the integrity column. These are expected refusals (accepted lobby staleness: the lobby may show a game that just filled), returned as values exactly like kernel rejections; the worker maps them to HTTP. Genuine protocol violations (acting on a seat you don't own) still throw.
UserRow
type UserRow = typeof users.$inferSelect;
Defined in: eigen-server/packages/server/src/auth/provision.ts:19
Variables
DEADLINE_GRACE_MS
const DEADLINE_GRACE_MS: 750 = 750;
Defined in: eigen-server/packages/kernel/dist/index.d.ts:443
Grace window (ms) added to every deadline comparison so a player who
submits on time is not rejected because network latency carried the request
past the deadline. Keep it small relative to per-action turnSeconds
windows. The client's display-only kServerDeadlineGrace mirrors this.
Functions
applyFinish()
function applyFinish(d1, input): Promise<RatingDelta[] | null>;
Defined in: eigen-server/packages/server/src/d1/apply.ts:48
Apply one finished game to D1. Returns the rated deltas (null for an unrated game) for the DO to deliver as the ratings transition. Throws on failure — the caller logs and keeps the outbox row (single attempt at the call site; the internal loop only absorbs CAS conflicts).
Parameters
| Parameter | Type |
|---|---|
d1 | D1Database |
input | FinishApplyInput |
Returns
Promise<RatingDelta[] | null>
createEngine()
function createEngine<TEnv, TDO>(cfg): ExportedHandler<TEnv>;
Defined in: eigen-server/packages/server/src/engine.ts:414
Creates the complete Cloudflare Worker for one game deployment.
Call this once from the default export of src/index.ts. The returned
handler mounts the authenticated game API, WebSocket upgrades, scheduled
lifecycle work, and any configured public/deep-link routes. Game
implementors provide only EngineConfig.gameModule and binding
accessors; routes, persistence, migrations, authentication, and session
dispatch stay engine-owned.
Type Parameters
| Type Parameter |
|---|
TEnv extends object |
TDO extends BaseGameDO<TEnv> |
Parameters
| Parameter | Type |
|---|---|
cfg | EngineConfig<TEnv, TDO> |
Returns
ExportedHandler<TEnv>
Example
export default createEngine({
gameModule,
appName: "My Game",
d1: (env: Env) => env.GAME_DB,
gameDO: (env: Env) => env.GAME_DO,
});
createFirebaseVerifier()
function createFirebaseVerifier(projectId, getKey?): TokenVerifier;
Defined in: eigen-server/packages/server/src/auth/firebase.ts:46
Parameters
| Parameter | Type |
|---|---|
projectId | string |
getKey? | JWTVerifyGetKey |
Returns
createGame()
function createGame(d1, input): Promise<void>;
Defined in: eigen-server/packages/server/src/d1/apply.ts:314
Write the games row + one participants row per seat, atomically. The DO lazy-inits from exactly these rows on first contact. Callers own the shortCode retry: a duplicate trips the UNIQUE index and throws.
Parameters
| Parameter | Type |
|---|---|
d1 | D1Database |
input | CreateGameInput |
Returns
Promise<void>
deriveBotKey()
function deriveBotKey(masterSecret, botId): Promise<string>;
Defined in: eigen-server/packages/server/src/bot/bot-auth.ts:60
The per-bot signing key as base64 — the operator utility. This is the
one value an external bot's owner is given, and the only one they need: it
is what they HMAC their request bodies with. The master
BOT_SIGNING_SECRET never leaves the operator, and because every bot's key
is derived from it, registering a bot needs no new secret and no redeploy.
Base64 to match the signature transport encoding. Equivalent to:
echo -n "<botId>" | openssl dgst -sha256 -hmac "<BOT_SIGNING_SECRET>" -binary | base64
Treat the result as a credential: it authenticates that bot to the engine for as long as it is registered. Rotating one bot's key means rotating the master secret, which rotates every bot's key — so issue per-bot keys only to owners you would re-issue all of them for.
Parameters
| Parameter | Type |
|---|---|
masterSecret | string |
botId | string |
Returns
Promise<string>
displayRating()
function displayRating(mu, sigma): number;
Defined in: eigen-server/packages/kernel/dist/index.d.ts:184
max(0, round((mu − 3σ) · 40)) — the one server-side home of the display formula (the client mirrors it for optimistic display only).
Parameters
| Parameter | Type |
|---|---|
mu | number |
sigma | number |
Returns
number
ensureUser()
function ensureUser(
d1,
claims,
now): Promise<{
avatarUrl: string | null;
createdAt: number;
displayName: string;
email: string | null;
id: string;
isAnonymous: boolean;
updatedAt: number;
username: string;
}>;
Defined in: eigen-server/packages/server/src/auth/provision.ts:51
Load the caller's row, creating or backfilling it as the token demands. One read on the hot path; writes only on first sight and on guest → permanent conversion.
Parameters
| Parameter | Type |
|---|---|
d1 | D1Database |
claims | AuthClaims |
now | number |
Returns
Promise<{
avatarUrl: string | null;
createdAt: number;
displayName: string;
email: string | null;
id: string;
isAnonymous: boolean;
updatedAt: number;
username: string;
}>
isTransientD1Error()
function isTransientD1Error(error): boolean;
Defined in: eigen-server/packages/server/src/d1/retry.ts:55
True for the D1 failures worth retrying — a network blip, a storage or Durable-Object reset, a code-update restart, or a transient routing failure.
Deliberately narrow. Overload and resource-limit errors are excluded (the
remedy is to shed load, not retry), as are deterministic failures such as a
constraint or type error, where retrying only delays the report. The whole
cause chain is examined, because drizzle rewraps failures in its own
message that does not carry the underlying text.
This is the default predicate for withRetry; pass
shouldRetry to override it.
Parameters
| Parameter | Type |
|---|---|
error | unknown |
Returns
boolean
mirrorRoster()
function mirrorRoster(d1, args): Promise<void>;
Defined in: eigen-server/packages/server/src/d1/apply.ts:280
The roster mirror after a committed waiting-room command — the DO's
roster is the integrity copy; this rewrites the D1 display copy wholesale
(delete + reinsert), which is idempotent and immune to per-row drift.
Fire-and-forget post-commit (the DO leaves it unawaited; no waitUntil),
single attempt.
Parameters
| Parameter | Type |
|---|---|
d1 | D1Database |
args | { gameId: string; now: number; seats: Seat[]; status: GameStatus; } |
args.gameId | string |
args.now | number |
args.seats | Seat[] |
args.status | GameStatus |
Returns
Promise<void>
openApiDocument()
function openApiDocument(version): OpenAPIObject;
Defined in: eigen-server/packages/server/src/engine.ts:490
Build the API document from an inert app — route handlers never run, so
the context can refuse everything. appName is an unused placeholder here:
with deepLink: null the landing route (its only reader) is never mounted.
version is an argument rather than a constant in here because it has
exactly one correct value — @eigeninteractive/server's own — and changesets
owns that value. Baked in as a literal it silently disagrees with the package
on the first release: nothing reads it back, and the CI drift check only
compares this file against itself, so the lie survives every check. The Dart
client's pubspec is stamped from the same source for the same reason.
Parameters
| Parameter | Type |
|---|---|
version | string |
Returns
OpenAPIObject
readGameRow()
function readGameRow(d1, gameId): Promise<
| {
access: GameAccess;
archivedAt: number | null;
budgetSeconds: number | null;
config: JsonObject;
createdAt: number;
createdBy: string | null;
finishedAt: number | null;
finishId: string | null;
id: string;
incrementSeconds: number | null;
maxPlayers: number;
minPlayers: number;
outcomes: OutcomeEntry[] | null;
participants: Seat[];
pendingPlayers: number[] | null;
rated: boolean;
ratingPool: string | null;
schemaVersion: number;
shortCode: string;
status: GameStatus;
turnDeadline: number | null;
turnSeconds: number | null;
updatedAt: number;
}
| undefined>;
Defined in: eigen-server/packages/server/src/d1/apply.ts:341
Lazy-init read: the D1 game + participants rows the DO copies into
its meta/roster on first contact — one batched round trip.
Parameters
| Parameter | Type |
|---|---|
d1 | D1Database |
gameId | string |
Returns
Promise<
| {
access: GameAccess;
archivedAt: number | null;
budgetSeconds: number | null;
config: JsonObject;
createdAt: number;
createdBy: string | null;
finishedAt: number | null;
finishId: string | null;
id: string;
incrementSeconds: number | null;
maxPlayers: number;
minPlayers: number;
outcomes: OutcomeEntry[] | null;
participants: Seat[];
pendingPlayers: number[] | null;
rated: boolean;
ratingPool: string | null;
schemaVersion: number;
shortCode: string;
status: GameStatus;
turnDeadline: number | null;
turnSeconds: number | null;
updatedAt: number;
}
| undefined>
updateSummary()
function updateSummary(d1, args): Promise<void>;
Defined in: eigen-server/packages/server/src/d1/apply.ts:262
The display upsert after a non-finishing transition — fire-and-forget
post-commit (the DO leaves it unawaited; no waitUntil), single attempt,
re-derivable from the DO at any time.
Parameters
| Parameter | Type |
|---|---|
d1 | D1Database |
args | { gameId: string; now: number; pendingPlayers: number[]; status?: "active"; turnDeadline: number | null; } |
args.gameId | string |
args.now | number |
args.pendingPlayers | number[] |
args.status? | "active" |
args.turnDeadline | number | null |
Returns
Promise<void>
withRetry()
function withRetry<T>(op, options?): Promise<T>;
Defined in: eigen-server/packages/server/src/d1/retry.ts:88
Run op, retrying a retryable failure with jittered exponential backoff up
to attempts. A non-retryable failure — or the last attempt — throws.
Safe to leave unawaited inside a Durable Object: the DO stays alive while the
returned promise (and its backoff timers) is pending, so the whole sequence
runs to completion without waitUntil, exactly like the single-attempt
writes it wraps.
op MUST be idempotent — a retry can fire after a write that actually
landed but whose acknowledgement was lost.
Type Parameters
| Type Parameter |
|---|
T |
Parameters
| Parameter | Type |
|---|---|
op | () => Promise<T> |
options | RetryOptions |
Returns
Promise<T>