Skip to content

feat(server): private match telemetry stream#4687

Open
Celant wants to merge 1 commit into
mainfrom
feat/private-match-telemetry
Open

feat(server): private match telemetry stream#4687
Celant wants to merge 1 commit into
mainfrom
feat/private-match-telemetry

Conversation

@Celant

@Celant Celant commented Jul 23, 2026

Copy link
Copy Markdown
Member

Add approved & assigned issue number here:

Resolves #(issue number)

Note: please link the approved/assigned issue before review — I didn't have the number to hand.

Description:

Adds a generic, private match-telemetry stream to the game server for operational observability. It records match lifecycle events and server-ingress intent decisions and ships them asynchronously to a single authenticated ingest endpoint.

  • Best-effort and bounded — a signed, size-bounded buffered emitter that never blocks or affects gameplay.
  • Opt-in via environment variables (see example.env); disabled by default, with no change to server behavior when off.
  • The deterministic public replay (GameRecord) is unchanged.

Changes:

  • src/server/telemetry/MatchTelemetry.ts — telemetry event/payload types
  • src/server/telemetry/MatchTelemetryConfig.ts — env-driven configuration
  • src/server/telemetry/BufferedMatchTelemetryEmitter.ts — bounded, signed, async buffered emitter
  • src/server/GameServer.ts, GameManager.ts, Worker.ts — observe match lifecycle + intent outcomes and wire telemetry through the workers
  • example.env — new configuration entries
  • Unit + integration tests (33 tests)

Please complete the following:

  • I have added screenshots for all UI updates — N/A (server-only, no UI)
  • I process any text displayed to the user through translateText() and I've added it to the en.json file — N/A (no user-facing text)
  • I have added relevant tests to the test directory

Please put your Discord username so you can be contacted if a bug or regression is found:

jish

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds opt-in match telemetry with typed events, validated configuration, bounded signed batching, lifecycle and intent instrumentation, worker integration, and automated tests.

Changes

Match telemetry

Layer / File(s) Summary
Telemetry contracts and configuration
example.env, src/server/telemetry/MatchTelemetry.ts, src/server/telemetry/MatchTelemetryConfig.ts, tests/server/telemetry/*
Adds typed events, counters, a no-op emitter, and validated environment settings.
Buffered signed delivery
src/server/telemetry/BufferedMatchTelemetryEmitter.ts, tests/server/telemetry/BufferedMatchTelemetryEmitter.test.ts
Adds bounded batching, per-player caps, HMAC-signed POST delivery, timeout handling, and delivery tests.
GameServer lifecycle and intent instrumentation
src/server/GameServer.ts, tests/server/MatchTelemetryIntegration.test.ts
Emits match, player, intent, turn, and finish events with rejection reasons and per-turn counters.
Worker telemetry wiring
src/server/Worker.ts, src/server/GameManager.ts
Creates worker telemetry, forwards it through game creation, includes the build hash, sizes WebSocket payloads, and stops telemetry on server close.

Repository hygiene

Layer / File(s) Summary
Local worktree ignore rule
.gitignore
Ignores the .worktrees/ directory.

Estimated code review effort: 5 (Critical) | ~90 minutes

Suggested reviewers: evanpelle

Poem

Events fly in batches bright,
Signed with care through server night.
Turns are counted, intents flow,
Safe queues catch the overflow.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change: adding a private match telemetry stream to the server.
Description check ✅ Passed The description clearly matches the telemetry feature, wiring, config, and tests in this changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Celant
Celant force-pushed the feat/private-match-telemetry branch from 614ea71 to 418aae3 Compare July 23, 2026 14:10

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/server/telemetry/MatchTelemetry.ts`:
- Around line 94-117: Replace the shared mutable ZERO_COUNTERS usage in
noopMatchTelemetryEmitter with a true no-op emitter: emit must return "dropped"
without mutating module-level state, and counters must return a fresh empty
MatchTelemetryCounters object for each call. Keep stop unchanged and ensure
disabled emitters do not share totals across GameServer or GameManager
instances.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 23378fcc-19da-4944-b1fb-473eccc035cd

📥 Commits

Reviewing files that changed from the base of the PR and between 2ecdbdf and 614ea71.

📒 Files selected for processing (11)
  • .gitignore
  • example.env
  • src/server/GameManager.ts
  • src/server/GameServer.ts
  • src/server/Worker.ts
  • src/server/telemetry/BufferedMatchTelemetryEmitter.ts
  • src/server/telemetry/MatchTelemetry.ts
  • src/server/telemetry/MatchTelemetryConfig.ts
  • tests/server/MatchTelemetryIntegration.test.ts
  • tests/server/telemetry/BufferedMatchTelemetryEmitter.test.ts
  • tests/server/telemetry/MatchTelemetryConfig.test.ts

Comment thread src/server/telemetry/MatchTelemetry.ts Outdated
@github-project-automation github-project-automation Bot moved this from Triage to Development in OpenFront Release Management Jul 23, 2026
@Celant Celant added this to the Backlog milestone Jul 23, 2026
@Celant
Celant force-pushed the feat/private-match-telemetry branch from 418aae3 to 5127bb1 Compare July 23, 2026 14:31

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/server/telemetry/MatchTelemetryConfig.ts (1)

1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Magic number duplicated with the websocket payload cap.

MAX_WEBSOCKET_PAYLOAD_BYTES (1MB) mirrors the maxPayload: 1024 * 1024 set on the WebSocketServer in Worker.ts. Since these two are only related in intent (not in code), they can silently drift if one is changed without the other.

♻️ Suggested fix: share the constant
-const MAX_WEBSOCKET_PAYLOAD_BYTES = 1024 * 1024;
+export const MAX_WEBSOCKET_PAYLOAD_BYTES = 1024 * 1024;

Then import and reuse it in Worker.ts's WebSocketServer construction instead of the separate literal.

Also applies to: 31-34

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/telemetry/MatchTelemetryConfig.ts` at line 1, Export
MAX_WEBSOCKET_PAYLOAD_BYTES from MatchTelemetryConfig and update the
WebSocketServer construction in Worker.ts to import and reuse it for maxPayload,
removing the duplicated 1024 * 1024 literal.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/server/GameServer.ts`:
- Around line 606-617: Update the default gameplay-intent path in GameServer’s
intent handling so paused intents receive a distinct telemetry reasonCode such
as "paused" instead of being indistinguishable from queued accepted intents.
Preserve the existing admin-bot rejection and queue behavior for unpaused
intents, and ensure the reason is attached to the outcome returned by finish.

In `@src/server/telemetry/MatchTelemetryConfig.ts`:
- Around line 73-74: Update the TELEMETRY_SIGNING_SECRET validation in
MatchTelemetryConfig to reject secrets shorter than the required minimum
strength, such as 32 characters, and return disabled with a clear validation
message. Preserve the existing enabled configuration path for secrets meeting
the minimum length.

---

Nitpick comments:
In `@src/server/telemetry/MatchTelemetryConfig.ts`:
- Line 1: Export MAX_WEBSOCKET_PAYLOAD_BYTES from MatchTelemetryConfig and
update the WebSocketServer construction in Worker.ts to import and reuse it for
maxPayload, removing the duplicated 1024 * 1024 literal.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c659b925-85d1-484e-8f57-3879c299de02

📥 Commits

Reviewing files that changed from the base of the PR and between 418aae3 and 5127bb1.

📒 Files selected for processing (12)
  • .gitignore
  • example.env
  • src/server/GameManager.ts
  • src/server/GameServer.ts
  • src/server/Worker.ts
  • src/server/telemetry/BufferedMatchTelemetryEmitter.ts
  • src/server/telemetry/MatchTelemetry.ts
  • src/server/telemetry/MatchTelemetryConfig.ts
  • tests/server/MatchTelemetryIntegration.test.ts
  • tests/server/telemetry/BufferedMatchTelemetryEmitter.test.ts
  • tests/server/telemetry/MatchTelemetry.test.ts
  • tests/server/telemetry/MatchTelemetryConfig.test.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • .gitignore
  • src/server/Worker.ts
  • example.env
  • src/server/GameManager.ts
  • tests/server/telemetry/MatchTelemetryConfig.test.ts
  • src/server/telemetry/BufferedMatchTelemetryEmitter.ts
  • tests/server/telemetry/BufferedMatchTelemetryEmitter.test.ts
  • tests/server/MatchTelemetryIntegration.test.ts

Comment thread src/server/GameServer.ts
Comment thread src/server/telemetry/MatchTelemetryConfig.ts
@Celant
Celant force-pushed the feat/private-match-telemetry branch from 5127bb1 to 2f86d52 Compare July 23, 2026 15:16

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/server/Worker.ts`:
- Around line 62-67: Update the telemetry configuration in the Worker
initialization to pass the existing normalized workerId local value instead of
calling ServerEnv.workerId() again. Keep the routing identity and telemetry
identity consistent when the environment value is absent.
- Line 69: Update startWorker and its shutdown/fatal-exit flows to explicitly
close the worker server and await telemetry completion rather than relying only
on the server "close" handler. Extend the telemetry emitter lifecycle with an
asynchronous flush/stop operation that drains already-enqueued events before
exit, and invoke it during shutdown while preserving prevention of future
batching.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d92b9596-528f-4284-a238-b0ff3c12a8bb

📥 Commits

Reviewing files that changed from the base of the PR and between 5127bb1 and 2f86d52.

📒 Files selected for processing (12)
  • .gitignore
  • example.env
  • src/server/GameManager.ts
  • src/server/GameServer.ts
  • src/server/Worker.ts
  • src/server/telemetry/BufferedMatchTelemetryEmitter.ts
  • src/server/telemetry/MatchTelemetry.ts
  • src/server/telemetry/MatchTelemetryConfig.ts
  • tests/server/MatchTelemetryIntegration.test.ts
  • tests/server/telemetry/BufferedMatchTelemetryEmitter.test.ts
  • tests/server/telemetry/MatchTelemetry.test.ts
  • tests/server/telemetry/MatchTelemetryConfig.test.ts
🚧 Files skipped from review as they are similar to previous changes (11)
  • tests/server/telemetry/MatchTelemetry.test.ts
  • .gitignore
  • src/server/GameManager.ts
  • example.env
  • src/server/telemetry/MatchTelemetryConfig.ts
  • tests/server/telemetry/MatchTelemetryConfig.test.ts
  • src/server/telemetry/MatchTelemetry.ts
  • tests/server/MatchTelemetryIntegration.test.ts
  • src/server/telemetry/BufferedMatchTelemetryEmitter.ts
  • tests/server/telemetry/BufferedMatchTelemetryEmitter.test.ts
  • src/server/GameServer.ts

Comment thread src/server/Worker.ts
Comment thread src/server/Worker.ts
Add a generic, private match-telemetry stream for operational
observability. It records match lifecycle events and server-ingress
intent decisions and ships them asynchronously to a single
authenticated ingest endpoint.

- Best-effort and bounded: a signed, size-bounded buffered emitter
  that never blocks gameplay.
- Opt-in via environment variables (see example.env); disabled by
  default, with no change to server behavior when off.
- The deterministic public replay (GameRecord) is unchanged.

Includes unit and integration tests for the emitter, config, and
server wiring.
@Celant
Celant force-pushed the feat/private-match-telemetry branch from 2f86d52 to d1bd49f Compare July 23, 2026 15:28
@Celant

Celant commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai resolve

@Celant

Celant commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Celant

Celant commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Comments resolved and changes approved.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/server/GameServer.ts`:
- Around line 235-241: Update identityFor to hash client.persistentID before
assigning it to the telemetry identity, using the existing shared hashing helper
for opaque player keys. Ensure all identifiers emitted through telemetry
identity fields are hashed and no raw persistentID reaches telemetry.emit.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 502715ee-3375-4e70-8007-cc6c31f8c7c8

📥 Commits

Reviewing files that changed from the base of the PR and between cade1cc and d1bd49f.

📒 Files selected for processing (12)
  • .gitignore
  • example.env
  • src/server/GameManager.ts
  • src/server/GameServer.ts
  • src/server/Worker.ts
  • src/server/telemetry/BufferedMatchTelemetryEmitter.ts
  • src/server/telemetry/MatchTelemetry.ts
  • src/server/telemetry/MatchTelemetryConfig.ts
  • tests/server/MatchTelemetryIntegration.test.ts
  • tests/server/telemetry/BufferedMatchTelemetryEmitter.test.ts
  • tests/server/telemetry/MatchTelemetry.test.ts
  • tests/server/telemetry/MatchTelemetryConfig.test.ts

Comment thread src/server/GameServer.ts
@Celant

Celant commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

Intentional design — not applying this one.

This private telemetry stream is deliberately built to carry raw identifiers. The payload is HMAC-signed and delivered to a single authenticated HTTPS ingest endpoint, and downstream consumers depend on the raw persistentID to correlate events for a given player across a match. Hashing it at the server would break that correlation contract, which the integration test pins explicitly (emits opened, joined, started, and finished events with raw identity).

So this is expected behavior rather than a leak. Marking as won't-fix.

@Celant

Celant commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai resolve

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Comments resolved and changes approved.

Comment thread src/server/GameServer.ts
return {
clientId: client.clientID,
publicId: client.publicId,
persistentId: client.persistentID,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to be safe should we omit the persistentId?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Development

Development

Successfully merging this pull request may close these issues.

2 participants