303 lines
11 KiB
Markdown
303 lines
11 KiB
Markdown
# HexaHost GameCloud — Implementation Status
|
|
|
|
Last updated: 2026-06-26
|
|
|
|
## Current phase: WHMCS hardening — mTLS + Product mappings ✅
|
|
|
|
### mTLS integration API
|
|
|
|
| Area | Status | Notes |
|
|
|------|--------|-------|
|
|
| Optional enforcement | Done | `INTEGRATION_MTLS_ENABLED=true` |
|
|
| Fingerprint binding | Done | Per `WhmcsInstallation` |
|
|
| Guard | Done | HMAC + mTLS layered |
|
|
| API | Done | `GET/PUT .../mtls/*` |
|
|
| PHP clients | Done | Addon + server module curl mTLS |
|
|
| nginx example | Done | `deploy/nginx/integration-mtls.conf.example` |
|
|
| Docs | Done | `docs/integrations/whmcs/mtls.md` |
|
|
|
|
### Product mapping UI
|
|
|
|
| Area | Status | Notes |
|
|
|------|--------|-------|
|
|
| Addon mappings page | Done | WHMCS product → GameCloud plan |
|
|
| Validation API | Done | `POST /catalog/validate-plan` |
|
|
| WHMCS table | Done | `mod_hexagamecloud_product_mappings` |
|
|
| Server module hook | Done | `ProductMapping.php` resolver |
|
|
| Docs | Done | `docs/integrations/whmcs/product-mappings.md` |
|
|
|
|
### Abnahmekriterium
|
|
|
|
- [x] Addon maps WHMCS products to validated GameCloud plan slugs
|
|
- [x] Provisioning uses mapping when active
|
|
- [x] mTLS can be enabled without breaking dev (HMAC-only default)
|
|
- [ ] Manual E2E: nginx mTLS + WHMCS provision
|
|
|
|
---
|
|
|
|
## WHMCS Addon Dashboard ✅
|
|
|
|
### WHMCS Addon module completed
|
|
|
|
| Area | Status | Notes |
|
|
|------|--------|-------|
|
|
| Addon PHP module | Done | Dashboard, events, reconciliation UI |
|
|
| Module tables | Done | `mod_hexagamecloud_*` on activation |
|
|
| Event poller | Done | Manual + daily cron hook |
|
|
| Dashboard API | Done | `GET /integrations/whmcs/dashboard/stats` |
|
|
| Cron hook | Done | `integrations/whmcs/hooks/hexagamecloud.php` |
|
|
| Docs | Done | `docs/integrations/whmcs/addon-module.md` |
|
|
|
|
### Billing providers
|
|
|
|
| Provider | Status | Notes |
|
|
|----------|--------|-------|
|
|
| WHMCS | Done | **Production default** (`BILLING_PROVIDER=whmcs`) |
|
|
| Internal | Done | Dev / free tier credits |
|
|
| Stripe | Stub | Optional fallback webhook — inactive unless `BILLING_PROVIDER=stripe` |
|
|
|
|
See `docs/billing/providers.md`.
|
|
|
|
### Abnahmekriterium Addon
|
|
|
|
- [x] WHMCS admin dashboard shows API health and sync stats
|
|
- [x] Event poll + ack from addon UI and cron hook
|
|
- [x] Reconciliation dry-run from addon UI
|
|
- [ ] Manual E2E: activate addon on WHMCS host
|
|
|
|
---
|
|
|
|
## WHMCS Phase E + F — Sync & Usage Billing ✅
|
|
|
|
### WHMCS Phase E completed
|
|
|
|
| Area | Status | Notes |
|
|
|------|--------|-------|
|
|
| ListAccounts API | Done | `GET /integrations/whmcs/accounts` |
|
|
| Service import | Done | `POST .../services/import` |
|
|
| Reconciliation | Done | Full + per-service, severity classification |
|
|
| Integration events | Done | Cursor poll + ack/dead-letter |
|
|
| Worker daily sync | Done | 03:00 UTC reconciliation tick |
|
|
| WHMCS PHP | Done | `ListAccounts`, reconcile/import client |
|
|
| Docs | Done | `docs/integrations/whmcs/reconciliation.md` |
|
|
|
|
### WHMCS Phase F completed
|
|
|
|
| Area | Status | Notes |
|
|
|------|--------|-------|
|
|
| Usage metrics API | Done | `GET .../services/:id/usage` |
|
|
| Usage adjustments | Done | `POST .../usage/adjustments` |
|
|
| Export audit | Done | `WhmcsUsageExport` + content hash |
|
|
| Test mode | Done | `testMode=true` → DRAFT exports |
|
|
| WHMCS PHP | Done | `MetricProvider`, `UsageUpdate` |
|
|
| Docs | Done | `docs/integrations/whmcs/usage-billing.md` |
|
|
|
|
### Abnahmekriterium Phase E
|
|
|
|
- [x] ListAccounts liefert alle verknüpften Services
|
|
- [x] Reconciliation erkennt Status-, Plan- und Orphan-Drift
|
|
- [x] Events cursorbasiert mit Ack/Dead-Letter
|
|
- [x] Import bestehender Server möglich
|
|
- [ ] Manual E2E: WHMCS Server-Sync + Reconcile-Button
|
|
|
|
### Abnahmekriterium Phase F
|
|
|
|
- [x] Metriken aus `usage_records` aggregiert (UTC-Perioden)
|
|
- [x] Idempotente Exporte mit Hash
|
|
- [x] Adjustments ohne Rohdaten-Mutation
|
|
- [x] WHMCS MetricProvider + UsageUpdate
|
|
- [ ] Manual E2E: WHMCS Usage Metrics Abrechnungslauf
|
|
|
|
---
|
|
|
|
## WHMCS Phase D — SSO ✅
|
|
|
|
### WHMCS Phase D completed
|
|
|
|
| Area | Status | Notes |
|
|
|------|--------|-------|
|
|
| `SsoTicket` model | Done | Hashed token, 60s TTL, single use |
|
|
| Impersonation sessions | Done | `UserSession.isImpersonation` + audit |
|
|
| API create SSO | Done | `POST .../services/:id/sso` |
|
|
| API consume SSO | Done | `POST /auth/sso/consume` → session cookie |
|
|
| Path allowlist | Done | Server panel routes only |
|
|
| Web SSO page | Done | `/auth/sso?ticket=` + impersonation banner |
|
|
| WHMCS PHP SSO | Done | `ServiceSingleSignOn`, `AdminSingleSignOn` |
|
|
| Docs | Done | `docs/integrations/whmcs/sso.md` |
|
|
|
|
### Abnahmekriterium Phase D
|
|
|
|
- [x] WHMCS „Server verwalten“ erzeugt einmaliges SSO-Ticket
|
|
- [x] Ticket-Einlösung setzt Session-Cookie und leitet auf Server-Panel weiter
|
|
- [x] Admin-SSO markiert Impersonation-Session sichtbar im Panel
|
|
- [x] Gesperrte/beendete Services erhalten kein SSO-Ticket
|
|
- [ ] Manual E2E: WHMCS client area → panel → server detail
|
|
|
|
### Local SSO test
|
|
|
|
1. `pnpm db:migrate && pnpm db:seed` (includes `local-dev-whmcs`)
|
|
2. Provision a service via WHMCS API (`PUT .../clients/1`, `POST .../services`)
|
|
3. `POST /api/v1/integrations/whmcs/services/{externalServiceId}/sso` (signed)
|
|
4. Open `{APP_URL}/auth/sso?ticket={token}` → redirects to `/servers/{id}`
|
|
5. Admin SSO: same flow with `kind=admin` → impersonation banner visible
|
|
|
|
See [docs/integrations/whmcs/sso.md](integrations/whmcs/sso.md).
|
|
|
|
---
|
|
|
|
## Phase 9 — WHMCS integration and production hardening ✅
|
|
|
|
### Phase 9 completed
|
|
|
|
| Area | Status | Notes |
|
|
|------|--------|-------|
|
|
| WHMCS Prisma models | Done | Installations, client/service links, idempotent operations |
|
|
| `@hexahost/integration-auth` | Done | HMAC-SHA256 request signing |
|
|
| WHMCS Integration API | Done | `/api/v1/integrations/whmcs/*` with nonce replay protection |
|
|
| Billing suspension | Done | `billingSuspendedAt`, blocks start/join-to-start |
|
|
| Billing provider mode | Done | `BILLING_PROVIDER=internal\|whmcs` |
|
|
| RFC2136 DNS provider | Done | `nsupdate` + worker DNS sync tick |
|
|
| WHMCS PHP module | Done | Create/Suspend/Unsuspend/Terminate/ChangePackage |
|
|
| Ansible playbooks | Done | `site.yml`, `control-plane.yml`, `game-node.yml` |
|
|
| Operations docs | Done | `dns.md`, `control-plane.md`, `game-node.md` |
|
|
|
|
### Abnahmekriterium Phase 9
|
|
|
|
- [x] WHMCS can provision/suspend/unsuspend/terminate via signed API
|
|
- [x] Production DNS provider abstraction with RFC2136
|
|
- [x] Documented control plane + two-node deployment path
|
|
- [x] Billing suspension enforced on panel and edge gateway
|
|
- [ ] Manual E2E: WHMCS order → GameCloud server → suspend/unsuspend
|
|
|
|
### Local WHMCS API test
|
|
|
|
1. `pnpm db:migrate && pnpm db:seed` (creates `local-dev-whmcs` installation)
|
|
2. Set `BILLING_PROVIDER=whmcs` optional for credit-bypass mode
|
|
3. Sign requests with `WHMCS_API_SECRET` from seed output
|
|
4. `PUT /api/v1/integrations/whmcs/clients/1` then `POST .../services`
|
|
|
|
### WHMCS module install
|
|
|
|
Copy `integrations/whmcs/modules/servers/hexagamecloud` to `/modules/servers/hexagamecloud` on your WHMCS host.
|
|
|
|
Server entry fields:
|
|
|
|
- Hostname: `https://api.example.net`
|
|
- Username: integration ID (`WHMCS_INTEGRATION_ID`)
|
|
- Password: API secret (`WHMCS_API_SECRET`)
|
|
|
|
---
|
|
|
|
## Phase 8 — DNS and join-to-start ✅
|
|
|
|
### Phase 8 completed
|
|
|
|
| Area | Status | Notes |
|
|
|------|--------|-------|
|
|
| Prisma join/DNS | Done | `joinSlug`, `joinToStartEnabled`, `ServerDnsRecord`, node port ranges |
|
|
| `@hexahost/dns` | Done | Slug builder, DNS record sync (DB metadata), join address assignment |
|
|
| API internal edge | Done | `GET/POST /internal/edge/v1/resolve|start/:slug` with API key + rate limits |
|
|
| Join-to-start start | Done | Edge triggers `startServerInternal` with billing/queue checks |
|
|
| Worker provision hook | Done | Assigns join slug + DNS after provision completes |
|
|
| Edge gateway (Go) | Done | TCP listener, Minecraft handshake, proxy + join-to-start |
|
|
| Web | Done | Join hostname on server card |
|
|
| Contracts | Done | `join.ts` edge resolve/start schemas, server join fields |
|
|
|
|
### Abnahmekriterium Phase 8
|
|
|
|
- [x] Stable join hostname per server (`slug.play.example.net`)
|
|
- [x] DNS metadata stored (A + SRV for Java)
|
|
- [x] Edge gateway resolves slug and can start stopped servers
|
|
- [x] Rate limiting on join-to-start (3/min per slug, 10/min per IP)
|
|
- [x] Credits/tariff checks on edge-triggered start
|
|
- [ ] Manual E2E: connect via fixed hostname while server stopped
|
|
|
|
### Local test
|
|
|
|
1. `pnpm db:migrate && pnpm db:seed`
|
|
2. Set in `.env`: `EDGE_GATEWAY_ENABLED=true`, matching `EDGE_INTERNAL_API_KEY`
|
|
3. `pnpm dev` (API + worker) and `cd apps/edge-gateway && go run ./cmd/gateway`
|
|
4. Provision server → note `joinHostname` on dashboard
|
|
5. Stop server → connect with Minecraft client to join hostname (or `slug.play.localhost` with hosts file)
|
|
6. Edge starts server and proxies connection when `RUNNING`
|
|
|
|
### Edge env defaults (`.env.example`)
|
|
|
|
- `GAME_BASE_DOMAIN=play.example.net`
|
|
- `EDGE_LISTEN_ADDR=:25565`
|
|
- `EDGE_INTERNAL_API_KEY=local-dev-edge-key-change-me-32chars`
|
|
|
|
---
|
|
|
|
## Phase 7 — Idle shutdown and free tier ✅
|
|
|
|
### Phase 7 completed
|
|
|
|
| Area | Status | Notes |
|
|
|------|--------|-------|
|
|
| `@hexahost/metering` | Done | RAM-GB-minute credits, Minecraft `list` parser |
|
|
| Prisma billing models | Done | `CreditWallet`, `CreditTransaction`, `UsageRecord` |
|
|
| Plan idle/quota fields | Done | Timeouts, countdown, `maxServersPerUser`, `queuePriority` |
|
|
| Idle controller (worker) | Done | Player poll, countdown, auto-stop |
|
|
| Usage metering (worker) | Done | 60s tick debits wallet, writes `UsageRecord` |
|
|
| API billing | Done | `GET /account/wallet`, `GET /account/usage` |
|
|
| API idle | Done | `GET /servers/:id/idle`, `POST .../idle/extend` |
|
|
| Quotas & credits | Done | Server create/start guards |
|
|
| Web | Done | Credit balance on dashboard, idle countdown on server card |
|
|
|
|
### Abnahmekriterium Phase 7
|
|
|
|
- [x] Empty running server triggers idle countdown and auto-stop
|
|
- [x] Player connection cancels countdown (via `list` poll)
|
|
- [x] Usage debited to credit wallet with ledger + usage records
|
|
- [x] Free plan grants credits on signup
|
|
- [x] Server quota per plan enforced
|
|
- [ ] Manual E2E: start server, wait idle, verify stop + usage
|
|
|
|
### Local test
|
|
|
|
1. `pnpm db:migrate && pnpm db:seed`
|
|
2. Register/login → dashboard shows **5000 credits** (free plan)
|
|
3. Create & start server → credits decrease while running
|
|
4. Leave server empty → after ~10 min idle + 60s countdown → auto-stop
|
|
5. `GET /account/usage` shows RAM-GB-minute records
|
|
|
|
### Free plan defaults (seed)
|
|
|
|
- `idleTimeoutMinutes`: 10
|
|
- `idleCountdownSeconds`: 60
|
|
- `monthlyCreditGrant`: 5000
|
|
- `maxServersPerUser`: 3
|
|
|
|
---
|
|
|
|
## Phase 6 — Multi-Node and queue ✅
|
|
|
|
---
|
|
|
|
## Phase 5 — Software & add-ons ✅
|
|
|
|
---
|
|
|
|
## Phase 4 — Worlds & backups ✅
|
|
|
|
---
|
|
|
|
## Phase 3 — Console, files, settings ✅
|
|
|
|
---
|
|
|
|
## Phase 2 — Single-Node Vertical Slice ✅
|
|
|
|
---
|
|
|
|
## Phase 1 — Authentication ✅
|
|
|
|
---
|
|
|
|
## Phase 0 — Monorepo foundation ✅
|
|
|
|
### Next phase: Post-MVP hardening
|
|
|
|
- Penetration test, configurable option mappings, IP allowlist for integration API
|