From 76b75bab135d964e07416e781339bfee685e8e78 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Por=C4=99bski?=
<164040529+TexablePlum@users.noreply.github.com>
Date: Mon, 6 Jul 2026 14:34:24 +0200
Subject: [PATCH 1/9] feat(contracts): define WebSocket payload schemas (#75)
- Add draft 2020-12 contracts for authoritative game-state and player-specific match-found STOMP messages.
- Document destinations, direction, recovery semantics, and additive v1 evolution rules.
- Validate real publisher DTO serialization with each service's Spring-configured ObjectMapper and networknt.
- Tests: game-service verify passed 214 tests; matchmaking-service verify passed 105 tests; a deliberate match-found field rename failed the contract test.
---
checkers-contracts/README.md | 3 +-
checkers-contracts/ws/v1/README.md | 25 +++++++
.../ws/v1/game-state.schema.json | 64 ++++++++++++++++
.../ws/v1/match-found.schema.json | 23 ++++++
services/game-service/README.md | 3 +
services/game-service/pom.xml | 6 ++
.../game/websocket/GameStateContractTest.java | 75 +++++++++++++++++++
services/matchmaking-service/README.md | 3 +
services/matchmaking-service/pom.xml | 6 ++
.../websocket/MatchFoundContractTest.java | 55 ++++++++++++++
10 files changed, 262 insertions(+), 1 deletion(-)
create mode 100644 checkers-contracts/ws/v1/README.md
create mode 100644 checkers-contracts/ws/v1/game-state.schema.json
create mode 100644 checkers-contracts/ws/v1/match-found.schema.json
create mode 100644 services/game-service/src/test/java/com/checkers/gameservice/game/websocket/GameStateContractTest.java
create mode 100644 services/matchmaking-service/src/test/java/com/checkers/matchmakingservice/matchmaking/websocket/MatchFoundContractTest.java
diff --git a/checkers-contracts/README.md b/checkers-contracts/README.md
index 2cff866..2451399 100644
--- a/checkers-contracts/README.md
+++ b/checkers-contracts/README.md
@@ -4,7 +4,8 @@ This directory is the versioned source of truth for platform boundaries:
- `*.proto` defines the `checkers.v1` gRPC APIs;
- `http/v1/api-error.schema.json` defines the common Java HTTP error body;
-- `auth/v1/` defines JWT access/refresh claims and the Redis access-token blacklist protocol.
+- `auth/v1/` defines JWT access/refresh claims and the Redis access-token blacklist protocol;
+- `ws/v1/` defines server-to-client STOMP payloads and their destinations.
The Protobuf API is stable under the `checkers.v1` package. Java code is generated under
`com.checkers.contracts.v1`; Python keeps the standard module names generated from each `.proto` file.
diff --git a/checkers-contracts/ws/v1/README.md b/checkers-contracts/ws/v1/README.md
new file mode 100644
index 0000000..de21379
--- /dev/null
+++ b/checkers-contracts/ws/v1/README.md
@@ -0,0 +1,25 @@
+# WebSocket contracts v1
+
+These JSON Schemas describe every server-to-client STOMP payload in the platform. Both publishers
+send application DTOs through Spring's Jackson-backed STOMP message converter.
+
+| Service | Destination | Direction | Schema |
+|---|---|---|---|
+| `game-service` | `/topic/games/{gameId}` | server to subscribed client | `game-state.schema.json` |
+| `matchmaking-service` | `/user/queue/matchmaking` | server to authenticated user | `match-found.schema.json` |
+
+`game-state` is the complete authoritative `GameResponse` snapshot. `match-found` is a best-effort,
+player-specific notification; clients recover missed delivery through the matchmaking REST status
+endpoint.
+
+## Evolution rules
+
+- Keep v1 changes additive: existing property names, JSON types, enum values and meanings remain stable.
+- Add optional properties when extending a payload so old clients can continue to consume it.
+- Update the schema and its service contract test in the same change as an additive DTO extension.
+- Publish incompatible replacements under `ws/v2` and on versioned destinations rather than changing v1.
+- Treat destination names and routing direction as part of the contract.
+
+Each publishing service serializes its real DTO with the application `ObjectMapper` and validates the
+result against the corresponding schema during `mvn verify`. This catches DTO/schema drift before a
+payload reaches clients.
diff --git a/checkers-contracts/ws/v1/game-state.schema.json b/checkers-contracts/ws/v1/game-state.schema.json
new file mode 100644
index 0000000..8b92732
--- /dev/null
+++ b/checkers-contracts/ws/v1/game-state.schema.json
@@ -0,0 +1,64 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://checkers.example/contracts/ws/v1/game-state.schema.json",
+ "title": "Game state v1",
+ "description": "Authoritative game snapshot published by game-service.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id", "variant", "status", "whitePlayer", "blackPlayer", "timeControl", "clock",
+ "moves", "currentFen", "legalMoves", "currentTurn", "result", "endReason",
+ "createdAt", "finishedAt"
+ ],
+ "properties": {
+ "id": { "type": "string", "format": "uuid" },
+ "variant": { "$ref": "#/$defs/variant" },
+ "status": { "enum": ["WAITING_FOR_OPPONENT", "IN_PROGRESS", "FINISHED"] },
+ "whitePlayer": { "oneOf": [{ "$ref": "#/$defs/player" }, { "type": "null" }] },
+ "blackPlayer": { "oneOf": [{ "$ref": "#/$defs/player" }, { "type": "null" }] },
+ "timeControl": { "oneOf": [{ "$ref": "#/$defs/timeControl" }, { "type": "null" }] },
+ "clock": { "oneOf": [{ "$ref": "#/$defs/clock" }, { "type": "null" }] },
+ "moves": { "type": "array", "items": { "type": "string" } },
+ "currentFen": { "type": "string", "minLength": 1 },
+ "legalMoves": { "type": "array", "items": { "type": "string" } },
+ "currentTurn": { "type": ["string", "null"], "enum": ["WHITE", "BLACK", null] },
+ "result": { "type": ["string", "null"], "enum": ["WHITE_WON", "BLACK_WON", "DRAW", null] },
+ "endReason": {
+ "type": ["string", "null"],
+ "enum": ["NORMAL", "RESIGNATION", "TIMEOUT", "ABANDONMENT", null]
+ },
+ "createdAt": { "type": "string", "format": "date-time" },
+ "finishedAt": { "type": ["string", "null"], "format": "date-time" }
+ },
+ "$defs": {
+ "variant": {
+ "enum": [
+ "STANDARD", "AMERICAN", "FRISIAN", "RUSSIAN", "BRAZILIAN", "ANTIDRAUGHTS",
+ "BREAKTHROUGH", "FRYSK"
+ ]
+ },
+ "timeControl": {
+ "enum": ["BULLET_1_0", "BLITZ_3_0", "BLITZ_5_0", "RAPID_10_0", "CLASSICAL_30_0"]
+ },
+ "player": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["type", "userId", "aiDifficulty"],
+ "properties": {
+ "type": { "enum": ["HUMAN", "AI"] },
+ "userId": { "type": ["string", "null"], "format": "uuid" },
+ "aiDifficulty": { "type": ["integer", "null"] }
+ }
+ },
+ "clock": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["whiteRemainingMillis", "blackRemainingMillis", "lastSwitchAt"],
+ "properties": {
+ "whiteRemainingMillis": { "type": "integer", "minimum": 0 },
+ "blackRemainingMillis": { "type": "integer", "minimum": 0 },
+ "lastSwitchAt": { "type": "string", "format": "date-time" }
+ }
+ }
+ }
+}
diff --git a/checkers-contracts/ws/v1/match-found.schema.json b/checkers-contracts/ws/v1/match-found.schema.json
new file mode 100644
index 0000000..66d92c0
--- /dev/null
+++ b/checkers-contracts/ws/v1/match-found.schema.json
@@ -0,0 +1,23 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://checkers.example/contracts/ws/v1/match-found.schema.json",
+ "title": "Match found v1",
+ "description": "Player-specific notification published by matchmaking-service after game creation.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["gameId", "color", "opponentId", "variant", "timeControl"],
+ "properties": {
+ "gameId": { "type": "string", "format": "uuid" },
+ "color": { "enum": ["WHITE", "BLACK"] },
+ "opponentId": { "type": "string", "format": "uuid" },
+ "variant": {
+ "enum": [
+ "STANDARD", "AMERICAN", "FRISIAN", "RUSSIAN", "BRAZILIAN", "ANTIDRAUGHTS",
+ "BREAKTHROUGH", "FRYSK"
+ ]
+ },
+ "timeControl": {
+ "enum": ["BULLET_1_0", "BLITZ_3_0", "BLITZ_5_0", "RAPID_10_0", "CLASSICAL_30_0"]
+ }
+ }
+}
diff --git a/services/game-service/README.md b/services/game-service/README.md
index dff6d11..af5a400 100644
--- a/services/game-service/README.md
+++ b/services/game-service/README.md
@@ -1007,6 +1007,9 @@ The build generates gRPC stubs, runs the suite and enforces **100% line and bran
handwritten service code via JaCoCo. **Docker must be running** (Testcontainers + embedded WebSocket
server). Layers covered (194 tests):
+The suite also serializes the real game-state WebSocket DTO with the application Jackson
+configuration and validates it against `checkers-contracts/ws/v1/game-state.schema.json`.
+
- **Unit (Mockito):** game lifecycle, moves and completion, AI turns, ELO reporting, deadline and
abandonment managers, the domain model, runtime support (clocks, locks, executors) and the variant
parity guard.
diff --git a/services/game-service/pom.xml b/services/game-service/pom.xml
index cb9c9db..b16862a 100644
--- a/services/game-service/pom.xml
+++ b/services/game-service/pom.xml
@@ -128,6 +128,12 @@
spring-boot-starter-test
test
+
+ com.networknt
+ json-schema-validator
+ 1.5.6
+ test
+
org.springframework.security
spring-security-test
diff --git a/services/game-service/src/test/java/com/checkers/gameservice/game/websocket/GameStateContractTest.java b/services/game-service/src/test/java/com/checkers/gameservice/game/websocket/GameStateContractTest.java
new file mode 100644
index 0000000..0959546
--- /dev/null
+++ b/services/game-service/src/test/java/com/checkers/gameservice/game/websocket/GameStateContractTest.java
@@ -0,0 +1,75 @@
+package com.checkers.gameservice.game.websocket;
+
+import com.checkers.gameservice.game.dto.GameClockResponse;
+import com.checkers.gameservice.game.dto.GameResponse;
+import com.checkers.gameservice.game.dto.PlayerResponse;
+import com.checkers.gameservice.game.dto.PlayerType;
+import com.checkers.gameservice.game.entity.GameStatus;
+import com.checkers.gameservice.game.entity.GameVariant;
+import com.checkers.gameservice.game.entity.PlayerColor;
+import com.checkers.gameservice.game.entity.TimeControl;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.networknt.schema.JsonSchema;
+import com.networknt.schema.JsonSchemaFactory;
+import com.networknt.schema.SpecVersion;
+import com.networknt.schema.ValidationMessage;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.json.JsonTest;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.Instant;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@JsonTest
+class GameStateContractTest {
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ @Test
+ void applicationSerializationMatchesGameStateV1Schema() throws Exception {
+ Instant now = Instant.parse("2026-07-06T10:15:30Z");
+ GameResponse payload = new GameResponse(
+ UUID.fromString("00000000-0000-0000-0000-000000000001"),
+ GameVariant.STANDARD,
+ GameStatus.IN_PROGRESS,
+ new PlayerResponse(
+ PlayerType.HUMAN,
+ UUID.fromString("00000000-0000-0000-0000-000000000002"),
+ null),
+ new PlayerResponse(PlayerType.AI, null, 3),
+ TimeControl.BLITZ_5_0,
+ new GameClockResponse(299_000, 300_000, now),
+ List.of("31-27"),
+ "W:W27,32:B1,6",
+ List.of("27-23", "27-24"),
+ PlayerColor.WHITE,
+ null,
+ null,
+ now,
+ null);
+
+ Set errors = schema().validate(objectMapper.valueToTree(payload));
+
+ assertTrue(errors.isEmpty(), () -> "game-state schema violations: " + errors);
+ }
+
+ private JsonSchema schema() throws Exception {
+ JsonNode schemaNode = objectMapper.readTree(Files.readString(contractPath()));
+ return JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012).getSchema(schemaNode);
+ }
+
+ private static Path contractPath() {
+ Path fromRoot = Path.of("checkers-contracts/ws/v1/game-state.schema.json");
+ return Files.exists(fromRoot)
+ ? fromRoot
+ : Path.of("../../checkers-contracts/ws/v1/game-state.schema.json");
+ }
+}
diff --git a/services/matchmaking-service/README.md b/services/matchmaking-service/README.md
index a90cf15..6f348d1 100644
--- a/services/matchmaking-service/README.md
+++ b/services/matchmaking-service/README.md
@@ -675,6 +675,9 @@ The build generates gRPC stubs, runs the suite and enforces **100% line and bran
handwritten service code via JaCoCo. Docker must be running for Redis Testcontainers. Layers covered
(102 tests):
+The suite also serializes the real match-found WebSocket DTO with the application Jackson
+configuration and validates it against `checkers-contracts/ws/v1/match-found.schema.json`.
+
- **Unit (Mockito):** queue service, ticket invariants, ELO window policy, matcher decisions,
scheduler error isolation, color assignment, presence registry/listener and disconnect grace.
- **In-process gRPC (`grpc-testing`):** `GrpcEloClient` and `GrpcMatchedGameClient` request mapping,
diff --git a/services/matchmaking-service/pom.xml b/services/matchmaking-service/pom.xml
index f25d130..a57b72a 100644
--- a/services/matchmaking-service/pom.xml
+++ b/services/matchmaking-service/pom.xml
@@ -107,6 +107,12 @@
spring-boot-starter-test
test
+
+ com.networknt
+ json-schema-validator
+ 1.5.6
+ test
+
org.springframework.security
spring-security-test
diff --git a/services/matchmaking-service/src/test/java/com/checkers/matchmakingservice/matchmaking/websocket/MatchFoundContractTest.java b/services/matchmaking-service/src/test/java/com/checkers/matchmakingservice/matchmaking/websocket/MatchFoundContractTest.java
new file mode 100644
index 0000000..56d10e0
--- /dev/null
+++ b/services/matchmaking-service/src/test/java/com/checkers/matchmakingservice/matchmaking/websocket/MatchFoundContractTest.java
@@ -0,0 +1,55 @@
+package com.checkers.matchmakingservice.matchmaking.websocket;
+
+import com.checkers.matchmakingservice.matchmaking.dto.MatchFoundMessage;
+import com.checkers.matchmakingservice.queue.entity.GameVariant;
+import com.checkers.matchmakingservice.queue.entity.PlayerColor;
+import com.checkers.matchmakingservice.queue.entity.TimeControl;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.networknt.schema.JsonSchema;
+import com.networknt.schema.JsonSchemaFactory;
+import com.networknt.schema.SpecVersion;
+import com.networknt.schema.ValidationMessage;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.json.JsonTest;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Set;
+import java.util.UUID;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@JsonTest
+class MatchFoundContractTest {
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ @Test
+ void applicationSerializationMatchesMatchFoundV1Schema() throws Exception {
+ MatchFoundMessage payload = new MatchFoundMessage(
+ UUID.fromString("00000000-0000-0000-0000-000000000001"),
+ PlayerColor.BLACK,
+ UUID.fromString("00000000-0000-0000-0000-000000000002"),
+ GameVariant.STANDARD,
+ TimeControl.BLITZ_5_0);
+
+ Set errors = schema().validate(objectMapper.valueToTree(payload));
+
+ assertTrue(errors.isEmpty(), () -> "match-found schema violations: " + errors);
+ }
+
+ private JsonSchema schema() throws Exception {
+ JsonNode schemaNode = objectMapper.readTree(Files.readString(contractPath()));
+ return JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012).getSchema(schemaNode);
+ }
+
+ private static Path contractPath() {
+ Path fromRoot = Path.of("checkers-contracts/ws/v1/match-found.schema.json");
+ return Files.exists(fromRoot)
+ ? fromRoot
+ : Path.of("../../checkers-contracts/ws/v1/match-found.schema.json");
+ }
+}
From 67fe17d2a3020caaa81c57f253003d7e345d5df4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Por=C4=99bski?=
<164040529+TexablePlum@users.noreply.github.com>
Date: Mon, 6 Jul 2026 14:34:42 +0200
Subject: [PATCH 2/9] ci(contracts): validate JSON contract instances (#77)
* feat(contracts): define WebSocket payload schemas
- Add draft 2020-12 contracts for authoritative game-state and player-specific match-found STOMP messages.
- Document destinations, direction, recovery semantics, and additive v1 evolution rules.
- Validate real publisher DTO serialization with each service's Spring-configured ObjectMapper and networknt.
- Tests: game-service verify passed 214 tests; matchmaking-service verify passed 105 tests; a deliberate match-found field rename failed the contract test.
* ci(contracts): validate JSON contract instances
- Define the draft 2020-12 schema for the authentication protocol manifest.
- Add explicit access and refresh claim instances and verify the access instance matches the compact JWT payload.
- Validate auth instances plus auth, HTTP, and WebSocket schema metaschemas with check-jsonschema in CI.
- Tests: the full local check-jsonschema sequence passed; JwtContractTest passed in api-gateway, user-service, game-service, and matchmaking-service.
---
.github/workflows/protobuf-contracts.yml | 38 ++++++++++++++-
checkers-contracts/README.md | 3 ++
checkers-contracts/auth/v1/README.md | 2 +
.../auth/v1/contract.schema.json | 48 +++++++++++++++++++
checkers-contracts/auth/v1/test-vectors.json | 21 ++++++++
5 files changed, 111 insertions(+), 1 deletion(-)
create mode 100644 checkers-contracts/auth/v1/contract.schema.json
diff --git a/.github/workflows/protobuf-contracts.yml b/.github/workflows/protobuf-contracts.yml
index a105363..a5458c4 100644
--- a/.github/workflows/protobuf-contracts.yml
+++ b/.github/workflows/protobuf-contracts.yml
@@ -86,8 +86,44 @@ jobs:
python -m pip install --disable-pip-version-check check-jsonschema
check-jsonschema --check-metaschema \
checkers-contracts/http/v1/api-error.schema.json \
+ checkers-contracts/auth/v1/contract.schema.json \
checkers-contracts/auth/v1/access-token-claims.schema.json \
- checkers-contracts/auth/v1/refresh-token-claims.schema.json
+ checkers-contracts/auth/v1/refresh-token-claims.schema.json \
+ checkers-contracts/ws/v1/game-state.schema.json \
+ checkers-contracts/ws/v1/match-found.schema.json
+ check-jsonschema \
+ --schemafile checkers-contracts/auth/v1/contract.schema.json \
+ checkers-contracts/auth/v1/contract.json
+ python - <<'PY'
+ import base64
+ import json
+ import os
+ from pathlib import Path
+
+ vectors = json.loads(
+ Path("checkers-contracts/auth/v1/test-vectors.json").read_text(encoding="utf-8")
+ )
+ encoded_payload = vectors["accessToken"].split(".")[1]
+ padding = "=" * (-len(encoded_payload) % 4)
+ token_claims = json.loads(base64.urlsafe_b64decode(encoded_payload + padding))
+ if token_claims != vectors["accessClaims"]:
+ raise SystemExit("accessClaims does not match the compact accessToken payload")
+
+ output = Path(os.environ["RUNNER_TEMP"]) / "auth-claim-instances"
+ output.mkdir()
+ (output / "access.json").write_text(
+ json.dumps(vectors["accessClaims"]), encoding="utf-8"
+ )
+ (output / "refresh.json").write_text(
+ json.dumps(vectors["refreshClaims"]), encoding="utf-8"
+ )
+ PY
+ check-jsonschema \
+ --schemafile checkers-contracts/auth/v1/access-token-claims.schema.json \
+ "$RUNNER_TEMP/auth-claim-instances/access.json"
+ check-jsonschema \
+ --schemafile checkers-contracts/auth/v1/refresh-token-claims.schema.json \
+ "$RUNNER_TEMP/auth-claim-instances/refresh.json"
- name: Reject breaking changes on pull requests
if: github.event_name == 'pull_request'
# Same baseline guard as the push variant below: a PR whose base predates the versioned
diff --git a/checkers-contracts/README.md b/checkers-contracts/README.md
index 2451399..26fd161 100644
--- a/checkers-contracts/README.md
+++ b/checkers-contracts/README.md
@@ -34,3 +34,6 @@ Validate every JSON artifact locally with Python:
python -m json.tool checkers-contracts/http/v1/api-error.schema.json > /dev/null
python -m json.tool checkers-contracts/auth/v1/contract.json > /dev/null
```
+
+CI additionally validates schema metaschemas, `auth/v1/contract.json`, the decoded access and refresh
+claim vectors, and the `ws/v1` schemas with `check-jsonschema`.
diff --git a/checkers-contracts/auth/v1/README.md b/checkers-contracts/auth/v1/README.md
index 7767087..844c367 100644
--- a/checkers-contracts/auth/v1/README.md
+++ b/checkers-contracts/auth/v1/README.md
@@ -17,6 +17,8 @@ The schemas validate decoded payloads. JWT headers additionally require `alg = H
present, remains the standard `JWT` header and is not the application token discriminator.
`test-vectors.json` contains a long-lived, non-production access token representing issuer output;
every Java validator consumes it to prove producer/consumer interoperability and constant parity.
+The file also carries explicit access and refresh claim instances. CI verifies that the access
+instance equals the compact token payload and validates both instances against their v1 schemas.
## Redis access-token blacklist
diff --git a/checkers-contracts/auth/v1/contract.schema.json b/checkers-contracts/auth/v1/contract.schema.json
new file mode 100644
index 0000000..eb49512
--- /dev/null
+++ b/checkers-contracts/auth/v1/contract.schema.json
@@ -0,0 +1,48 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://checkers.example/contracts/auth/v1/contract.schema.json",
+ "title": "Authentication protocol contract v1",
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["version", "jwt", "blacklist"],
+ "properties": {
+ "version": { "const": "v1" },
+ "jwt": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "algorithm", "minimumSecretBytes", "issuer", "audience", "tokenTypeClaim",
+ "accessTokenType", "refreshTokenType", "jtiFormat", "subjectFormat"
+ ],
+ "properties": {
+ "algorithm": { "const": "HS256" },
+ "minimumSecretBytes": { "type": "integer", "minimum": 32 },
+ "issuer": { "const": "checkers-user-service" },
+ "audience": { "const": "checkers-platform" },
+ "tokenTypeClaim": { "const": "token_type" },
+ "accessTokenType": { "const": "access" },
+ "refreshTokenType": { "const": "refresh" },
+ "jtiFormat": { "const": "uuid" },
+ "subjectFormat": { "const": "uuid" }
+ }
+ },
+ "blacklist": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "keyPrefix", "keyFormat", "value", "ttlUnit", "ttlRounding", "expiredTokenBehavior",
+ "writeFailurePolicy", "readFailurePolicy"
+ ],
+ "properties": {
+ "keyPrefix": { "const": "auth:blacklist:" },
+ "keyFormat": { "const": "auth:blacklist:" },
+ "value": { "const": "1" },
+ "ttlUnit": { "const": "seconds" },
+ "ttlRounding": { "const": "ceiling" },
+ "expiredTokenBehavior": { "const": "skip-write" },
+ "writeFailurePolicy": { "const": "fail-closed" },
+ "readFailurePolicy": { "const": "fail-open" }
+ }
+ }
+ }
+}
diff --git a/checkers-contracts/auth/v1/test-vectors.json b/checkers-contracts/auth/v1/test-vectors.json
index 72073ec..b46c02a 100644
--- a/checkers-contracts/auth/v1/test-vectors.json
+++ b/checkers-contracts/auth/v1/test-vectors.json
@@ -1,6 +1,27 @@
{
"secret": "checkers-contract-test-secret-0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJjaGVja2Vycy11c2VyLXNlcnZpY2UiLCJhdWQiOlsiY2hlY2tlcnMtcGxhdGZvcm0iXSwic3ViIjoiMDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMDAxIiwianRpIjoiMDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMDAyIiwiaWF0IjoxNzA0MDY3MjAwLCJleHAiOjQxMDI0NDQ4MDAsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJzaWQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDMiLCJ1c2VybmFtZSI6ImNvbnRyYWN0LXVzZXIiLCJlbWFpbCI6ImNvbnRyYWN0QGV4YW1wbGUuY29tIn0.KTNdSTuuU-xp9qWX5CFWApoZ86MIHfmfas9Tk5-5Fso",
+ "accessClaims": {
+ "iss": "checkers-user-service",
+ "aud": ["checkers-platform"],
+ "sub": "00000000-0000-0000-0000-000000000001",
+ "jti": "00000000-0000-0000-0000-000000000002",
+ "iat": 1704067200,
+ "exp": 4102444800,
+ "token_type": "access",
+ "sid": "00000000-0000-0000-0000-000000000003",
+ "username": "contract-user",
+ "email": "contract@example.com"
+ },
+ "refreshClaims": {
+ "iss": "checkers-user-service",
+ "aud": ["checkers-platform"],
+ "sub": "00000000-0000-0000-0000-000000000001",
+ "jti": "00000000-0000-0000-0000-000000000004",
+ "iat": 1704067200,
+ "exp": 4102444800,
+ "token_type": "refresh"
+ },
"expectedUserId": "00000000-0000-0000-0000-000000000001",
"expectedJti": "00000000-0000-0000-0000-000000000002",
"note": "Public interoperability vector only; this secret must never be used outside tests."
From 11b5fcb51f4d09d985afed27547630cae6e98d48 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 6 Jul 2026 14:37:10 +0200
Subject: [PATCH 3/9] build(deps): bump the minor-and-patch group (#71)
Bumps the minor-and-patch group in /services/game-service with 6 updates:
| Package | From | To |
| --- | --- | --- |
| [org.springframework.boot:spring-boot-starter-parent](https://github.com/spring-projects/spring-boot) | `3.5.11` | `3.5.16` |
| [org.springdoc:springdoc-openapi-starter-webmvc-ui](https://github.com/springdoc/springdoc-openapi) | `2.8.6` | `2.8.17` |
| [io.jsonwebtoken:jjwt-api](https://github.com/jwtk/jjwt) | `0.12.6` | `0.13.0` |
| [io.jsonwebtoken:jjwt-impl](https://github.com/jwtk/jjwt) | `0.12.6` | `0.13.0` |
| io.jsonwebtoken:jjwt-jackson | `0.12.6` | `0.13.0` |
| [com.google.protobuf:protobuf-java](https://github.com/protocolbuffers/protobuf) | `4.29.3` | `4.35.1` |
Updates `org.springframework.boot:spring-boot-starter-parent` from 3.5.11 to 3.5.16
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.11...v3.5.16)
Updates `org.springdoc:springdoc-openapi-starter-webmvc-ui` from 2.8.6 to 2.8.17
- [Release notes](https://github.com/springdoc/springdoc-openapi/releases)
- [Changelog](https://github.com/springdoc/springdoc-openapi/blob/main/CHANGELOG.md)
- [Commits](https://github.com/springdoc/springdoc-openapi/compare/v2.8.6...v2.8.17)
Updates `io.jsonwebtoken:jjwt-api` from 0.12.6 to 0.13.0
- [Release notes](https://github.com/jwtk/jjwt/releases)
- [Changelog](https://github.com/jwtk/jjwt/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jwtk/jjwt/compare/0.12.6...0.13.0)
Updates `io.jsonwebtoken:jjwt-impl` from 0.12.6 to 0.13.0
- [Release notes](https://github.com/jwtk/jjwt/releases)
- [Changelog](https://github.com/jwtk/jjwt/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jwtk/jjwt/compare/0.12.6...0.13.0)
Updates `io.jsonwebtoken:jjwt-jackson` from 0.12.6 to 0.13.0
Updates `com.google.protobuf:protobuf-java` from 4.29.3 to 4.35.1
- [Release notes](https://github.com/protocolbuffers/protobuf/releases)
- [Commits](https://github.com/protocolbuffers/protobuf/commits)
---
updated-dependencies:
- dependency-name: org.springframework.boot:spring-boot-starter-parent
dependency-version: 3.5.16
dependency-type: direct:production
update-type: version-update:semver-patch
dependency-group: minor-and-patch
- dependency-name: org.springdoc:springdoc-openapi-starter-webmvc-ui
dependency-version: 2.8.17
dependency-type: direct:production
update-type: version-update:semver-patch
dependency-group: minor-and-patch
- dependency-name: io.jsonwebtoken:jjwt-api
dependency-version: 0.13.0
dependency-type: direct:production
update-type: version-update:semver-minor
dependency-group: minor-and-patch
- dependency-name: io.jsonwebtoken:jjwt-impl
dependency-version: 0.13.0
dependency-type: direct:production
update-type: version-update:semver-minor
dependency-group: minor-and-patch
- dependency-name: io.jsonwebtoken:jjwt-jackson
dependency-version: 0.13.0
dependency-type: direct:production
update-type: version-update:semver-minor
dependency-group: minor-and-patch
- dependency-name: com.google.protobuf:protobuf-java
dependency-version: 4.35.1
dependency-type: direct:production
update-type: version-update:semver-minor
dependency-group: minor-and-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
services/game-service/pom.xml | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/services/game-service/pom.xml b/services/game-service/pom.xml
index b16862a..1112036 100644
--- a/services/game-service/pom.xml
+++ b/services/game-service/pom.xml
@@ -5,7 +5,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.5.11
+ 3.5.16
com.checkers
@@ -16,7 +16,7 @@
21
1.82.1
- 4.29.3
+ 4.35.1
0.8.15
@@ -43,7 +43,7 @@
org.springdoc
springdoc-openapi-starter-webmvc-ui
- 2.8.6
+ 2.8.17
org.springframework.boot
@@ -79,18 +79,18 @@
io.jsonwebtoken
jjwt-api
- 0.12.6
+ 0.13.0
io.jsonwebtoken
jjwt-impl
- 0.12.6
+ 0.13.0
runtime
io.jsonwebtoken
jjwt-jackson
- 0.12.6
+ 0.13.0
runtime
From f465e779bf586652b56af9a24d2605c826e08a13 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 6 Jul 2026 14:37:13 +0200
Subject: [PATCH 4/9] build(deps): bump the minor-and-patch group (#72)
Bumps the minor-and-patch group in /services/user-service with 6 updates:
| Package | From | To |
| --- | --- | --- |
| [org.springframework.boot:spring-boot-starter-parent](https://github.com/spring-projects/spring-boot) | `3.5.11` | `3.5.16` |
| [org.springdoc:springdoc-openapi-starter-webmvc-ui](https://github.com/springdoc/springdoc-openapi) | `2.8.6` | `2.8.17` |
| [com.google.protobuf:protobuf-java](https://github.com/protocolbuffers/protobuf) | `4.29.3` | `4.35.1` |
| [io.jsonwebtoken:jjwt-api](https://github.com/jwtk/jjwt) | `0.12.6` | `0.13.0` |
| [io.jsonwebtoken:jjwt-impl](https://github.com/jwtk/jjwt) | `0.12.6` | `0.13.0` |
| io.jsonwebtoken:jjwt-jackson | `0.12.6` | `0.13.0` |
Updates `org.springframework.boot:spring-boot-starter-parent` from 3.5.11 to 3.5.16
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.11...v3.5.16)
Updates `org.springdoc:springdoc-openapi-starter-webmvc-ui` from 2.8.6 to 2.8.17
- [Release notes](https://github.com/springdoc/springdoc-openapi/releases)
- [Changelog](https://github.com/springdoc/springdoc-openapi/blob/main/CHANGELOG.md)
- [Commits](https://github.com/springdoc/springdoc-openapi/compare/v2.8.6...v2.8.17)
Updates `com.google.protobuf:protobuf-java` from 4.29.3 to 4.35.1
- [Release notes](https://github.com/protocolbuffers/protobuf/releases)
- [Commits](https://github.com/protocolbuffers/protobuf/commits)
Updates `io.jsonwebtoken:jjwt-api` from 0.12.6 to 0.13.0
- [Release notes](https://github.com/jwtk/jjwt/releases)
- [Changelog](https://github.com/jwtk/jjwt/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jwtk/jjwt/compare/0.12.6...0.13.0)
Updates `io.jsonwebtoken:jjwt-impl` from 0.12.6 to 0.13.0
- [Release notes](https://github.com/jwtk/jjwt/releases)
- [Changelog](https://github.com/jwtk/jjwt/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jwtk/jjwt/compare/0.12.6...0.13.0)
Updates `io.jsonwebtoken:jjwt-jackson` from 0.12.6 to 0.13.0
---
updated-dependencies:
- dependency-name: org.springframework.boot:spring-boot-starter-parent
dependency-version: 3.5.16
dependency-type: direct:production
update-type: version-update:semver-patch
dependency-group: minor-and-patch
- dependency-name: org.springdoc:springdoc-openapi-starter-webmvc-ui
dependency-version: 2.8.17
dependency-type: direct:production
update-type: version-update:semver-patch
dependency-group: minor-and-patch
- dependency-name: com.google.protobuf:protobuf-java
dependency-version: 4.35.1
dependency-type: direct:production
update-type: version-update:semver-minor
dependency-group: minor-and-patch
- dependency-name: io.jsonwebtoken:jjwt-api
dependency-version: 0.13.0
dependency-type: direct:production
update-type: version-update:semver-minor
dependency-group: minor-and-patch
- dependency-name: io.jsonwebtoken:jjwt-impl
dependency-version: 0.13.0
dependency-type: direct:production
update-type: version-update:semver-minor
dependency-group: minor-and-patch
- dependency-name: io.jsonwebtoken:jjwt-jackson
dependency-version: 0.13.0
dependency-type: direct:production
update-type: version-update:semver-minor
dependency-group: minor-and-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
services/user-service/pom.xml | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/services/user-service/pom.xml b/services/user-service/pom.xml
index 3a74816..1553453 100644
--- a/services/user-service/pom.xml
+++ b/services/user-service/pom.xml
@@ -5,7 +5,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.5.11
+ 3.5.16
com.checkers
@@ -29,7 +29,7 @@
21
1.82.1
- 4.29.3
+ 4.35.1
0.8.15
@@ -73,7 +73,7 @@
org.springdoc
springdoc-openapi-starter-webmvc-ui
- 2.8.6
+ 2.8.17
@@ -128,18 +128,18 @@
io.jsonwebtoken
jjwt-api
- 0.12.6
+ 0.13.0
io.jsonwebtoken
jjwt-impl
- 0.12.6
+ 0.13.0
runtime
io.jsonwebtoken
jjwt-jackson
- 0.12.6
+ 0.13.0
runtime
From 349ff93934bc92fbe9fbcf809b621a2f09f35a05 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 6 Jul 2026 14:37:15 +0200
Subject: [PATCH 5/9] build(deps): bump the minor-and-patch group (#74)
Bumps the minor-and-patch group in /services/matchmaking-service with 6 updates:
| Package | From | To |
| --- | --- | --- |
| [org.springframework.boot:spring-boot-starter-parent](https://github.com/spring-projects/spring-boot) | `3.5.11` | `3.5.16` |
| [org.springdoc:springdoc-openapi-starter-webmvc-ui](https://github.com/springdoc/springdoc-openapi) | `2.8.6` | `2.8.17` |
| [io.jsonwebtoken:jjwt-api](https://github.com/jwtk/jjwt) | `0.12.6` | `0.13.0` |
| [io.jsonwebtoken:jjwt-impl](https://github.com/jwtk/jjwt) | `0.12.6` | `0.13.0` |
| io.jsonwebtoken:jjwt-jackson | `0.12.6` | `0.13.0` |
| [org.jacoco:jacoco-maven-plugin](https://github.com/jacoco/jacoco) | `0.8.13` | `0.8.15` |
Updates `org.springframework.boot:spring-boot-starter-parent` from 3.5.11 to 3.5.16
- [Release notes](https://github.com/spring-projects/spring-boot/releases)
- [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.11...v3.5.16)
Updates `org.springdoc:springdoc-openapi-starter-webmvc-ui` from 2.8.6 to 2.8.17
- [Release notes](https://github.com/springdoc/springdoc-openapi/releases)
- [Changelog](https://github.com/springdoc/springdoc-openapi/blob/main/CHANGELOG.md)
- [Commits](https://github.com/springdoc/springdoc-openapi/compare/v2.8.6...v2.8.17)
Updates `io.jsonwebtoken:jjwt-api` from 0.12.6 to 0.13.0
- [Release notes](https://github.com/jwtk/jjwt/releases)
- [Changelog](https://github.com/jwtk/jjwt/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jwtk/jjwt/compare/0.12.6...0.13.0)
Updates `io.jsonwebtoken:jjwt-impl` from 0.12.6 to 0.13.0
- [Release notes](https://github.com/jwtk/jjwt/releases)
- [Changelog](https://github.com/jwtk/jjwt/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jwtk/jjwt/compare/0.12.6...0.13.0)
Updates `io.jsonwebtoken:jjwt-jackson` from 0.12.6 to 0.13.0
Updates `org.jacoco:jacoco-maven-plugin` from 0.8.13 to 0.8.15
- [Release notes](https://github.com/jacoco/jacoco/releases)
- [Commits](https://github.com/jacoco/jacoco/compare/v0.8.13...v0.8.15)
---
updated-dependencies:
- dependency-name: org.springframework.boot:spring-boot-starter-parent
dependency-version: 3.5.16
dependency-type: direct:production
update-type: version-update:semver-patch
dependency-group: minor-and-patch
- dependency-name: org.springdoc:springdoc-openapi-starter-webmvc-ui
dependency-version: 2.8.17
dependency-type: direct:production
update-type: version-update:semver-patch
dependency-group: minor-and-patch
- dependency-name: io.jsonwebtoken:jjwt-api
dependency-version: 0.13.0
dependency-type: direct:production
update-type: version-update:semver-minor
dependency-group: minor-and-patch
- dependency-name: io.jsonwebtoken:jjwt-impl
dependency-version: 0.13.0
dependency-type: direct:production
update-type: version-update:semver-minor
dependency-group: minor-and-patch
- dependency-name: io.jsonwebtoken:jjwt-jackson
dependency-version: 0.13.0
dependency-type: direct:production
update-type: version-update:semver-minor
dependency-group: minor-and-patch
- dependency-name: org.jacoco:jacoco-maven-plugin
dependency-version: 0.8.15
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: minor-and-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
services/matchmaking-service/pom.xml | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/services/matchmaking-service/pom.xml b/services/matchmaking-service/pom.xml
index a57b72a..132d290 100644
--- a/services/matchmaking-service/pom.xml
+++ b/services/matchmaking-service/pom.xml
@@ -5,7 +5,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.5.11
+ 3.5.16
com.checkers
@@ -17,7 +17,7 @@
21
1.82.1
4.35.1
- 0.8.13
+ 0.8.15