← Server architecture · all tasks
Real accounts — sign up, log in, and attach a device to an existing account
server-architecture/010 · created 2026-08-12 · updated 2026-08-13
What
Named by Kris on 2026-08-12 as the next significant build, alongside the world map
(world-map-and-portals): “creating accounts and being able to log in and attach to your
account.” He flagged it explicitly as needing significant, careful architecture on both
the server and the client — not a login form bolted on.
This is the deferred half of a decision already made. overview.md’s Identity section
committed to persistent accounts from day one, no authentication for the MVP — issue and
remember a token, get the identity column and its foreign keys right, and let the login
form come later. That reasoning was: adding real auth to an existing populated user table
is straightforward, retrofitting durable identity onto anonymous sessions is not. This task
is “later” arriving. Do not re-litigate the deferral — the deferral was correct and the
table it protected is exactly what makes this task tractable.
Where it stands today
game/director/src/schema.sql—accounts (id, token UNIQUE, display_name, created_at). The comment states the current model plainly: “Whoever holds this token is this account, full stop, until real auth replaces it.”game/director/src/accounts.ts—createAccount,getAccountByToken,getAccountById. No credentials, no sessions, no revocation.worlds.owner_idandworld_members.account_idare already foreign-keyed toaccounts, so real auth is additive rather than a data migration.- The client stores its token per install. Known symptom of the current model: two
native clients on one machine share a single account unless they get separate
$HOMEs — the token is the identity, and there is no way to say “this is me, on a second device.” - The title screen already has a “Change Account” button
(
game/world/client/ui/title_screen.gd), andplayer_state.gdis documented as the single seam where server-backed accounts and collections plug in.
Scope
- Sign up — a real account with a credential, not just an issued token. Which credential (email + password, passkey, OAuth, device-code) is an open architectural call and part of this task’s job to decide and justify.
- Log in — establishing a session against an existing account from a fresh install.
- Attach a device to an account — Kris’s own phrasing. This is the interesting case, not an afterthought: the same human on a phone and a browser is one account with two devices, and today that’s impossible. Sessions, not a single shared secret.
- Upgrade path for existing token-only accounts. People (Kris included) already have worlds and members tied to token accounts. Claiming an existing token-account with a credential has to work, or the deferral’s whole benefit is lost.
- Client architecture — where identity lives, how a session is stored and refreshed,
and how every screen reads it.
player_state.gdis the intended seam; confirm it’s still the right one at this scale or say what replaces it. - Server architecture — session/token handling in the director, what the world
processes are allowed to trust, and how
hello’saccountId(currently asserted by the client, unverified —world_server.gd) stops being self-asserted.
Explicit non-negotiables
- Do not foreclose offline play. Kris stated on 2026-08-12 that the finished game must
be playable offline, and stated it precisely so nothing gets architected against it. An
auth model that makes the client useless without a live director violates this. See
world-map-and-portals/overview.md’s offline section. - Server-authoritative stays server-authoritative. Identity being partly client-held must not become identity being client-asserted.
Definition of done
Kris asked for this written up as a task now, with the intention of handing it to a planning session for a full implementation plan before any code is written. So: the first deliverable of this task is the architecture, not the feature. A plan that names the credential model, the session model, the device-attach flow, the migration for existing token accounts, and the client seam — reviewed by Kris — and then the build against it.
The question this probably raises
Choosing a credential model may reach outside the repo (an email sender, an OAuth provider,
a hosted identity service) and may cost money — which clears PICKUP.md’s question bar.
If so, that is the one question worth raising on this task, with options and a
recommendation.
Implementation plan — approved 2026-08-12
The architecture this task names as its first deliverable now exists and is approved:
../010-accounts-plan.md — planned and signed off by
Kris in a live session on 2026-08-12, design verified against the current code. The
implementing agent builds against that document (start at its “Staged build order”,
stage 1); this task stays ready until claimed for the build.
Two amendments from that session supersede the text above:
- Slate wipe. The “Upgrade path for existing token-only accounts” scope item is
void — Kris’s explicit call. Existing
accounts/worlds/world_membersrows are disposed at deploy via a deliberate, guarded migration. Ignore the earlier “claiming an existing token-account with a credential has to work” line. - No gameplay mechanics. Collections land as storage plus a server→client read path only (seeded with the current demo set). No pickup logic, no award paths, no health — nothing item-mechanical belongs to this task.
Audit — 2026-08-13
Kris asked for an independent audit of the delivered build before moving the project
forward: ../010-accounts-audit.md. Verdict in short:
foundation solid, handoff honest, both suites reproduce green — but one high-severity
security gap (password reset doesn’t revoke sessions), the QR half of device-attach
never built (?link= web redemption missing), and the client’s failure paths (no
reconnect, silent dead-ends — the likely cause of the observed “sometimes doesn’t
run” bug). The audit’s Recommendations section proposes the follow-up tasks.
Decisions settled (no longer open calls): one LoR account with linked sign-in
providers, guest-first (play never gates on sign-in); email+password is v1’s typed
credential; Resend is the email sender (Kris sets up the account + DNS — the build
uses a log-fallback mailer until the key exists); device attach = sessions + link codes
with a QR; the self-asserted hello accountId is replaced by director-minted join
grants; the anti-cheat handoff’s rate limits and movement-pacing fix ride along.
Google Play Games sign-in is split out as task 011.
Build complete — in review, 2026-08-12
All six stages built, tested, committed, and pushed to land-of-lor — see
land-of-lor/docs/handoff/historic/010-accounts.md
for the full report: what shipped stage by stage, test coverage (76/76 director +
75/75 pure-logic GDScript, both green), how the new e2e script was verified without
touching production, and the one thing deliberately left undone — the live
droplet was not deployed to. The runbook’s slate-wipe step
(LOR_WIPE_V1=yes-destroy-v1-data npm run migrate-v2) destroys the droplet’s
existing accounts/worlds rows irreversibly; that’s Kris’s call to make live, not
something to run unattended on a pre-approved plan. Everything up to that point —
code, tests, the runbook itself — is ready. This task stays review until Kris
either runs the deploy himself or directs a session to.
Deployed 2026-08-12 — backend + web live, Android blocked
Kris gave explicit go-ahead to run the live deploy. Executed: verified backup
(pg_dump, non-empty, gzip-checked) before the wipe, migrate-v2 on the droplet
(destroyed 76 accounts / 9 worlds / 74 members, matching the backup), director
restarted on v2, verify-accounts-e2e 23/23 passed against prod. Web client
rebuilt and pushed live at /play (confirmed the shipped build actually contains
the accounts v2 client code, not just a timestamp bump). Full record in the handoff
note’s new “Deploy” section, including a flagged-but-not-blocking gap in three older
live-check scripts that still use the pre-grant hello.
Android is not done — stays out of done for this reason alone. Rebuilding the
APK needs .secrets/lor2026.keystore (+ .password), which this workspace’s own
CLAUDE.md says has lived in this container’s .secrets/ since 2026-08-04 but
which is not actually present here. The currently-downloadable APK now fails
UNSUPPORTED_VERSION against the live v2 director (expected cutover behavior, but
means Android play is broken until this is resolved). Needs Kris: restore
lor2026.keystore + lor2026.keystore.password into this container’s
land-of-lor/../.secrets/, then a session can run ./export-android.sh --upload
in minutes — everything else in that pipeline (Godot 4.7.1, export templates,
droplet upload path) was confirmed working this session.
View source in repository · also available as raw markdown.