← Server architecture · all tasks
Stage 1 — director service + Postgres + protocol (no Godot)
server-architecture/005 · created 2026-08-02 · updated 2026-08-03
What to do
Build the director service and its Postgres-backed persistence, with no Godot process involved yet — this stage proves the persistence model (overview.md’s “Persistence” section) and the message protocol shape (overview.md’s “Netcode” section) work, before any game engine is in the loop at all.
- Schema: a
worldstable (at minimum: id, owner, member list or a join table,last_played_at— per the decided-from-day-one column even though nothing reads it yet — and whatever snapshot-state column/blob the world’s serialised state lives in) and whateveraccounts/identity table backs the “no auth for MVP, but a real durable ID” decision. - Director process: owns world lookup, membership checks, and (stubbed is fine at this stage, since there’s no Godot process yet to actually spawn) the world-instance lifecycle hooks. Runtime: agent’s choice between the two candidates
overview.mdnames (Go, or Node/TS leaning per the stated tooling-reuse reason) — pick one, state which and why in the handoff note; this task is what actually answers the epic’s one remaining open question, in practice if not yet by updating the epic’s own prose. - Protocol: the versioned, JSON, schema-defined message envelope described in
overview.md’s “Netcode” section — implement it for whatever minimal message set this stage needs (create world, join world, world-state response), not the full in-game protocol yet. - CLI test client: a script (not a GUI) that: creates/loads a world, confirms it hydrates from Postgres, and — this is the actual proof this stage exists to deliver — kill the director process, restart it, and confirm the world’s state (and membership) survived the restart intact. Also confirm membership routing actually rejects a non-member’s request to join a world they don’t belong to.
- Automated tests covering the above (schema round-trip, protocol envelope encode/decode, the restart-persistence property, the membership-rejection property).
- Write the handoff note:
land-of-lor/pocs/multiplayer-poc/handoff/stage-1.md— what was built, which runtime was chosen and why, how to run the tests and the CLI test client, and what stage 2 can assume exists (the protocol shape, the schema, how to talk to the director).
Definition of done
Automated tests pass; the CLI test client demonstrably proves world-hydration-from-Postgres and survival-across-a-full-process-restart; the handoff note exists. Set this task’s own state to review once that’s true — do not mark it done yourself; that’s Kris’s call (or a session explicitly acting on his confirmation) once he’s verified it, per overview.md’s “Build plan” section. Marking done unblocks stage 2.
Where the work lands
land-of-lor/pocs/multiplayer-poc/ (per overview.md’s “Where PoC code lives”).
Docs to read first
server-architecture/overview.md in full, especially “Persistence,” “Runtime” (director runtime candidates), and “Netcode” (protocol envelope, no direct-position messages). deliverables/004-server-setup-guide.md §4 for the Postgres shape this stage’s schema will eventually run against on the real droplet (this stage can run against a local/dev Postgres — it doesn’t need the droplet from server-instance to exist yet).
Status (2026-08-03)
State: done. Kris personally ran npm test and npm run test-client in land-of-lor/pocs/multiplayer-poc/director/ on 2026-08-03: all tests passed, including the 9 test-client checks. This is his explicit confirmation per the epic’s build-plan gating rule — Stage 2 (006) is unblocked.
Director runtime: Node/TypeScript — decided directly by Kris on 2026-08-03 (not an agent assumption this time; this was the epic’s one open question and he closed it explicitly before this task was picked up). overview.md’s “Runtime” section and “Key open questions” are updated accordingly.
Built: land-of-lor/pocs/multiplayer-poc/director/ — a Node/TS + ws + pg + zod director service, a Postgres schema (accounts, worlds, world_members), a versioned JSON message envelope, 17 passing automated tests (npm test, includes a real SIGKILL + process-restart persistence test and a membership-routing test), and a headless CLI test client (npm run test-client) that proves all three of the stage’s verification bullets end-to-end with no human interaction. Local Postgres for dev/test is provided by the embedded-postgres devDependency (a real Postgres binary run userspace, no root needed) — the production code only ever talks to DATABASE_URL and doesn’t know or care that it’s this vs. the droplet’s real apt-installed Postgres.
Full detail: land-of-lor/pocs/multiplayer-poc/handoff/stage-1.md.
Assumptions
- Membership semantics: the world owner is automatically inserted as a member at world-creation time (one row in
world_members, no separate “is owner” special case anywhere else) — matchesoverview.md’s “membership, not matchmaking” framing, where the owner is just the first member. last_played_atis written on every snapshot (periodic, and on last-player-leave), even though nothing reads it yet — the column exists per the decided architecture; writing it opportunistically now costs nothing and means Stage 4+ offline-progression work won’t also need a backfill.- Local/dev Postgres via
embedded-postgresrather than an apt-installed system Postgres — this container has no root/sudo, soapt install postgresql(as deliverable #4 assumes on the droplet) isn’t available here.embedded-postgresgives a real Postgres binary with no privilege requirement; it’s a devDependency only, irrelevant to how the director talks to Postgres in production. - Protocol message set kept to the stage’s stated minimum (
create_account,create_world,join_world,leave_world,add_member,set_world_note) rather than anticipating Stage 2+ messages —set_world_notestands in for a real gameplay intent purely to prove a mutate-then-snapshot-then-restart round trip exists; it is not meant to survive into Stage 2’s actual protocol.
View source in repository · also available as raw markdown.