Milestone 1 Complete: Control Plane Skeleton and CI Pipeline Live

  • Home /
  • Posts /
  • Milestone 1 Complete: Control Plane Skeleton and CI Pipeline Live

Milestone 1 of the ggscale MVP is complete. This is the foundation everything else builds on.

What Shipped

Control plane skeleton. ggscale-server is a single Go binary using the chi router, slog structured JSON logging, and Prometheus metrics at /metrics. It starts in under 200ms and exposes GET /v1/healthz returning {"status":"ok","version":"v1","commit":"<sha>"}.

Postgres + Row-Level Security. Migration 0009_rls.up.sql enables RLS on every tenant-scoped table with a single policy: tenant_id = current_setting('app.tenant_id', true)::bigint. The , true flag makes a missing GUC return NULL, so the policy fails closed. Combined with the HTTP middleware that injects tenant_id into every authenticated request, this gives us defence-in-depth tenant isolation from day one.

K3s + Agones smoke test. The CI e2e job starts the full compose stack including a single-node K3s cluster and an Agones install. The smoke test (TestAgonesAllocation_AssignsHostPort_ReachableViaUDP) allocates a GameServer CRD, waits for Status.State == Ready, reads the dynamic UDP port, and asserts the simple-game-server image echoes a packet back. This is the parity contract the rest of the fleet work builds on.

CI pipeline. Three jobs on ubuntu-24.04: lint-test (golangci-lint, go test -race, govulncheck), docker-build (multi-stage, exported as artifact), and e2e (loads the artifact, starts the k8s compose profile, runs the smoke test). Action versions are SHA-pinned; workflow permissions default to contents: read.

Olric embedded cache. CACHE_BACKEND=olric starts an embedded Olric cluster in-process across the app VMs in each region. Rate limiting and connection-cap state are shared regionally without a separate cache service. CACHE_BACKEND=memory (default) covers dev, single-VM self-host, and integration tests.

Where This Sits in the Roadmap

    
flowchart LR
    M1["Milestone 1: Foundations
(shipped)"] --> M2["Milestone 2: Tenanted API
(in progress)"] M2 --> M3["Milestone 3: Realtime + Fleet"] M3 --> M4["Milestone 4: Billing + Compliance"] M4 --> M5["Milestone 5: Public Beta"] M1 -. delivers .-> D1["control plane skeleton
Postgres + RLS
K3s + Agones smoke
CI pipeline"] M2 -. delivers .-> D2["auth, storage, leaderboards
tenant isolation
rate limiting"] classDef done fill:#d4edda,stroke:#28a745,color:#000 classDef active fill:#fff3cd,stroke:#ffc107,color:#000 class M1 done class M2 active

What’s Next: Milestone 2

Milestone 2 adds the tenant-gated API surface:

  • POST /v1/auth/signup, /v1/auth/login, /v1/auth/refresh, /v1/auth/logout
  • /v1/storage/*: tenant-isolated key-value blob storage
  • /v1/leaderboards/*: per-project ranked leaderboards
  • internal/tenant HTTP middleware: extracts Authorization: Bearer <api_key>, hashes SHA-256, looks up api_keys table, injects tenant_id into context
  • HTTP rate limiting (sliding window per API key via Olric/memory token bucket) and WebSocket connection caps

The test-first rule applies to all of Milestone 2: every endpoint has a failing integration test before any implementation lands.