89 lines
11 KiB
Markdown
89 lines
11 KiB
Markdown
---
|
|
context: default
|
|
phase: 09-botfights-platform-upgrade (already complete — this is off-plan work)
|
|
task: n/a
|
|
total_tasks: n/a
|
|
status: paused
|
|
last_updated: 2026-08-02T10:34:47.198Z
|
|
---
|
|
|
|
# BLOCKING CONSTRAINTS — Read Before Anything Else
|
|
|
|
- [ ] CONSTRAINT: Never assume pushing one repo pushed another — this session pushed `archy` repeatedly via `git push gitea-ai main`, but the `botfight` repo's last 4 commits (the entire security-fix body of work) sat **local-only** the whole time and were only discovered/pushed at the very end of this session, during this handoff step. Structural mitigation: whenever a session touches more than one git repo, explicitly run `git status -sb` (ahead/behind vs. the tracked remote) in **every** repo touched before ending the session — not just the one most recently `git push`ed.
|
|
|
|
**Do not proceed until the box above is checked (i.e. verify both repos are still in sync with their remotes before doing anything else).**
|
|
|
|
<current_state>
|
|
This is **not** a GSD plan/task in progress. Phase 09 (BotFights Platform Upgrade) is fully complete — plans 09-01 through 09-07 all have SUMMARY.md files, the last dated 2026-07-31 05:08. Everything described below happened *after* that, as live, user-directed, reactive work preparing for a same-day BotFights demo ("two real fighters playing with cashu"). None of it was tracked against a PLAN.md task list — the original GSD task (execute 09-06-PLAN.md: bump manifest + sign catalog) completed normally and stopped cleanly at its signing checkpoint, exactly as designed. Everything after that was ad hoc.
|
|
|
|
**As of this handoff, everything is committed and pushed in both repos, and both demo nodes are deployed and verified healthy.** There is nothing mid-flight to resume — this file exists so a future session (or this one, after compaction) has the full picture instead of re-discovering it.
|
|
</current_state>
|
|
|
|
<completed_work>
|
|
|
|
**botfight repo** (`/home/archipelago/Projects/botfight`, pushed to `origin/main` @ `10d4209`):
|
|
- iframe embedding fix (X-Frame-Options was unconditional), native Archipelago signer bridge (`nostr-provider.js`), "Sign in with Archipelago" docs for app developers
|
|
- Discoverability fixes: mode-picker guide banner, AI-answer visibility, "Latest Bouts" cut off on short viewports
|
|
- Fixed a proxy-URL leak (local/Tailscale addresses leaking into AI setup prompts via client-side `window.location.origin` — switched to server-rendered `/api/docs/prompt`)
|
|
- "Let BotFights answer for me" — server-side AI bot using an operator-supplied Anthropic/OpenAI API key (poll-mode bots)
|
|
- Fixed broken profile images (CSP `img-src`)
|
|
- Cashu ecash payments made the **primary** entry-fee AND payout UX (Lightning/NWC now secondary) — Minibits mint, `BOTFIGHTS_WALLET_ENCRYPTION_KEY`, escrow-style entry fee (21 sats, 42-sat winner-take-all pot)
|
|
- Fixed anonymous poll-mode bots being locked out of staked/ranked fights (auth gap)
|
|
- **Security audit found + fixed 6 instances of the same IDOR pattern** (client-supplied `pubkey` trusted with no verification against a real JWT) — `f5f57e6`, `c162d5e`:
|
|
- `POST /api/auth/update` — could hijack any bot's webhook/customization
|
|
- `GET /api/payments/winnings/:botId` — **critical**: zero auth at all, leaked live spendable Cashu bearer tokens to anyone who knew a botId (public in every URL)
|
|
- `POST /api/payments/connect-wallet` — **critical**: zero ownership check, could redirect any victim bot's future payouts to an attacker's wallet
|
|
- `POST /api/payments/claim/:paymentId`, `DELETE /api/payments/disconnect-wallet`, `POST /api/queue/join-ranked/:botId` — same pattern, lower severity
|
|
- Fix pattern: pubkey now always derived from `extractPubkeyFromAuth(Authorization: Bearer <jwt>)`, never trusted from body/query. Added `verifyBotOwner()` helper in `bot-auth.ts` for routes serving both nostr-owner and anonymous-bot-secret audiences.
|
|
- Built the two things actually requested when the audit was found: **AI-answer settings reachable for existing bots** (`/api/bots/:name/ai-config`, not just at creation) and a **claim-winnings UI** (Cashu payouts were minted server-side but had zero frontend consumer — `41f1b93`)
|
|
- `10d4209`: fixed a real `tsc` error the podman build caught that local verification initially missed (misread a wrapper's exit code instead of the actual log content — lesson: always check log *content*, not just the shell wrapper's `$?`)
|
|
- Built + pushed `146.59.87.168:3000/lfg2025/botfights:1.2.11`
|
|
|
|
**archy repo** (pushed to `gitea-ai/main`, my commits at `aea17248`/`b0a08345` — many other agents' commits have landed on top since, this is a busy shared tree):
|
|
- `apps/botfights/manifest.yml` bumped to 1.2.11; fixed `data_uid` from `1001` to `999` (the container's real internal UID — first attempt copied fedimint-clientd/barkd's value without checking this image's actual `Dockerfile`, which does `useradd --system` with no explicit UID)
|
|
- `scripts/image-versions.sh` kept in lockstep
|
|
- Catalog regenerated, signed (user ran `sign-catalog.sh`), published — verified live on `146.59.87.168:3000/lfg2025/archy/raw/branch/main/releases/app-catalog.json`
|
|
- Deployed to both nodes via RPC (`package.update`), both verified healthy:
|
|
- **archi-dev-box** (local): `botfights` container on `1.2.11`, `/api/health` → ok
|
|
- **x250-beta** (`archy-x250-beta.tail08d8f2.ts.net`): `botfights` container on `1.2.11`, `/api/health` → ok, `/api/bots` confirmed identical to `botfights.archipelago-foundation.org` (arena-proxy forwarding correctly)
|
|
</completed_work>
|
|
|
|
<remaining_work>
|
|
Nothing blocking the demo. One loose end, likely moot:
|
|
- Framework PT (`100.65.115.109`) SSH access is still blocked — the password was rotated 2026-07-26 and the current one isn't recorded anywhere. User redirected the demo plan away from Framework PT to x250-beta earlier in the session, so this probably doesn't matter anymore unless the user brings it up again.
|
|
</remaining_work>
|
|
|
|
<decisions_made>
|
|
- Cashu is now the primary UX for both paying entry fees AND receiving payouts, Lightning/NWC demoted to a secondary "or connect a Lightning wallet instead" option — explicit user instruction.
|
|
- `data_uid: 999:999` (not 1001) in the botfights manifest — verified against the running container's actual `id` output, not assumed from another app's manifest.
|
|
- ai-config routes accept EITHER a nostr JWT (new, for browser owners) OR the bot's own secret (existing, for anonymous AI-agent poll-mode bots) — additive, not a replacement, since both audiences are real and pre-existing.
|
|
</decisions_made>
|
|
|
|
<blockers>
|
|
- Framework PT SSH: password unknown since 2026-07-26 rotation. Not currently blocking anything (user moved to x250-beta).
|
|
</blockers>
|
|
|
|
## Required Reading (in order)
|
|
1. This file, obviously.
|
|
2. `.planning/phases/09-botfights-platform-upgrade/09-06-SUMMARY.md` and `09-07-SUMMARY.md` — the actual last GSD-tracked work in this area, for anyone confused about why there's no PLAN.md for tonight's work.
|
|
3. If continuing security work: re-read the fix pattern in `botfight` repo commits `f5f57e6` and `c162d5e` before touching any other route that reads a pubkey — the same bug class may exist elsewhere in the codebase that wasn't audited (only `auth.ts`, `payments.ts`, and `queue.ts` were checked; `bots.ts`, `tournaments.ts`, `bets.ts` were not re-audited for this exact pattern).
|
|
|
|
## Critical Anti-Patterns (do NOT repeat these)
|
|
- **ANTI-PATTERN: trusting a shell wrapper's exit code instead of the actual command output.** During this session, `tsc --noEmit ... ; echo "EXIT=$?"` was read as "passed" from the *notification summary* (which reports the wrapper's own exit code, always 0 because `echo` always succeeds) rather than the log *content*. This let a real `tsc` compile error through to a `podman build` failure. → Structural mitigation: always `cat`/`Read` the actual log file and look for the error pattern or an explicit `EXIT=N` marker line before treating a background verification command as passed.
|
|
- **ANTI-PATTERN: assuming multi-repo work is saved because one repo was pushed.** → Structural mitigation described in the BLOCKING CONSTRAINT above.
|
|
- **ANTI-PATTERN (from earlier this session, already corrected): never run `archipelago --version` on a fleet node** — it starts the full daemon rather than printing a version string (deployed binaries predate the flag). Use source-reading instead of the binary for investigation.
|
|
|
|
## Infrastructure State
|
|
- **archi-dev-box** (local node): `archipelago` daemon healthy, RPC on `127.0.0.1:5678` (session cookie in `/tmp/archy-dev-cookies.txt`, likely stale by the time this is read — re-login with `auth.login` / password `ThisIsWeb54321@`). `botfights` container healthy on `1.2.11`.
|
|
- **x250-beta** (`archy-x250-beta.tail08d8f2.ts.net`, tailnet IP rotates — resolve by MagicDNS name): reachable via plain `ssh archipelago@archy-x250-beta.tail08d8f2.ts.net` this session (no password prompt hit — key-based or cached). RPC session cookie in `/tmp/archy-cookies.txt` **on that remote node**, likely stale — re-login same way. `botfights` container healthy on `1.2.11`.
|
|
- Both nodes' local `/tmp` filled up mid-session (a 12G tmpfs, hit 0MB free once) — if you hit `ENOSPC` from the harness itself (not the actual command), check `df -h /tmp` and clean up stray large files (this session's culprit: two OTA release assets, ~260MB, downloaded to `/tmp` on the **local** machine as a relay step for an unrelated node update earlier in the session).
|
|
- Canonical arena: `https://botfights.archipelago-foundation.org` — both demo nodes proxy to this via `ARENA_UPSTREAM_URL`, confirmed serving identical bot/fight data on both.
|
|
|
|
<context>
|
|
The user is demoing BotFights live, same day, wants two real fighters paying/winning with Cashu ecash across two real node installs. All of that is now in place and verified. The security audit was NOT originally requested — it was triggered by investigating the user's question "can we confirm the fighter wins all the cashu sats into their node wallet automatically", which led to reading `payments.ts` end to end and discovering the payout claim flow had no frontend UI *and* the backend route serving it had no auth at all. That in turn led to checking every other route with a similar shape, which is how 5 more instances of the same bug were found. This is worth remembering: a seemingly simple product question ("where does the money go") uncovered a real, live, exploitable vulnerability in a publicly-deployed app — treat "let me just check how this actually works end to end" as time well spent, not scope creep.
|
|
</context>
|
|
|
|
<next_action>
|
|
Nothing is required to "resume" — this was a complete, self-contained session of off-plan work, fully committed, pushed, deployed, and verified. If the user opens a new session and says something like "continue" or "where were we", the right first move is to summarize the state above (both nodes on `1.2.11`, security fixes live, demo-ready), not to look for a GSD plan to execute. If the user wants to resume *GSD-tracked* work specifically, `STATE.md` says Phase 10 (Key-Material Hardening, KEY-01..KEY-04, sourced from `docs/security/ENTROPY-SEED-AUDIT-2026-07-31.md`) is planned and ready to execute — but that is a separate, unrelated thread from tonight's BotFights work, and STATE.md is being actively updated by other concurrent agents working other phases (01, 02, 10) in this shared tree, so re-read it fresh rather than trusting anything cached.
|
|
</next_action>
|