239 lines
16 KiB
Markdown
239 lines
16 KiB
Markdown
---
|
|
phase: 09-botfights-platform-upgrade
|
|
plan: 07
|
|
type: execute
|
|
wave: 4
|
|
depends_on: [09-06]
|
|
files_modified:
|
|
- .planning/phases/09-botfights-platform-upgrade/09-DEMO-CHECKLIST.md
|
|
autonomous: false
|
|
requirements: [BOT-01, BOT-02, BOT-03, BOT-04]
|
|
|
|
must_haves:
|
|
truths:
|
|
- "archi-dev-box runs BotFights 1.2.0 installed through the normal signed-catalog path — not hand-placed — with its JWT secret injected from the orchestrator's secret store rather than a plaintext env value (D-04/BOT-04)"
|
|
- "The BotFights UI on archi-dev-box shows fighters that live in the VPS2 arena, and a fight started there is visible from the arena (D-03/BOT-03)"
|
|
- "A real nostr signer (NIP-07 browser extension) logs a human in on archi-dev-box, and the session survives a page reload without any bare-pubkey request (D-01/BOT-01)"
|
|
- "A cloud-hosted AI agent builds a working bot from the single prompt URL alone, registers, and fights — with no other document (D-02/BOT-02)"
|
|
- "The demo path is written down as a checklist so it can be re-run on demo day without re-deriving anything"
|
|
artifacts:
|
|
- path: .planning/phases/09-botfights-platform-upgrade/09-DEMO-CHECKLIST.md
|
|
provides: "The exact demo-day run sheet: URLs, click path, expected results, and the fallback if the arena is unreachable"
|
|
contains: "arena"
|
|
key_links:
|
|
- from: archi-dev-box's installed botfights container
|
|
to: the canonical VPS2 arena
|
|
via: "ARENA_UPSTREAM_URL delivered by the signed catalog's embedded manifest, verified in the running container's env"
|
|
pattern: "ARENA_UPSTREAM_URL"
|
|
---
|
|
|
|
<objective>
|
|
Land the phase where it has to land: BotFights 1.2.0 installed on archi-dev-box through the real
|
|
signed-catalog path, then the three things only a human can confirm — a real signer login, real
|
|
cross-node fighter visibility, and a cloud bot built from the prompt alone.
|
|
|
|
Decision IDs map to `09-CONTEXT.md` **Locked Decisions**: D-01 = BOT-01 signer login,
|
|
D-02 = BOT-02 unified prompt, D-03 = BOT-03 shared public arena, D-04 = BOT-04 signed catalog.
|
|
The hard deadline in `09-CONTEXT.md` (demo on 2026-07-31 from archi-dev-box with a cloud "openclaw"
|
|
bot) is what this plan exists to satisfy.
|
|
|
|
Purpose: every prior plan proved a component. This one proves the product, on the machine the demo
|
|
runs from, through the path a real user takes. `09-RESEARCH.md` Pitfall 5 is explicit that the
|
|
Playwright suite never drives a real `window.nostr`, so green tests are not evidence here — a human
|
|
with an extension is.
|
|
Output: a verified archi-dev-box install, three human-confirmed checks, and a demo run sheet.
|
|
|
|
**Host: archi-dev-box (this machine).** `x250-dev` was offline during planning — if it is up, repeat
|
|
Task 1 there to satisfy the standing dev-pair rule and record the result.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@$HOME/.claude/gsd-core/workflows/execute-plan.md
|
|
@$HOME/.claude/gsd-core/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/ROADMAP.md
|
|
@.planning/STATE.md
|
|
@.planning/phases/09-botfights-platform-upgrade/09-CONTEXT.md
|
|
@.planning/phases/09-botfights-platform-upgrade/09-RESEARCH.md
|
|
@.planning/phases/09-botfights-platform-upgrade/09-VALIDATION.md
|
|
@.planning/phases/09-botfights-platform-upgrade/09-04-SUMMARY.md
|
|
@.planning/phases/09-botfights-platform-upgrade/09-05-SUMMARY.md
|
|
@.planning/phases/09-botfights-platform-upgrade/09-06-SUMMARY.md
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Update archi-dev-box to 1.2.0 through the signed catalog and verify the real install</name>
|
|
<precondition>The signed catalog carrying BotFights 1.2.0 is published and reachable at the vps2 raw URL (plan 09-06 Task 3 verified this), and the archipelago service on archi-dev-box can be running — it was inactive at planning time.</precondition>
|
|
<reversibility rating="costly">Updating the installed app switches this node's BotFights to proxy
|
|
mode; its local database (115 bots, 102,440 fights) is preserved on disk but no longer displayed.
|
|
Reverting a single node means removing `ARENA_UPSTREAM_URL` from the container env; reverting the
|
|
fleet means republishing the catalog (plan 09-06).</reversibility>
|
|
<files>.planning/phases/09-botfights-platform-upgrade/09-DEMO-CHECKLIST.md</files>
|
|
<read_first>
|
|
- `.planning/phases/09-botfights-platform-upgrade/09-06-SUMMARY.md` — the published catalog URL
|
|
and the gate decision (federated by default, or held).
|
|
- `.planning/phases/09-botfights-platform-upgrade/09-05-SUMMARY.md` — the verified arena URL and
|
|
the 1.2.0 image digest to compare the node's pulled image against.
|
|
- `core/archipelago/src/container/app_catalog.rs` lines 326-400 — `refresh_catalog` and how
|
|
catalog URLs are derived from the configured update mirrors, plus the on-disk cache at
|
|
`<data_dir>/app-catalog.json`; this is what must be refreshed before the node can see 1.2.0.
|
|
- `core/archipelago/src/container/prod_orchestrator.rs` lines 1385-1470 — `load_manifests` and
|
|
the catalog overlay: the catalog's embedded manifest wins over the on-disk one, which is why
|
|
the update must come from the catalog and not from editing `/opt/archipelago/apps/botfights/`.
|
|
- `CLAUDE.md` "Don't deploy via systemctl until Quadlet" and the deploy-to-dev-pair rule.
|
|
- The current state to compare against: `podman inspect botfights` today shows a plaintext
|
|
`JWT_SECRET` env value, `ARCHY_EMBEDDED=1` and `FIGHT_LOOP_ENABLED=true` from the pre-1.2.0
|
|
install — after the update the secret must come from the orchestrator's secret store instead.
|
|
</read_first>
|
|
<action>
|
|
Bring archi-dev-box's node to 1.2.0 through the supported path:
|
|
1. Ensure the archipelago service is running (it was inactive at planning time). Start it and
|
|
confirm it is healthy before touching apps.
|
|
2. Force a catalog refresh so the node picks up the newly published signed catalog, then confirm
|
|
`/var/lib/archipelago/app-catalog.json` now shows `botfights` at 1.2.0 with a valid
|
|
`signed_by`, and that the log line recording release-root signature verification appears.
|
|
3. Update the BotFights app through the orchestrator's normal app-update path (the same one the
|
|
UI's per-app Update button drives). Do not hand-create the container and do not edit the
|
|
on-disk manifest — a catalog-covered app ignores disk edits, and hand-created containers are
|
|
exactly what this phase is moving away from.
|
|
4. Verify the resulting container:
|
|
- image tag `1.2.0` and a digest matching the one recorded in `09-05-SUMMARY.md`;
|
|
- `ARENA_UPSTREAM_URL` present and equal to the arena URL;
|
|
- `JWT_SECRET` delivered as a podman secret reference rather than a plaintext env value, and
|
|
`/var/lib/archipelago/secrets/botfights-jwt-secret` existing with mode 0600 and 64 hex
|
|
characters of content (check the mode and length; do not print the value);
|
|
- container healthy, `curl -fsS http://127.0.0.1:9100/api/health` ok;
|
|
- `curl -fsS http://127.0.0.1:9100/api/bots` returns the arena's fighters (proxy mode live);
|
|
- `curl -fsS http://127.0.0.1:9100/api/docs/prompt` returns the unified prompt.
|
|
5. Confirm the node's own database file is intact and untouched at
|
|
`/var/lib/archipelago/botfights/botfights.db` (same size/mtime class as before — data is
|
|
preserved, just not read).
|
|
6. If `x250-dev` is reachable, repeat steps 1-4 there and record the outcome; if it is still
|
|
offline, record that the dev-pair rule was satisfied on one node only and why.
|
|
|
|
Write `.planning/phases/09-botfights-platform-upgrade/09-DEMO-CHECKLIST.md`: the demo-day run
|
|
sheet. It must contain the arena URL, the node UI URL, the prompt URL to hand the cloud agent,
|
|
the exact click path for the signer login, the expected visible results at each step, and the
|
|
fallback if the arena is unreachable on the day (a node can be switched back to standalone by
|
|
removing one env var — state the command).
|
|
</action>
|
|
<verify>
|
|
<automated>podman inspect botfights --format '{{.Config.Image}}' | grep -q ':1.2.0' && podman inspect botfights --format '{{json .Config.Env}}' | grep -q 'ARENA_UPSTREAM_URL' && curl -fsS http://127.0.0.1:9100/api/health | grep -q '"status":"ok"' && curl -fsS http://127.0.0.1:9100/api/docs/prompt | grep -q '/api/bots' && test -f /var/lib/archipelago/botfights/botfights.db</automated>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- `podman inspect botfights --format '{{.Config.Image}}'` ends in `:1.2.0` and the container reports healthy.
|
|
- `podman inspect botfights --format '{{json .Config.Env}}'` contains `ARENA_UPSTREAM_URL` and does NOT contain a plaintext `JWT_SECRET=` assignment; `stat -c %a /var/lib/archipelago/secrets/botfights-jwt-secret` prints `600` and its content length is 64.
|
|
- `curl -fsS http://127.0.0.1:9100/api/bots` returns the same fighter set as `curl -fsS <arena>/api/bots`.
|
|
- `curl -fsS http://127.0.0.1:9100/api/docs/prompt` returns the unified prompt with the arena URL substituted.
|
|
- `/var/lib/archipelago/app-catalog.json` shows `botfights` at 1.2.0 with the expected `signed_by`.
|
|
- `/var/lib/archipelago/botfights/botfights.db` still exists with its pre-update row counts (verified read-only).
|
|
- `09-DEMO-CHECKLIST.md` exists and names the arena URL, the prompt URL and the standalone-fallback command.
|
|
</acceptance_criteria>
|
|
<done>The demo machine runs 1.2.0, installed the way every user installs it, proxying to the shared arena with a per-install secret.</done>
|
|
</task>
|
|
|
|
<task type="checkpoint:human-verify" gate="blocking">
|
|
<name>Task 2: Real nostr signer login and live arena fighters on archi-dev-box</name>
|
|
<what-built>
|
|
BotFights 1.2.0 is installed on archi-dev-box and is a thin client of the public arena. Two
|
|
things need a human: a real nostr signer (no automated harness exists — there is no
|
|
`window.nostr` mock anywhere in the Playwright suite), and a visual confirmation that the
|
|
fighters shown here really are the arena's.
|
|
</what-built>
|
|
<how-to-verify>
|
|
Open the BotFights UI on archi-dev-box (the URL is in `09-DEMO-CHECKLIST.md`).
|
|
|
|
**1. Signer login (D-01).** With a NIP-07 extension installed (nos2x or Alby), click Sign In and
|
|
approve the signature request in the extension. Expect: you are signed in, your bot/profile
|
|
appears, and the extension prompted you to sign an event — you were never asked for a private
|
|
key. Then reload the page: you should still be signed in without a second signature prompt.
|
|
Optional but valuable: repeat from an Android phone using Amber (NIP-55) against the same URL.
|
|
|
|
**2. Shared arena (D-03).** The fighter list / leaderboard here should show the same fighters as
|
|
the public arena URL open in a second tab. Pick any fighter visible only on the arena and confirm
|
|
it is listed here too. Start a fight and confirm the rounds appear live (not all at once at the
|
|
end).
|
|
|
|
Report anything that looks wrong — a spinner that never resolves, an empty list, a login that
|
|
silently fails, rounds that appear only when the fight finishes.
|
|
</how-to-verify>
|
|
<resume-signal>Type "approved" or describe what you saw</resume-signal>
|
|
</task>
|
|
|
|
<task type="checkpoint:human-verify" gate="blocking">
|
|
<name>Task 3: Cloud openclaw bot registers and fights using only the unified prompt URL</name>
|
|
<what-built>
|
|
The unified AI bot-setup prompt is live at the public arena's `/api/docs/prompt` and contains
|
|
everything an agent needs: registration, credentials, both the webhook and polling protocols,
|
|
every endpoint and every response format. This is the D-02 acceptance test and the demo's
|
|
centrepiece.
|
|
</what-built>
|
|
<how-to-verify>
|
|
Hand your cloud "openclaw" agent nothing but the prompt URL (in `09-DEMO-CHECKLIST.md`) —
|
|
no other document, no extra instructions beyond "set yourself up as a BotFights bot using this".
|
|
|
|
Expect the agent to be able to, from that alone:
|
|
1. register itself (`POST /api/bots`) and receive an id and secret,
|
|
2. authenticate and poll for challenges (or stand up a webhook, if it chooses that mode),
|
|
3. join the queue and fight,
|
|
4. have that fight appear in the BotFights UI on archi-dev-box.
|
|
|
|
If the agent gets stuck, the exact question it could not answer from the prompt is the finding —
|
|
please quote it. That tells us precisely what the prompt is still missing, which is more useful
|
|
than "it didn't work".
|
|
</how-to-verify>
|
|
<resume-signal>Type "approved" or paste the point where the agent got stuck</resume-signal>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<threat_model>
|
|
## Trust Boundaries
|
|
|
|
| Boundary | Description |
|
|
|----------|-------------|
|
|
| signed catalog → this node's orchestrator | Decides the image and env this node runs |
|
|
| browser + NIP-07 extension → node UI → arena | The human's private key stays in the signer throughout |
|
|
| cloud AI agent → public arena | An anonymous internet client registering and fighting |
|
|
|
|
## STRIDE Threat Register
|
|
|
|
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
|
|-----------|----------|-----------|----------|-------------|-----------------|
|
|
| T-09-34 | Tampering | a hand-placed container diverging from what the catalog describes | high | mitigate | The update goes through the orchestrator's catalog path only; the acceptance criteria assert the image digest matches the published one (Task 1) |
|
|
| T-09-35 | Information disclosure | the JWT secret remaining a plaintext container env value after the update | high | mitigate | Asserted absent from the container env and present as a 0600 secret file (Task 1) |
|
|
| T-09-36 | Denial of service | the node's app becoming unusable if the arena is down on demo day | medium | mitigate | `/api/health` stays local, the UI degrades to a 502 on data calls, and the checklist documents the one-env-var switch back to standalone |
|
|
| T-09-37 | Spoofing | a signer flow that silently falls back to a locally generated key | high | mitigate | The human check explicitly requires the extension's signature prompt and that no private key was ever requested (Task 2) |
|
|
| T-09-38 | Repudiation | claiming BOT-01/BOT-02 done on green automated tests alone | medium | mitigate | Both are gated behind blocking human checkpoints, per `09-RESEARCH.md` Pitfall 5 and the CLAUDE.md "test before claiming fixed" rule |
|
|
</threat_model>
|
|
|
|
<verification>
|
|
- `podman inspect botfights` shows 1.2.0, the arena URL, and no plaintext secret.
|
|
- The node's fighter list matches the arena's.
|
|
- Human-confirmed: real NIP-07 login (and Amber if tested), live cross-node fighter visibility,
|
|
cloud agent bootstrapped from the prompt alone.
|
|
- Closes the `09-VALIDATION.md` "Manual-Only Verifications" list in full: real NIP-07 login, Amber
|
|
login, cross-node fighter visibility, and the cloud openclaw bot using only the unified prompt.
|
|
- Dev-pair rule: recorded for `x250-dev` (repeat Task 1 there if it is online, otherwise state why not).
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- archi-dev-box runs BotFights 1.2.0 from the signed catalog, proxying to the shared arena, with a
|
|
per-install secret and its local data preserved.
|
|
- A human has logged in with a real nostr signer and seen arena fighters on the node.
|
|
- A cloud agent has built a working bot from the single prompt URL.
|
|
- A demo run sheet exists for tomorrow.
|
|
</success_criteria>
|
|
|
|
<output>
|
|
Create `.planning/phases/09-botfights-platform-upgrade/09-07-SUMMARY.md` when done, recording the
|
|
human verdicts verbatim (including anything the cloud agent could not answer from the prompt — that
|
|
is the highest-value finding this phase can produce).
|
|
Commit the checklist and SUMMARY in archy, staged by explicit path, and `git push gitea-ai main`.
|
|
</output>
|