← Server architecture · all tasks

Real accounts — sign up, log in, and attach a device to an existing account

done priority 1 estimate L

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

Scope

Explicit non-negotiables

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:

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.