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>
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>
Nothing persisted agent heartbeats before: companion_last_seen was
written once at setup and connection_status stayed 'connected' forever.
HostAgentConsumerService now consumes corrosion.*.host.heartbeat
(updates last_seen + status, auto-creates the bare_metal connection row
on first contact), host.going_offline (graceful offline), and sweeps
connections offline after 180s of heartbeat silence. License-existence
tenant validation with caching per NATS-consumer doctrine. WS bridge
forwards host_heartbeat/host_going_offline to the panel.
Contract-verified against production NATS with the backend's own nats
lib: v2 subjects, schema 2, real telemetry, offline beacon.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Real @Public() NestJS endpoint persisting to the existing early_access_signups table (email + server_count), matching the schema exactly (no migration). Duplicate-email safe (pre-check + unique-constraint catch -> friendly success). Wired into app.module. Makes the marketing early-access form functional end-to-end on next API deploy. tsc/nest build green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Migration 021: raidablebases_configs table with JSONB config_data
- Entity, module, controller (7 endpoints), service with NATS deploy/import
- Frontend: 4-tab editor (General, Difficulty, NPC, Loot & Rewards)
- Pinia store, types, router route, sidebar nav with Swords icon
- Top 30 most common settings with actual RaidableBases.json key paths
- Difficulty sub-tabs for Easy/Medium/Hard/Expert/Nightmare with spawn day toggles
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- DB migrations 017 (betterchat_configs) and 020 (timedexecute_configs) applied
- TypeORM entities matching production schema exactly
- NestJS modules with full CRUD + apply-to-server + import-from-server
- Pinia stores following teleport config pattern
- BetterChatView: Chat Groups editor with color pickers, font sizes, format strings; Settings tab with word filter, anti-flood, player tagging
- TimedExecuteView: TimerRepeat with presets, RealTime-Timer, OnConnect/OnDisconnect command lists
- Wired into app.module.ts, router, DashboardLayout nav
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Seven endpoints for managing NTeleportation configs: list summaries,
get full config, create, update, delete, deploy to server via NATS,
and import live config from server. Follows loot module pattern.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements GET/POST /api/files proxy for all VueFinder operations
(list, search, preview, download, delete, rename, move, copy, mkdir,
new-file, save, upload). Routes via NATS request-reply to the companion
agent on corrosion.{license_id}.files.cmd with 30s timeout. Gated
behind files.view (GET) and files.manage (POST) permissions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: super_admin JWT returned early with no license_id, causing
@CurrentTenant() to pass undefined to every tenant-scoped service query.
- jwt.strategy: Move license lookup before super_admin early return so
admins who own licenses get their license_id in the JWT payload
- CurrentTenant decorator: Throw 401 with clear message when license_id
is undefined instead of letting undefined cascade into TypeORM queries
- Wipe store: Fix 6 wrong routes (/profiles → /wipes/profiles, etc.)
and remove redundant manual license_id guards
- Changelog module: Add stub controller/service returning empty array
to eliminate 404 on /api/changelog
- ChangelogView: Handle both array and {entries} response shapes
- AGENTS.md: Streamlined 3-tier roster (Opus/Sonnet/Haiku)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>