Commit Graph

624 Commits

Author SHA1 Message Date
Josh Creek 98ff2aed81 test(multiplayer): detect macOS Godot bundle 2026-09-01 17:13:18 +01:00
Josh Creek 0787702f23 test(multiplayer): record ENet integration gate 2026-09-01 17:11:55 +01:00
Josh Creek f1b9527d3f test(multiplayer): verify domain fuzz targets 2026-09-01 17:10:26 +01:00
Josh Creek c5f6c95ca5 docs(multiplayer): close snapshot disconnect race note 2026-09-01 17:09:23 +01:00
Josh Creek d1bcb51225 test(multiplayer): add consolidated local gate 2026-09-01 17:07:44 +01:00
Josh Creek f1b8366531 feat(multiplayer): export bounded API metrics 2026-09-01 17:06:38 +01:00
Josh Creek d85c8b8194 feat(multiplayer): expose action retry in matchmaking UI 2026-09-01 17:03:31 +01:00
Josh Creek 6ef55affcb feat(multiplayer): add idempotent client mutation retries 2026-09-01 17:02:55 +01:00
Josh Creek 9ae01ecc5a fix(multiplayer): propagate allocated playlist 2026-09-01 17:00:37 +01:00
Josh Creek 8bd1455a2c feat(multiplayer): propagate allocated launch configuration 2026-09-01 16:58:09 +01:00
Josh Creek 315c524c42 feat(multiplayer): propagate allocated launch configuration 2026-09-01 16:57:27 +01:00
Josh Creek 75cb8faac4 feat(multiplayer): acknowledge supervisor shutdown 2026-09-01 16:54:40 +01:00
Josh Creek cc12260225 feat(multiplayer): expose server shutdown acknowledgement 2026-09-01 16:50:26 +01:00
Josh Creek 1d926e705b fix(multiplayer): preserve planned shutdown reason 2026-09-01 16:44:42 +01:00
Josh Creek 4e88ea80f3 feat(multiplayer): handle planned shutdowns on clients 2026-09-01 16:43:47 +01:00
Josh Creek b24f9fc448 feat(multiplayer): notify clients before server shutdown 2026-09-01 16:42:15 +01:00
Josh Creek 8c2dc66c7c fix(multiplayer): release innocent no-show participants 2026-09-01 16:39:46 +01:00
Josh Creek 334d8a26ac feat(multiplayer): enforce allocated initial connect policy 2026-09-01 16:36:26 +01:00
Josh Creek 74e50caf70 feat(multiplayer): publish allocation state events 2026-09-01 16:31:17 +01:00
Josh Creek d15d16d593 feat(multiplayer): publish allocation state events 2026-09-01 16:30:35 +01:00
Josh Creek ec82367c50 fix(multiplayer): close initial connect sweep rows 2026-09-01 16:28:08 +01:00
Josh Creek a6d2bdf8bd feat(multiplayer): sweep initial connect outcomes 2026-09-01 16:27:15 +01:00
Josh Creek bae7458668 feat(multiplayer): apply initial connect outcomes durably 2026-09-01 16:26:06 +01:00
Josh Creek b7642ad2be feat(multiplayer): model initial connect outcomes 2026-09-01 16:21:40 +01:00
Josh Creek 3dbecb0bd5 fix(multiplayer): fence roster topology at persistence 2026-09-01 16:20:31 +01:00
Josh Creek a439a1059b fix(multiplayer): fence roster topology at persistence 2026-09-01 16:20:01 +01:00
Josh Creek 80d6ee8cf5 fix(multiplayer): validate allocated roster shape 2026-09-01 16:18:31 +01:00
Josh Creek 69a8402f11 fix(multiplayer): honor assigned team and slot 2026-09-01 16:17:10 +01:00
Josh Creek 5812386676 feat(observability): log authenticated read routes 2026-09-01 16:14:20 +01:00
Josh Creek e62909d457 docs(multiplayer): mark identity and launch gates 2026-09-01 16:10:39 +01:00
Josh Creek 12b9712a8d test(multiplayer): lock allocated launch overrides 2026-09-01 16:09:57 +01:00
Josh Creek b4ea50d76a fix(multiplayer): reclaim slots by signed identity 2026-09-01 16:08:01 +01:00
Josh Creek bdb3c8f4a7 fix(multiplayer): gate allocated matches on full roster 2026-09-01 16:03:43 +01:00
Josh Creek 498f219ab9 fix(multiplayer): align NA fleet region runtime 2026-09-01 16:01:38 +01:00
Josh Creek c0861bfcad feat(multiplayer): wire allocated fleet runtime 2026-09-01 16:00:03 +01:00
Josh Creek 68e76b5feb feat(multiplayer): deliver allocated server rosters 2026-09-01 15:54:07 +01:00
Josh Creek 863cf61f1a test(multiplayer): verify result websocket fanout 2026-09-01 15:47:08 +01:00
Josh Creek 802e5fc96f test(multiplayer): fence conflicting result races 2026-09-01 15:44:47 +01:00
Josh Creek f3b56f70e3 feat(multiplayer): dispatch completed result events 2026-09-01 15:43:32 +01:00
Josh Creek 9ec707674a test(multiplayer): verify allocated supervisor registration 2026-09-01 15:39:28 +01:00
Josh Creek 207ab47866 test(multiplayer): verify allocator worker lifecycle 2026-09-01 15:34:51 +01:00
Josh Creek dad26a164c fix(multiplayer): recover stale proposal mutations 2026-09-01 15:31:01 +01:00
Josh Creek 04b5d3b29d feat(multiplayer): verify ranked profile delivery 2026-09-01 15:29:49 +01:00
Josh Creek d081a72b9a feat(multiplayer): wire ranked profile runtime policy 2026-09-01 15:28:37 +01:00
Josh Creek f096e8ff0b test(multiplayer): verify live assignment delivery 2026-09-01 15:25:36 +01:00
Josh Creek 79a6092b28 feat(multiplayer): wire production proposal outbox delivery 2026-09-01 15:21:49 +01:00
Josh Creek aa93aeec95 test(multiplayer): verify two-player proposal round trip 2026-09-01 15:17:53 +01:00
Josh Creek 1e1db3525e docs(multiplayer): record the workload-token delivery channel closing
Updates §8.10 and §8.28's cross-reference to reflect the previous two
commits: the token now binds allocation_id only (resolved durably at verify
time, not embedded match/server), and the delivery channel itself is wired
end to end (cmd/allocator mints -> Agones annotation -> supervisor reads ->
Authorization header), not just the verification core. Records what's left:
this has only run against HTTP-level Agones fakes, never a real cluster, so
the object_meta JSON casing remains unverified from this sandbox; fleet.yaml
still needs its own environment-specific manifest values.
2026-09-01 14:58:30 +01:00
Josh Creek 544f76c502 feat(multiplayer): deliver signed workload tokens via Agones allocation annotation
Closes the remaining gap the previous two commits left open: WorkloadVerify
itself worked, but nothing minted a real token at allocation time or handed
it to a running pod, so it had no real caller yet.

agones.Client gains WorkloadSecret/WorkloadTokenTTL. When set, Allocate
mints a signed workload token for the allocation (allocation_id is known at
request-construction time, before Agones has picked a server -- see the
previous commit for why that's the only identifier the token can bind) and
requests it as a third cosmic-clash.io/workload-token annotation, alongside
the existing match-id/allocation-id ones. Left unset (the default), Allocate
requests no such annotation, so a deployment not yet using this path is
unaffected. cmd/allocator wires it from a new --workload-secret /
COSMIC_CLASH_WORKLOAD_SECRET flag (must match cmd/control-plane's own), with
a startup warning if left unset.

supervisor.Supervisor.workloadToken() resolves the bearer credential for
control-plane registration: an explicitly configured --workload-token-path
always wins (kept for a future Kubernetes-projected-JWT WorkloadVerify path,
not yet wired server-side), otherwise it falls back to the
cosmic-clash.io/workload-token annotation on the allocated GameServer --
the same annotation-fallback pattern matchID already used for
cosmic-clash.io/match-id. WorkloadTokenPath is accordingly no longer
required at construction time when ControlPlaneURL is set.

Verified: new agones test proves the annotation is requested (and parses/
verifies against the same secret, naming the right allocation) when
WorkloadSecret is configured, and that it's absent when it isn't; new
supervisor tests prove the annotation-sourced token is what's actually sent
as the Authorization bearer, and that Start fails closed with neither a
configured path nor an annotation present. Full
`go build ./... && go vet ./... && gofmt -l . && go test ./... -race` and
`go test -tags integration ./... -race` both clean.
2026-09-01 14:57:45 +01:00
Josh Creek d588898f5d fix(multiplayer): bind signed workload tokens to allocation_id only
The just-landed signed workload token embedded (allocation_id, match_id,
server_id) as claims. That doesn't actually work for its intended delivery
channel: the token is meant to be requested as a GameServerAllocation
annotation in the SAME request that asks Agones to pick a server, so at mint
time the allocator knows allocation_id (it generates it) but not yet which
server_id Agones will return -- server_id only exists in Agones's response,
after the annotation request has already been sent. Embedding it was simply
not possible for the real caller this was built for; only the (allocator ->
signed_token) unit tests and hand-constructed integration tests happened to
supply it directly, masking the gap.

Fixes it by having the token bind only allocation_id (the one identifier
actually known at mint time) plus expiry. match_id/server_id are resolved at
verify time from the durable allocations table via the new
store.AllocationBindingByAllocationID, keyed by allocation_id -- which the
allocator already records immediately after Agones responds. This is
strictly stronger, not just a workaround: a caller can no longer claim any
match/server pairing at all, even one that happens to be internally
consistent -- the binding returned is entirely durable-record-derived.

Verified: server/workload's unit tests updated for the new two-field claim
shape; server/api's Postgres integration suite gains
TestWorkloadVerifierFromSignedTokenNeverTrustsCallerSuppliedBinding (two
distinct real allocations each resolve to their own, and only their own,
match/server pairing) replacing the now-inapplicable mismatched-triple test.
Full `go build ./... && go vet ./... && gofmt -l . && go test ./... -race`
and `go test -tags integration ./... -race` both clean; the api integration
suite re-run 3x clean against a live postgres:17-alpine container.
2026-09-01 14:54:38 +01:00