# ServerStats Backend Plugin Integration
This document outlines the backend capabilities the ServerStats plugin needs so it can push telemetry into the platform and retrieve insights for in-game surfaces. It covers API contracts, data schemas, security requirements, and operations considerations.
## 1. Goals & Assumptions
- **Targets Minecraft Java network operators.** Plugin runs on Paper/Fabric servers living behind a proxy (Velocity/Bungee). Multiple shards per network.
- **Data volume:** thousands of concurrent players; telemetry sent every 10–15 seconds.
- **Two-directional:** plugin needs to *push* real-time events (players, chat, vault, shard health) and *pull* insights (alerts, retention plays, tasks).
- **Zero-knowledge of infra:** plugin only knows shard name + API credentials; backend hydrates dashboards and automations.
## 2. Authentication & Provisioning
| Flow | Description | API |
| --- | --- | --- |
| Provision API key | Admin presses “Create key” in Console. Key is scoped to a network + shard and now seeds a stable shard UUID the plugin must persist. | `POST /api/plugin-keys { shardId, label } → { apiKey, shardToken, shardUuid, shardSecret }` |
| Plugin bootstrap | Plugin configured with `apiKey`, `shardToken`, `shardUuid`, `shardSecret`, `baseUrl`. On start it exchanges key for short-lived JWT. | `POST /api/plugin-auth { apiKey, shardToken, shardUuid, signature } → { accessToken, expiresIn }` |
| Token refresh | Plugin refreshes before expiry. | `POST /api/plugin-auth/refresh` |
Console operators can mint a key + shard UUID via the API or a curl invocation:
```bash
curl -X POST https://console.serverstats.gg/api/plugin-keys \
-H "Authorization: Bearer <console-session-token>" \
-H "Content-Type: application/json" \
-d '{ "shardId": 2, "label": "Velocity Proxy" }'
# → { "apiKey": "sk_a12...", "shardToken": "st_b77...", "shardUuid": "3f23f20c-7b5c-4f10-98a4-7a502c8f3275", "shardSecret": "1c2da..." }
Before calling `/api/plugin-auth`, the plugin signs the payload using the `shardSecret`:
```bash
payload='{ "apiKey": "sk_a12...", "shardToken": "st_b77...", "shardUuid": "3f23..." }'
signature=$(echo -n "$payload" | openssl dgst -sha256 -hmac "1c2da..." | awk '{print $2}')
curl -X POST https://ingest.serverstats.gg/api/plugin-auth \
-H "Content-Type: application/json" \
-d "{ \"apiKey\": \"sk_a12...\", \"shardToken\": \"st_b77...\", \"shardUuid\": \"3f23...\", \"signature\": \"$signature\" }"
# → { "accessToken": "1db4...", "expiresIn": 900 }
```
Refresh uses the short payload:
```bash
curl -X POST https://ingest.serverstats.gg/api/plugin-auth/refresh \
-H "Content-Type: application/json" \
-d '{ "accessToken": "1db4..." }'
```
```
All plugin requests include:
- `Authorization: Bearer <accessToken>`
- `X-Signature: HMAC_SHA256(body, shardSecret)`
- `X-Shard-ID`
- `X-Shard-UUID`
- `X-Plugin-Version`
The plugin must also include the UUID and signature in any ping/backoff calls. Example health check (used by `/api/plugin/health`):
```bash
curl https://ingest.serverstats.gg/api/plugin/health \
-H "Authorization: Bearer <accessToken>" \
-H "X-Shard-ID: 2" \
-H "X-Shard-UUID: 3f23f20c-7b5c-4f10-98a4-7a502c8f3275" \
-H "X-Signature: $(printf '' | openssl dgst -sha256 -hmac 1c2da... | awk '{print $2}')"
```
Every plugin request follows this envelope:
1. Compute `signature = HMAC_SHA256(body, shardSecret)` (sign an empty string for GETs).
2. Send `Authorization: Bearer <accessToken>` plus shard headers (`X-Shard-ID`, `X-Shard-UUID`).
3. Include `X-Plugin-Version` for rollout coordination.
## 3. Telemetry Ingest APIs
### 3.1 Players Snapshot
```
POST /api/ingest/players
{
"shardId": 2,
"collectedAt": "2024-07-23T19:02:11.213Z",
"online": 612,
"queue": 23,
"uniqueJoins": 84,
"tps": 19.78,
"latencyMs": 63,
"topPlayers": [
{
"playerId": 39102,
"name": "QuartzLion",
"sessionMinutes": 64,
"vaultBalance": 684120,
"netWorth": 1000200,
"retentionRate": 82
}
]
}
```
Backend persists payloads in `plugin_ingest_events` (type `players-snapshot`) and updates player summaries.
### 3.2 Player Stream Events
| Event | Endpoint | Payload highlights |
| --- | --- | --- |
| Login/logout | `POST /api/ingest/player-events` | `{ eventType: "LOGIN"|"LOGOUT", playerId, uuid, ip, timestamp }` |
| Chat | `POST /api/ingest/chat` | `{ channel: "PUBLIC"|"PRIVATE", fromPlayerId, toPlayerId?, message, sentimentScore }` |
| Vault transaction | `POST /api/ingest/vault` | `{ txnId, playerId, amount, direction, counterpart }` |
| Punishment | `POST /api/ingest/punishment` | `{ eventType: "BAN"|"MUTE"|..., moderator, expiresAt }` |
### 3.3 Economy & Performance
```
POST /api/ingest/performance
{
"shardId": 2,
"collectedAt": "...",
"ticksPerSecond": 19.6,
"cpuPercent": 68,
"memoryPercent": 72,
"chunksLoaded": 4823,
"hopperCount": 602
}
```
```
POST /api/ingest/economy
{
"shardId": 2,
"collectedAt": "...",
"auctionVolume": 182000,
"shopVolume": 92000,
"emeraldInflux": 128000,
"flaggedVolume": 12000
}
```
## 4. Pull APIs (Plugin → Console)
| Use case | Endpoint | Notes |
| --- | --- | --- |
| Fetch retention actions | `GET /api/plugin/shards/:id/retention-sprints` | Plugin shows tasks to staff (e.g., “Grant VIP crate to player X”). |
| Live alerts | `GET /api/plugin/shards/:id/alerts?since=<timestamp>` | Returns flagged fraud, TPS drops, etc. |
| Automation queue | `GET /api/plugin/shards/:id/automations` | For autop-run commands (kick bots, throttle farms). |
| Player intelligence lookup | `GET /api/plugin/players/:playerId` | Serve `/inspect player` command in-game. |
| Shop purchase hook | `POST /api/ingest/shop-purchase` | ShopGUI+, Tebex, or custom web store posts purchases here (player, sku, price, currency, paymentMethod, processor, orderId). Plugin can capture ShopGUI+ console logs and forward them. |
Responses should be lightweight (small lists with `nextCursor` to support pagination).
Implemented pull routes:
- `GET /api/plugin/shards/:id/retention-sprints`
- `GET /api/plugin/shards/:id/alerts?since=<timestamp>`
- `GET /api/plugin/shards/:id/automations`
- `GET /api/plugin/shards/:id/config`
- `GET /api/plugin/players/:playerId`
## 5. Data Model Requirements
- **Players:** full schema includes `id`, `uuid`, `name`, `shard_id`, `first_seen_at`, `last_login_at`, `playtime_minutes`, `session_count`, `retention_rate`, `risk_level`, `vault_balance`, `net_worth`, `discord_id?`, `country?`, `client_version`, `edition`, `linked_accounts(jsonb)`, `ranks(jsonb)`, `shop_purchases(jsonb)`, `flags(jsonb)` for automation tags.
- **Player Sessions:** `id`, `player_id`, `shard_id`, `started_at`, `ended_at`, `duration_minutes`, `avg_tps`, `avg_latency`, `actions(jsonb)` for commands or triggers.
- **Chat Stream:** `id`, `player_id`, `shard_id`, `channel`, `message`, `sentiment`, `toxicity_score`, `created_at`, `target_player_id?`.
- **Vault Transactions:** `id`, `shard_id`, `player_id`, `amount`, `direction`, `currency`, `counterparty_type`, `counterparty_id`, `risk_flag`, `created_at`.
- **Player Purchases:** `id`, `player_id`, `shard_id`, `sku`, `price`, `currency`, `source` (web/shop), `payment_method`, `processor` (Stripe, Tebex, BTC wallet), `order_id`, `receipt_metadata`, `created_at`. ShopGUI+ must emit events/logs so plugin forwards them to the backend within a few seconds of purchase.
- **Ranks/Permissions:** `id`, `player_id`, `rank`, `tier`, `price`, `assigned_at`, `expires_at`.
- **Linked Accounts:** `id`, `player_id`, `type` (ip, hardware, discord), `value`, `first_seen_at`, `last_seen_at`.
- **Shard metrics:** time-series table with `players`, `uniqueJoins`, `tps`, `latency`, `sessions`, `cpu`, `memory`, `chunks`, `queue`.
- **Shard identifiers:** `shard_identifiers` table to map numeric shard IDs to the immutable `shard_uuid` that plugins send back. Ensures every event + ingest payload can be deduplicated per shard even if IDs change.
- **Plugin keys:** `plugin_keys` storing hashed `apiKey`, `shardToken`, `shardSecret`, binding user + shard.
- **Plugin sessions:** `plugin_sessions` keeping active access tokens + expirations for `/api/plugin-auth`.
- **Ingest log:** `plugin_ingest_events` capturing every POST body for observability + replay.
- **Economy flows:** aggregated volumes + transaction ledger (`vault_transactions`).
- **Chat stream:** persisted for 24h (for sentiment + risk). Should support ingestion backlog w/ `eventId` dedupe.
### 5.1 Database bootstrap command
Run this against the `serverstats` MySQL schema (safe to run repeatedly thanks to `IF NOT EXISTS`):
```bash
mysql -u serverstats_user -p serverstats <<'SQL'
CREATE TABLE IF NOT EXISTS plugin_keys (
id INT NOT NULL AUTO_INCREMENT,
shard_id INT NOT NULL,
label VARCHAR(255) NULL,
api_key_hash CHAR(64) NOT NULL,
shard_token_hash CHAR(64) NOT NULL,
shard_uuid CHAR(36) NOT NULL,
shard_secret CHAR(64) NOT NULL,
created_by INT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_used_at TIMESTAMP NULL,
PRIMARY KEY (id),
CONSTRAINT fk_plugin_keys_shard FOREIGN KEY (shard_id) REFERENCES shards(id) ON DELETE CASCADE,
CONSTRAINT fk_plugin_keys_user FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS plugin_sessions (
id INT NOT NULL AUTO_INCREMENT,
plugin_key_id INT NOT NULL,
access_token CHAR(64) NOT NULL,
expires_at DATETIME NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY idx_plugin_sessions_token (access_token),
CONSTRAINT fk_plugin_sessions_key FOREIGN KEY (plugin_key_id) REFERENCES plugin_keys(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS plugin_ingest_events (
id BIGINT NOT NULL AUTO_INCREMENT,
plugin_key_id INT NOT NULL,
shard_id INT NOT NULL,
shard_uuid CHAR(36) NOT NULL,
event_type VARCHAR(64) NOT NULL,
payload LONGTEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY idx_plugin_ingest_shard (shard_id),
CONSTRAINT fk_plugin_ingest_key FOREIGN KEY (plugin_key_id) REFERENCES plugin_keys(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
SQL
```
## 6. Frontend View Data Contracts
This section maps each page/component of the site to the datasets and API endpoints it needs. When implementing backend services, ensure these contracts stay stable so the React pages render without additional JOIN-heavy logic.
### 6.1 Marketing Landing Page (`/`)
| Section | Data needs | Notes |
| --- | --- | --- |
| Hero | Static copy only. | No API needed. |
| Feature grid | Hard-coded features. | None. |
| Dashboard preview | Consumes `GET /api/marketing/dashboard-preview` returning demo scenarios with timelines, flows, retention, version mix, edition mix, etc. The payload should mirror real shards so marketing and console can share components. |
| Pricing cards | Currently static tiers. For future dynamic pricing, expose `GET /api/marketing/pricing` with tier metadata. |
| Payments highlight + Crypto card | No server data, but if we expose payment configs later, `GET /api/payments/providers`. |
### 6.2 Console Home (`/console`)
| Module | Data requirements | Existing API |
| --- | --- | --- |
| Navbar + session | `GET /api/session` (user, avatar, plan). | Already available via Next auth utilities. |
| Dashboard summary | `GET /api/dashboard/snapshot` returns: shards list, player timelines, unique timelines, TPS timelines, economy timelines, player leaderboard, vault summary, stats (avg session, peak players, new players). |
| Console dashboard cards | Needs the same `snapshot` object. `PlayerCountCard`, `NumbersRow`, `EconomyFlowCard`, `TpsCard`, etc. rely on the data structure defined above. |
| Alert Composer | `GET /api/alerts/templates`, `POST /api/alerts` to create. |
| Retention sprints | `GET /api/retention-sprints`, `POST /api/retention-sprints/:id/complete`. |
| Shop telemetry widget | `GET /api/shop/purchases?shardId=...&limit=20` to populate “recent buyers” or revenue charts. |
### 6.3 Shard Layout (`/shards/:id`)
| Component | Data | Endpoint |
| --- | --- | --- |
| Shard header/tabs | Shard metadata (`id`, `name`, `status`, `region`, `latency`). | `GET /api/shards/:id` |
| Dashboard tab | `GET /api/shards/:id/detail` containing: `playerTimelines`, `joinTimelines`, `tpsTimelines`, `economyTimelines`, `stats`, `vaultSummary`, `playerLeaderboard`, `playerSeries` used by AI summary. |
| Economy tab | `GET /api/shards/:id/economy` (totals, flows, transactions). |
| Players tab | `GET /api/shards/:id/players` returning table rows plus insights: engagement series, region stats, retention chart, version/edition distributions. |
| Performance tab | `GET /api/shards/:id/performance` (uptime, restarts, series for TPS/latency/CPU/memory/chunks). |
### 6.4 Player Detail (`/players/:id`)
Requires a consolidated payload:
- `player` object with bio stats, shard info, vault/net worth, retention, last login.
- `riskReasons`, `externalSignals`.
- `ranks`, `riskLevel`, `recentVault` transactions, `shopPurchases`.
- `linkedAccounts`, `ipHistory`.
- `playerJourney` data: retention funnel, session timeline, cohort analysis.
Expose via `GET /api/players/:id`.
### 6.5 Settings/Plugin Pages
| Feature | Data | Endpoint |
| --- | --- | --- |
| API/Plugin keys | `GET /api/plugin-keys`, `POST /api/plugin-keys`, `DELETE /api/plugin-keys/:id`. Keys include shard binding + last used timestamp. |
| Billing | `GET /api/billing/plan`, `POST /api/billing/checkout-session`. |
| Audit log | `GET /api/audit?resource=plugin-key` etc. |
### 6.6 Marketing / Blog Add-ons (Future)
If we later add testimonials, case studies, etc., prefer `GET /api/content/<slug>` so CMS data is centralized.
## 7. Reliability & Queuing
- Plugin batches events (up to 100 records) to reduce HTTP chatter.
- If backend returns `429` or network unavailable, plugin enqueues in disk-backed queue (3k messages).
- Backend validates schema via JSON Schema; rejects invalid rows with descriptive error.
- Provide `/api/plugin/health` so plugin can test connectivity.
## 8. Security
- Rotate API keys; store hashed in DB.
- Require TLS 1.2+.
- Rate limits per shard (e.g., 600 requests/min). Plugin should respect `Retry-After`.
- Audit log for key creation, revocation, plugin auth attempts.
## 9. Observability
- Metrics: ingest throughput, error rate, latency per endpoint.
- Dashboards for active shards, last-event timestamps, backlog depth.
- Alerting for missing telemetry (e.g., no player snapshot in 2 minutes).
## 10. Roadmap Items
1. **Offline buffering:** plugin writes queue to disk at shutdown to avoid data loss during restarts.
2. **Command streaming:** allow console to push commands (e.g., `/alert staff`) via SSE/WebSocket.
3. **Schema versioning:** include `schemaVersion` in payloads; backend handles migrations.
4. **Plugin diagnostics endpoint:** `GET /api/plugin/shards/:id/config` to fetch server-specific instructions.
### 10.1 Command Stream Schema
When command streaming ships (roadmap item #2), the backend will expose an SSE/WebSocket stream at `/api/plugin/shards/:id/commands`. Every message on that stream follows:
```json
{
"id": "cmd_e7f8615b",
"type": "COMMAND", // COMMAND | MESSAGE | ALERT
"command": "/alert staff",
"payload": {
"priority": "HIGH",
"body": "TPS dipped below 18"
},
"metadata": {
"issuedAt": "2024-08-04T18:01:27.000Z",
"expiresAt": "2024-08-04T18:06:27.000Z",
"source": "console-user|automation"
},
"ackToken": "ack_7b02cd0e"
}
```
Plugins must ACK once a command finishes (or fails) via `POST /api/plugin/shards/:id/command-acks`:
```json
{
"ackToken": "ack_7b02cd0e",
"status": "SUCCESS", // SUCCESS | FAILED
"details": "Broadcasted to 4 staff members",
"completedAt": "2024-08-04T18:01:32.921Z"
}
```
Future metadata fields can be added without breaking consumers; unrecognized keys should be ignored.
## 11. Deliverables Checklist
- [ ] REST API design approved (OpenAPI spec).
- [ ] Authentication service (API keys + JWT exchange).
- [ ] Ingest services for players, events, economy, performance.
- [ ] Plugin-facing read APIs with pagination + filters.
- [ ] Monitoring + rate limiting.
- [ ] Developer docs & example plugin snippets (Java/Kotlin).
Once these components exist, the plugin can reliably create/push data and pull insights, ensuring the backend is ready to support production networks.