Files
archy/.planning/phases/09-botfights-platform-upgrade/09-06-SUMMARY.md
T

17 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 06 infra
manifest
app-catalog
secrets
jwt
arena-federation
signing-ceremony
phase provides
09-botfights-platform-upgrade (plan 05) botfights:1.2.1 image in the registry (146.59.87.168:3000/lfg2025/botfights:1.2.1), the canonical public arena running it at https://botfights.archipelago-foundation.org
apps/botfights/manifest.yml at 1.2.1 with generated_secrets/secret_env for JWT_SECRET (hex32) and default-on ARENA_UPSTREAM_URL
app-catalog/catalog.json and scripts/image-versions.sh bumped in lockstep (drift-checker clean)
releases/app-catalog.json regenerated, SIGNED by the release-root key, committed, pushed, and published live to the raw URL every node fetches
the release-signer binary built and staged at /tmp/archy-sign-bin/release/archipelago (reusable for future ceremonies)
the per-node bot/fight impact briefing used at the signing-ceremony gate
09-07 (demo verification)
added patterns
generated_secrets (kind hex32) + secret_env, mirroring apps/netbird-server/manifest.yml and apps/barkd/manifest.yml — per-install secret materialised 0600, never a manifest literal
gitea-ai (source.archipelago-foundation.org) and gitea-vps2/origin (146.59.87.168:3000) resolve to the SAME physical Gitea backend (confirmed via DNS + byte-identical published content) — pushing gitea-ai main IS the publish, no separate vps2 push is required in practice. The gitea-vps2 remote alias's own HTTP Basic Auth token is independently stale (fails auth) and should be rotated or removed to stop it looking like a required-but-broken step.
created modified
apps/botfights/manifest.yml
app-catalog/catalog.json
scripts/image-versions.sh
releases/app-catalog.json
Used the POST_PLANNING FACTS override version 1.2.1 (not the plan's original 1.2.0) throughout the manifest, catalog.json, image-versions.sh and the regenerated releases/app-catalog.json, since 09-05 shipped an additional prompt-hardening commit on top of 1.2.0 and the live arena is already on 1.2.1.
Split the work across two sessions at the human-only signing ceremony: Task 1 (manifest/catalog edits + regeneration + signer build) was committed and pushed WITHOUT releases/app-catalog.json (deliberately left uncommitted while unsigned, per CLAUDE.md/memory's rule against ever committing an unsigned release-manifest state). After the user ran the ceremony, this session independently re-verified the signature/signed_by/payload, then committed and pushed the signed file.
Discovered gitea-ai (source.archipelago-foundation.org, the dev push target) and gitea-vps2/origin (146.59.87.168:3000, the host nodes fetch releases/app-catalog.json raw from) are the SAME Gitea backend, not two separate mirrors needing separate pushes — confirmed by DNS resolution (source.archipelago-foundation.org -> 146.59.87.168) and by the raw catalog URL reflecting the gitea-ai push immediately, byte-for-byte identical to the local signed file (sha256 f66bd717...). A direct `git push gitea-vps2 main` was attempted as well per the plan's Task 3 instructions but failed with a stale HTTP Basic Auth token on that specific remote alias — reported rather than worked around, but non-blocking since the gitea-ai push already fully published the signed catalog.
Built the release signer binary (scripts/sign-catalog.sh's prerequisite) into /tmp/archy-sign-bin/release/archipelago ahead of the ceremony so the human's signing step was not blocked on a ~50-minute cold compile of the full archipelago workspace — the ceremony ran immediately once invoked.
BOT-04
id description requirement verification human_judgment
D1 apps/botfights/manifest.yml describes BotFights 1.2.1 with a per-install generated JWT_SECRET (hex32) and default-on ARENA_UPSTREAM_URL, verified by both targeted cargo tests and grep-level acceptance checks BOT-04
kind ref status
unit cd core && cargo test -p archipelago catalog_overlay_accepts_all_real_image_manifests pass
kind ref status
unit cd core && cargo test -p archipelago-container manifest (38 passed, incl. parse_every_real_manifest) pass
kind ref status
other python3 scripts/check-app-catalog-drift.py -> metadata_drift: 0 pass
false
id description requirement verification human_judgment
D2 releases/app-catalog.json regenerated with the 1.2.1 manifest embedded (secrets, secret_env, ARENA_UPSTREAM_URL all present), verified structurally against the previously-published catalog to show zero other-app drift BOT-04
kind ref status
other python3 one-liner asserting botfights.version==1.2.1, image tag :1.2.1, generated_secrets/secret_env/ARENA_UPSTREAM_URL present -> 'catalog ok'; structural key-by-key diff against prior commit showed only the botfights entry changed (65 other apps byte-identical) pass
false
id description requirement verification human_judgment rationale
D3 The release-root signing ceremony and fleet publish — the user ran scripts/sign-catalog.sh at their own terminal; the resulting catalog was independently re-verified and published BOT-04
kind ref status
other python3 assert signature present + signed_by == did:key:z6MkkidEnEpo6qHMCNSZoNKWtvQvxq3whnaME9wGgEFhq7ur + botfights.version==1.2.1 -> 'signed ok' pass
kind ref status
other curl http://146.59.87.168:3000/lfg2025/archy/raw/branch/main/releases/app-catalog.json (off-host) -> byte-identical to local signed file, sha256 f66bd717f45cff0bf5dfe1eb609dbc7abcf3aa28cffbf4a652556711426bf48f on both sides pass
false Signing itself required the human's mnemonic and could not be automated; the RESULT is fully machine-verifiable and was verified, so this deliverable is proven, not merely asserted by the human.
~4h across two sessions (dominated by a ~50min cold `cargo build --release` of the signer binary, run in the background while other verification proceeded; the ceremony + publish session itself was a few minutes) 2026-07-31 complete

Phase 9 Plan 6: BotFights 1.2.1 Manifest + Signed Catalog Publish Summary

Bumped apps/botfights/manifest.yml to 1.2.1 with a per-install generated JWT_SECRET and default-on shared-arena federation, then got the regenerated catalog signed by the release-root key (human ceremony) and published live to the raw URL every node fetches — BOT-04 complete.

Performance

  • Duration: ~4h across two sessions (session 1: manifest/catalog edits, regeneration, signer build, verification, stopped at the human-only signing checkpoint; session 2, after the user ran the ceremony: independent re-verification, commit, push, off-host publish confirmation)
  • Tasks: 3/3 completed
  • Files modified: 4 (apps/botfights/manifest.yml, app-catalog/catalog.json, scripts/image-versions.sh, releases/app-catalog.json)

Accomplishments

  • apps/botfights/manifest.yml bumped to 1.2.1 (superseding the plan's originally-planned 1.2.0 per the session's POST_PLANNING FACTS — 09-05 shipped one additional prompt-hardening commit after 1.2.0 was built, and the live arena is already on 1.2.1). Image tag updated, container.generated_secrets adds botfights-jwt-secret (kind: hex32, matching apps/barkd/manifest.yml's pattern — not base64 like netbird, since jwt.ts uses the value directly as an HMAC key with no decode step), secret_env maps it to JWT_SECRET. environment gains PORT=9100 and ARENA_UPSTREAM_URL=https://botfights.archipelago-foundation.org with an in-file comment documenting the per-node opt-out (delete the line to run standalone). Every other block (security/ports/volumes/health_check/interfaces/metadata) is untouched.
  • app-catalog/catalog.json's botfights entry and scripts/image-versions.sh's BOTFIGHTS_IMAGE bumped to 1.2.1 in lockstep — scripts/check-app-catalog-drift.py reports metadata_drift: 0.
  • Regenerated releases/app-catalog.json via scripts/generate-app-catalog.sh (66 apps, 56 embedded manifests). Verified programmatically and structurally: botfights.version == "1.2.1", image tag :1.2.1, generated_secrets/secret_env/ARENA_UPSTREAM_URL all present, and zero other apps changed vs the previously-published catalog.
  • Built the release-signer prerequisite (scripts/sign-catalog.sh refuses to compile its own signer) into /tmp/archy-sign-bin/release/archipelago ahead of the ceremony, so the human's signing step ran immediately with no compile wait.
  • Ran the plan's required verification: cargo test -p archipelago catalog_overlay_accepts_all_real_image_manifests (pass) and cargo test -p archipelago-container manifest (38 passed, including parse_every_real_manifest).
  • Gathered the fleet-impact briefing for the human gate: read the local BotFights SQLite DB on archi-dev-box directly — 115 bots, 102,440 fights, matching the plan's stated pre-measurement exactly. x250-dev was unreachable this session; the .228 resilience node has no local botfights.db (app not installed there).
  • User ran the signing ceremony (scripts/sign-catalog.sh) and confirmed success. Independently re-verified (not just trusting the script's own report): signature present, signed_by == did:key:z6MkkidEnEpo6qHMCNSZoNKWtvQvxq3whnaME9wGgEFhq7ur, botfights entry still 1.2.1 with the embedded manifest's secrets/env/arena URL intact, and a structural diff confirming signing altered nothing else in the payload.
  • Committed the signed catalog (a99522d0) and pushed to gitea-ai main. Discovered mid-publish that gitea-ai (source.archipelago-foundation.org) and gitea-vps2/origin (146.59.87.168:3000, the host nodes actually fetch the raw catalog from) are the same physical Gitea backend — DNS resolves the domain to the same IP, and the raw URL reflected the push immediately. Confirmed via off-host curl: published bytes are byte-identical to the local signed file (sha256:f66bd717f45cff0bf5dfe1eb609dbc7abcf3aa28cffbf4a652556711426bf48f on both sides).

Task Commits

  1. Task 1: Manifest 1.2.1 — generated JWT secret, default-on arena, catalogs in lockstepf281c7aa (feat) — apps/botfights/manifest.yml, app-catalog/catalog.json, scripts/image-versions.sh. Pushed to gitea-ai main.
  2. Task 2: Sign the regenerated catalog (human ceremony) — done by the user at their own terminal via scripts/sign-catalog.sh; no commit (script only mutates the working tree).
  3. Task 3: Verify signature, commit, publisha99522d0 (chore) — releases/app-catalog.json (signed). Pushed to gitea-ai main; confirmed same content live at the vps2 raw URL nodes fetch.

Plan metadata: this SUMMARY, committed separately in two docs commits (921f450e mid-plan, this rewrite folded into the same file) — .planning/STATE.md/ROADMAP.md/REQUIREMENTS.md were intentionally NOT touched per this run's explicit instruction; a follow-up should run the normal state-update step to mark BOT-04 complete in those files.

Files Created/Modified

  • apps/botfights/manifest.yml — version/image → 1.2.1, generated_secrets/secret_env for JWT_SECRET, ARENA_UPSTREAM_URL added to environment.
  • app-catalog/catalog.json — legacy botfights entry version/dockerImage → 1.2.1.
  • scripts/image-versions.shBOTFIGHTS_IMAGE tag → 1.2.1.
  • releases/app-catalog.json — regenerated, signed by the release-root key, committed, published.

Decisions Made

See key-decisions in frontmatter. Most notable: the catalog was deliberately kept off git while unsigned across the checkpoint boundary (per CLAUDE.md/memory's rule against ever committing an unsigned release-manifest state), and committed only after independent re-verification of the signature once the human ceremony succeeded. Also notable: gitea-ai and gitea-vps2/origin turned out to be the same backend — the plan's Task 3 wording (written assuming two separate remotes needing separate pushes) is slightly stale; a direct git push gitea-vps2 main was still attempted per the plan's literal instructions but failed on a stale token specific to that remote alias, which is reported below as a non-blocking issue rather than worked around.

Deviations from Plan

1. [User-directed] Version 1.2.1 instead of the plan's 1.2.0. The plan text was written against 1.2.0; the session's POST_PLANNING FACTS explicitly superseded this with 1.2.1 (an additional prompt-hardening commit d2fc998 landed on top of 09-05's 1.2.0 build, and the live arena is already running 1.2.1). Applied everywhere the plan said 1.2.0 — manifest, both catalog files, image-versions.sh, and the regenerated releases/app-catalog.json.

2. [User-directed] Execution split across the human checkpoint into two sessions. The plan's own Task 2 is a checkpoint:human-action gate; this run's explicit instruction additionally forbade attempting to sign or ask for the mnemonic in the first session. That session did all automatable prep and returned exact ceremony instructions; this second session picked up after the user confirmed the ceremony succeeded, independently re-verified, committed, and published.

3. [Rule 3 - blocking issue, reported not worked around] git push gitea-vps2 main failed with a stale HTTP Basic Auth token. The plan's Task 3 read_first assumes a direct push to the vps2 remote is required to publish. In practice gitea-ai (already pushed in Task 1/3) and gitea-vps2/origin are the same backend (confirmed via DNS + byte-identical published content), so the publish was already complete before this push was attempted. The gitea-vps2 remote alias's own credentials are independently stale — this is the same "known recurring issue with the vps2/local-origin remotes" flagged in project memory. Not fixed (out of scope — requires an admin token rotation), reported here instead of improvised around.

No Rule 1-2 auto-fixes were needed — the manifest/catalog edits matched the plan and the analog patterns (netbird-server, barkd) exactly, and both required cargo tests passed on the first run.

Issues Encountered

  • The signer binary build (cargo build --release -p archipelago, a fresh CARGO_TARGET_DIR with no incremental cache) took ~50 minutes on this heavily-loaded shared dev box (load average 24+ during the build). Backgrounded and monitored to completion; no errors, BUILD_EXIT=0.
  • x250-dev (100.72.136.6) was unreachable during the impact-briefing gathering — could not check whether it holds local BotFights data. Not blocking: the plan's own pre-measured reference count (115 bots/102,440 fights on archi-dev-box) was independently reproduced exactly, and the other checked node (.228) has no local install.
  • git push gitea-vps2 main failed with fatal: Authentication failed — see Deviation #3. Non-blocking; the gitea-ai push already published the signed catalog to the same backend.

User Setup Required

None remaining for this plan. Recommended follow-up (not done here per this run's explicit instruction to leave STATE/ROADMAP/REQUIREMENTS untouched): run the standard GSD state-update step to mark BOT-04 complete in .planning/REQUIREMENTS.md, advance .planning/STATE.md, and update .planning/ROADMAP.md's plan-progress row for 09-06. Separately: the gitea-vps2 git remote's stored HTTP Basic Auth token is stale and should be rotated (or the redundant remote alias removed, since gitea-ai already reaches the same backend).

Next Phase Readiness

  • BOT-04 is complete. Every node that refreshes its catalog now sees BotFights 1.2.1: the JWT_SECRET crash-loop fix and the default-on shared-arena federation are both live in the signed, published catalog, verified byte-for-byte from off-host.
  • 09-07 (demo verification) can proceed — the manifest/catalog side of the platform upgrade is done, on top of 09-05's already-verified live arena (health/prompt/registration/poll/SSE/cross-instance all proven working at 1.2.1).
  • Four clearly-named test bots remain in the arena from 09-05 (wavetest2/3/4, routefix) — unaffected by this plan, no cleanup action taken here.

Phase: 09-botfights-platform-upgrade Completed: 2026-07-31

Self-Check: PASSED

  • apps/botfights/manifest.yml, app-catalog/catalog.json, scripts/image-versions.sh, releases/app-catalog.json confirmed present on disk with 1.2.1 signed content.
  • Commits f281c7aa and a99522d0 confirmed present in git log --oneline and confirmed pushed to gitea-ai main.
  • releases/app-catalog.json confirmed signed (signature present, signed_by matches the expected release-root DID) and confirmed byte-identical to the content served live at http://146.59.87.168:3000/lfg2025/archy/raw/branch/main/releases/app-catalog.json (sha256 match).
  • git show HEAD grepped for mnemonic/private-key patterns: 0 matches.
  • /tmp/archy-sign-bin/release/archipelago confirmed executable and was the binary actually used for the ceremony.