08 — Services & Platform
This document specifies the Every Last City platform layer: the account and profile model, cross-device cloud save and settings sync, the online game service (correspondence and cloud async play under the Orders/Cascade turn structure, the turn-bundle drop-box, workshop hosting, sharing), the notification fan-out, statistics and leaderboards built on the Victory system's Standing, Tenure and Track records, the simulation-farm and AI-pipeline infrastructure, telemetry and privacy, the self-hosting story, and operational requirements for the official hosted service. Protocol-level multiplayer behaviour lives in 05-multiplayer.md; content and package formats live in 07-modding-content.md; the save/replay container this platform stores and syncs is defined by 03-architecture.md; the turn structure the async service exists to carry is defined by 10-turn-model.md; the records the statistics system reports on are defined by 14-victory.md.
Status: Draft v0.2 · Owner: unassigned · Depends on: docs/design/00-direction.md, docs/design/01-decision-turn-model.md, 03-architecture.md (the save container, determinism, the CLI and its farm verb, plugin runtime config), 05-multiplayer.md (wire protocol, authority modes, fog enforcement, invites, stall policy), 06-ai.md (the AI seat contract and the self-play tuning pipeline), 07-modding-content.md (package formats, the WASM sandbox, upload validation), 10-turn-model.md (Orders, the Cascade, the Reckoning, deadlines, reserve banks, absence and substitution), 13-command.md (Attention Events and the Dispatch), 14-victory.md (Tracks, Standing, Tenure, the Seal record, the Chronicle, placings), 04-ui-ux.md (the screens this document supplies content for)
1. Platform principles
SVC-010 Guest-first: the game MUST be fully playable without any account or network connection. Solo play, hotseat, LAN/direct-connect play, local saves, local mods, the map/scenario editor, and the unit-set editor MUST all work for an offline guest profile. No feature may nag a guest to sign up more than once per install.
Rationale: an account is a network dependency, and a patient single-player strategy game is exactly the thing people play on a laptop with no connection. Requiring identity to start a solo game buys the platform nothing and costs the player the whole product when the service is unreachable. Guest-first is also the honest position given SVC-970: if nothing may ever require an account, the offline path has to be the primary one rather than a degraded fallback.
SVC-020 A cloud account MUST be free, and MUST be required only for the four capabilities that
inherently need durable server-side identity: (a) cross-device sync of saves/settings/history,
(b) participating in server-hosted games — cloud async and correspondence play, the turn-bundle
drop-box (SVC-448), and Internet real-time relayed or authoritative through an @everylastcity/server
instance (MP-050), (c) publishing to the workshop, and (d) appearing on leaderboards. Browsing and
downloading public workshop content MUST NOT require an account (subject to rate limits, SVC-465). An
instance-scoped guest identity (SVC-167) satisfies (b) only where the instance's account policy permits
it; it never satisfies (a), (c), or (d).
SVC-030 Local-first: every piece of account-scoped data (profiles, settings, saves, history) MUST have its authoritative working copy on the player's device and remain usable with the service unreachable. The cloud is a sync/replication target, never a gate. Loss of connectivity MUST NOT lose player data or block offline features.
SVC-040 Self-host parity: everything the official service does MUST be achievable by self-hosting
the published @everylastcity/server Docker image (§10) — one image, containing both network surfaces
(SVC-401). The official hosted instance MUST run the same code and image that is published for
self-hosting. Features MUST NOT be gated to the official instance except where §10 explicitly notes an
operational exception.
SVC-050 Boring infrastructure: @everylastcity/server MUST be a single Node 22 LTS application
written in TypeScript (strict), whose only required external dependencies are PostgreSQL 16+ and a
blob store (local filesystem by default, any S3-compatible store for scale). PostgreSQL MUST be the only
supported relational database: a second SQL dialect would mean two schemas, two migration paths, and two
sets of query semantics to verify, and mechanical verification is the safety net this project depends
on. Redis MAY be used for room presence and horizontal scale (SVC-407) but MUST remain optional — a
single-process instance MUST work without it. No other infrastructure component (queues, search engines)
may be introduced into the base spec without a revision here. Zero-configuration self-hosting survives by
shipping PostgreSQL inside the published image (SVC-800), not by supporting a second engine.
Rationale: small-studio scope; one process + one DB + one bucket is operable by one person. Losing an
embedded-database default is a real cost, but it is paid once in image size and first-run time (SVC-800)
rather than continuously in dialect drift across every query the service issues.
2. Identity and accounts
2.1 Local profiles
SVC-100 The client MUST support multiple local profiles per install. A profile MUST exist before play begins; the client MUST auto-create a default guest profile on first run so play is zero-friction.
Rationale: three separate needs converge on the same object. Machines are shared. Hotseat play (10-turn-model.md TM-460) puts several people at one keyboard and each of them wants their own keybindings and their own record. And a player who wants to try a strategy without it landing in the record they care about needs somewhere to put it (SVC-630).
SVC-110 Each profile MUST carry a permanent unique identifier (UUIDv7) generated at creation. Display names MUST NOT be required to be unique.
Rationale: uniqueness on a human-chosen name is a tax with no benefit at profile level — identity is the UUID, the public handle of SVC-155 is the thing that must be unique, and two people called "Ironside" on one machine is not a problem worth solving.
SVC-120 A profile MUST store, at minimum: display (commander) name; accent/theme colour (§3); options/settings document; keybinding profiles; per-device-class input layouts (desktop/touch/gamepad); saved setup presets, map-generator option presets, AI option presets, and saved victory Terms configurations (14-victory.md §12.5); notification preferences; and game history/statistics records (§7).
SVC-130 The in-game commander name MUST be changeable at any time, independent of the profile's identity, and MAY differ per game.
Rationale: the name on the profile is administrative and the name on the empire is part of playing. Binding them would mean a player who wants to be someone else this game has to create a second profile and lose their record to do it.
SVC-135 Profiles MUST be exportable to and importable from a single portable file (see SVC-190). Import into an existing profile MUST merge rather than replace (SVC-660).
Rationale: an export that cannot be merged back is a snapshot, not portability — it forces a player moving between two machines to nominate one of them as the real one forever. Merging is possible here by construction because history records are keyed by game UUID (SVC-660).
2.2 Cloud accounts and linking
SVC-140 Any local profile MUST be promotable in place to a linked cloud account without data loss: on linking, the profile's UUID becomes (or is aliased to) the account identity and all local data eligible for sync is uploaded. Unlinking MUST leave a fully functional local profile with all data intact.
SVC-150 One cloud account MUST be linkable on any number of devices concurrently. Each linked device appears in a device list with name, platform, and last-seen time; the player MUST be able to sign out any device remotely.
SVC-155 Cloud accounts MUST have: an immutable account UUID; a unique, changeable public handle (rename cooldown SHOULD be 30 days); a non-unique display name; an optional verified email address; and zero or more linked OAuth identities. Handles are the invite/leaderboard identifier; display names are cosmetic.
SVC-160 Accounts MUST require a stated age of 13+ (16+ where local law requires); guest profiles carry no age gate. The service MUST NOT knowingly retain personal data of children below the threshold.
2.3 Authentication and sessions
SVC-165 The official service MUST support passwordless email sign-in (one-time code or magic link) as the baseline method. OAuth providers (Steam, Apple, Google) MAY be added; if any third-party OAuth login is offered in the iOS client, Sign in with Apple MUST be offered there (App Store requirement). The service MUST NOT store user-chosen passwords for the official instance. Self-hosted instances MAY enable a local username+password mode for closed groups (Argon2id-hashed) since SMTP may be unavailable (§10).
SVC-167 Instance-scoped guest access: an instance's account policy (open / invite / guest — MP-1200)
MAY offer guest identities so a player can be invited into a game on that instance without creating a
durable account. (A guest identity is a server-side thing, distinct from the purely local guest
profile of SVC-010/SVC-100, which touches no server at all.) A guest identity MUST be: minted only
from a valid invite capability token or join code (SVC-420, MP-470); scoped to the single server
instance that issued it and to the games it was invited to; authenticated by a guest token presented
in Auth (MP-270) with the same session rules as SVC-170 (short-lived access token, rotating revocable
refresh token bound to one device record). A guest identity MUST NOT require an email address, MUST NOT
be usable on another instance or device, MUST NOT be a workshop publisher, MUST NOT appear on
leaderboards or be rated (SVC-640), and MUST NOT receive cross-device sync (SVC-320) — i.e., only
SVC-020(b) is unlocked. Lifetime: a guest identity MUST survive as long as it holds a seat in a live
game on that instance, plus an instance-configurable idle expiry (default 90 days) after its last game
ends; on expiry the seat history remains in the game record under the guest's display name. A guest MUST
be able to upgrade in place to a full cloud account on that instance, keeping its seats, history, and
notification bindings (the server-side analogue of SVC-140). Guests are subject to the same age
statement as accounts (SVC-160) and to instance moderation (SVC-480, SVC-645). The official instance
MUST default guest access to off, so SVC-020's account gates apply there unless the operator
deliberately enables it; self-hosted instances choose their own policy. Rationale: 05-multiplayer.md
builds on instance-scoped guests (MP-050, MP-270, MP-1200); this is the identity model those
requirements assume, kept deliberately thin so guests never become a second, unmanaged account tier.
SVC-170 Sessions MUST use short-lived access tokens (≤ 1 hour) plus rotating, revocable refresh tokens bound to a device record. All tokens MUST be revoked on remote sign-out or account deletion. All API traffic MUST use TLS 1.2+.
SVC-175 Per-game passwords, password checks on load, and passwords embedded inside transferable game files MUST NOT exist anywhere in this design. Access to a server-hosted game is governed by account identity plus per-game membership and invite capability tokens (05-multiplayer.md).
Rationale: a secret that travels inside the file it protects authenticates the file, not the person holding it, and it is disclosed to everyone the file is ever forwarded to. It also cannot be revoked, rotated, or audited, and it produces the worst possible failure — a player permanently locked out of a months-long correspondence game by a forgotten string. Real membership does all of the work and none of the harm.
SVC-180 New-device sign-in MUST trigger a security notification (§6) to the account's verified email.
2.4 Data portability
SVC-190 "Export everything": the client (and, for linked accounts, a service endpoint) MUST produce a
single archive containing, in documented open formats: profile metadata, all settings and keybinding
documents, all saved setup/generator/AI/Terms presets, every save (as 03-architecture.md containers —
initial state plus order log), all bookmarks/annotations, the full game-history record set including the
per-turn series of SVC-605, each finished game's stored Terms paragraph (VC-2000) and complete Chronicle
(VC-2450), workshop publication metadata, and the telemetry consent record. The archive MUST be
importable by any Every Last City client and by any @everylastcity/server instance. What "every save"
resolves to for a server-hosted game is bounded by MP-730 and not widened by this endpoint: for a live
authoritative fog-enforced game the export carries that seat's own fog-filtered view cache (AR-590),
never the container or the log (MP-250); the full container becomes exportable only where MP-730 says it
does. Solo, hotseat, relay-only, and bundle/drop-box games export in full.
SVC-195 Nothing in the export may be proprietary-encrypted: a player with only the published format documentation (03, 07) and no Every Last City software MUST be able to parse it.
3. Settings and administration
04-ui-ux.md owns the screens. This section owns scope, storage, and sync semantics — which setting lives where, and what may never be stored at all.
SVC-200 Settings MUST be organized into exactly three scopes, each a versioned document: device (video/resolution, audio device, performance, data directory location), profile (gameplay options, interface mode, keybinds, accent colour, notification prefs — sync-eligible), and per-game (view preferences such as per-position colour overrides). A setting MUST live in exactly one scope, and SVC-260 is the normative assignment for every setting that plausibly belongs to two.
SVC-210 All audio controls, including title/intro music volume, MUST live in the single audio settings panel.
Rationale: a volume control reachable only from an administration screen is a defect, not a feature. One panel per concern is the rule, and audio is the case where the temptation to make an exception is strongest because the title music plays before a profile is loaded — which is a loading-order problem to solve, not a reason to scatter the control.
SVC-220 New-profile defaults: the device MUST hold an editable "defaults for new profiles" template (options + keybinds), applied at profile creation only and never as a live override of an existing profile.
Rationale: on a shared machine the person who sets up the game wants the next person's profile to start sensibly, but a template that kept applying itself would silently undo deliberate per-profile choices. Creation-time-only makes the template predictable and makes "reset to defaults" a distinct, explicit act.
SVC-230 Profile accent colour: each profile MUST have an accent colour/theme applied to out-of-game client chrome (menus, dialogs). In-game colouring is determined by the player position, not by the profile, so that every participant sees the same board in the same colours. The player MUST be able to override the accent independent of any preset palette.
SVC-240 Hosting defaults survive only as self-hosted server configuration (MP-470, MP-1200;
SVC-810) — not as a profile setting. No profile, device, or per-game setting may store a session/game
password: lobbies are joined by auto-generated invite link or join code (MP-470) and per-game passwords
are prohibited outright (SVC-175). The client MAY keep one device-scope (SVC-200) preferred listen port
used to prefill direct/LAN hosting (MP-440(b)) and manual host:port connection (MP-450, MP-500); it is
a convenience default with no security role. Decision: 05-multiplayer.md owns the direct-hosting flow
and it has no password concept, so 08 stores nothing that flow would consume.
SVC-250 Storage/administration page: the client MUST expose the locations of the installed content directory and the user data directory with one-click "open folder" and "move data directory" actions. Relocating the data directory MUST be an action in the client, never a text file the player is expected to author; forced-resolution and display-mode overrides MUST be normal video settings in the device scope, never an override file.
Rationale: every configuration mechanism that lives outside the settings UI is a support burden and an undocumented API. If a setting is worth having it is worth a control; if it is not worth a control it is not worth supporting.
SVC-260 Scope assignment (normative; this table discharges SVC-200's "exactly one scope" claim for every setting that could plausibly be argued into two):
| Setting | Scope | Specified by |
|---|---|---|
| Resolution, renderer, display mode | device | SVC-200, SVC-250 |
| Audio output device | device | SVC-200 |
| Every volume level, title music included | profile | SVC-210 |
| Data directory location | device | SVC-250 |
| Preferred listen port for direct/LAN hosting | device | SVC-240 |
| Keybindings, per device class | profile | SVC-270 |
| Accent colour / out-of-game theme | profile | SVC-230 |
| Notification channels, quiet hours, per-game mute | profile (per-game override) | SVC-530 |
| Telemetry and crash consent | device and profile jointly | SVC-700 |
| Saved setup / generator / AI / Terms presets | profile | SVC-120, SVC-320 |
| Defaults applied to newly created profiles | device | SVC-220 |
| Per-position colour override within one game | per-game | SVC-200 |
| Rewind discipline | per-game | SVC-315 |
| Orders deadline and reserve bank | per-game, in the game record | 10-turn-model.md TM-480, TM-490 |
| Stall policy and vacation pool | per-game, in the game record | 05-multiplayer.md MP-990; SVC-560 |
| Instance account policy, listen port/TLS, quotas, retention | server configuration | SVC-810 |
| Any session or game password | nowhere — prohibited | SVC-175, SVC-240 |
SVC-270 Profile-scope settings of a linked account MUST sync across devices (§4). Keybinding profiles MUST be cloud-synced per device class so a desktop layout never overwrites a touch layout.
4. Cloud saves and sync
SVC-300 The sync payload for a game MUST be exactly the 03-architecture.md save representation: an immutable initial state, an append-only order/event log, and optional snapshot caches. Sync MUST be delta-based — the client uploads only records after the server's last acknowledged index — and resumable after interruption. Snapshots are never authoritative and MAY be regenerated rather than transferred.
SVC-310 Every synced game MUST retain its full order history for its lifetime. The player-facing timeline supports named bookmarks on any index; restoring a bookmark MUST create a non-destructive branch. Nothing in Every Last City may destructively overwrite prior game history.
Rationale: the whole architecture is event-sourced, so a branch is cheap and an overwrite is a deliberate destruction of information the engine was going to keep anyway. Named archives as the only rollback mechanism — the usual arrangement — makes players hoard save slots to work around a limitation that no longer exists.
SVC-315 Commitment discipline: game setup MUST offer a per-game option "no rewind — bookmarks only", which restricts the rollback UI without altering anything the rules compute. Its default MUST be off in solo and hotseat games and on in every multiplayer game. In server-hosted multiplayer games, rollback MUST additionally be an audited admin action requiring the consent of all active human participants by default, or admin-only when that policy was declared at game creation (MP-740).
Rationale: the turn model asks a player to commit orders before seeing the Cascade (docs/design/01-decision-turn-model.md), and that commitment is where the tension lives. A solo player who wants to rewind and re-issue is spending their own game and the design does not moralise about it — so the default is off. A multiplayer game is not one player's to rewind, which is why the default flips and why consent is required rather than assumed. Restricting the UI rather than the rules keeps the option outside the simulation entirely: no hash, no replay divergence, no rules variant.
SVC-320 Sync set: for linked accounts the following MUST sync — in-progress and finished solo game saves (opt-out per game), profile-scope settings (SVC-270), saved setup presets, map-generator option presets, AI option presets, saved victory Terms configurations, the last-used setup, bookmarks, game-history records, and workshop subscriptions. Server-hosted multiplayer games live canonically on their server and are cached locally, not synced as personal saves.
SVC-330 Conflict handling MUST never silently discard data. Settings documents merge per-key with last-writer-wins using hybrid logical clocks; a lost write MUST be recoverable from the document's version history (≥ 10 versions retained). Divergent extensions of the same solo game's order log MUST be kept as two branches and surfaced to the player to choose or keep both.
SVC-340 Offline queue: all sync operations MUST queue locally and drain automatically when connectivity returns. A device that stays offline indefinitely loses no functionality (SVC-030).
SVC-350 Encryption: sync traffic MUST use TLS; save blobs and order logs MUST be encrypted at rest server-side (AES-256, keys held by the service operator). Client-side end-to-end encryption of solo saves (operator-blind, passphrase-derived key) MAY be offered; E2E saves are excluded from server-side features that must read state.
SVC-360 Quotas: the free tier MUST include at least 1 GB of sync storage per account (configurable per instance; self-hosts choose their own). Order logs are small (a typical game SHOULD be under 5 MB per 03-architecture.md targets), so the quota is expected to be effectively unlimited in practice. At quota, sync pauses with a clear client warning; the service MUST NOT delete player data to reclaim space without explicit consent. Snapshot caches MAY be evicted server-side at any time. Server-hosted game storage — async order logs and observation streams (SVC-410, SVC-413) and drop-box bundles (SVC-448) — is instance-side storage charged to the game, and MUST NOT count against any participant's personal sync quota; running out of personal quota MUST never block submitting orders in a server-hosted game.
SVC-370 Deleting a game from one device MUST be a sync-visible tombstone with a 30-day undo window before server copies are purged.
5. The game service (@everylastcity/server)
5.1 API fundamentals
SVC-400 @everylastcity/server MUST expose a versioned HTTPS JSON API (/api/v1/...) served by
Fastify, described by a published OpenAPI document generated from the same schemas the handlers
validate against — one definition, never a hand-maintained second copy. Alongside it runs the real-time
channel defined in 05-multiplayer.md (SVC-401). All object identifiers are UUIDv7. Breaking changes
require a new major API version; each server MUST advertise at /api/meta its supported versions and
which optional surfaces it actually runs — the real-time room server (SVC-404) and the turn-bundle
drop-box (SVC-448) — and clients MUST interoperate across at least one major version of skew.
SVC-401 Runtime composition: one image, one source tree, two network surfaces. The published artifact
MUST contain both the Fastify HTTP API (§§4–8) and the Colyseus room server that carries the
05-multiplayer.md wire protocol (MP-260), built from the same TypeScript sources and running on Node 22
LTS. The server MUST import the same @everylastcity/core package the client imports; a separate
server-side implementation of any rule is prohibited. One consequence is normative here: because that
one core computes client-side preview, authoritative Cascade resolution (MP-340), and the headless
re-simulation that gates rated results (MP-1180), rated-play verification is only as sound as
cross-runtime determinism. The re-simulation verifier MUST therefore be covered by the cross-platform
determinism CI of 03-architecture.md AR-960, and a Node-versus-browser divergence MUST be surfaced as an
infrastructure fault, never as a player-conduct signal (SVC-645).
SVC-402 Colyseus scope (binding): the room server MUST be used for rooms, matchmaking, lobby presence, Orders-phase presence, connection lifecycle, and reconnection — and for nothing else. Its schema state-synchronization feature MUST NOT be used as the transport for game state. Orders travel up and validated events travel down exactly as 05-multiplayer.md specifies (MP-250, MP-260, MP-270); what crosses the room's socket is that protocol's message envelope, not a framework-computed state diff.
| Colyseus MAY carry | Colyseus MUST NOT carry |
|---|---|
| Room membership, lobby occupancy and seat-claim status, per-seat presence including Orders-phase commit markers and remaining time (TM-440), pre-launch lobby chat, matchmaking queries, connection/reconnection lifecycle | Game state in any form: the order log or any segment of it, snapshots, ViewCheckpoint/ViewDelta data, fog-filtered view state, Cascade observation streams (TM-2150), order results, turn outcomes — and in-game chat, which is a protocol message (MP-185, MP-270) |
Where 05-multiplayer.md defines a protocol message for something (MP-270), that message is the contract and schema sync MUST NOT become a second, undocumented channel for it. Presence data carried by schema sync is non-authoritative, MUST carry no fact about the game world (TM-440, TM-2070), and MUST be reconstructible from PostgreSQL and the log; losing all of it at once MUST cost nothing but a reconnect. Rationale: this project is event-sourced — the append-only order log is the truth, and replays, spectating, desync detection (MP-1150), fog enforcement, and mode switching all derive from it. Adopting the framework's headline feature as the state transport would destroy every one of those properties, so it is deliberately declined.
SVC-403 Room lifecycle: a room is an ephemeral, in-memory view onto a durable game, never the
game itself. The service MUST be able to materialize a room for any game on demand from that game's
persisted log (SVC-410, MP-090), and MUST dispose it when the last client leaves. Room creation,
disposal, process restart, deploy, and crash MUST NOT end a game, alter its log, forfeit a seat, or lose
a submitted order: an order MUST be durable before it is acknowledged (MP-160, and the durable-ack
objective of SVC-900), and a re-materialized room rebuilds from the stored log. The framework's own
reconnection window is a transport convenience only — the authoritative resume contract is MP-290, which
MUST behave identically for a client returning after its room was disposed and rebuilt as for one
returning to a room still in memory. Room identifiers MUST NOT be exposed as durable game identifiers;
GameId (MP-100) remains the only identity appearing in links, invites, saves, notifications, and the
API.
SVC-404 Async play MUST NOT depend on a live room. Cloud async and correspondence play — the primary
multiplayer mode (§5.2) — MUST be complete over the HTTP API alone: listing games, fetching a game and
the content that seat is entitled to receive, issuing and revising orders, committing, and receiving
notifications MUST all succeed with no room running, no other participant connected, and the real-time
room server stopped, crashed, absent, or disabled by configuration (SVC-810). The turn-bundle drop-box
(SVC-448) MUST likewise be HTTP-only. An instance MAY be operated with rooms disabled and MUST then
still serve async games, cloud sync, the workshop, notifications, and statistics in full, advertising
the absence at /api/meta (SVC-400); real-time play is the only capability it forgoes. Conversely, a
real-time game's log lives in the same store as an async game's, which is what keeps mode switching a
scheduling change rather than a data migration.
Entitlement is a property of the seat, never of the transport. MP-250 governs what a seat may be served over this HTTP API exactly as it governs the room socket, and this requirement states no exception to it:
- A seat already entitled to the whole log — relay-only, direct-LAN, and bundle/drop-box participants (MP-250, MP-340, MP-730) — MAY fetch the canonical order log and a full-state snapshot over HTTP.
- A seat in an authoritative fog-enforced game — which cloud async games default to (MP-380), so
this is the ordinary case, not an edge case — MUST instead be served only its own compacted
observation stream (TM-2150) plus an AR-500
ViewCheckpointof that seat's view, and MUST NOT be served the canonical order log or any segment of it,RefereeViewdata, or a full-state snapshot, under any circumstance including an explicit request for one (MP-250, MP-290, MP-1150, AR-590). An authoritative-mode client that asks for the log MUST be refused, not accommodated.
The fog-enforcement rules themselves remain owned by 05-multiplayer.md and 10-turn-model.md §14; this clause exists only so the HTTP surface cannot be read as a route around them.
Rationale: correspondence games are designed to run for months to years (MP-630) across restarts, deploys, and outages. Binding a turn submission to a live in-memory room would make the most durable mode in the product depend on the least durable component in the stack. Room independence is a durability property, though — not an entitlement exemption. The SVC-402 table already forbids the log on the room socket; a leak over REST would be the same leak, and the primary async mode is precisely the one that runs fog-enforced.
SVC-405 Mutation endpoints MUST be idempotent via client-supplied idempotency keys, so order submissions, commits, and uploads can be retried safely over bad links.
SVC-407 Scale and CPU isolation: Node runs one event loop per process, so the platform MUST NOT resolve a Cascade on the loop that serves requests. Cascade resolution (SVC-413), headless re-simulation for rated verification (MP-1180), map generation, uploaded-package validation (SVC-485), and farm batches (§8) MUST run off the request/room loop — worker threads or separate processes — behind a bounded queue, with saturation surfaced as an explicit 429/503 rather than as unbounded latency (SVC-490, SVC-900). A single-process instance MUST work with no Redis; multi-process or multi-node deployments MUST use Redis for room presence and matchmaking and MUST route a client to the node holding its room. The room is the sharding unit and a single game MUST NOT span nodes.
Rationale: the Cascade is the one genuinely CPU-shaped thing the service does, and it arrives in a burst the instant the last player commits. Every seat is waiting on it at once, which is exactly when the request loop must not be blocked.
5.2 Async games, the Orders phase, and the turn-bundle drop-box
The turn protocol, ordering, fog enforcement, and anti-cheat are specified in 05-multiplayer.md; the turn structure itself is 10-turn-model.md's. This section specifies the platform obligations that carry them.
SVC-410 The service MUST host async games server-side: the authoritative record is the game's order log plus membership. There are no player-visible files, inbound/outbound directories, SMTP configuration, or hand-typed position numbers. The service MUST NOT transmit turn data by email: service-originated mail carries links only (SVC-540). This bans service-side SMTP turn delivery, not the player's own hand-off — exporting a turn bundle to the OS share sheet or the player's mail client with the file attached (MP-840) is a permanent supported capability (MP-080) and is unaffected by this requirement. Submitting orders in an async game is an HTTP operation against durable storage (PostgreSQL), never a live-room operation: SVC-404 governs, and a room is materialized only while someone is actually connected to watch (SVC-403).
SVC-411 The unit of async work MUST be one Orders submission per player per turn, not a baton
passed between seats. The platform MUST expose a turn to a seat as: fetch that seat's entitled view of a
board that will not change for the phase (TM-2050); issue, revise and withdraw order records freely
(TM-330, TM-360); CommitOrders (TM-390); and afterwards fetch that seat's observation stream for the
resolved Cascade (SVC-413). A seat MUST be able to complete all of that in one session, at any hour, with
no other participant present, no live room, and no dependency on any other seat having acted (SVC-404,
TM-560).
Rationale: this is why the turn model was chosen and it is the platform's single most important property. A sequential model costs one network round-trip per unit activation; six players with thirty units each is 180 sequential hand-offs, potentially hours apart, which is not a slow correspondence game but a broken one (docs/design/01-decision-turn-model.md). One submission per player per turn is the shape the service has to be built for, and every other requirement in this section follows from it.
SVC-412 The Orders deadline and the reserve bank MUST be durable service state. 10-turn-model.md puts these clocks outside the core and reduces them to two logged facts (TM-480, TM-490, TM-510, TM-550); this document places the clocks in the service and constrains them:
- Deadline and reserve state MUST survive process restart, deploy, and crash, and MUST be recomputed from durable storage rather than held in process memory.
- The service MUST emit at most one
TurnTimeoutper seat per turn (TM-520) and exactly oneCloseOrdersper turn (TM-430), and both MUST be durable before the Cascade begins (SVC-413). - The service MUST close the phase immediately when every expected seat has committed, without waiting for the deadline (TM-400).
- Where the deadline is
None— the default for solo, two-human and hotseat games (TM-490, TM-500) — the service MUST schedule no timer at all and MUST NOT surface a countdown. - A
TurnTimeoutMUST NOT be presented anywhere in the platform as a forfeit, a penalty, or a conduct signal (TM-540, SVC-645).
Rationale: the clock is the one piece of wall-clock time in the whole system, it is deliberately kept
out of the simulation, and it therefore has no home but here. Making it durable is not a nicety: a
correspondence game whose 48-hour deadline resets on every deploy has no deadline, and one that fires
twice because two nodes both held a timer produces two TurnTimeout records for a seat that missed one
turn.
SVC-413 Cascade execution and delivery:
- The service MUST NOT begin a Cascade before its turn's
CloseOrdersrecord is durable, and MUST resolve each game's Cascade exactly once per turn. - Cascade resolution MUST run off the request/room loop as a bounded queued job (SVC-407).
- Because the Cascade is a pure function of committed state and the committed order set, a crash mid-resolution MUST be recoverable by re-running it from the durable log to an identical result. The service MUST NOT persist a partially applied Cascade.
- Each seat MUST be served its own compacted observation stream (TM-2150, TM-2160) as a single payload after the Cascade has fully completed (TM-2170), and nothing else. A fog-enforced seat MUST NOT be served the canonical log (SVC-404).
- Observation streams MUST be retained for the life of the game, so that a correspondence player arriving days later can review the previous Cascade during the current Orders phase (TM-2230) without the service recomputing it.
- Playback presentation is 04-ui-ux.md's, driven by 10-turn-model.md §15; the platform supplies the stream and nothing about its pacing.
SVC-414 Absence MUST NOT be an exception path. A seat that never opens its Orders phase MUST resolve exactly as 10-turn-model.md and 13-command.md specify — persisted order stacks and Stances execute normally (TM-530), units with no Posture adopt their Fallback Posture for that turn only, and Dispatch items persist to that player's next session rather than being consumed (CM-2010). The platform MUST NOT invent a substitute behaviour of its own, and MUST NOT skip, freeze, or fast-forward an unattended seat. The service MUST additionally:
- maintain each position's
consecutiveMissedTurns(TM-570) as durable authoritative state; - run the substitution offer at the threshold — a vote of the remaining human players, or automatic
substitution in rated play (TM-580) — and record
SubstitutePlayerandResumePlayeras ordinary logged orders (TM-590, TM-600); - support adjournment (TM-620) as a participant- and operator-visible game state that halts at a turn boundary and resumes from the identical state and log;
- record every substitution and resumption in the game-history record (SVC-648).
Rationale: this is what makes correspondence play humane, and it is a genuine property of the turn model rather than a courtesy the platform grants. Because orders are persistent conditional policies evaluated at activation (13-command.md), a player who misses a turn has their standing plan carried out rather than their army frozen. The platform's job is to not get in the way of that: no forfeits, no skipped seats, no invented defaults.
SVC-420 Joining MUST be by invite: either a capability-token link or a short human-typable code (8 characters, case-insensitive). The server assigns and tracks positions; players MUST NOT be required to enter a position number.
Rationale: a hand-typed position number is a data-entry hazard with no compensating benefit — a mistyped digit puts a player in the wrong seat of the right game, which is the most confusing failure the system can produce. A capability token identifies both the game and the seat and can be revoked.
SVC-430 Game start MUST NOT require a bootstrap circulation round: no file has to visit every player before the map can exist, and every join happens through the service rather than by forwarding a game file. Start timing is owned by 05-multiplayer.md MP-660 — by default the map is generated and turn 1 begins when all human seats are claimed, and the creator MAY instead start immediately with unclaimed seats held by stand-in AI until claimed. The platform MUST support both without a circulation round.
SVC-440 Retention: this document owns the async-game retention lifecycle; 05-multiplayer.md MP-720 states the same rule and cites this requirement. An async game MUST remain available while active. A stalled game (no orders from any seat) MUST first be nudged through §6 notifications, and is archived after a single server-configurable dormancy period — default 180 days, minimum 90 days (MP-720): an instance MAY raise the default but MUST NOT configure a period below the 90-day floor. All participants MUST be notified at least 30 days before archiving and again when it happens (SVC-510). Archiving is non-destructive: the game is frozen and delisted from active lists but retained in full — downloadable/exportable by any participant (SVC-190, MP-730) and revivable by any participant with one action (MP-720), which returns it to active status and restarts the dormancy clock. Hard deletion happens only on the unanimous request of the remaining participants (a shared game is no single player's to destroy) or under the instance's published data-retention policy (SVC-760/SVC-770), never by timeout.
Rationale: a game that silently expires destroys something several people spent months on, and the storage it reclaims is a few megabytes of order log. Making archiving non-destructive and one-click revivable reduces the cost of a wrong dormancy default from data loss to mild annoyance, which is what lets the default be a guess at all (open question 8).
SVC-445 Recovery is an audited server action: any state repair (rollback to an order index, substitute player, remove player) MUST be performed through the service with an audit record visible to all participants.
SVC-448 Turn-bundle drop-box: the @everylastcity/server image MUST implement the drop-box bridge
that 05-multiplayer.md requires of it (MP-1190). An operator MAY switch it off for their instance
(MP-880 makes offering it optional) and the instance MUST then say so in /api/meta; the official
instance MUST offer it. Under the Orders/Cascade structure the drop-box is a collection point for one
turn's order bundles, not a baton passed between seats. Platform obligations where it is enabled:
- Scope. The drop-box stores and hands over signed order bundles keyed by
(GameId, turn, seat). The service MUST NOT simulate, validate, or rewrite a bundle's contents: it reads only the bundle header (GameId, turn number, issuing seat, content hashes) and treats the payload as opaque, so the game keeps bundle-mode trust semantics (signatures, no server simulation, MP-880). It is a plain HTTP path (SVC-404): upload, download, and collection MUST NOT require a live room or any other participant to be online. - Collection, not possession. The service MUST track which seats' bundles for the current turn have arrived, MUST accept a replacement bundle from the same seat for the same turn until that turn's set is declared complete (mirroring TM-360's free revision) and retain only the latest, and MUST reject a bundle for a turn already complete by returning the conflict to the client rather than overwriting it. Rollback stays the participants' explicit, co-ordinated act (MP-860).
- Every participant resolves the same Cascade. Once a turn's bundle set is complete, every participant holds the identical committed order set and computes the identical Cascade locally. The service MUST NOT be required to arbitrate the result, and download MUST be available to every participant with no one-download limit (MP-870).
- Notifications. A completed turn set MUST emit the orders-open event (SVC-510) for the next turn to every participant, and an incomplete set past the participants' agreed cadence MUST emit a reminder to the seats still outstanding. Drop-box games carry reminder notifications only — no enforceable timers or auto-turns (MP-1040) — so the stall escalations of SVC-560/MP-990 do not apply.
- Storage and quota. Bundle storage is instance-side game storage charged to the game, not to any participant's personal sync quota (SVC-360). The service MUST retain the current turn's set plus an instance-configurable number of complete predecessor turns (default 3) per game, subject to the instance's storage paths/quotas configuration (SVC-810) and the API rate limits of SVC-490.
- Retention. Drop-box games follow the SVC-440 lifecycle. Because every participant already holds the bundle chain locally (MP-730), archiving a drop-box game MUST NOT be treated as data loss.
- Access. Using the drop-box is server-hosted participation and therefore requires an account or an instance guest identity on that instance (SVC-020(b), SVC-167); the same game MUST remain fully playable by plain file hand-off with no account at all (MP-080, MP-840), and one action MUST convert it to full cloud async (MP-880).
Rationale, and it is why file-based play is worth keeping rather than a legacy obligation. Bundle mode is the only mode that needs no server, no account and no trust in an operator, which makes it the structural guarantee behind SVC-870 and pillar #5. The turn model improves it substantially: under sequential turn-taking a file had to visit all six players in order before turn N could end, so one slow participant blocked everyone. Here the six bundles are independent and arrive in any order, which collapses a round-robin into a single round per turn. That is a real gain, and it is the reason the drop-box is specified as a collector rather than as a mailbox holding one game file.
Contract, recorded rather than paraphrased: 05-multiplayer.md §10 still describes bundle play as a sequential possession chain with a single active seat (MP-830, MP-850, MP-860, MP-900). Those requirements MUST be respecified for the Orders/Cascade structure; this requirement states what the platform provides and does not attempt to fix them from here.
5.3 Workshop and sharing
Package formats, manifests, and client-side install behaviour are owned by 07-modding-content.md. This section specifies the hosting service.
SVC-450 The workshop MUST host every content kind 07-modding-content.md defines, in one service, matching MOD-820 and the manifest content keys of MOD-040 exactly: maps, scenarios, unit sets, asset/graphics packs, localization packs, name lists, rule presets, AI plugins, and worldgen (map-generator) plugins. Saved setup, map-generator-option, AI-option and victory Terms presets are not workshop content in v1 — they are profile data that syncs per SVC-320 and travels in the export (SVC-190); publishing them would require a package content key and format in 07 (MOD-040), which does not exist (see open question 10).
SVC-460 Each workshop item MUST have: a stable item UUID; the package id (manifest.json id —
reverse-DNS or UUID, MOD-030) and the sim hash (MOD-140/MOD-160) of the package it publishes; an owning
publisher account; a display name and description; content-type; searchable tags; auto-extracted
metadata from the package manifest (for maps and scenarios: dimensions, wrap flags, position count,
unit-set dependency, and the victory Terms the scenario sets); explicit semantic versions with
changelogs; declared dependencies resolvable at install time; and optional screenshots/thumbnails.
Rationale: every one of those facts is machine-extractable from the manifest, and a service that does not extract them forces publishers to encode them in the display name, where they cannot be filtered on, cannot be validated, and go stale the moment the package changes. Versions with changelogs rather than overwrite-in-place matter for the same reason they matter in any package registry: a game in progress pins a version (MOD-160), and a publisher who can silently replace it breaks that game.
SVC-462 Item UUID vs. package id: the item UUID is a service-local handle for a listing on one
instance (listings, moderation, comments, subscriptions); the package id is the content identity that
clients, saves, lobbies, and hashes speak (MOD-030, MOD-160). The service MUST therefore: resolve
downloads by (package id, version) as well as by item UUID; keep the package id immutable for the life
of an item and refuse a new version whose manifest id differs; enforce one owning account per package id
per instance (MOD-910); and never require a package id to be listed in the workshop at all — sideloaded
(MOD-900) and server-served (MOD-870) packages are first-class. Item UUIDs are not portable between
instances (SVC-980); package ids are.
SVC-465 Browsing, searching, and downloading public items MUST work anonymously and via a plain web page per item. Publishing, rating, and commenting require an account. Anonymous downloads MAY be rate-limited.
SVC-470 Subscription state ("what I have installed, which items have updates") MUST be tracked server-side per account and synced. Update notifications flow through §6.
Rationale: client-side install tracking breaks the moment a file is moved or a device is replaced, and the failure is silent — the player simply stops being told about updates. Server-side state also makes the lobby's content resolution (SVC-475) a lookup rather than a scan.
SVC-475 In a multiplayer lobby, required content MUST be resolvable automatically. The lobby advertises (package id, version, sim hash) for every sim-affecting package, exactly as MOD-160 requires; clients verify their local sim hashes and fetch anything missing before launch, and a mismatch blocks launch (MOD-160, MP-110/MP-120). The workshop is one resolution source: a client MAY equally satisfy the requirement from already-installed or sideloaded content (MOD-900) or from the hosting server's own content service (MOD-870), so lobby content need not be published in any workshop. Whatever the source, the fetched package MUST match the advertised sim hash. No manual file copying, and no lobby ever keys content to a workshop item UUID (SVC-462).
5.4 Moderation and abuse
SVC-480 Every workshop item and public profile MUST carry a report action. Reports enter a staff review queue; outcomes are: no action, item unlisted, item removed, publisher warned, publisher suspended. A takedown process for IP claims MUST exist (DMCA-compliant for the official US-hosted instance).
SVC-485 Uploaded packages MUST pass the automated validation defined in 07-modding-content.md (schema, size caps, WASM sandbox constraints) before listing. The service MUST run no uploaded code outside the 07 sandbox.
SVC-490 All API surfaces MUST be rate-limited per token/IP with 429 responses; upload size limits are defined per content type in 07-modding-content.md, with a default per-account publisher quota of 2 GB (instance-configurable).
6. Notifications
SVC-500 The service MUST implement notification fan-out with five channels: (a) Web Push (VAPID) — every instance MUST support it; it is the required non-proprietary baseline for browser clients and for any desktop client that speaks the Web Push protocol, and the one push channel a self-hosted operator can run without a third-party developer account (MP-980); (b) mobile push via APNs and FCM for the native iOS/Android clients; (c) desktop notification via the connected client's real-time channel — available only while that client holds a live room connection (SVC-403) — surfaced with native OS notifications; (d) email; (e) an authoritative in-app inbox that always receives every event. Where no push channel is available to a client, that client MUST fall back to polling its game list (MP-980); the in-app inbox (e) is never optional.
SVC-510 Minimum event set:
| Event | Meaning | Source |
|---|---|---|
orders-open |
The Orders phase for turn N is open to you | TM-020, SVC-411 |
cascade-resolved |
The turn resolved; your observation stream is ready to review | TM-2150, SVC-413 |
deadline-approaching |
Your deadline is near and your reserve will begin to draw down | TM-510, SVC-412 |
reserve-low / reserve-exhausted |
Your reserve bank is nearly or fully spent in this game | TM-510, TM-520 |
attention |
A non-adaptable Attention Event on your Dispatch, where 14-victory.md publishes it to you — VictoryAlarm, VerdictOffered (VC-3150, VC-2440) |
CM-1720, CM-2010 |
substitution-vote |
A seat has missed the threshold; you are asked to vote | TM-580 |
substituted / resumed |
A seat was handed to an AI substitute, or reclaimed | TM-590, TM-600 |
game-invite / game-started |
Invitation received; the game has begun | MP-660, SVC-420 |
game-finished |
With your placing, final Standing and Tenure, and the winning route | VC-3030, VC-3050 |
dormancy-warning / archived |
Advance notice and the archive notice | SVC-440 |
bundle-turn-complete |
Every seat's bundle for the turn has arrived | SVC-448 |
workshop-updated / workshop-comment |
A subscribed item changed; a published item was commented on | SVC-470 |
security |
New-device sign-in — email mandatory | SVC-180 |
maintenance |
Service maintenance affecting active games | SVC-900 |
An attention notification MUST carry no fact its recipient could not already observe: Attention Events
are generated per player from that player's fog-filtered view alone (TM-2210, CM-1850), and the
notification MUST NOT widen them.
SVC-520 Latency: orders-open SHOULD be dispatched within 60 seconds (p95) of the Cascade completing
and the next Orders phase opening.
Rationale: in a live game 60 seconds is the difference between a player noticing and a player wandering off; in a correspondence game it costs nothing and there is no reason to be slower. This is the number SVC-900's operational target is set against.
SVC-530 Preferences MUST be per-profile and overridable per game: channel on/off per event type,
quiet hours in the profile's time zone, and a per-game mute. Defaults: orders-open → push + desktop,
with email fallback only if the seat has not committed for 24 h; attention and substitution-vote →
push + email, and MUST NOT be suppressible by quiet hours in a game with a deadline; invites → push +
email; security → email always.
Rationale: quiet hours are for convenience notifications. An event that will resolve without you — a Verdict vote that counts abstention as "play on" (VC-2770), or a rival two turns from winning — is exactly the class 13-command.md exempts from adaptive demotion (CM-1790), and the same reasoning applies to a notification channel as applies to the Dispatch.
SVC-540 Service-originated email MUST be notification-only: the service MUST NOT attach game data (turn bundles, saves, order logs) to any message it sends — links into the client or web view only. This constrains the service, not the player: a player exporting a turn bundle and sending it from their own mail client or share sheet (MP-840) is explicitly unaffected and MUST remain possible with no account and no service involvement (MP-080). A daily digest mode MUST be offered that coalesces all open-orders, Dispatch-backlog and workshop events into one message. Every notification email MUST carry a one-click unsubscribe honouring CAN-SPAM/GDPR; transactional security email is exempt from unsubscribe.
SVC-550 Device push tokens MUST be stored per device record, refreshed by the client, and purged when the provider reports them invalid or the device is signed out.
SVC-560 Vacation mode: a player MAY set an away window. The away window and the stall policy's vacation allowance (MP-990) are one mechanism, not two: while the window is in effect, the service MUST suspend that player's deadline and reserve draw-down (SVC-412) in every server-hosted game whose stall policy grants a vacation pool, drawing down that game's per-player pool (default 30 days/year, MP-990) one day per calendar day away, and MUST announce the away state to each affected game's participants. Days beyond an exhausted pool MUST NOT suspend that game's timers — the timer resumes, the player is warned before the pool runs out, and normal expiry actions (MP-990/MP-1000, TM-520) apply from then on. Reminder suppression applies for the whole window regardless of the pool, and in games with no deadline (the default for solo, two-player and hotseat games, TM-490) the window is purely a courtesy announcement. Pools are per game, so an away window may exhaust one game's pool while another still has days left. Bundle and drop-box games have no enforceable timers (MP-1040), so the window only suppresses their reminders.
An away window MUST NOT halt a game. The absent seat's turns resolve normally under SVC-414 — persisted
orders execute, and consecutiveMissedTurns accrues toward the substitution threshold exactly as it does
for any other absence (TM-570, TM-610).
Rationale: vacation mode buys a player relief from the clock, not the right to freeze five other people's game for a fortnight. The turn model makes that separation cheap, because an unattended seat plays its standing orders rather than doing nothing.
7. Statistics, history, and leaderboards
14-victory.md computes and retains everything in this section; the platform stores it, aggregates it, and reports on it. No requirement here recomputes a victory value, and none may present one at a precision finer than the band published for it (VC-3170).
SVC-600 Every finished game MUST produce a game-history record on each participating profile, containing at minimum:
| Group | Contents |
|---|---|
| Identity | Game UUID; name; instance; authority mode (MP-340); rated or unrated |
| Versions | RulesVersion, EngineVersion + build digest, victoryLogicVersion (VC-3220), FormatVersion (AR-880) |
| Content | Unit set and terrain set by package id, version and sim hash (AR-150, MOD-160); whether every sim-affecting package was certified, and the "Modded" label where it was not (MOD-170) |
| Terms | The Terms preset name where one was used unmodified (VC-2120), the stored Terms paragraph verbatim (VC-2000), the enabled Track set with their parameters, and every dismissed setup-linter warning (VC-3050) |
| Map | Source (generator + seed, premade map, or scenario id), size, MAP_CITIES, Landmark and Region counts |
| Participants | Accounts, guest identities, or AI identities with plugin version and weight version (AI-670); every seat substitution with predecessor, successor, turn and reason (SVC-648) |
| Outcome | The winner and the route by which they won; every player's placing, final Standing, final Tenure, and the turn they left (VC-3030, VC-3050) |
| Narrative | The complete Chronicle, unredacted (VC-2420, VC-2450) |
| Shape | Turn count; wall-clock duration; per-seat total time in Orders; count of turns each seat committed, timed out, or was absent for |
| Series | The per-turn series of SVC-605 |
Records are append-only and included in the export (SVC-190).
SVC-605 The per-turn series MUST be the retained Seal records themselves (VC-250), not a second computation. For every player at every Seal that is: Standing and each of its five components (VC-510), Tenure, Seatless/Broken/Reduced state, and for every enabled Track the raw progress, threshold, ratio, Vigil counter and visibility band. The platform MUST additionally record per turn, from the documents that own them: cities held and their Integration distribution (12-economy.md), effective Works per turn (VC-530), Manpower stock, army Works-cost in supply (VC-540), units commissioned and lost, and the count of Clashes resolved (11-combat.md).
Rationale: this is a far better dataset than a raw unit-and-city count, and it costs nothing to collect because 14-victory.md already retains it for the Board (VC-2400). Standing decomposed into its five components tells a reader whether an empire grew by conquest or by development; Tenure tells them who was ahead and for how long; Track progress tells them what everyone was actually trying to do. Deriving the series a second time would be a second implementation of the scoring model and would eventually disagree with the first.
SVC-610 Aggregates (games played; win rate and mean placing by Terms preset, by ruleset, by unit set, by map class; distribution of winning Tracks; human-versus-AI splits; hours played) MUST be derived from the record set, never stored as the only copy — so merges (SVC-660) and recomputation are always possible.
SVC-615 Route-of-victory reporting: the service MUST be able to report, for a given ruleset version and Terms configuration, the distribution of winning Tracks, the median and 20th/80th-percentile game length in turns against the estimate the Terms presented (VC-2030), the rate at which games ended by Capitulation, by the Verdict, by mathematical elimination and by the Horizon, and the rate at which a Reduced player recovered (VC-2910). Reports MUST be aggregate-only and MUST NOT identify a player.
Rationale: 14-victory.md builds its length estimate from a build-time calibration table and makes a promise to the player on the setup screen about how long an evening will take (VC-2030, VC-2070), with CI failing when the estimate drifts more than 25 percent (VC-3290). The calibration comes from self-play (§8); this requirement is the check against reality, and it is the one measurement only the platform can make. It also answers the question the design most needs answered — whether the Track catalogue is actually varied in practice or whether one Track wins nine games in ten.
SVC-620 Hours played MUST be tracked locally for every profile, guest or linked. An opt-in "community hours" board on the official service MUST let linked accounts contribute their total to a public community counter and listing.
Decision, replacing a flag that asked this be checked against another game: the board counts a single number — total hours played, summed across every mode including solo — and it is opt-in, resettable only by deleting the account's contribution, and displayed as a leaderboard of handles plus a headline community total. It is deliberately not a skill measure and MUST NOT be presented next to the ladders of SVC-640; conflating time spent with ability is the failure mode of every such board.
SVC-630 Multiple commander profiles are the sanctioned way to keep records separate, and a linked account MAY hold several whose statistics never commingle.
SVC-635 Ladder keys: a leaderboard MUST be keyed by exactly the tuple that determines what skill means in that game, and by nothing else:
- Ruleset major version — a rules change that alters play invalidates comparison (AR-880).
- Content class — either certified stock content (every sim-affecting package certified, MOD-170) or one named non-certified content set. A "Modded" game MUST NOT be rated on a stock ladder.
- Terms configuration class — one of the named presets of VC-2120 used unmodified, or the single catch-all class Custom Terms.
A ladder MUST NOT be keyed by a per-game Terms hash, by map, or by player count.
Rationale: this is the trade-off between comparability and fragmentation, and both extremes are bad. One global ladder rates a 40-turn Blitz against a 320-turn Long War as though they measured the same thing. A ladder per exact configuration gives almost every game a ladder of one, where a rating is meaningless and a first win is a first place. Three axes, with named presets as the unit, keeps each ladder populated while keeping the games on it recognisably the same game. Custom Terms is deliberately one bucket rather than many: players who build their own Terms are a small population and are better served by one ladder that has other people on it.
SVC-640 Leaderboards: the official service MUST provide ladders keyed per SVC-635, using Glicko-2 (or an equivalent published system, open question 5). Only games that ran in authoritative mode with server-side simulation and fog enforcement are ratable (MP-340, MP-350, MP-1160), and the service MUST verify a game by headless re-simulation before it enters ratings (MP-1180). Relay-only games and turn-bundle games — including drop-box games (SVC-448) — are never ratable: they are unrated by construction and the UI says so (MP-340, MP-1160). Every other game defaults to unrated; ranked participation is opt-in per game at setup.
SVC-642 Rating from placings: a finished game MUST enter ratings as the full placing order (VC-3030), decomposed into pairwise outcomes with explicit handling of shared placings, rather than as winner-versus-field. Final Tenure MUST NOT itself be fed to the rating system as a score; it is the quantity that produced the placing (VC-1520) and feeding both would double-count it.
Rationale: in a six-player game "did not win" describes five very different performances, and a rating system that cannot tell second from sixth throws away most of what the game measured. Placings are safe to rate here in a way they usually are not, because 14-victory.md guarantees a placing is non-decreasing in the number of Seals a player survived (VC-3040) — so rating on placings cannot create an incentive to quit early, which is the standard objection to it. The design priced quitting before this document arrived; this requirement simply declines to un-price it.
SVC-645 Competitive conduct, reports, and enforcement (this requirement discharges MP-1160's
delegation of "ratings, reporting, and enforcement policy" to this document): every rated game MUST carry
a player-conduct report action available to each participant during the game and for 30 days after it
finishes. Both player reports and the automated tamper signals raised by 05-multiplayer.md — repeated
impossible or illegal orders (MP-1130), log-integrity or desync failures (MP-1140/MP-1150), and failed
re-simulation verification (MP-1180) — MUST enter the same staff review queue as SVC-480, with the game's
order log attached as the dispute artifact (MP-1180: the log is the evidence). Outcomes are: no action;
result annulled and removed from ratings with affected rating changes reverted; time-boxed ranked-play
suspension; account suspension (SVC-480). An automated signal MUST NOT by itself impose a penalty — a
human review decision is required — and every outcome that changes a player's rating or standing MUST be
shown to that player with its reason and an appeal path. A TurnTimeout (TM-520) and an unattended turn
(SVC-414) are not conduct signals and MUST NOT enter this queue. Self-hosted instances run their own
queue and outcomes (SVC-670, SVC-820); nothing here is gated to the official instance.
SVC-648 Substitution recording and rating eligibility (discharging MP-1020's "record the substitution for rating integrity"):
- The game-history record (SVC-600) and the game's rating entry MUST record every seat substitution — predecessor, successor, turn, and reason (missed-turn threshold, voluntary, admin action) — and every turn a seat was absent or timed out (TM-570).
- A timed-out or unattended turn MUST NOT by itself affect rateability. Under TM-530 the seat's own persisted orders execute; that is the player's plan being carried out, not a machine playing for them. Such turns MUST be counted in the record and MAY be shown on the ladder listing, but MUST NOT annul a result.
- A human→human substitution MUST leave the game ratable: each account is rated only on the turns it actually played, the replacement inherits the seat but never the predecessor's rating history (MP-1020), and both appear in the record.
- A seat handed to an AI substitute (TM-580, TM-590) MUST end rating eligibility for that seat from the substitution turn. If fewer than two human-played seats remain, the whole game MUST be recorded unrated. A seat reclaimed under TM-600 does not become ratable again for the turns the substitute played.
- Substitution MUST NOT alter the departed player's placing, which is recorded against them and computed from a Tenure that keeps accruing while the substitute holds the seat (VC-3060, VC-3070).
Rationale: the turn model changes what "played by someone else" means, and the rating rule has to follow it rather than the older sequential-turn intuition. Missing an Orders phase leaves your standing orders in command — a legitimate, if suboptimal, way to play a correspondence game, and penalising it would make the game hostile to exactly the audience correspondence play exists for. An AI substitute is different in kind: a different agent is now choosing, and rating the account for what it does would be rating the wrong player.
SVC-650 Leaderboard privacy: entries show the public handle only; a player MAY set "rate me but list me as Anonymous". Profile pages are private by default with per-section opt-in visibility.
SVC-655 Match pages: a finished game MUST be able to produce a shareable match page rendering its end-of-game record (VC-3050) — the placings, the Terms paragraph verbatim, the Standing and Tenure series of SVC-605, and the complete unredacted Chronicle (VC-2450). Visibility MUST follow SVC-650 and MUST require the consent of every human participant before the page is public.
Rationale: the Chronicle is already a readable account of the game — VC-2460 shows an entire endgame in eight lines — and publishing it is the cheapest genuinely good thing the platform can do with the victory system. It is also the artifact a player wants after a six-month correspondence game, and the one thing that makes a long game's ending shareable.
SVC-660 Merging: importing a profile export (SVC-135/SVC-190) or linking a second device MUST merge history by union on game UUID — duplicates are impossible by construction, and merging is the reason SVC-610 forbids storing aggregates as the only copy.
SVC-670 Self-hosted instances keep their own statistics and ladders; ratings and records do not transfer between instances in v1 (SVC-980).
8. The simulation farm and the AI pipeline
Three documents need large volumes of headless games and none of them owns the machinery: 06-ai.md's self-play tuning pipeline (AI-650 through AI-690), 03-architecture.md's nightly AI-vs-AI farm (AR-970), and 14-victory.md's build-time calibration and its CI gate (VC-3210, VC-3290). This section specifies what the platform provides them. It provides execution, records, and reproducibility; it does not provide experiment design, optimizer choice, promotion criteria, or benchmark content.
SVC-680 Boundary: this document MUST NOT define an experiment design, an optimizer, a promotion criterion, a benchmark suite, or a statistical threshold. Those belong to 06-ai.md (AI-650, AI-660, AI-720) and 14-victory.md (VC-3210). Where this section and an owning document both name a quantity, the owning document governs and this section is in error.
SVC-681 Batch execution: the platform MUST provide a farm job service that accepts an experiment
configuration plus a complete version pin set (SVC-682) and executes N headless games through
@everylastcity/cli farm (AR-810) on a worker pool, off any request or room loop (SVC-407). It MUST
support at least the batch sizes the owning documents require — AR-970's nightly ≥ 500 games, AI-660's
≥ 1,000-game promotion gate, and VC-3210's ≥ 2,000 games per calibration cell — and MUST report partial
progress and per-game exit codes (AR-820) rather than only a final verdict.
SVC-682 Reproducibility pinning: every farm job record MUST pin, and every result row MUST carry:
EngineVersion plus the core build digest (AR-300), RulesVersion, victoryLogicVersion (VC-3220),
every content package by id/version/sim hash, every AI plugin version with its weight version (AI-670),
and the PluginRuntimeConfig including the fuel transform id and version and which enforcement actually
applied (AR-857, AI-410). A job whose pins cannot all be resolved MUST fail rather than substitute a
nearest version.
Rationale: a farm result is evidence about a specific build, and evidence whose provenance is approximate is not evidence. The fuel transform is pinned for a reason 06-ai.md states directly: fuel counts are only comparable between hosts running the identical transform (AI-410), so a result produced under an unrecorded transform cannot be compared with anything.
SVC-683 Result store: the platform MUST retain, per farm game, a queryable outcome row — placings, winning Track and route, final Standing and Tenure per seat, turn count, Chronicle class counts, per-seat fuel consumed, wall-clock cost, and exit code — indexed by every pin of SVC-682 and by the experiment and optimizer step that produced it.
SVC-684 Artifact retention: for any farm game that crashed, diverged, stalled, violated a rules invariant, or regressed a budget, the platform MUST retain the minimized replay container AR-970 requires, for at least 90 days, and indefinitely while it is attached to an open defect. For a successful game the container MAY be discarded, but the seed and the full configuration MUST be retained so the game can be re-run exactly.
SVC-685 Candidate registry and promotion gates: the platform MUST hold AI weight and parameter candidates as versioned immutable artifacts with lineage (parent candidate, experiment, optimizer step), MUST be able to run a candidate against a named incumbent under a named gate, and MUST record the gate's verdict together with the statistics it rested on and the pins of SVC-682. The platform MUST NOT promote on its own. It computes and records; the criteria are AI-660's and the persona and tier-monotonicity regressions are AI-740's and AI-610's, and a promotion MUST be an explicit recorded action referencing a specific gate record.
Rationale: separating measurement from decision is what makes a promotion auditable six months later, and it keeps the platform from silently becoming the owner of the AI's balance. It also makes the failure mode visible: an automated promoter that quietly ships a candidate which passed one gate and regressed a persona test is very hard to notice, and very easy to prevent by requiring the decision to be a recorded act.
SVC-686 Rating pool: the farm MUST maintain a persistent rating pool over AI candidates, difficulty tiers and shipped releases, with frozen reference anchors that are never re-rated, so that a rating recorded six months ago still means what it said. The farm's rating namespace MUST be entirely separate from the player ladders of §7, and no rating MUST flow between them in either direction.
Rationale: a pool where everything is re-rated continuously drifts, and a drifting scale cannot answer the only question that matters — is this release stronger than the last one (AI-720). Anchors fix the scale. Keeping the namespaces apart is not fastidiousness: a player ladder rates humans under verification (SVC-640), the farm pool rates plugin builds under fuel metering, and a number that crossed between them would be meaningless in its destination.
SVC-687 Release gates: per release candidate, the platform MUST run and expose as CI-consumable results the release-to-release AI non-regression comparison AI-720 defines and the calibration regeneration VC-3210 defines, so that AR-960's determinism gate and VC-3290's estimate-drift gate have something to read.
SVC-688 Determinism evidence: farm batches MUST be executable across the runtime matrix 03-architecture.md AR-960 defines, and a hash divergence between legs MUST be reported as an infrastructure or build failure, never as an AI result. A result produced with an unpinned or mismatched fuel transform MUST be marked not verification evidence (AI-410) rather than discarded, so the distinction survives into the record.
SVC-689 Modder parity: the same farm interface — job submission, result store, artifact retention, and the rating pool — MUST be usable on self-hosted hardware against third-party AI plugins (AI-690), with no capability reserved to the official instance (SVC-040).
SVC-690 Isolation and cost control: farm workloads MUST run on capacity separate from player-serving capacity, MUST NOT contend with the request loop, the room server, or the Cascade workers of SVC-407, and MUST be subject to a per-job resource budget with a hard cap and a visible cost estimate before submission.
SVC-691 Farm inputs MUST be synthetic. The farm MUST generate its own games from configurations and seeds. Player games, player order logs, player observation streams, and telemetry MUST NOT be used as farm inputs, as tuning targets, or as training data. The telemetry consent of SVC-700 is not consent for this, and no requirement in this specification grants it; any future use of player data for AI training requires its own consent mechanism and its own spec revision (SVC-750).
Rationale: players did not agree to be training data, the balance telemetry of SVC-720 is deliberately aggregate and anonymous, and an AI tuned on real games would create an incentive to collect more of them than the privacy stance permits. Synthetic-only also keeps the farm reproducible, which player data never would be.
SVC-692 No training on player-serving infrastructure: 06-ai.md forbids shipped AIs from self-modifying weights during or between player games (AI-680). The platform MUST NOT execute a tuning or training job inside a player-serving instance's game path, and a self-hosted operator's labs mode MUST be explicit, separately enabled, and off by default.
9. Telemetry, crash reporting, and privacy
SVC-700 Gameplay telemetry MUST be opt-in, default off, presented once at first run in plain language with equal-weight Yes/No choices. Consent state is stored per device + profile, changeable any time in settings, and recorded in the data export.
SVC-710 Telemetry MUST be anonymous: keyed to a random telemetry ID (player-resettable, one click), never to account UUID, handle, email, or hardware serials. Payloads MUST NOT contain free text typed by players, save contents, order logs, chat, or IP-derived location beyond country.
SVC-720 Permitted telemetry event categories (exhaustive; additions require a spec revision):
| Category | Contents | Purpose |
|---|---|---|
| Session | app version, OS family, locale, device class | support matrix |
| Setup fingerprint | unit set key, ruleset version, Terms configuration class (SVC-635), map size and class, player count and types | which modes people actually play |
| Performance | frame-time p50/p95, Cascade resolution ms/turn, observation-stream size, memory class, map size bucket | the budgets of 03-architecture.md |
| Balance | per-game aggregate unit commissioning/loss/win counts by unit class | roster balance (02-units-and-industry.md) |
| Victory | winning Track, game length in turns against the Terms estimate, and counts of Capitulation, Verdict, mathematical elimination and Horizon endings | the calibration honesty check of SVC-615 and VC-3290 |
| Feature use | anonymous counters for named UI features | UX priorities (04-ui-ux.md) |
SVC-730 Crash reporting MUST be a separate consent (ask-at-crash allowed). With consent, a crash report MAY include stack traces, logs scrubbed of PII, and — with an explicit additional checkbox — the deterministic replay container (initial state + order log + seed, per 03-architecture.md) that reproduces the failure.
Rationale: a deterministic engine turns a bug report into a reproduction, which is the single largest support-cost reduction available to this project. It is also the most sensitive thing a player can send — a container is their whole game — which is why it is a separate, explicit, per-incident choice rather than part of the telemetry consent.
SVC-740 No third-party analytics or advertising SDKs, ever, on any platform. Telemetry and crash
endpoints are first-party (the configured @everylastcity/server instance). Self-hosted servers and
their clients MUST default all telemetry off and MUST NOT transmit anything to the official service
(§10).
SVC-750 A public privacy policy MUST be published (web + viewable in client), versioned in the repository, written in plain language, and enumerating exactly the data categories in this spec. Material changes require notice and fresh consent for affected processing.
SVC-760 Data-subject rights: machine-readable export (SVC-190) and account deletion MUST be self-service. Deletion completes within 30 days: PII erased; workshop publications either deleted or, at the owner's prior choice, anonymized-in-place; finished-game records of other participants are anonymized (the opponent becomes "Deleted player"), never removed from their histories. A 30-day grace period allows undo before purge. The service MUST NOT sell or share personal data with third parties for their own purposes.
SVC-770 Server request logs MUST truncate/anonymize IP addresses within 30 days. Backups containing deleted-account data MUST age out within 90 days.
SVC-780 The project SHOULD publish an annual aggregate transparency post (player counts, takedown counts, telemetry aggregates) as the public face of the privacy stance.
10. Self-hosting
SVC-800 Each release MUST publish a public, multi-arch (x64/arm64) Docker image of
@everylastcity/server, carrying the Node 22 LTS runtime, both network surfaces (SVC-401), and
@everylastcity/cli (03-architecture.md AR-800). docker run -p 8080:8080 -v elc:/data everylastcity/service MUST yield a working instance with zero configuration: a PostgreSQL server
bundled inside the image, initialized on first run into the mounted volume, a filesystem blob store in
that same volume, and a printed one-time admin bootstrap token. Setting a DATABASE_URL MUST make the
instance use that external database and MUST NOT start the bundled one, so a single image serves both the
one-command hobby deployment and a managed-Postgres production deployment; the official instance MUST
deploy this same image (SVC-040) against an external database.
Decision and its cost, stated rather than deferred: bundling the database makes this a multi-process
container and couples PostgreSQL major-version upgrades to image upgrades. That cost is accepted because
"one command, no prerequisites" is load-bearing for pillar #5 and because the alternative — a second
supported SQL engine — is worse (SVC-050). The obligation it creates is discharged by SVC-860: the image
MUST ship a documented, tested major-version upgrade path and expose pg_dump/pg_restore from inside
the container. Until that path is demonstrated against a volume holding a multi-year correspondence
game, this arrangement is a decision under test rather than a settled one (open question 12).
SVC-810 Configuration MUST be via environment variables and/or one mounted config file:
DATABASE_URL (absent ⇒ the bundled PostgreSQL of SVC-800), blob store (path or S3 endpoint/bucket/
credentials), public base URL, real-time room server enable/disable and its listen port (SVC-404 —
disabling it MUST leave every non-real-time capability intact), optional REDIS_URL for multi-process
room presence and matchmaking (SVC-407), SMTP relay for email (optional — without it, email features
degrade gracefully to in-app/push only), Web Push VAPID key pair and contact address (SVC-500,
MP-1200; auto-generated on first run if absent, so push works out of the box with no third-party
account), APNs/FCM credentials (optional), auth mode flags including the instance account policy
open/invite/guest (SVC-165, SVC-167, MP-1200), listen port/TLS and external URL (MP-1200), default stall
policies (MP-990) and default Orders deadlines and reserve banks for games created on the instance
(TM-480, TM-490), Cascade worker count and per-game CPU budget (SVC-407, SVC-413), farm worker pool size
and per-job budget cap (SVC-690), and retention/quota overrides (SVC-360, SVC-440 subject to its 90-day
dormancy floor, SVC-448, SVC-490).
SVC-820 Feature parity: async and correspondence games, the turn-bundle drop-box (SVC-448), real-time
relay and authoritative simulation (MP-1190), the workshop, sync, notifications, statistics and ladders,
the farm interface (SVC-689), and moderation and enforcement tooling (SVC-480, SVC-645) MUST all function
on a self-hosted instance. Operational exceptions are limited to: native mobile push to the
iOS/Android clients requires the operator's own APNs/FCM credentials; email requires an SMTP relay;
nothing else. Turning the real-time room server off (SVC-810) is an operator's deliberate choice
announced at /api/meta, not a capability the image lacks — the image MUST always be able to run it. Web
Push (SVC-500) requires no third-party account and therefore MUST work on every self-hosted instance out
of the box (MP-980), so an instance without APNs/FCM or SMTP still delivers push and in-app
notifications.
SVC-830 The client MUST support multiple configured servers (official plus any number of self-hosted URLs), with a per-server identity and a clear indicator of which server a game or workshop view belongs to. Accounts, content, and ratings are scoped to one server (no federation in v1, SVC-980).
SVC-840 A self-hosted instance MUST NOT contact the official service or any third party as a side effect of normal operation. An update-availability check MAY exist but MUST default off.
SVC-850 Sizing guidance (SHOULD, informative targets): 2 vCPU / 2 GB RAM / 10 GB disk serves a group of ≤ 50 accounts with hundreds of concurrent correspondence games and a handful of live rooms; the async model is storage-light because games are order logs (03-architecture.md). Correspondence load is bursty rather than sustained — a game's whole CPU cost is one Cascade per turn, arriving when the last seat commits — so the binding constraint is peak concurrent Cascade resolution, not average utilization. 1 GB RAM remains workable only with an external database (SVC-800), no authoritative real-time games, and no farm workers on the same host (SVC-690).
SVC-860 Operator documentation MUST ship with the image: compose example, TLS guidance (reverse
proxy), backup/restore procedure (pg_dump + blob dir — the bundled database of SVC-800 MUST expose
pg_dump/pg_restore from inside the container so the one-command deployment is still backup-able), and
upgrade/migration notes including the PostgreSQL major-version path required by SVC-800. Database
migrations MUST be checked-in SQL files, MUST run automatically at startup, and MUST be reversible one
version back. Rationale: migrations generated at runtime from an ORM model are exactly the kind of
implicit behaviour that is unreviewable and untestable; plain SQL under version control is a diffable
artifact.
SVC-870 Continuity guarantee: because the image, formats, and API are published, the official service shutting down MUST NOT strand players — see SVC-950. Server-to-server game export/import (a game archive any instance can ingest) MUST exist so a community can move a game off a dying instance.
Rationale: a correspondence game is designed to run for years (MP-630) and may easily outlive the company hosting it. A game people have played for eighteen months must not be destroyed by a hosting decision they had no part in, and the only real defence is that every instance runs the same published image over published formats — self-host parity is a structural guarantee rather than a promise.
11. Operations (official instance)
SVC-900 Service level objectives are internal targets, not contractual SLAs:
| Metric | Target |
|---|---|
| API monthly availability | ≥ 99.5% |
Order submission and CommitOrders durable-ack latency (HTTP path, SVC-404) |
≤ 5 s p99 |
Cascade resolved after CloseOrders becomes durable (8 players, Standard-tier map) |
≤ 10 s p95 (SVC-413) |
| Room materialization for an existing game (SVC-403) | ≤ 2 s p95 |
| Orders-open → push dispatched | ≤ 60 s p95 (SVC-520) |
| Database RPO | ≤ 24 h (nightly backups) |
| Game order-log RPO | ~0 (synchronous replication / versioned bucket) |
| Full-disaster RTO | ≤ 72 h |
SVC-910 Backups: nightly database dumps and blob-store versioning MUST be enabled, retained ≥ 30 days (subject to SVC-770 aging for deleted accounts), stored off the serving infrastructure, and restore-tested at least quarterly.
SVC-920 Observability: the service MUST expose /healthz (liveness/readiness) and Prometheus metrics;
logs MUST be structured (JSON) with request IDs and MUST NOT contain PII beyond what SVC-770 permits.
Readiness MUST report the HTTP surface and the room server as separate subsystems, and the HTTP
surface MUST report ready while the room server is unavailable — otherwise a room-server fault would take
async play down with it, contradicting SVC-404. Metrics MUST include live room count and occupancy,
event-loop lag, Cascade queue depth and resolution time per game size, farm queue depth (§8), and the
count of games whose Orders phase is open past its deadline. A public status page MUST report availability
and incidents, distinguishing a real-time outage from an async/API outage.
SVC-930 Security operations: dependencies scanned in CI; images signed; a published vulnerability-disclosure policy with a security contact; breach notification per applicable law. The service holds no payment data ever (SVC-960), keeping compliance surface minimal. The dependency graph MUST be pinned by a committed lockfile, installed with package lifecycle scripts disabled in CI and in the image build, and the published image MUST carry build provenance. Rationale: the npm ecosystem's transitive graph and install-time script execution are a materially large supply-chain surface; these are the mitigations, and they are a standing operational cost, not a one-time setup step.
SVC-940 Client/server compatibility windows: the official service MUST support the current and previous major client API versions (SVC-400); clients older than that receive a clear must-update response, and in-progress correspondence games MUST remain completable during any deprecation window of at least 90 days.
SVC-950 Sunset policy: if the official service is ever discontinued, the project MUST give ≥ 180 days notice, keep export (SVC-190) and game-archive migration (SVC-870) available through end-of-life, and publish a final service release plus migration guide so communities can continue on self-hosted instances. This is a standing commitment of pillar #5.
12. Non-goals (base spec)
SVC-960 No monetization inside the platform: no advertising, no ad SDKs, no microtransactions, no paid currencies, no loot boxes, no paid workshop marketplace, and no payment processing of any kind in the base spec. Any future paid offering (e.g., cosmetic DLC sold through platform stores) requires its own spec revision and MUST NOT touch gameplay or gate SVC-020 capabilities.
SVC-970 No mandatory online: nothing in this spec may ever make an account, connectivity, or telemetry a requirement for the offline feature set (SVC-010). This clause wins over any other requirement in this document.
SVC-980 No federation in v1: no cross-instance identity, matchmaking, rating transfer, or workshop
mirroring between @everylastcity/server instances. The API is designed not to preclude it later.
SVC-990 No social platform: no friend feeds, DM system, or presence broadcasting beyond what lobbies, invites (05-multiplayer.md) and the Orders-phase presence of TM-440 require; no scraping-friendly public member directory; no email marketing (announcement newsletters only by separate double opt-in).
Open questions
- Server code licensing: is
@everylastcity/serveropen source or source-available? SVC-040/SVC-800 require the published image; the pillar-4 spirit suggests source, and this is a business decision — but the stack narrows it, since a Node image ships readable JavaScript and "binary-image-only" is no longer a meaningful third option. - Community hours board (SVC-620): is a single all-modes total the right number, or should solo hours be listed separately? A board dominated by solo hours measures something different from one dominated by correspondence hours, and the design has not decided which it wants to celebrate.
- Which OAuth providers ship at launch (Steam certainly for the Steam build; Apple mandatory on iOS if any others are present) and whether Steam identity can satisfy account creation with no email at all (impacts SVC-155's "optional verified email" and security notices SVC-180).
- Free-tier quota (SVC-360) and workshop publisher quota (SVC-490) final numbers once real container and package sizes exist from 03/07 implementations.
- Rating system choice (SVC-640, SVC-642). Glicko-2 with placings decomposed into pairwise outcomes is the current answer, but a rating system that consumes a full placing order natively — Weng–Lin / OpenSkill and its relatives — fits this game's shape better and would avoid the decomposition entirely. Decide before the first ladder opens, because ratings cannot be recomputed backwards across a system change without invalidating everyone's history. Team games remain a separate question (05-multiplayer.md dependency).
- Whether E2E-encrypted solo saves (SVC-350 MAY) make v1.0 or wait, given they complicate support and key handling. Every client is now a web client (the desktop and mobile builds wrap the same one), so browser key storage is the only case rather than an edge case, and a cleared browser origin means an unrecoverable save.
- Data residency: single-region hosting is assumed; is an EU region needed at launch for GDPR comfort, or is a documented processor list sufficient?
- Dormancy default (a single 180-day window with a 90-day floor and 30-day advance notice, SVC-440, MP-720): validate against real correspondence cadence. The design lifetime is months to years (MP-630), so the default may need to be longer; because archiving is non-destructive and one-click revivable, the cost of a short window is annoyance rather than loss. SVC-615's data will answer this directly once games exist.
- Official-instance guest policy (SVC-167): the default is guests off — do we ever enable them for invite-only real-time games, and if so how do guests interact with moderation, appeals, and report credibility (SVC-645)?
- Should saved setup, map-generator-option, AI-option and victory Terms configurations become shareable workshop content (SVC-450)? Terms configurations make the strongest case of the four — a good Terms setup is a designed thing that other people would want — but it requires a content key and package format in 07-modding-content.md (MOD-040) that does not exist, and it interacts with SVC-635's "Custom Terms" ladder bucket: a widely shared custom Terms configuration is arguably a preset and arguably deserves its own ladder.
- Reframed by the turn model; the old form is answered. The previous question asked how many auto-turns a rated seat may take before annulment. Under Orders and the Cascade a missed Orders phase is ordinary play — the seat's own persisted orders execute (TM-530) — so it is not an integrity event at all, and SVC-648 says so. What remains is the narrower question: a seat handed to an AI substitute loses rating eligibility from that turn, but how many substituted turns should annul the whole result rather than merely mark it? Needs ladder playtesting (cf. 05-multiplayer.md's annulment question).
- Is a bundled PostgreSQL inside the image the right zero-config answer (SVC-800)? The alternatives are a compose file as the blessed one-command path (which gives up "one image, one command" and with it some of SVC-040's simplicity) or an embedded WASM Postgres for tiny instances (which reintroduces a second engine's behaviour under the same dialect). The deciding factor is the major-version upgrade path for a volume that holds years-long correspondence games; nothing here is settled until that is demonstrated, and SVC-860 is where the demonstration lands.
- Colyseus dependency risk (SVC-402): the framework sits in the critical path for real-time play, and its blast radius is deliberately small — rooms, matchmaking, presence, lifecycle, with the protocol and all game state kept outside it. Should we cost a plain WebSocket room server behind the same MP-260 protocol as a fallback before committing, and what signal would trigger it?
- Deployment topology (SVC-401, SVC-407): does the official instance run the HTTP API, the Cascade workers and the room server as separately scaled deployments while self-hosts run the single image? SVC-040 requires the same code and image, not the same topology — that reading should be confirmed explicitly, or the official instance quietly drifts from what an operator can reproduce.
- Cascade capacity (SVC-413, SVC-850, SVC-900): the ≤ 10 s p95 target is an estimate, not a measurement, and it is the number the whole async service is sized against. How many concurrent 8-player Standard-tier Cascades does one worker sustain, and what does a Large-tier game cost? Measure against 03-architecture.md's reference fixture before SVC-850's sizing numbers are treated as guidance.
- Runtime lifecycle vs. game lifetime (SVC-940, MP-630): correspondence games are designed to run for years and will outlive Node 22's maintenance window. What is the runtime-upgrade commitment for an instance hosting live games, and MUST an image upgrade ever require a participant to do anything? The answer should be no, but it has not been designed.
- How HTTP entitlement is enforced in the API shape (SVC-404, SVC-190). That a fog-enforced seat gets
its compacted observation stream and an AR-500
ViewCheckpointrather than the log is settled (MP-250 binds every transport). What is not settled is the mechanism: does every game-scoped read endpoint re-evaluate entitlement from(GameId, seat, authority mode)on each request, or does the service mint a seat-scoped read capability at join time? The capability form is cheaper to audit but MUST be re-issued on an MP-905 authority-mode switch or it silently outlives the mode it was scoped to. This must be decided before the OpenAPI document (SVC-400) freezes, because the two answers give the game-fetch endpoint different response shapes. - When a fog-enforced game's log becomes fetchable over HTTP (SVC-404, SVC-440). MP-730 unlocks the full container only after the game ends (the MP-1100 replay) or through a consent-gated mode switch, so the service has an entitlement transition to implement: which server-side event flips it, and is a game that was archived while unfinished (SVC-440) still "live" for MP-730? It should be — archiving is non-destructive and one-click revivable, so treating it as an ending would turn dormancy into a back door to the log of a game still in progress — but nothing states that yet, here or in 05.
- Drop-box turn completion (SVC-448). The collection model needs one rule the platform cannot invent
on its own: in a bundle game there is no server clock and no authority, so what declares a turn's
order set complete when one seat never submits? The candidates are a participant-agreed quorum, an
explicit "close the turn" action available to any participant with the others' standing consent, or a
client-side local timer. This interacts with SVC-414 — under a server it is
CloseOrders, and bundle play has no equivalent — and it must be settled together with 05-multiplayer.md's respecification of its bundle chapter. - Farm capacity and cost (SVC-681, SVC-690). VC-3210 requires ≥ 2,000 games per calibration cell and the cell count is the product of Track × map class × player count; AI-660 requires ≥ 1,000 games per promotion gate and a tuning run may produce many candidates. Nobody has multiplied those out against a measured per-game cost, and until someone does, SVC-690's budget cap is a mechanism with no number in it.