18 KiB
phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, requirements-completed, coverage, duration, completed, status
| phase | plan | subsystem | tags | requires | provides | affects | tech-stack | key-files | key-decisions | requirements-completed | coverage | duration | completed | status | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 09-botfights-platform-upgrade | 02 | auth |
|
|
|
|
|
|
|
|
|
55min | 2026-07-31 | complete |
Phase 9 Plan 02: Close the bare-pubkey auth gap (BOT-01) Summary
JWT-gated GET /api/auth/me replaces the client's bare-pubkey auto-restore call, and POST /api/auth/login's creator auto-create/auto-upgrade side effects are removed so an unauthenticated request can no longer mutate the database.
Performance
- Duration: 55 min
- Started: 2026-07-31T01:38:00Z (approx.)
- Completed: 2026-07-31T02:33:48Z
- Tasks: 2 (both
type="auto", no checkpoints) - Files modified: 6 (1 new test file, 5 modified)
Accomplishments
GET /api/auth/mederives the caller's identity from a verified, non-expired, non-blacklisted JWT only (extractPubkeyFromAuth→verifyJwt), returns the same{ exists, bot }shapePOST /loginused to, and performs no writes.useNostr.ts's session auto-restore now callsauthFetch('/api/auth/me')(a bare GET, no body) instead ofPOST /api/auth/loginwith{ pubkey: pubkey.value }— the client never sends a bare pubkey to claim an identity.POST /api/auth/login's creator auto-create branch and creator auto-upgradedb.updateblock were removed; the endpoint is now a pure, documented-deprecated read. The identical creator logic still runs, correctly gated behind NIP-98 verification, insidePOST /nostr/session.e2e/helpers/auth.ts's doc comments now describeloginWithPubkeyas a read-only test lookup helper, not a login — its request/signature is unchanged so existing e2e specs are unaffected.- Fixed a pre-existing, previously-deferred bug (Rule 1 deviation):
server/src/db/migrate.tshad drifted fromschema.ts(missing 7 tables, 9 columns) and was the reasonauth.test.ts/auth-audit.test.ts/auth-edge.test.ts/tournaments.test.tsfailed against a freshly migrated dev DB. Brought it back in sync with the already-correct DDL inserver/src/db/startup.ts.
Auth surface: before → after
| Endpoint | Before | After |
|---|---|---|
POST /api/auth/login |
Accepted a bare {pubkey}; if unregistered AND a creator pubkey, auto-created a bot row and returned it — an unauthenticated request could mutate the DB. If registered, auto-upgraded the creator's archetype/webhookUrl on every call. |
Pure read: looks up the bot by pubkey, returns {exists, bot} or {exists:false, pubkey}. Zero db.insert/db.update calls. Doc comment marks it deprecated, session-establishing/token-issuing capability removed. Kept only for backward-compatible lookups (leaderboard-style) and the e2e test helper. |
POST /api/auth/nostr/session |
NIP-98 verified, issues JWT, creator auto-create/upgrade gated behind verification. Unchanged by this plan. | Unchanged — remains the only way to establish a session. |
GET /api/auth/me |
Did not exist. | New. Authorization: Bearer <jwt> required; 401 on missing/malformed/forged/expired/blacklisted token (all via extractPubkeyFromAuth/verifyJwt); 200 {exists, bot} for a valid token, read-only. |
Client session restore (useNostr.ts) |
authFetch('/api/auth/login', {method:'POST', body: JSON.stringify({pubkey})}) |
authFetch('/api/auth/me') — GET, no body, no bare pubkey. |
Task Commits
Each task was committed atomically (TDD RED/GREEN split for Task 1):
- Task 1 RED — failing test for GET /api/auth/me —
e824f4c(test) - Task 1 GREEN — GET /api/auth/me implementation —
635ee39(feat) - Deviation — sync migrate.ts with schema.ts —
bf240ce(fix, committed separately per plan's explicit allowance) - Task 2 — retire the bare-pubkey session path (client + server) —
2a343ac(feat)
All four commits pushed to origin main (https://source.archipelago-foundation.org/lfg2025/botfights.git).
Files Created/Modified
server/src/routes/auth-me.test.ts— new: 7 tests covering missing/malformed/forged/blacklisted/unregistered/valid-token cases forGET /me, plus a no-writes assertion.server/src/routes/auth.ts— addedGET /me; reducedPOST /loginto a read-only deprecated lookup (removed creator auto-create + auto-upgrade); deduped the now-singleextractPubkeyFromAuthimport.server/src/routes/auth.test.ts— added a case asserting an unregistered creator pubkey via/loginreturnsexists:falseand leaves thebotsrow count unchanged.frontend/src/composables/useNostr.ts— auto-restore now callsGET /api/auth/meinstead ofPOST /api/auth/login.e2e/helpers/auth.ts— doc comments updated to describeloginWithPubkeyas a read-only test helper, not a login.server/src/db/migrate.ts— (deviation) brought the standalonepnpm migrateCLI script's DDL back in sync withschema.ts, matchingstartup.ts's already-correct migrations.
Decisions Made
- Fixed
migrate.tsas an in-scope deviation rather than deferring it again — the failures it caused were directly in this plan's own auth test blast radius, and the correct DDL already existed verbatim instartup.ts, making the fix low-risk (copy, don't invent). - Left every other
pubkey-in-body client call site (register, update, wallet ops, betting, tournament join) untouched — those are authenticated actions using pubkey as an established resource key, not "claim an identity to get a session," and are out of this plan's stated scope. - Used direct binary invocation (
./node_modules/.bin/vitest,./node_modules/.bin/tsc,./frontend/node_modules/.bin/vue-tsc) for verification runs instead ofpnpm vitest/pnpm exec tsc, because this environment'spnpmrepeatedly required an interactivepnpm approve-buildsstep unrelated to this task; each time it was run to unblock a test pass it produced a large, unrelatedpnpm-lock.yaml/pnpm-workspace.yamldiff (~825 lockfile line deletions) that was reverted (git checkout --) rather than committed, since committing it was outside this plan's scope and risked contaminating the concurrently-worked-on shared tree.
Deviations from Plan
Auto-fixed Issues
1. [Rule 1 - Bug] server/src/db/migrate.ts DDL had drifted from schema.ts, causing 15 pre-existing auth/tournament test failures
- Found during: Baseline test run before Task 1 (per the executor prompt's explicit instruction to check whether this was cheap to fix within the auth blast radius).
- Issue:
migrate.ts(the standalonepnpm --filter server migrateCLI script) was missing 7 tables (payments,wallet_connections,bets,tournaments,tournament_entries,analytics,tournament_matches) and 9 columns onbots/fights(sats_won,sats_wagered,has_wallet,zaps_received,bot_type,mode,pot_sats,payout_status,current_season) present inschema.ts. The route-level tests (auth.test.ts,auth-audit.test.ts,auth-edge.test.ts,tournaments.test.ts) hit the realdb/index.tssingleton against the on-disk, gitignoredserver/data/botfights.db, which onlystartup.ts'srunMigrations()(called fromindex.tsat server boot, never fromapp.tswhich tests import directly) or thismigrate.tsscript populate. Vitest itself never runs a migration, so a freshly migrated or freshly deleted dev DB reproduced the failures described in plan 09-01'sdeferred-items.md. - Fix: Rewrote
migrate.ts'sCREATE TABLE/ALTER TABLEstatements to exactly matchschema.ts, column-for-column and table-for-table — copying the already-correct DDL that exists inserver/src/db/startup.ts(the actual runtime migration path, which was never stale). - Files modified:
server/src/db/migrate.ts. - Verification:
pnpm --filter server migrateagainst a fresh DB, then full server suite went from 15 failed / 789 passed (baseline) to 6 failed / 798 passed on the first re-run, and 810 passed / 7 failed after this plan's own auth changes and the concurrent agent's unrelated commits landed — the remaining failures are all pre-existing timing/perf flakes (answers.test.ts,lifecycle.test.tsx2-3,bot-auth.test.tsconstant-time,docs.test.ts,speed-meta.test.ts,tier-balance.test.ts) documented in 09-01'sdeferred-items.mdas CPU-contention-sensitive, none touching auth. - Committed in:
bf240ce(separate commit, per the executor prompt's explicit instruction).
Total deviations: 1 auto-fixed (Rule 1 — bug fix, out-of-scope-but-cheap per explicit plan/prompt allowance). Impact on plan: Necessary for this plan's own auth test verification to be meaningful (running against tables that didn't exist would have masked real regressions). No scope creep — the fix only touches DDL already correctly defined elsewhere in the same repo; no new architecture, no new endpoints.
Test Counts
- Baseline before any change (per executor prompt instruction):
pnpm vitest run --project server→ 15 failed / 789 passed (804 total). Matches the ~14 pre-existing failures the prompt described. - After the migrate.ts fix alone: 6 failed / 798 passed (804 total) — all 15 previously-failing auth/tournament tests now pass; the 6 remaining are pre-existing timing/perf flakes.
- After both auth tasks (final):
- Targeted:
auth.test.ts+auth-edge.test.ts+auth-audit.test.ts+auth-me.test.ts= 56/56 pass (7 new inauth-me.test.ts, 1 new inauth.test.ts, 48 pre-existing). - Full server suite: 810 passed / 7 failed (817 total; the +8 vs. the 804/809 baseline count is the new
auth-me.test.tstests plus the newauth.test.tscase). The 7 remaining failures are the same pre-existing timing/perf-under-CPU-load class documented indeferred-items.md(lifecycle.test.tsx2-3,speed-meta.test.ts,tier-balance.test.ts,bot-auth.test.tsconstant-time) — zero of them touch auth, and this run happened after the concurrent 09-03 agent's commits (a080956,2dd9947) had already landed in the same shared working tree, confirming no cross-plan regression. tsc --noEmit -p server/tsconfig.json— exit 0.vue-tsc --noEmit -p frontend/tsconfig.json— exit 0.
- Targeted:
Issues Encountered
- This environment's
pnpmrequired an interactivepnpm approve-buildsstep (native module build approval forbetter-sqlite3/esbuild/onnxruntime-node/protobufjs/sharp) beforepnpm vitest/pnpm exec tscwould run non-interactively. Runningpnpm approve-builds --allunblocked it but also produced a large, unrelatedpnpm-lock.yaml/pnpm-workspace.yamldiff each time (~825 lockfile line deletions plus anallowBuildsblock) that had nothing to do with this task. Resolved by invoking the already-built binaries directly (./node_modules/.bin/vitest,./node_modules/.bin/tsc,./frontend/node_modules/.bin/vue-tsc), which bypass pnpm's install-gate entirely, and reverting the lockfile/workspace diff (git checkout --) after every verification run so it never entered a commit. - The working tree is shared with a concurrent agent (per the executor prompt's warning) working on plan 09-03 (unified prompt/docs). Their commits (
a080956,2dd9947) appeared directly in localgit logmid-session since it's the same checkout, not a separate clone — no merge conflicts occurred since their files (DocsPage.vue,JoinBoutPage.vue,BotProfilePage.vue,docs.ts,docs.test.ts,e2e/signup-bot.spec.ts) never overlapped this plan's auth files. Push succeeded as a fast-forward at every step.
User Setup Required
None — no external service configuration required. JWT_SECRET provisioning for production (the crash-loop risk documented in 09-RESEARCH.md Pitfall 1) is BOT-04's manifest work, not this plan's.
Next Phase Readiness
- BOT-01's server/client auth surface is complete and tested;
POST /api/auth/nostr/session(signer login) andGET /api/auth/me(session restore) are the only two ways to obtain or restore a session;POST /api/auth/loginis inert w.r.t. writes. - Not yet done, and explicitly out of this plan's scope: real-browser NIP-07 extension login and real Amber NIP-55 login — deferred to plan 09-07's human checkpoint per
09-RESEARCH.mdPitfall 5 (no automated harness exists for a realwindow.nostrprovider). This plan's own<verification>section states this is deliberately not claimed here. - Ready for 09-03 (unified prompt/docs) and 09-04 (arena deploy/manifest, including the
JWT_SECRETgenerated_secretsfix) to proceed independently — no auth-surface blockers introduced.
Self-Check: PASSED
All 6 key files confirmed present on disk; all 4 task/deviation commit hashes (e824f4c, 635ee39, bf240ce, 2a343ac) confirmed present in git log --oneline --all and pushed to origin main.
Phase: 09-botfights-platform-upgrade Completed: 2026-07-31