mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-10 16:04:04 +00:00
docs: record the closed root blocker and the new probe/rotation contract
docs/MATCHMAKING.md is the stated source of truth for this design, so it changes first: the probe challenge endpoint and why probing gates matching rather than merely improving it, and the key-ID rotation procedure that makes overlapping-key rotation concrete. multiplayer-next.md §0's root blocker is closed rather than deleted -- what it was, why it blocked everything, and how it was resolved, since the reasoning is what a future reader needs. Tasks 7.6, 8.15 and 8.31 updated to what actually remains, which in every case is now external rather than unbuilt. TODO.md #14 asked for a join-signing design decision; that decision is recorded with its rationale. CLAUDE.md no longer says a real deployment cannot complete a match end to end.
This commit is contained in:
+32
-18
@@ -41,21 +41,32 @@ blocker and is in progress.** It is larger than anything below and adds a
|
||||
backend service outside the Godot project. Tasks are in §7; the design is in
|
||||
[`docs/MATCHMAKING.md`](docs/MATCHMAKING.md).
|
||||
|
||||
**The current root blocker** ([#14](https://github.com/jcreek/CosmicClash/issues/14)): nothing in production ever publishes a
|
||||
player's signed match assignment. `store.SaveAssignment`/`SaveAssignments`/
|
||||
`SaveVerifiedAssignmentRoster` and `allocator.Service.PublishRoster` are
|
||||
fully built and tested in isolation, but no real code path
|
||||
(`allocator/worker.go`, `cmd/allocator`) ever calls them — only tests do, by
|
||||
seeding the table directly rather than exercising the real write path. Since
|
||||
**The former root blocker** ([#14](https://github.com/jcreek/CosmicClash/issues/14)) **is closed.** Nothing in production
|
||||
used to publish a player's signed match assignment:
|
||||
`store.SaveVerifiedAssignmentRoster` and `allocator.Service.PublishRoster`
|
||||
were fully built and tested in isolation, but no real code path called them —
|
||||
only tests did, by seeding the table directly. Since
|
||||
`AdvanceServerRegistration`'s SQL requires an `assignments` row per
|
||||
participant before a match can reach `ASSIGNMENT_READY`, **a real deployment
|
||||
cannot advance any match past `PROCESS_READY`** — no player can ever receive
|
||||
a real assignment or connect, regardless of how correct every other piece
|
||||
(including the client-side connect-wiring in task 8.41) is. See task 8.31
|
||||
for the full detail. Closing it needs new security-relevant design (a
|
||||
join-signing key shared between allocator and game server, roster-digest
|
||||
computation, per-player authorisation construction) — flagged rather than
|
||||
built, at the user's explicit direction, pending a decision on that design.
|
||||
participant before a match can reach `ASSIGNMENT_READY`, a real deployment
|
||||
could not advance any match past `PROCESS_READY`.
|
||||
|
||||
`allocator.Worker.RunOnce` now builds one signed join authorisation per
|
||||
durable participant and publishes the roster after binding the allocation, and
|
||||
`cmd/allocator` refuses to start without key material rather than stranding
|
||||
every match silently. The signing-key design that was pending a decision is
|
||||
settled: HMAC-SHA256 over the canonical claim bytes, with a **key ID** in
|
||||
those bytes so allocated servers can hold the set of currently-valid keys and
|
||||
rotation does not invalidate authorisations already issued for in-flight
|
||||
matches. See `docs/MATCHMAKING.md` §2 for the rotation procedure.
|
||||
|
||||
Two further blockers of the same shape were found and closed alongside it:
|
||||
regional RTT probing had no nonce-issuing endpoint (so no client-created
|
||||
ticket could ever be selected — the matcher requires non-empty RTT evidence),
|
||||
and the Kubernetes base deployed a control-plane image nothing built while
|
||||
building a matcher image nothing deployed. What remains for a live deployment
|
||||
is external: a Steamworks App ID and publisher key ([#15](https://github.com/jcreek/CosmicClash/issues/15)),
|
||||
custom GodotSteam builds ([#16](https://github.com/jcreek/CosmicClash/issues/16)),
|
||||
and a real cluster ([#17](https://github.com/jcreek/CosmicClash/issues/17)).
|
||||
|
||||
### Blocking sign-off — the work exists, the verification does not
|
||||
|
||||
@@ -141,7 +152,7 @@ retrofitting one.
|
||||
| 7.3 `[D:7.2]` `[P]` | Server-browser UI and `ISteamMatchmakingServers` adapter | Unimplemented until real Steam SDK/API access is available; ENet direct-IP remains the supported browser-free path meanwhile |
|
||||
| 7.4 `[D:7.2]` `[P]` | Auth tickets in `hello` → `BeginAuthSession`, Steam identity in the roster, persistent ban list | Real GodotSteam auth integration, server-side VAC state, durable ban storage remain. **Fixes known defect C** for direct/community servers once landed |
|
||||
| 7.5 `[D:7.2]` `[P]` | `SteamBootstrap` gating (stock builds keep ENet, explicit Steam selection fails closed) | Custom Steam client/server export smoke remains blocked on pinned GodotSteam binaries |
|
||||
| 7.6 `[D:7.4]` | Backend `AuthCoordinator`, session persistence, `ControlPlaneClient.login_steam()` | Real Steam `BeginAuthSession`/`EndAuthSession` adapter, login UI, live PostgreSQL/session integration remain |
|
||||
| 7.6 `[D:7.4]` | Backend `AuthCoordinator`, session persistence, `ControlPlaneClient.login_steam()`, real `ISteamUserAuth/AuthenticateUserTicket` adapter (`server/steam`), client web-API ticket acquisition, sign-in before matchmaking | Needs a real App ID and publisher key ([#15](https://github.com/jcreek/CosmicClash/issues/15)) and a custom GodotSteam build ([#16](https://github.com/jcreek/CosmicClash/issues/16)) to exercise live; sign-in is config-gated and returns 503 until both are set |
|
||||
| 7.7 `[D:7.1]` `[P]` | Obtain the production App ID, publisher key, SDR coordinator SDK/signing approval, certificates and hosted-data-centre support from Valve | Not started |
|
||||
| 7.8 `[D:7.6,7.7]` | Ticketed Hosted Dedicated Server SDR: routing registration, coordinator-issued player→server relay tickets, client ticket installation, reconnect and expiry | Not started; depends on 7.6 and 7.7 |
|
||||
|
||||
@@ -154,7 +165,10 @@ the largest architectural departure in the project's history; read the
|
||||
design doc before picking up any task below. The local control-plane,
|
||||
durable-store, allocated-server, and verification paths are substantially
|
||||
implemented; every row below lists only what's still open, not what's
|
||||
built. **The critical path is task 8.31 — see §0's root blocker.**
|
||||
built. **Task 8.31, formerly the critical path, is done — see §0.** What now
|
||||
gates a live deployment is external: an App ID ([#15](https://github.com/jcreek/CosmicClash/issues/15)),
|
||||
GodotSteam builds ([#16](https://github.com/jcreek/CosmicClash/issues/16)), and a
|
||||
cluster ([#17](https://github.com/jcreek/CosmicClash/issues/17)).
|
||||
|
||||
**Hard dependency on 7.6 and 7.8.** The local allocated path binds slot
|
||||
reclaim to a control-plane-signed player identity and locks its team/slot
|
||||
@@ -196,7 +210,7 @@ are done; everything below is what's left on the tasks still open.
|
||||
| # | Task | Remaining |
|
||||
|---|---|---|
|
||||
| 8.14 `[D:8.4,8.5,8.8]` | Queue policy (ownership, heartbeat/expiry, candidate projection) | Live Redis failover-under-load and worker integration remain |
|
||||
| 8.15 `[D:7.8,8.3]` | Probe validation (RTT, nonce/freshness/region, quarantine) | Steam coordinator, regional probe adapters, multi-region probe population remain |
|
||||
| 8.15 `[D:7.8,8.3]` | Probe validation (RTT, nonce/freshness/region, quarantine), `POST /v1/probes/{region}/challenge`, durable single-use nonces, client probe collection before queueing, candidate-index refresh after probe | Steam coordinator ping-location source remains (a placeholder blob is sent without a Steam runtime); multi-region endpoint deployment remains |
|
||||
| 8.16 `[D:8.14,8.15]` | Candidate/team formation, matcher worker | Long-running worker integration remains |
|
||||
| 8.17 `[D:8.14,8.16]` | Proposal policy (response window, cooldowns, offender/innocent split) | Live PostgreSQL execution and allocation integration remain |
|
||||
| 8.18 `[D:8.5,8.14,8.17]` | Store layer (serializable retries, claim SQL, atomic promotion) | Allocation runtime integration remains |
|
||||
@@ -217,7 +231,7 @@ are done; everything below is what's left on the tasks still open.
|
||||
| 8.28 `[D:8.6,8.27]` | Process-ready/Agones-Ready separation, control-plane registration | Remaining gates are live Agones annotation/shutdown behavior and production cluster readiness — see task 8.49 |
|
||||
| 8.29 `[D:8.26,8.27]` | Dynamic port/SDR env propagation | Real Agones dynamic/passthrough mapping, POP/cert/firewall/NAT, multi-match fixture remain |
|
||||
| 8.30 `[D:8.18,8.26,8.28,8.29]` | Allocation leasing, compatibility validation | Full unknown-outcome cluster recovery and signed roster metadata remain |
|
||||
| **8.31** `[D:8.9,8.30]` | Signed assignment/roster persistence, player recovery | **This is the actual root blocker of the whole allocation-to-connect pipeline (see §0).** `store.SaveAssignment`/`SaveAssignments`/`SaveVerifiedAssignmentRoster` and `allocator.Service.PublishRoster` are built and tested but never called from `allocator/worker.go`, `cmd/allocator`, or anywhere else in production — only tests seed the table directly. A real match cannot advance past `PROCESS_READY`. Closing it needs new security-relevant design: a join-signing key shared between the allocator (to sign) and the game server (`fleet.yaml` already mounts one for verification via `--join-authorisations-key-file`, but no control-plane binary has a matching signing flag), roster-digest computation, and per-player `domain.JoinAuthorisation` construction via the already-built `domain.SignJoinAuthorisationHMAC`. Flagged rather than fixed at the user's explicit direction, pending a decision on that design |
|
||||
| **8.31** `[D:8.9,8.30]` | Signed assignment/roster persistence, player recovery | **Done — this was the root blocker of the allocation-to-connect pipeline.** `allocator.Worker.RunOnce` now builds one signed join authorisation per durable participant and calls `PublishRoster` after binding; `cmd/allocator` takes `--join-authorisations-key-file`/`--join-authorisations-key-id` and refuses to start without them. The signing design is settled: HMAC-SHA256 over the canonical claim bytes with a key ID inside them, so servers hold a key *set* and rotation does not invalidate in-flight matches. The provider endpoint is now persisted on the allocation so a worker crashing between allocating and publishing can retry. Verified by an integration test that drives the real worker through the supervisor's own roster read path without seeding `assignments`. Live Agones verification remains ([#17](https://github.com/jcreek/CosmicClash/issues/17)) |
|
||||
| 8.32 `[D:8.2,8.26,8.30]` | FleetAutoscaler baseline, Ready buffer | Regional on-demand node pools/failure domains, pre-pull rollout, warm-allocation p95/p99, N+1 certification remain |
|
||||
| 8.33 `[D:8.26,8.32]` | Fleet scheduling, zone spread | Regional node pools, forced node-loss testing, measured N+1 headroom remain |
|
||||
| 8.34 `[D:8.28,8.29]` | Native x86_64 benchmark of boot-to-process-ready/assignment-ready, p99 CPU/RSS/network, node cap with 30% headroom | Not started |
|
||||
|
||||
Reference in New Issue
Block a user