236 Commits

Author SHA1 Message Date
Vantz Stockwell
9c9c7a8a97 feat(faq): wire Dr. Flask intro video into the phone-frame lightbox
Some checks failed
CI / backend-types (push) Successful in 11s
CI / frontend-build (push) Successful in 17s
CI / agent-tests (push) Failing after 32s
CI / integration (push) Has been skipped
The 85s v2 intro plays click-to-play in the phone mockup with fully custom
controls (play/pause, green seek bar, live timecode, mute, fullscreen) — no
loop, pause on close, Esc/backdrop/X to dismiss. Opens with sound on the cover
click (user gesture); falls back to muted autoplay if the browser blocks it.

- Transcoded the 163 MB / ~15 Mbps export -> 10.8 MB (720x1280, H.264 CRF 28,
  +faststart) so it only downloads when a visitor opts in (preload=metadata).
- Poster = a v2 frame grabbed from the video (drflask-poster.jpg, ~60 KB).
- Source 163 MB master stays untracked in docs/character/.

Verified live via Playwright: video loads (readyState 4, 85s), autoplays on
open, timecode/seek-fill track, play/pause + mute buttons both toggle state.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 11:37:03 -04:00
Vantz Stockwell
907cfcb428 docs(brand): v2 voice lock — VHS voice rule, catchphrase bank, 'Dr. Flask Appears' series
All checks were successful
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 47s
CI / integration (push) Successful in 22s
Oracle's post-v2 voice position: the 'trapped in a chemistry edutainment VHS'
one-liner, refined is/is-not lists, a 10-line canonical catchphrase bank, the
'Degree not included' footer gag, and the flagship recurring short-form format
'Dr. Flask Appears' (uninvited-helper episode structure + example).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 10:45:19 -04:00
Vantz Stockwell
b1961df18e docs(character): v2 Dr. Flask model sheet — the 90s spoof, approved
Some checks failed
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Failing after 30s
CI / integration (push) Has been skipped
v2 redesign: cartoon mascot with googly eyes, askew mortarboard, yellow bow tie,
lab coat, pointer stick, white gloves. Identity refreshed (Ph.D. Self-Certified,
Specialty: Server Chemistry, Height: One Flask Tall, 'No Degree Required'), with
the Clippy homage written into the board notes ('Appears whenever you need him.
Sometimes when you don't.'). New expression/posture/gesture sets recorded; v1
sheet marked superseded.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 10:43:07 -04:00
Vantz Stockwell
cfdec62a1d docs(brand): sharpen Dr. Flask voice — core vibe, influences, signature line
Some checks failed
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Failing after 29s
CI / integration (push) Has been skipped
Refined creative direction: 'lovable 90s help mascot with chaotic educational
confidence.' Adds the influence stack (Clippy/Mr. DNA/Weird Al/early-internet
tutorial), the homage-not-a-copy guardrail, and the canonical signature line —
'It looks like you're about to wipe a Rust server. Would you like help turning
that into a controlled reaction?' Deeper 5-video script punch-up queued for v2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 10:20:22 -04:00
Vantz Stockwell
e510f8b005 docs(character): 90s-spoof tone direction + v2 wardrobe + 12-beat storyboard
Some checks failed
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 15s
CI / agent-tests (push) Failing after 30s
CI / integration (push) Has been skipped
- Voice guide + bible gain the comedic north star: loving spoof of Clippy +
  Mr. DNA with Weird Al 'White & Nerdy' energy — Clippy's charm, never his
  intrusiveness.
- Record v2 wardrobe (bow tie, googly eyes, askew mortarboard, pointer stick),
  render incoming; v1 model sheet relabeled.
- Add drflask-storyboard.webp (12-beat sequence) + document its panel->script map.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 10:19:10 -04:00
Vantz Stockwell
cf1f1dea9a docs(brand): brand kit — voice guide, social channels, content series, trailer brief
All checks were successful
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 43s
CI / integration (push) Successful in 22s
Captures Oracle's full brand system as canonical collateral: positioning +
taglines, Dr. Flask voice guide (the 'plain English then one wink' rule),
handle strategy (CorrosionMgmt brand / DrFlaskPhD mascot split), copy-paste
YouTube/X/Twitch setups, the 5-video Dr. Flask mini-series, and the 9:16 brand
trailer brief + VO. Single source of truth for launch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 10:12:04 -04:00
Vantz Stockwell
2e72850b97 docs(character): add Dr. Flask model sheet + sync bible to the board
Some checks failed
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 15s
CI / agent-tests (push) Failing after 30s
CI / integration (push) Has been skipped
invideo produced a full character design board (turnarounds, 8-expression
progression, micro-expressions, postures, bubble-hand gestures, silhouettes,
color palette). Committed as the definitive reference and folded its details
(palette, expression/posture/gesture lists, character note) into the bible.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 10:02:25 -04:00
Vantz Stockwell
9f9785fc09 docs(character): Dr. Flask character bible — canonical identity + design notes
Some checks failed
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Failing after 30s
CI / integration (push) Has been skipped
Single source of truth for Dr. Flask (Corrosion Guide / Ph.D. / Catalyst
Expert / Erlenmeyer / neon-green / mortarboard) as the character gets
storyboarded across tools. Documents the lab-zone green rationale, where he
appears, the 9:16 intro-video plan, and the asset inventory.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 10:00:13 -04:00
Vantz Stockwell
142ba21113 feat(faq): expanded chemistry glossary + Dr. Flask lab zone
All checks were successful
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 45s
CI / integration (push) Successful in 21s
Replaces the compact 3-column table with the full long-form glossary
(Commander + Gemini copy):
- Dr. Flask cover card beside the intro (web-optimized 560px from
  docs/character/drflask-final.png, 1.8MB -> 394KB).
- 'In plain English' callout + the Formulae->...->Lab Notes flow strip.
- 8 term cards (Catalyst Console, re-Agent, Substrate, Formulae, Reactions,
  Compounds, Lab Notes, The Exchange) — chemistry meaning / Corrosion role /
  punchy kicker each.
- Dr. Flask sign-off card with his bio + quips.
- Lab-zone treatment: green accent scoped to .sec--lab so the whole section
  reads as a deliberate 'lab' corner, breaking up the orange brand.

Visually verified via Playwright on the dev server (marketing host): 8 cards,
green theming, Dr. Flask cover + sign-off all render; no page errors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 09:43:39 -04:00
Vantz Stockwell
04e664045b feat(faq): chemistry glossary — 'Brush up on your chemistry while managing your game server'
All checks were successful
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 15s
CI / agent-tests (push) Successful in 44s
CI / integration (push) Successful in 22s
Public-facing brand asset (Oracle + Commander): a glossary section on the FAQ
page mapping each chemistry term to its real role in Corrosion, plus the
chemistry-true pipeline as a flow strip.

- 8-term table (Term / Chemistry meaning / In Corrosion): Catalyst, re-Agent,
  Substrate, Formulae, Reaction, Compound, Lab Notes, The Exchange.
- Substrate is the host/bare-metal SURFACE servers run on — NOT the 'automation
  layer' (corrected the drift the Commander rejected; re-Agent installs on it,
  Reactions execute against it).
- Flow strip + closer: Formula defines -> Catalyst kicks off -> re-Agent runs it
  on the Substrate as a Reaction -> Lab Notes record the result.

Verified live via Playwright on the dev server (marketing host): table, flow
strip, and closer all render correctly; no errors from the page.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 09:14:49 -04:00
Vantz Stockwell
cef95540fc copy(roadmap): Multi-game Formulae, Operator API, The Exchange, Fleet Block clarifier
All checks were successful
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 42s
CI / integration (push) Successful in 21s
Commander copy pass:
- 'Multi-game expansion — game Formulae' -> 'Multi-game Formulae' (cleaner grammar)
- 'API access and integrations' -> 'Operator API and integrations' (operator-grade framing)
- 'Integrated storefront' -> 'The Exchange' (chemistry-flavored; ion-exchange nod,
  no collision with the locked 'Compound' = stack group)
- 'Fleet Block capacity management' gains a clarifier note: pooled host capacity,
  allocation, and utilization

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 09:02:42 -04:00
Vantz Stockwell
7f2207bc28 feat(settings): password change, 2FA enable/disable, API-key UI + Swagger; fix Owner RBAC drift
All checks were successful
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 15s
CI / agent-tests (push) Successful in 42s
CI / integration (push) Successful in 21s
Settings was missing self-service account security and any API-key UI:
- Account security (new Security tab): change password (POST /auth/change-password
  — verifies current via Argon2, rejects unchanged), enable 2FA (wires the
  existing /auth/2fa/setup QR + /auth/2fa/verify), and disable 2FA (new
  POST /auth/2fa/disable, requires a current code so a hijacked session can't
  strip the second factor).
- New API tab: create/list/revoke per-license API keys (the overnight backend
  had no UI), plaintext shown once, plus an 'API docs' button to /api/docs (Swagger).

Root-cause RBAC fix — the system-default Owner role enumerated per-resource
wildcards (server.*, wipe.*, ...) and drifted: apikeys, webhooks, alerts,
analytics, chat, schedules, notifications, map, users and ALL plugin-config
modules (plus singular plugin.* vs granted plugins.*) were locked out for any
non-super-admin Owner. Owner = full control of its license:
- migration 025 sets the Owner role to {"*": true}
- PermissionsGuard honors '*' as allow-all
- frontend hasPermission honors '*' and resource.* wildcards (was exact-match
  only, so wildcard-based roles silently failed)

Backend tsc + frontend build green. NOTE: migration 025 auto-applies on a fresh
DB (Saturday); the live DB needs the one-line UPDATE applied to unlock the API
tab for a non-super-admin owner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 08:57:17 -04:00
Vantz Stockwell
57858a1e1c feat(agent): systemd service install/uninstall subcommands (alpha.11)
All checks were successful
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 1m34s
Build Host Agent (Rust) / build (push) Successful in 1m44s
CI / integration (push) Successful in 22s
For Saturday's Ubuntu host + Linux VM: 'corrosion-host-agent install' writes a
systemd unit (Type=simple — the agent already handles SIGTERM cleanly),
daemon-reloads, and enables+starts the service; 'uninstall' reverses it.

- new service.rs: pure unit_file_contents() generator (unit-tested) + Linux
  install/uninstall via systemctl; non-Linux returns a clear 'Linux only' error
  (Windows SCM is the follow-up).
- ExecStart honors the resolved --config path (default or explicit).
- Runs as root: the agent supervises game processes + their files, needs broad
  filesystem access.

cargo check + service unit test green. Tag agent-v2.0.0-alpha.11 -> CI signs ->
CDN /host-agent/alpha/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
agent-v2.0.0-alpha.11
2026-06-12 02:31:45 -04:00
Vantz Stockwell
5b323137e0 feat(auth): API-key authentication — corr_ bearer key acts as license owner
Some checks failed
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 15s
CI / agent-tests (push) Failing after 31s
CI / integration (push) Has been skipped
Closes the 'Public REST API' last mile: external callers authenticate with a
per-license API key instead of a JWT. Additive and zero-regression:

- JwtAuthGuard: a corr_-prefixed bearer token (or X-API-Key header) is
  validated via ApiKeysService.validateKey and sets request.user shaped like a
  JWT user, scoped to the key's license. JWTs are eyJ... and never collide with
  the corr_ prefix, so the existing JWT path is byte-for-byte unchanged.
- API-key calls act AS the license owner: validateKey now resolves
  license.owner_user_id so sub is a real UUID — any @CurrentUser/created_by FK
  insert works and attributes correctly. (ApiKeysModule gains the License repo.)
- PermissionsGuard: is_api_key principals get full access to their own license
  (always tenant-scoped). Future: scoped/read-only keys.

Backend tsc green. Untested at runtime (no local DB) — needs a curl smoke test
on Saturday's fresh stack before the roadmap item flips to shipped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 02:26:59 -04:00
Vantz Stockwell
4d455918f5 docs(roadmap): check off webhooks + API key management (API access -> in progress)
All checks were successful
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 45s
CI / integration (push) Successful in 21s
Webhook events and per-license API key management shipped (commits 55c9893,
0effaaf, a1768bd). Moved 'API access and integrations' to in-progress with
per-item notes; key-authenticated external API access is the remaining piece.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 02:22:28 -04:00
Vantz Stockwell
a1768bdd2a feat(wipes): report wipe status from agent reply + wipe_completed webhook; harden webhook delivery against SSRF
All checks were successful
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 15s
CI / agent-tests (push) Successful in 45s
CI / integration (push) Successful in 21s
Wipe status reporting (closes the wipe_history-stays-pending gap):
- triggerWipe now dispatches the wipe non-blocking (a wipe is stop+delete+
  start, up to a minute+) and records the outcome from the agent's reply:
  status -> success/failed, started_at/completed_at, error_message. The row
  used to be created 'pending' and never advance, so history lied.
- On success, fires the third webhook event: 'wipe_completed'
  (server_down + player_banned shipped in 0effaaf).

SSRF hardening (security review HIGH on webhook delivery):
- new common/ssrf-guard.ts: resolve the URL host and reject private /
  loopback / link-local / reserved (v4 + v6, incl. 169.254.169.254 metadata,
  IPv4-mapped, fc00::/7, fe80::/10). http/https only.
- Applied at storage (create/update -> early 400) AND immediately before each
  delivery (DNS-rebinding/TOCTOU). fetch uses redirect:'manual' so a 3xx
  can't bounce delivery to an internal host; a redirect is a failed delivery.
- Verified IP range math + IPv6 bracket-strip (URL keeps '[::1]') empirically.

Backend tsc green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 02:20:24 -04:00
Vantz Stockwell
0effaaf86c feat(api): outbound webhooks — server_down + player_banned events
Some checks failed
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 15s
CI / agent-tests (push) Failing after 30s
CI / integration (push) Has been skipped
Roadmap 'Webhook events': per-license outbound webhooks with HMAC-SHA256
signatures (X-Corrosion-Signature), 5s timeout, fire-and-forget (a webhook
failure never breaks the triggering action), last_delivery_at/last_status
tracked.

- migration 024_webhooks; Webhook entity (events as simple-array);
  WebhooksModule (@Global, exports WebhooksService) wired into app.module;
  CRUD controller (license-scoped, webhooks.view/manage).
- Hooked events: players.performAction ban -> 'player_banned';
  host-agent-consumer going-offline + staleness sweep -> 'server_down'.
- 'wipe_completed' event lands next (needs wipe status from the agent reply).

Backend tsc green. Migration applies on a fresh DB (Saturday).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 02:13:13 -04:00
Vantz Stockwell
55c9893131 feat(api): per-license API key management + roadmap sync
All checks were successful
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 15s
CI / agent-tests (push) Successful in 46s
CI / integration (push) Successful in 22s
API keys (roadmap: 'API key management per license'):
- migration 023_api_keys; ApiKey entity; ApiKeysModule (@Global, exports
  ApiKeysService) wired into app.module.
- Service: create (corr_<prefix>_<secret>, returns plaintext once, stores
  sha256 hash + prefix), list (no hash), revoke, and validateKey(rawKey) ->
  { license_id } for a future API-key auth guard. Controller license-scoped +
  RBAC (apikeys.view/manage).

Roadmap: moved the shipped multi-game items (multi-instance host runtime,
per-game wipe + event scheduling) into a 'Phase 2 — Multi-game runtime' shipped
group; Dune/Conan/Soulmask Formulae stay in-progress.

Backend tsc + frontend build green. Migration applies on a fresh DB (Saturday host).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 02:04:41 -04:00
Vantz Stockwell
62bc9cd2a3 feat(wipes): wire the auto-wiper — scheduled wipes now actually fire
Some checks failed
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 15s
CI / agent-tests (push) Failing after 30s
CI / integration (push) Has been skipped
wipe_schedules rows existed but nothing read or fired them — an operator could
set a wipe schedule and it would never trigger (the headline auto-wipe feature
was inert; the manual trigger worked, the scheduler did not).

- WipesService now implements OnModuleInit/OnModuleDestroy with a 60s executor
  (mirrors SchedulesService): bootstraps next_scheduled_run, then fires every
  active schedule whose next_scheduled_run <= now via triggerWipe(...'scheduled')
  -> instancesService.wipeForLicense -> the agent wipe handler, advancing
  next_scheduled_run from the cron each cycle (advances even on failure so a
  broken schedule can't re-fire every 60s).
- triggerWipe parameterized with triggerType ('manual' | 'scheduled') so
  wipe_history records the real origin.
- Extracted nextCronDate into src/common/cron.util.ts (shared by the event and
  wipe schedulers; was duplicated/private). Cron is evaluated UTC — the per-
  schedule timezone column is still not honored, a known limitation shared by
  both schedulers (follow-up: tz-aware cron lib).

Backend tsc green. Scheduling logic is at parity with the in-production event
scheduler; live end-to-end (a scheduled wipe deleting real files) verifies when
a game stack + agent are connected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 01:50:49 -04:00
Vantz Stockwell
e23b6a7e69 feat(brand): chemistry rebrand across panel + marketing
Some checks failed
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Failing after 34s
CI / integration (push) Has been skipped
The logged-in panel is now Catalyst Console (by Corrosion); the marketing site
keeps Corrosion as the platform/company and introduces the lexicon.

- Wordmark: panel/auth Logo lockup -> 'Catalyst' / 'by Corrosion'; the shared
  C-core house mark (CorrosionMark) is untouched. Marketing nav/footer keep the
  'Corrosion' wordmark.
- Titles: panel routes -> '{View} · Catalyst'; auth -> Catalyst; document.title
  fallback + index.html -> 'Catalyst Console'. Marketing titles stay '— Corrosion'.
- Host agent user-facing copy -> 're-Agent' across panel + marketing (the
  binary filename / CDN URLs / config paths / domains are UNCHANGED — that's the
  separate infra/binary-rename sprint; 'Download re-Agent' fetching
  corrosion-host-agent-* is the intended intermediate state).
- Deploy-recipe 'blueprint/template' -> 'Formula/Formulae' in marketing + roadmap;
  Rust in-game 'blueprint wipe' kept (game term).
- docs/BRANDING.md added (Oracle review + locked lexicon).

vue-tsc + vite green; rendered clean both faces (Catalyst panel / Corrosion
marketing), 0 console errors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 01:19:01 -04:00
Vantz Stockwell
215355d1cb fix(security): prevent RCON command injection in player kick/ban/unban (HIGH)
Some checks failed
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Failing after 29s
CI / integration (push) Has been skipped
Player id and ban reason flowed unsanitized into the single-line RCON command,
so a control char (newline/CR) in 'reason' could break the framing and inject a
second console command — an RBAC-escalation vector (a Moderator-role user could
run arbitrary RCON via the ban reason field).

- validate player id against a safe token charset /^[A-Za-z0-9_.:-]{1,64}$/ and
  reject otherwise (multi-game safe — not a Rust-only SteamID64 regex, so
  Conan/Funcom and Dune ids still pass)
- strip C0 control chars from reason, collapse whitespace, cap at 200 chars
- coerce ban duration to a non-negative integer

Flagged by automated commit security review. Backend tsc green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 22:36:44 -04:00
Vantz Stockwell
440474290b feat: wire the panel command surface to the live Rust agent + wipe handler
All checks were successful
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 15s
CI / agent-tests (push) Successful in 1m35s
Build Host Agent (Rust) / build (push) Successful in 1m48s
CI / integration (push) Successful in 23s
The legacy Go agent was never deployed, so the entire backend command surface
published to a dead cmd.server/cmd.wipe/files.cmd void. Route it all to the
Rust agent's instance-scoped subjects.

Agent (corrosion-host-agent, alpha.10):
- New src/wipe.rs + 'wipe' func on {instance}.cmd: stop -> delete game files by
  type (map/blueprint/full, with optional backup) -> restart. Jailed to the
  instance root, symlink-safe (lstat, no cross-boundary follow — Lesson 26).
  8 tests incl. jail-escape + symlink-skip proofs. Agent suite 64 tests green.

Backend (NestJS):
- InstancesService is now @Global with license-scoped convenience wrappers
  (lifecycleForLicense/rconForLicense/writeFileForLicense/readFileForLicense/
  deleteFileForLicense/wipeForLicense) + resolveDefaultInstance (license ->
  primary instance).
- Routed to the agent: servers start/stop/restart/command; players kick/banid/
  unban via RCON; schedules restart/announce/command/plugin-reload; wipes ->
  wipeForLicense (real wipe now); plugins reload/unload/upload via rcon+file
  ops; all 9 plugin-config module applies -> writeFileForLicense + oxide.reload
  rcon, imports -> readFileForLicense (server:// prefix stripped).
- Honestly gated (need agent funcs not yet built): server deploy-from-panel,
  Oxide install, one-click uMod install -> 503 coming-soon instead of dead
  publishes.

Backend tsc green; agent cargo test green (64).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
agent-v2.0.0-alpha.10
2026-06-11 22:30:18 -04:00
Vantz Stockwell
6f783bfac8 feat(panel): Beta sweep — multi-game coherence, honesty, UX fixes
All checks were successful
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 15s
CI / agent-tests (push) Successful in 45s
CI / integration (push) Successful in 22s
Multi-game rebrand (no more Rust-only leftovers): game-neutral setup wizard +
deploy/store defaults; player-id labels driven by game profile (Steam ID only
for Rust); blueprint wipe type + verify-plugins gated to uMod games; oxide
command examples + Rust-only plugin pages (AutoDoors/FurnaceSplitter/BetterChat)
guarded behind mods==='umod' with empty-states for other games.

Honesty: webstore checkout shows coming-soon (backend now 503s); 'integrated
webstore' marketed as coming-soon; Discord references neutralized to
community/webhook; migration FAQ marked in-development; analytics dev phase
labels removed; Network pricing tier set to Custom/Contact (was a confusing
duplicate of Operator); docs/PRICING.md rewritten to match live subscriptions.

UX/bugs: fixed ServerView oxide-status operator-precedence bug; dead 'Deploy
server' button wired; non-functional topbar search removed; alert()/confirm()
replaced with toasts across schedules/alerts/migration/public store+server;
analytics chart arrays null-guarded; production console.logs gated to DEV.

Frontend build (vue-tsc + vite) green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 22:06:10 -04:00
Vantz Stockwell
f2ea415840 fix(api): Beta hardening — real 500 fix, encryption guard, honest payments
All checks were successful
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 15s
CI / agent-tests (push) Successful in 1m36s
CI / integration (push) Successful in 23s
- analytics: getMapAnalytics queried map.name but the map_library column is
  display_name (no name column) — every map-analytics call 500'd. Fixed select
  + groupBy to map.display_name.
- setup: guard ENCRYPTION_KEY length before AES-256-GCM createCipheriv — an
  unset key crashed bare-metal setup with an opaque 'Invalid key length' 500;
  now returns a clear 503. Also stop falsely marking bare-metal connected on
  completeSetup; leave offline until the agent's first heartbeat.
- webstore: public checkout returned a FAKE PayPal order token + sandbox URL
  that resolves to nowhere. Refuse honestly with 503 (payments coming soon)
  instead of faking a transaction.
- store: module purchase wrote a fake txn_<ts> implying a charge; record it
  honestly as a free Beta grant (transaction_id=beta-free-grant, amount 0).

Backend tsc green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 21:53:22 -04:00
Vantz Stockwell
d13f2cb8b1 feat(host-agent): Phase 2 — Dune docker-compose adapter via Supervisor trait
Some checks failed
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 15s
CI / agent-tests (push) Failing after 35s
CI / integration (push) Has been skipped
Build Host Agent (Rust) / build (push) Successful in 1m45s
Introduce a Supervisor trait (async-trait) so the agent manages games with
different models behind one wire contract. ProcessSupervisor (spawned process:
rust/conan/soulmask) and the new DockerComposeSupervisor (dune) both impl it;
Agent.supervisors is now HashMap<String, Arc<dyn Supervisor>> and instancecmd
dispatch is game-agnostic — start/stop/restart/status identical across games,
selected by a per-game factory in main. InstanceState moved to the shared
supervisor module.

DockerComposeSupervisor drives docker-compose up-d / stop / restart against
the instance's compose project, with -f/-p/single-service support and a
configurable compose binary. New [instance.docker_compose] config block.
First cut = lifecycle + cached state; container crash-detection + restart
adoption deferred to Phase 3b (reconcilable with a compose ps probe).

Trait choice (dyn over enum) per Commander: scales to future planes (kubectl,
AMP/podman, SSH) as new struct+impl, no central match.

56 tests green (6 new docker-compose mock-binary tests + 5 refactored process
tests), zero warnings. Live verification pending a real Dune stack.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
agent-v2.0.0-alpha.9
2026-06-11 21:33:00 -04:00
Vantz Stockwell
651a35d4be docs(reference): import Dune: Awakening server-manager references
All checks were successful
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 15s
CI / agent-tests (push) Successful in 39s
CI / integration (push) Successful in 22s
Phase 2 references for the host-agent Dune adapter, moved out of volatile /tmp
into docs/reference-repos/ (per Commander). Three upstream projects, .git +
node_modules + compiled binaries stripped (16MB source). Nested AI-instruction
files (.claude/, CLAUDE.md) removed so they don't pollute Corrosion sessions.

- icehunter/    dune-admin (Go+React) — 4 control planes; SETUP_DOCKER.md is the
                closest analog to our agent's Dune docker control plane (compose
                lifecycle, docker logs, RabbitMQ-via-exec, dune Postgres schema)
- adainrivers/  Rust/Tauri desktop — SSH+k8s BattleGroup control, maintenance
                daemon, in-game admin console (Rust idiom reference)
- the4rchangel/ Node web UI replacing battlegroup.bat — matches the Commander's
                Hyper-V self-host path + game-config schema

See docs/reference-repos/README.md for the full index + how we use each.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 21:08:05 -04:00
Vantz Stockwell
0715492ddf chore(panel): fleet-aware shell footer + drop dead vuefinder dep
All checks were successful
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 14s
CI / agent-tests (push) Successful in 49s
CI / integration (push) Successful in 22s
COA-B cleanup:
- Sidebar agent-health footer now reads the fleet store (host count / online
  count / per-host status + last heartbeat) instead of the single legacy
  server.connection row, which disagreed with the multi-host fleet. Removed the
  legacy useServerStore dependency from the shell.
- Removed the unused 'vuefinder' dependency (replaced by the native file
  manager): dep + main.ts plugin/CSS registration. Main JS chunk 588kB -> 165kB.

Recon reclassified the 'dead cmd.server v1' item: it is the LIVE license-level
command path (module config applies, plugin install, schedules, legacy
start/stop) served only by the Go agent — a Rust-agent parity gap, not dead
code. Left intact.

Build-green (vue-tsc) + boots clean in-browser (0 console errors).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 21:04:09 -04:00
Vantz Stockwell
4ef5db5b0d feat(panel): drive active game from deployed fleet instances
All checks were successful
CI / backend-types (push) Successful in 8s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 40s
CI / integration (push) Successful in 23s
The shell skin / sidebar nav / dashboard terminology now follow the games
actually deployed (game_instances.game, agent-reported) instead of a
localStorage-only toggle. syncActiveGameFromFleet() derives: one game ->
auto-skin to it; zero/multiple -> 'all' neutral. A manual GameSwitcher pick
persists and overrides the heuristic. Wired into DashboardLayout via a watch
on the fleet store.

No schema change: a license's games are the distinct games of its instances
(the normalized source of truth) — deliberately not duplicating into a
licenses.game column that would drift (Lesson 20).

Build-green (vue-tsc) + boots clean in-browser (0 console errors, theming
initializes). Authenticated auto-derive confirms live on next instance deploy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 20:51:36 -04:00
Vantz Stockwell
bb71763714 docs: Lesson 28 — base64-encode multi-line CI secrets (minisign signing key)
All checks were successful
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 39s
CI / integration (push) Successful in 21s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 20:38:56 -04:00
Vantz Stockwell
f18b45e3f2 fix(ci): base64-decode minisign secret key (CI mangles multi-line); bump alpha.8
Some checks failed
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 1m30s
CI / integration (push) Failing after 13s
Build Host Agent (Rust) / build (push) Successful in 1m45s
The 'Sign artifacts' step failed on alpha.7 with 'Error while loading the
secret key file' (exit 2): minisign downloaded and ran, but the reconstructed
key file was unparseable. A minisign secret key is two lines (comment + base64
blob); Gitea/act_runner secret storage mangles the embedded newline, collapsing
it to one line. Decode the secret as base64 (single-line, mangling-proof) with
auto-detect fallback to a raw two-line key. Fails loudly with the fix command
if the secret is neither form.

Requires re-storing MINISIGN_SECRET_KEY as: base64 < secret.key | tr -d '\n'

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
agent-v2.0.0-alpha.8
2026-06-11 20:31:48 -04:00
Vantz Stockwell
702de24e28 fix(ci): fetch minisign static binary (not in bullseye apt); bump alpha.7
Some checks failed
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 15s
CI / agent-tests (push) Successful in 43s
Build Host Agent (Rust) / build (push) Failing after 1m33s
CI / integration (push) Successful in 22s
alpha.6 signing failed: 'E: Unable to locate package minisign' —
minisign isn't packaged for node:20-bullseye. Download the official
static linux binary instead. Forward to alpha.7 (alpha.6 published
nothing).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
agent-v2.0.0-alpha.7
2026-06-11 20:18:08 -04:00
Vantz Stockwell
6b3e805ac2 feat(host-agent): Phase 3a signed self-update (minisign) + CI signing gate
Some checks failed
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 1m27s
CI / integration (push) Successful in 21s
Build Host Agent (Rust) / build (push) Failing after 1m33s
Agent only ever runs a binary whose minisign signature verifies against
the EMBEDDED public key. NATS host.cmd func 'update' {url}: download
binary + .minisig from the CDN -> verify against embedded pubkey ->
atomic swap (.old rollback) -> relaunch. URL allowlist (https + cdn.
corrosionmgmt.com only, rejects userinfo-bypass), 100MiB cap. Closes the
supply-chain hole: even a malicious CDN upload can't run unsigned.

CI: build-host-agent.yml signs every artifact with MINISIGN_SECRET_KEY
(Gitea secret) and publishes .minisig alongside; the step FAILS the
build if the secret is absent (refuses to ship unsigned). Bumped to
alpha.6.

6 deterministic tests (accept valid / reject tampered+garbage+empty sig,
URL allowlist incl userinfo-bypass, atomic swap+rollback). Fixtures
signed with the real release key so tests need no key at runtime. Full
suite 50/50 green; musl + native build clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 20:00:36 -04:00
Vantz Stockwell
7c84912ff5 chore(frontend): bump version 1.0.0 -> 1.0.1
All checks were successful
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 50s
CI / integration (push) Successful in 28s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 19:38:52 -04:00
Vantz Stockwell
355a53f6e3 feat(files): native instance-scoped file browser (replaces broken VueFinder)
All checks were successful
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 42s
CI / integration (push) Successful in 22s
FileManagerView rewritten as a native DS browser on the per-instance
file bridge: instance selector, breadcrumb nav, dir-first listing
(name/size/modified), folder drill-down, inline file editor (read/save),
toolbar (new folder/file/refresh), per-row rename + delete-confirm.
New files store wraps the /instances/:id/files* endpoints. VueFinder
import + RemoteDriver fully removed — no more retired-protocol /api/files.
Honest empty (no instance -> Server page) + error (retry) states, never
the global error boundary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 19:31:01 -04:00
Vantz Stockwell
589516a021 feat(api): complete per-instance file op-set (delete/rename/mkdir/mkfile/move/copy)
All checks were successful
CI / backend-types (push) Successful in 8s
CI / frontend-build (push) Successful in 15s
CI / agent-tests (push) Successful in 54s
CI / integration (push) Successful in 25s
Rounds out the per-instance file bridge to the agent's full jailed file
manager so a real file browser can be built on it: POST :id/files/
{delete,rename,mkdir,mkfile,move,copy}, all via requestScoped (license-
scoped reply) on the new agent {op,path} protocol. files.manage. The
broken legacy VueFinder /api/files (retired Go fm_* protocol, wrong
subject, default _INBOX) is superseded by this — frontend rewrite next.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 19:24:31 -04:00
Vantz Stockwell
f60e6abd33 feat(server): config file editor — read/edit/save a host config file per instance
All checks were successful
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 44s
CI / integration (push) Successful in 21s
The Server page's config-honesty note now leads somewhere real: a
Configuration file panel that loads a config file from the instance
(prefilled with the game's primaryConfigFile hint — server.cfg,
ServerSettings.ini, GameXishu.json), edits it in a mono textarea, and
saves it straight to the host through the jailed agent file bridge.
Not-found is handled gracefully (empty editor to create). Works across
games; gameProfiles gains primaryConfigFile per game.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 19:07:59 -04:00
Vantz Stockwell
877fadcb6c feat(api): per-instance file bridge — list/read/write via the new agent file manager
All checks were successful
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 44s
CI / integration (push) Successful in 21s
GET /api/instances/:id/files (list) + /file (read), PUT /file (write) —
tenant-guarded, routed through requestScoped to the per-instance
corrosion.{license}.{instance}.files.cmd using the new agent's {op,path}
protocol (jailed to the instance root, symlink-safe). files.view /
files.manage perms. Foundation for the per-game config editor and for
fixing the legacy VueFinder File Manager (which still speaks the retired
Go fm_* protocol on the wrong subject and is broken under per-license
auth — separate reconciliation).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 19:00:28 -04:00
Vantz Stockwell
e897a4802f fix(server): apply lifecycle reply state optimistically (heartbeat lag)
All checks were successful
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 42s
CI / integration (push) Successful in 21s
The agent reply is authoritative for the action just taken; the fleet
DB only updates on the next heartbeat (~10s), so the immediate refetch
read a stale state and reverted the UI (Start -> still Stopped). Now
apply the reply's state/uptime directly to the instance.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 18:41:19 -04:00
Vantz Stockwell
c0b20f2f78 feat(server): instance-centric controls — real per-instance state + lifecycle
All checks were successful
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 55s
CI / integration (push) Successful in 22s
The Server page now manages the selected GAME INSTANCE, not the legacy
host connection. New instances store flattens the fleet and drives the
command bridge. New 'Game instance' panel: real state badge
(running/stopped/crashed/configured), uptime, host, and an instance
selector when >1. Start/Stop/Restart/Refresh wired to POST
/api/instances/:id/lifecycle — gated on the actual instance state (not
host connectivity), with telemetry-only instances flagged. Works across
all four games (state + lifecycle are game-agnostic).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 18:37:53 -04:00
Vantz Stockwell
06e832fca1 feat(fleet): remove host — DELETE /api/fleet/hosts/:id + Fleet card action
All checks were successful
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 36s
CI / integration (push) Successful in 21s
Self-service host removal. DELETE /api/fleet/hosts/:id (server.manage,
tenant-guarded): refuses while the host is 'connected' (409 — a live
agent re-registers on its next heartbeat, stop it first), deletes the
host's game_instances explicitly (FK is SET NULL, would otherwise
orphan them; instance_stats cascade), and clears the legacy
server_connections row if it was the license's last host. Fleet view:
offline host cards get a Remove button with inline confirm + toast.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 18:21:04 -04:00
Vantz Stockwell
009ceb86ad feat(server): real agent credentials + agent.toml setup; per-game config honesty
All checks were successful
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 17s
CI / agent-tests (push) Successful in 45s
CI / integration (push) Successful in 22s
Server page Host-agent panel now fetches GET /api/servers/agent-
credentials and renders the real agent.toml (license UUID, nats_user,
nats_password) instead of the broken LICENSE_ID=license_key env
commands that would never connect. Password masked by default with a
reveal toggle; copy-to-clipboard uses the real value. Setup commands
point at --config /etc/corrosion/agent.toml.

Configuration panel: World size / Current seed (Rust-only Facepunch
concepts) gated behind isRust; Conan/Soulmask/Dune get an honest note
pointing to the File Manager for their real config files instead of
fake Rust fields.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 13:23:47 -04:00
Vantz Stockwell
6f31c41dc3 feat(api): instance command bridge + agent credentials endpoint
All checks were successful
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 43s
CI / integration (push) Successful in 21s
Backend layer wiring the panel to the host agent's per-instance command
channel (the unblocker for the Server-page rework):
- NatsService.requestScoped(): request-reply with a LICENSE-SCOPED reply
  subject (corrosion.{license}.reply.<id>) so per-license-scoped agents
  (no _INBOX permission) can actually reply — the design from the NATS
  auth work, now exercised.
- InstancesModule: POST /api/instances/:id/lifecycle {action} (start/
  stop/restart/status/steam_update, server.manage) and POST :id/rcon
  {command} (server.console). Tenant-guarded via game_instances.
- GET /api/servers/agent-credentials: derives the agent's NATS user/
  password (HMAC) so a customer can configure their agent — closes the
  post-auth setup gap.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 13:05:22 -04:00
Vantz Stockwell
99433a09d1 docs(claude): Lesson 27 — lint infra config before deploy; compose up -d recreates changed deps
All checks were successful
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 42s
CI / integration (push) Successful in 22s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 12:53:06 -04:00
Vantz Stockwell
b442ef4102 fix(api): consumer rejects malformed heartbeats with no host block (no phantom hosts)
All checks were successful
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 41s
CI / integration (push) Successful in 21s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 12:49:53 -04:00
Vantz Stockwell
856106174a fix(nats): no_auth_user is top-level, not inside authorization{} — broke broker startup
All checks were successful
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 43s
CI / integration (push) Successful in 22s
Caught during the live cutover: nats-server rejects 'unknown field
no_auth_user' when it is nested in the authorization block, taking the
whole broker down. Both the generator (open stage) and the committed
bootstrap default emitted it nested. Moved to top level. Enforce-stage
output was unaffected (no no_auth_user), which is what the live broker
now runs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 12:47:14 -04:00
Vantz Stockwell
463908b18e fix(nats): security review — secure-by-default + per-tenant inbox isolation
All checks were successful
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 43s
CI / integration (push) Successful in 23s
Two HIGH findings from automated review on the generator, both fixed:
1. Cross-tenant inbox access: per-license users were granted _INBOX.>,
   letting license A subscribe to license B's request-reply responses.
   Now scoped to corrosion.{license}.> ONLY; replies must ride the
   license namespace (corrosion.{license}.reply.<id>) — documented in
   PROTOCOL.md. Agent unchanged (responds to msg.reply); constraint is
   on the requester (internal user has full >).
2. Default-open auth bypass: generator defaulted to stage=open with a
   full-access anonymous user — a stale regen left the broker wide open.
   Now defaults to enforce (secure by default); the explicit 'open'
   migration stage maps anonymous to a harmless corrosion.unclaimed.>
   namespace, never real tenant subjects. Committed bootstrap default
   hardened the same way.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 12:39:31 -04:00
Vantz Stockwell
00cff51ce5 feat(nats): per-license auth mechanism — agent user/password, scoped broker, generator (non-breaking)
All checks were successful
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 17s
CI / agent-tests (push) Successful in 1m23s
Build Host Agent (Rust) / build (push) Successful in 1m38s
CI / integration (push) Successful in 23s
Closes the open broker (anonymous publish to any tenant's corrosion.*).
Per-license isolation via NATS user/password + subject permissions:
each license -> user=license_id, password=HMAC-SHA256(license_id,
NATS_TOKEN_SECRET), scoped to corrosion.{license_id}.> + _INBOX. Backend
uses a privileged internal user.

- Agent (alpha.5): nats_user/nats_password config + env, user_and_password
  auth; falls back to token/anonymous (transition-safe)
- Backend: connects with NATS_INTERNAL_USER/PASSWORD when set, else anon
- scripts/generate-nats-auth.mjs: regenerates nats-auth.conf from the
  licenses table; NATS_AUTH_STAGE=open keeps a no_auth_user fallback
  (verify creds first), =enforce rejects anonymous
- committed nats-auth.conf is the SAFE OPEN default (no secrets); the
  host copy carries real users and is not committed
- compose: NATS_INTERNAL_USER/PASSWORD/NATS_TOKEN_SECRET, mount nats-auth.conf

Entirely non-breaking until secrets+config deployed; staged cutover next.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
agent-v2.0.0-alpha.5
2026-06-11 12:33:27 -04:00
Vantz Stockwell
7a07d600e7 feat(fleet): Phase B — fleet overview UI + GET /api/fleet read endpoint
Tenant-scoped fleet read: GET /api/fleet returns agent_hosts (host
metrics) each with their game_instances, plus a summary
(host/instance/online counts). FleetView lists host cards (status, CPU/
mem/disk/uptime/last-heartbeat) with their instances (game, state badge,
uptime); honest empty state -> Server page when no hosts. New 'Fleet'
sidebar nav item across all four game profiles, /fleet route. Store
follows the no-throw-on-fetch pattern (error state, never bricks). The
marketing hero made real from the live fleet tables.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 12:32:55 -04:00
Vantz Stockwell
4a4ae7a5d4 docs(claude): Lesson 26 — jail-at-entry doesn't jail the recursive walk (security review caught what my review missed)
All checks were successful
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 41s
CI / integration (push) Successful in 21s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 12:04:23 -04:00
Vantz Stockwell
930f655bf5 feat(api): fleet data model Phase A — License -> Host -> Instance
All checks were successful
CI / backend-types (push) Successful in 14s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 42s
CI / integration (push) Successful in 22s
Migration 022 adds agent_hosts / game_instances / instance_clusters /
instance_stats (named agent_hosts to avoid the existing B2B hosts
table). HostAgentConsumerService now parses the full v2 heartbeat and
upserts an agent_hosts row (host metrics: cpu/mem/disk/agent version,
keyed by license_id+hostname until enrollment) plus one game_instances
row per heartbeat instance entry (state + uptime, the billing unit).
Legacy server_connections write retained so the current panel keeps
working — additive migration, nothing breaks. Staleness sweep + offline
beacon now flip agent_hosts too. cluster_id FK reserved for Soulmask/
Dune. Migration applied to live DB; tsc green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 12:00:52 -04:00