Player Report and Abuse-Moderation Pipelines for Community Game Servers: Webhooks, Queues and Evidence Retention

Every community server eventually receives a report it cannot ignore: a player accused of cheating, harassment, or exploiting a duplication bug, with a screenshot and a timestamp that may or may not be accurate. Handling that report well is an operational problem, not a moral one. It needs a durable intake path, deduplication, a queue with priorities and ownership, and an evidence policy that survives an appeal three weeks later.

The pattern that works at scale is a webhook into a small queue, a worker that enriches the payload with server-side context, and a moderation dashboard that drains the queue in priority order. This article walks the pipeline end to end, including the retention rules that keep storage and privacy under control.

Why a chat channel is not an intake system

Most servers start with reports arriving in a Discord channel or a staff inbox. That works to about fifty reports a week and then collapses for three reasons: no state, no ownership, and no ordering. A message in a busy channel has no notion of “claimed by a moderator,” no SLA clock, and no way to tell whether it was actioned or scrolled past.

A structured intake gives every report an identifier, a status, and an assignee from the moment it arrives. That single change is what makes it possible to answer “what happened to my report” without a moderator searching six weeks of chat history, and it turns moderation from an act of memory into a routine.

Designing the webhook payload

The payload should carry only what the client can know and nothing it can forge into privileged fields. Send the reporter identity, the accused identity, the category, a free-text description, and attachments by reference — never by inlined base64, which will blow past body limits. Everything authoritative (player IP, session ID, tick number, prior offence count) is added server-side by the receiving worker.

Sign the request with an HMAC over the raw body and a shared secret, and include a timestamp so replays outside a five-minute window can be rejected. Treat the client as untrusted: a modified game client can send any payload you accept, so the receiver must validate types, clamp string lengths, and rate-limit per reporter.

POST /moderation/ingest
Content-Type: application/json
X-Signature: sha256=<hmac>
X-Timestamp: 1761472800

{
  "report_id": "c7f1e2d0-4b1a-4f2e-9c1d-7a3b2c9d4e10",
  "server_id": "survival-eu-03",
  "category": "cheat|harassment|exploit|spam",
  "reporter": {"uuid": "0d8f...", "name": "Kestrel"},
  "accused":  {"uuid": "a41c...", "name": "Volt"},
  "occurred_at": "2026-10-26T05:12:44Z",
  "description": "x-ray through spawn wall, see demo",
  "attachments": [{"kind": "replay", "ref": "s3://evidence/2026/10/c7f1e2d0.mcap"}],
  "client_version": "1.21.4"
}
Check Where Pass Warn
HMAC signature valid Edge 100% accepted any 401 spike
UUID pair resolves Worker > 99.5% < 98%
Dedupe collapse rate Queue 10–25% > 50% (brigading)
Enrichment complete Worker > 95% in 60 s > 5 min
Live-lane first response Moderator < 15 min > 60 min
Evidence locked at ingest Bucket 100% of upheld any missing
Appeal audit row DB 100% of actions any orphan

Reject anything without a signature, a recognised category, and a valid UUID pair. A report that cannot be tied to two real identities is not actionable and should be dropped at the edge rather than queued, because garbage in the queue is what makes moderators stop reading it.

Queue mechanics: priorities, dedupe, and drain rate

Use two queues: a fast lane for live abuse (harassment, doxxing, threats) with a fifteen-minute target, and a standard lane for cheat and exploit reports with a seventy-two-hour target. Deduplicate on the tuple of accused UUID, category, and a six-hour bucket, incrementing a counter rather than creating a second row. Ten reports about the same player in ten minutes are one case with weight ten, not ten cases.

Drain rate matters more than total throughput. A worker that handles fifty items a second is useless if moderators can review three per minute; the queue should expose a per-moderator claim endpoint that leases an item for a fixed window and releases it automatically on timeout. Leases prevent two staff handling the same case and prevent a case being silently held by someone who went offline.

Server-side enrichment before a human looks

By the time a moderator opens a case, the worker should have attached the context that narrows the decision: session join and leave times for both parties, the accused player’s recent chat excerpts, prior upheld reports in the last ninety days, and the world coordinates at the reported timestamp if the game exposes them. Enrichment turns a five-minute investigation into a thirty-second one.

Run enrichment asynchronously and mark the case “incomplete” until it finishes, rather than blocking ingestion. If an enrichment source is down — the log shipper, the chat index — record that as a degraded field instead of failing the report, so the pipeline degrades rather than stops.

Evidence retention: capture early, delete on schedule

Evidence has a short half-life. Chat logs rotate, replays are overwritten, and a world backup from the relevant moment may be gone within a week. Capture at ingest: pull the last 200 chat lines for both parties, freeze any replay reference, and copy the region file header for the reported coordinates into an evidence bucket with an object lock.

Then delete on schedule. A defensible default is thirty days for closed cases with no action, ninety days for upheld cases, and one year for bans and appeals. Store only the fields you need, hash player IPs unless a security review requires them, and record the retention class on every object so a deletion job can run from metadata rather than guesswork.

Moderator actions, audit trail, and appeals

Every action taken from the queue — warn, mute, tempban, permanent ban, dismiss — should write an immutable audit row containing the moderator, the case ID, the evidence IDs relied on, and the exact reason text shown to the player. That row is what makes an appeal reviewable by someone who was not involved, and it is also the only reliable way to spot moderators who are over- or under-enforcing.

Close the loop with the reporter. A one-line automated message saying the case was actioned, dismissed, or needs more information costs almost nothing and roughly halves repeat submissions. Players report more reliably when they can see that reporting works.

Sizing the pipeline to your player count

Report volume scales sublinearly with players but superlinearly with competitive intensity. A 500-slot survival network typically sees 20 to 60 reports a day; a competitive shooter of the same size can see 300. Size storage and moderator hours from measured volume after two weeks, not from an estimate.

The pipeline itself is small: one endpoint, one queue, one worker, one dashboard. It will run comfortably on modest hardware, so the real constraint is moderator attention. For nodes that will host the game servers and the moderation stack side by side, see the game-ready configurations with measured clock and storage details; the same hardware guidance applies to servers built for sustained game workloads.

Leave a Reply