Backend: - Complete FastAPI backend with 42+ REST endpoints (auth, servers, config, players, bans, missions, mods, games, system) - Game adapter architecture with Arma 3 as first-class adapter - WebSocket real-time events for status, metrics, logs, players - Background thread system (process monitor, metrics, log tail, RCon poller) - Fernet encryption for sensitive config fields at rest - JWT auth with admin/viewer roles, bcrypt password hashing - SQLite with WAL mode, parameterized queries, migration system - APScheduler cleanup jobs for logs, metrics, events Frontend: - Server Detail page with 7 tabs (overview, config, players, bans, missions, mods, logs) - Settings page with password change and admin user management - Create Server wizard (4-step; known bug: silent validation failure) - New hooks: useServerDetail, useAuth, useGames - New components: ServerHeader, ConfigEditor, PlayerTable, BanTable, MissionList, ModList, LogViewer, PasswordChange, UserManager - WebSocket onEvent callback for real-time log accumulation - 120 unit tests passing (Vitest + React Testing Library) Docs: - Added .gitignore, CLAUDE.md, README.md - Updated FRONTEND.md, ARCHITECTURE.md with current implementation state - Added .env.example for backend configuration Known issues: - Create Server form: "Next" buttons don't validate before advancing, causing silent submit failure when fields are invalid - Config sub-tabs need UX redesign for non-technical users
32 KiB
Languard Server Manager — API Reference
Base URL
http://localhost:8000/api
All HTTP endpoints are prefixed with /api. The WebSocket endpoint is at /ws (no /api prefix).
Authentication
All endpoints except POST /auth/login and GET /system/health require a JWT bearer token:
Authorization: Bearer <access_token>
For WebSocket, pass the token as a query parameter:
ws://localhost:8000/ws?token=<access_token>&server_id=1
JWT payload:
{
"sub": "user_id",
"username": "string",
"role": "admin|viewer",
"exp": 1745000000
}
Roles
| Role | Access |
|---|---|
admin |
Full access: create, update, delete, start/stop servers |
viewer |
Read-only: list servers, view config (sensitive masked) |
Standard Response Envelope
All responses use a consistent envelope:
Success:
{
"success": true,
"data": { ... },
"error": null
}
Error:
{
"success": false,
"data": null,
"error": {
"code": "NOT_FOUND",
"message": "Server with id 5 not found"
}
}
Some error responses (from FastAPI HTTPException) use a detail object instead:
{
"detail": {
"code": "NOT_FOUND",
"message": "Server 5 not found"
}
}
HTTP Status Codes
| Code | Meaning |
|---|---|
| 200 | Success |
| 201 | Created (resource successfully created) |
| 204 | No content (successful DELETE) |
| 400 | Bad request (validation error, capability not supported) |
| 401 | Unauthenticated (missing or invalid token) |
| 403 | Forbidden (insufficient role — admin required) |
| 404 | Not found (resource or capability not available) |
| 409 | Conflict (already running, port in use, version conflict) |
| 413 | Payload too large (file upload exceeds 500 MB) |
| 422 | Unprocessable entity (Pydantic validation failure) |
| 500 | Internal server error |
| 503 | Service unavailable (RCon connection failed) |
Capability-Based Routing
Some endpoints depend on the server's game adapter supporting a specific capability. If the adapter does not support the capability, the endpoint returns 400 with:
{
"success": false,
"data": null,
"error": {
"code": "NOT_SUPPORTED",
"message": "Game type 'rust' does not support mission management"
}
}
Auth Endpoints
POST /auth/login
Authenticate and receive a JWT token. Public endpoint (no auth required).
Request:
{
"username": "admin",
"password": "secret"
}
Response 200:
{
"success": true,
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "bearer",
"expires_in": 86400,
"user": {
"id": 1,
"username": "admin",
"role": "admin"
}
},
"error": null
}
Error 401: Invalid credentials.
{
"success": false,
"data": null,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid credentials"
}
}
POST /auth/logout
Invalidate session (client-side token deletion; no server-side blacklist).
Auth: Required (any role)
Response 200:
{
"success": true,
"data": { "message": "Logged out" },
"error": null
}
GET /auth/me
Return the currently authenticated user.
Auth: Required (any role)
Response 200:
{
"success": true,
"data": {
"id": 1,
"username": "admin",
"role": "admin"
},
"error": null
}
PUT /auth/password
Change the authenticated user's own password. Any authenticated user can change their own password (not admin-only).
Auth: Required (any role)
Request:
{
"current_password": "old_password",
"new_password": "new_password"
}
Response 200:
{
"success": true,
"data": { "message": "Password changed" },
"error": null
}
Error 401: Current password is incorrect.
GET /auth/users
List all users.
Auth: Admin required
Response 200:
{
"success": true,
"data": [
{
"id": 1,
"username": "admin",
"role": "admin",
"created_at": "2026-04-16T00:00:00",
"last_login": "2026-04-17T10:30:00"
}
],
"error": null
}
POST /auth/users
Create a new user.
Auth: Admin required
Request:
{
"username": "viewer1",
"password": "secure_password",
"role": "viewer"
}
role defaults to "viewer" if omitted. Valid values: "admin", "viewer".
Response 201:
{
"success": true,
"data": {
"id": 2,
"username": "viewer1",
"role": "viewer",
"created_at": "2026-04-17T12:00:00"
},
"error": null
}
Error 409: Username already taken.
DELETE /auth/users/{user_id}
Delete a user. Cannot delete yourself.
Auth: Admin required
Response 204: No content.
Error 400: Attempting to delete your own account.
Games Endpoints
GET /games
List all registered game types with their capabilities.
Auth: Not required (public)
Response 200:
{
"success": true,
"data": [
{
"game_type": "arma3",
"display_name": "Arma 3",
"version": "1.0.0",
"capabilities": [
"config_generator",
"process_config",
"log_parser",
"remote_admin",
"mission_manager",
"mod_manager",
"ban_manager"
]
}
],
"error": null
}
GET /games/{game_type}
Get details for a specific game type, including capabilities, config sections, and allowed executables.
Auth: Not required (public)
Path params:
| Parameter | Type | Description |
|---|---|---|
game_type |
string | Game type identifier |
Response 200:
{
"success": true,
"data": {
"game_type": "arma3",
"display_name": "Arma 3",
"version": "1.0.0",
"schema_version": "1.0",
"capabilities": [
"config_generator",
"process_config",
"log_parser",
"remote_admin",
"mission_manager",
"mod_manager",
"ban_manager"
],
"config_sections": ["server", "basic", "profile", "launch", "rcon"],
"allowed_executables": ["arma3server_x64.exe", "arma3server.exe"]
},
"error": null
}
Error 404: Unknown game type.
GET /games/{game_type}/config-schema
Returns JSON Schema for each config section defined by the adapter. Used by the frontend to build dynamic config forms.
Auth: Not required (public)
Path params:
| Parameter | Type | Description |
|---|---|---|
game_type |
string | Game type identifier |
Response 200:
{
"success": true,
"data": {
"server": { "...": "JSON Schema for server.cfg params" },
"basic": { "...": "JSON Schema for basic.cfg params" },
"profile": { "...": "JSON Schema for Arma3Profile params" },
"launch": { "...": "JSON Schema for launch params" },
"rcon": { "...": "JSON Schema for RCon params" }
},
"error": null
}
GET /games/{game_type}/defaults
Default config values for new server creation.
Auth: Not required (public)
Path params:
| Parameter | Type | Description |
|---|---|---|
game_type |
string | Game type identifier |
Response 200:
{
"success": true,
"data": {
"server": { "hostname": "My Arma 3 Server", "max_players": 40 },
"basic": { "min_bandwidth": 800000 },
"profile": { "reduced_damage": 0 },
"launch": { "world": "empty", "limit_fps": 50 },
"rcon": { "max_ping": 200, "enabled": 1 }
},
"error": null
}
System Endpoints
GET /system/health
Public health check. Used by load balancers and Docker health probes.
Auth: Not required
Response 200:
{
"status": "ok"
}
Note: This endpoint does not use the standard envelope.
GET /system/status
System status including version and running server counts.
Auth: Required (any role)
Response 200:
{
"success": true,
"data": {
"version": "1.0.0",
"running_servers": 2,
"total_servers": 3,
"supported_games": ["arma3"]
}
}
Server Endpoints
GET /servers
List all servers with current status and live metrics.
Auth: Required (any role)
Query params:
| Parameter | Type | Description |
|---|---|---|
game_type |
string | Filter servers by game type |
Response 200:
{
"success": true,
"data": [
{
"id": 1,
"name": "Main Server",
"description": "Primary COOP server",
"game_type": "arma3",
"status": "running",
"pid": 12345,
"game_port": 2302,
"rcon_port": 2306,
"auto_restart": true,
"max_restarts": 3,
"restart_count": 0,
"player_count": 15,
"cpu_percent": 34.2,
"ram_mb": 1850.5,
"started_at": "2026-04-16T10:00:00Z"
}
],
"error": null
}
cpu_percent, ram_mb, and player_count are merged from live metrics. They will be null if no metrics are available.
POST /servers
Create a new server. The game_type determines which adapter handles this server.
Auth: Admin required
Request:
{
"name": "Main Server",
"description": "Primary COOP server",
"game_type": "arma3",
"exe_path": "C:/Arma3Server/arma3server_x64.exe",
"game_port": 2302,
"rcon_port": 2306,
"auto_restart": true,
"max_restarts": 3
}
| Field | Type | Required | Default | Validation |
|---|---|---|---|---|
name |
string | Yes | — | |
description |
string | No | null |
|
game_type |
string | No | "arma3" |
Must be a registered type |
exe_path |
string | Yes | — | Filename must be in allowlist |
game_port |
integer | Yes | — | 1024–65535, must not be in use |
rcon_port |
integer | No | auto | 1024–65535, auto = game_port + 3 |
auto_restart |
boolean | No | false |
|
max_restarts |
integer | No | 3 |
0–20 |
The adapter provides default config values for all sections. Auto-generated credentials (e.g., password_admin, rcon_password for Arma 3) are returned in the creation response only.
Response 201: Returns the full server object.
Errors:
- 400
GAME_TYPE_NOT_FOUND— Unknown game type - 400
EXE_NOT_ALLOWED— Executable filename not in adapter allowlist - 409
PORT_IN_USE— Game port or RCon port already in use
GET /servers/{server_id}
Get server detail with full status.
Auth: Required (any role)
Response 200:
{
"success": true,
"data": {
"id": 1,
"name": "Main Server",
"description": "Primary COOP server",
"game_type": "arma3",
"status": "running",
"pid": 12345,
"exe_path": "C:/Arma3Server/arma3server_x64.exe",
"game_port": 2302,
"rcon_port": 2306,
"auto_restart": true,
"max_restarts": 3,
"restart_count": 0,
"player_count": 15,
"cpu_percent": 34.2,
"ram_mb": 1850.5,
"started_at": "2026-04-16T10:00:00Z",
"uptime_seconds": 3600
},
"error": null
}
Error 404: Server not found.
PUT /servers/{server_id}
Update server metadata (name, description, exe_path, ports). Only provided fields are updated; omitted fields keep their current values.
Auth: Admin required
Request:
{
"name": "Updated Server Name",
"description": "New description",
"exe_path": "C:/Arma3Server/arma3server_x64.exe",
"game_port": 2302,
"rcon_port": 2306,
"auto_restart": false,
"max_restarts": 5
}
All fields are optional. Port validation (1024–65535) applies if provided.
Response 200: Returns the updated server object.
DELETE /servers/{server_id}
Delete a server. The server must be in a stopped, crashed, or error state.
Auth: Admin required
Response 204: No content.
Error 409: SERVER_NOT_STOPPED — Server is currently running.
Note: Also deletes the server's directory on disk (servers/{id}/).
POST /servers/{server_id}/start
Start the server. The system resolves the adapter, writes config files atomically, builds launch arguments, starts the process, and attaches monitoring threads.
Auth: Admin required
Response 200:
{
"success": true,
"data": {
"status": "starting",
"pid": 12345
},
"error": null
}
Errors:
- 400
EXE_NOT_ALLOWED— Executable not in adapter allowlist - 400
INVALID_CONFIG— Config validation or launch args failed - 409
SERVER_ALREADY_RUNNING— Server is already running or starting - 409
PORT_IN_USE— Game port or RCon port already in use - 500
CONFIG_WRITE_ERROR— Failed to write config files to disk
POST /servers/{server_id}/stop
Graceful stop. Sends a shutdown signal via the adapter's remote admin, then force-kills after 30 seconds if the process has not exited.
Auth: Admin required
Request (optional body):
{
"force": false,
"reason": "Scheduled maintenance"
}
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
force |
boolean | No | false |
Skip graceful shutdown, terminate immediately |
reason |
string | No | null |
Optional reason for the stop |
Response 200:
{
"success": true,
"data": { "status": "stopped" },
"error": null
}
Error 409: SERVER_NOT_RUNNING — Server is already stopped or crashed.
POST /servers/{server_id}/restart
Stop then start the server. Equivalent to calling stop then start sequentially.
Auth: Admin required
Response 200: Returns the start result (same shape as start).
POST /servers/{server_id}/kill
Force-kill the server process immediately. Use for emergency situations only; does not attempt graceful shutdown.
Auth: Admin required
Response 200:
{
"success": true,
"data": { "status": "stopped" },
"error": null
}
Server Config Endpoints
Config sections are defined by the server's game adapter. The core game_configs table stores them as JSON. Each section tracks a config_version for optimistic locking.
GET /servers/{server_id}/config
Get all config sections combined. Sensitive fields (passwords) are masked with "***".
Auth: Required (any role — sensitive fields are masked for non-admin viewers)
Response 200:
{
"success": true,
"data": {
"server": {
"hostname": "My Server",
"max_players": 40,
"_meta": { "config_version": 3, "schema_version": "1.0" }
},
"basic": {
"max_bandwidth": 50000000,
"_meta": { "config_version": 1, "schema_version": "1.0" }
},
"profile": {
"third_person_view": 0,
"_meta": { "config_version": 2, "schema_version": "1.0" }
},
"launch": {
"world": "empty",
"limit_fps": 50,
"_meta": { "config_version": 1, "schema_version": "1.0" }
},
"rcon": {
"rcon_password": "***",
"max_ping": 200,
"enabled": 1,
"_meta": { "config_version": 1, "schema_version": "1.0" }
}
},
"error": null
}
GET /servers/{server_id}/config/preview
Rendered config for preview. Admin only because it may contain plaintext credentials.
Auth: Admin required
Returns a dict of {label: rendered_content} where labels are filenames for file-based configs, variable names for env-var configs, or argument names for CLI configs.
Response 200:
{
"success": true,
"data": {
"server.cfg": "hostname = \"My Server\";\nmaxPlayers = 40;\n...",
"basic.cfg": "// Generated basic.cfg\n...",
"server.Arma3Profile": "// Generated profile\n..."
},
"error": null
}
GET /servers/{server_id}/config/{section}
Get a single config section. Section names are defined by the adapter (e.g., server, basic, profile, launch, rcon for Arma 3).
Auth: Required (any role — sensitive fields are masked)
Response 200:
{
"success": true,
"data": {
"hostname": "My Server",
"max_players": 40,
"_meta": { "config_version": 3, "schema_version": "1.0" }
},
"error": null
}
Error 404: Config section not found (not a valid section for this adapter).
PUT /servers/{server_id}/config/{section}
Update a config section. Uses optimistic locking — the request must include config_version from the last read. Validated against the adapter's Pydantic model for that section. Omitted fields keep their current values (partial update).
Auth: Admin required
Request:
{
"hostname": "Updated Server Name",
"max_players": 64,
"config_version": 3
}
config_version is required for conflict detection. Any omitted field retains its current value.
Arma 3 section examples:
server section:
{
"hostname": "My Arma Server",
"max_players": 40,
"battleye": 1,
"verify_signatures": 2,
"motd_lines": ["Welcome!", "Have fun"],
"motd_interval": 5.0,
"config_version": 3
}
basic section:
{
"max_bandwidth": 50000000,
"max_msg_send": 256,
"config_version": 1
}
profile section:
{
"third_person_view": 0,
"weapon_crosshair": 0,
"ai_level_preset": 3,
"skill_ai": 0.7,
"precision_ai": 0.6,
"config_version": 2
}
launch section:
{
"world": "empty",
"limit_fps": 50,
"auto_init": false,
"load_mission_to_memory": true,
"config_version": 1
}
rcon section:
{
"rcon_password": "newpassword",
"rcon_port": 2306,
"max_ping": 300,
"enabled": 1,
"config_version": 1
}
Note: rcon_port is stored in the servers table, not in the config JSON. The service layer updates both tables as needed.
Response 200: Returns the updated section (sensitive fields masked).
Error 409 — Optimistic locking conflict:
{
"success": false,
"data": null,
"error": {
"code": "CONFIG_VERSION_CONFLICT",
"message": "Config was modified by another user. Re-read and merge.",
"current_config": { "...": "latest values" },
"current_version": 5
}
}
Error 422: Pydantic validation failure — the submitted values do not match the adapter's schema.
RCon Endpoints
POST /servers/{server_id}/rcon/command
Send an RCon command to a running server. Requires the adapter to support the remote_admin capability.
Auth: Admin required
Request:
{
"command": "#restart"
}
Response 200:
{
"success": true,
"data": {
"response": "Command executed"
},
"error": null
}
Errors:
- 400
NOT_SUPPORTED— Game type does not support RCon - 400
NO_RCON_PASSWORD— RCon password not configured for this server - 503
RCON_ERROR— RCon connection or command failed
Arma 3 available RCon commands:
| Command | Description |
|---|---|
#restart |
Restart current mission |
#reassign |
Restart with roles unassigned |
#missions |
Open mission selection |
#lock |
Lock the server |
#unlock |
Unlock the server |
#mission NAME.TERRAIN [difficulty] |
Load a specific mission |
#shutdown |
Shut down the server |
#monitor N |
Toggle performance monitoring |
say -1 MESSAGE |
Message all players |
Player Endpoints
GET /servers/{server_id}/players
List currently connected players (cached from RemoteAdminPollerThread).
Auth: Required (any role)
Response 200:
{
"success": true,
"data": {
"server_id": 1,
"player_count": 2,
"players": [
{
"slot_id": "1",
"name": "PlayerOne",
"ping": 45
},
{
"slot_id": "2",
"name": "PlayerTwo",
"ping": 78
}
]
},
"error": null
}
GET /servers/{server_id}/players/history
Get historical player session records.
Auth: Required (any role)
Query params:
| Parameter | Type | Default | Description |
|---|---|---|---|
limit |
integer | 100 | Maximum records to return |
offset |
integer | 0 | Pagination offset |
search |
string | — | Filter by player name |
Response 200:
{
"success": true,
"data": {
"total": 150,
"items": [
{
"id": 1,
"player_name": "PlayerOne",
"guid": "abc123...",
"joined_at": "2026-04-16T10:15:00Z",
"left_at": "2026-04-16T11:30:00Z"
}
]
},
"error": null
}
Ban Endpoints
GET /servers/{server_id}/bans
List all active bans for a server.
Auth: Required (any role)
Response 200:
{
"success": true,
"data": [
{
"id": 1,
"server_id": 1,
"guid": "abc123...",
"name": null,
"reason": "Cheating",
"banned_by": "admin",
"ban_type": "GUID",
"duration_minutes": 0,
"expires_at": null,
"created_at": "2026-04-16T10:00:00Z"
}
],
"error": null
}
POST /servers/{server_id}/bans
Create a new ban. Writes to the database and syncs to the game's bans.txt file (if adapter supports ban_manager).
Auth: Admin required
Request:
{
"player_uid": "abc123...",
"ban_type": "GUID",
"reason": "Cheating",
"duration_minutes": 0
}
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
player_uid |
string | Yes | — | GUID or IP to ban |
ban_type |
string | No | "GUID" |
Must be "GUID" or "IP" |
reason |
string | No | "" |
Reason for the ban |
duration_minutes |
integer | No | 0 |
0 = permanent, >0 = timed ban |
Response 201:
{
"success": true,
"data": {
"id": 2,
"server_id": 1,
"guid": "abc123...",
"reason": "Cheating",
"ban_type": "GUID",
"expires_at": null
},
"error": null
}
Note: If the bans.txt file sync fails, the database ban is still created. The error is logged but does not fail the request.
DELETE /servers/{server_id}/bans/{ban_id}
Revoke (deactivate) a ban. Removes from bans.txt if the adapter supports ban_manager.
Auth: Admin required
Response 200:
{
"success": true,
"data": { "message": "Ban 2 revoked" },
"error": null
}
Error 404: Ban not found or does not belong to this server.
Mission Endpoints
All mission endpoints require the adapter to support the mission_manager capability. Returns 400 NOT_SUPPORTED if the game type does not support missions.
GET /servers/{server_id}/missions
List all available mission/scenario files on disk.
Auth: Required (any role)
Response 200:
{
"success": true,
"data": {
"server_id": 1,
"total": 2,
"missions": [
{
"filename": "MyMission.Altis.pbo",
"mission_name": "MyMission.Altis",
"terrain": "Altis",
"file_size": 102400
}
]
},
"error": null
}
POST /servers/{server_id}/missions
Upload a mission file. Multipart form-data. Maximum file size: 500 MB. File extension is validated by the adapter (e.g., .pbo for Arma 3).
Auth: Admin required
Request: multipart/form-data with field file.
Response 201:
{
"success": true,
"data": {
"filename": "NewMission.Stratis.pbo",
"mission_name": "NewMission.Stratis",
"terrain": "Stratis",
"file_size": 51200
},
"error": null
}
Errors:
- 400
NO_FILENAME— No filename provided in upload - 400
ADAPTER_ERROR— Upload failed (wrong extension, etc.) - 413
FILE_TOO_LARGE— File exceeds 500 MB
DELETE /servers/{server_id}/missions/{filename}
Delete a mission file by filename. Removes the file from disk.
Auth: Admin required
Path params:
| Parameter | Type | Description |
|---|---|---|
filename |
string | Mission filename (e.g. MyMission.Altis.pbo) |
Response 200:
{
"success": true,
"data": { "message": "Mission 'MyMission.Altis.pbo' deleted" },
"error": null
}
Error 404: Mission file not found.
Mod Endpoints
All mod endpoints require the adapter to support the mod_manager capability. Returns 400 NOT_SUPPORTED if the game type does not support mods.
GET /servers/{server_id}/mods
List all available mods and which are currently enabled for this server.
Auth: Required (any role)
Response 200:
{
"success": true,
"data": {
"server_id": 1,
"enabled_count": 2,
"mods": [
{
"name": "@CBA_A3",
"folder_path": "C:/Arma3Server/@CBA_A3",
"enabled": true
},
{
"name": "@ACRE2",
"folder_path": "C:/Arma3Server/@ACRE2",
"enabled": true
},
{
"name": "@USAF",
"folder_path": "C:/Arma3Server/@USAF",
"enabled": false
}
]
},
"error": null
}
PUT /servers/{server_id}/mods/enabled
Set the list of enabled mods. This replaces the entire enabled list — send the complete list every time. The server must be restarted for changes to take effect.
Auth: Admin required
Request:
{
"mods": ["@CBA_A3", "@ACRE2"]
}
| Field | Type | Required | Description |
|---|---|---|---|
mods |
array[string] | Yes | Complete list of mod names to enable |
Response 200:
{
"success": true,
"data": {
"message": "Enabled mods updated. Restart the server for changes to take effect.",
"enabled_mods": ["@CBA_A3", "@ACRE2"]
},
"error": null
}
Error 409 VERSION_CONFLICT — Config was modified by another request while updating mods.
WebSocket API
Connection
ws://localhost:8000/ws?token=<JWT>&server_id=1&server_id=2
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
token |
string | Yes | JWT access token |
server_id |
integer (repeatable) | No | Server IDs to subscribe to. Omit for all servers |
Authentication: JWT is passed as a query parameter because the browser WebSocket API does not support custom headers. If the token is missing or invalid, the connection is closed with code 4001.
Welcome message on connect:
{
"type": "connected",
"data": {
"user": "1",
"subscriptions": [1, 2]
}
}
If server_id is omitted, subscriptions will be "all".
Server-Sent Event Types
All events follow this format:
{
"type": "<event_type>",
"server_id": 1,
"data": { ... }
}
server_status
Emitted when a server's status changes (starting, running, stopping, stopped, crashed, error).
{
"type": "server_status",
"server_id": 1,
"data": {
"status": "running",
"pid": 12345,
"started_at": "2026-04-16T10:00:00Z"
}
}
log
Emitted for each log line from the server process.
{
"type": "log",
"server_id": 1,
"data": {
"timestamp": "2026-04-16T10:05:23Z",
"level": "info",
"message": "BattlEye Server: Initialized (v1.240)"
}
}
players
Emitted when the player list changes.
{
"type": "players",
"server_id": 1,
"data": {
"players": [
{ "slot_id": "1", "name": "PlayerOne", "ping": 45 }
],
"count": 1
}
}
metrics
Periodic resource usage update.
{
"type": "metrics",
"server_id": 1,
"data": {
"cpu_percent": 34.2,
"ram_mb": 1850.5,
"player_count": 1,
"timestamp": "2026-04-16T10:05:25Z"
}
}
Subscription Model
- Each connection subscribes to zero or more
server_idvalues. - Subscribing to
server_id=None(omitting the parameter) means "all servers." broadcast(server_id, message)delivers to all clients subscribed to thatserver_idplus all global subscribers.- If the event loop is closed, events are silently dropped.
Rate Limiting
| Endpoint | Limit |
|---|---|
POST /auth/login |
5 attempts per minute per IP |
| All other endpoints | 60 requests per minute per token |
Exceeded limits return 429 Too Many Requests.
Implemented via slowapi middleware.
Error Codes Reference
| Code | HTTP Status | Description |
|---|---|---|
UNAUTHORIZED |
401 | Missing or invalid token |
FORBIDDEN |
403 | Insufficient role (admin required) |
NOT_FOUND |
404 | Resource not found |
NOT_SUPPORTED |
400 | Adapter lacks required capability |
CONFLICT |
409 | Duplicate resource (e.g., username taken) |
SERVER_ALREADY_RUNNING |
409 | Start called on running server |
SERVER_NOT_RUNNING |
409 | Stop/command on stopped server |
SERVER_NOT_STOPPED |
409 | Delete called on running server |
PORT_IN_USE |
409 | Game or RCon port already occupied |
CONFIG_VERSION_CONFLICT |
409 | Optimistic locking conflict on config update |
VERSION_CONFLICT |
409 | Config modified by another request during mod update |
GAME_TYPE_NOT_FOUND |
404/400 | No adapter registered for this game type |
EXE_NOT_ALLOWED |
400 | Executable not in adapter allowlist |
INVALID_CONFIG |
422/400 | Config validation failed (adapter-specific) |
CONFIG_WRITE_ERROR |
500 | Config file write failed (disk, permissions) |
NO_RCON_PASSWORD |
400 | RCon password not configured |
RCON_ERROR |
503 | RCon connection or command failed |
ADAPTER_ERROR |
400/500 | Generic adapter error |
FILE_TOO_LARGE |
413 | Upload exceeds 500 MB |
NO_FILENAME |
400 | No filename in upload request |
VALIDATION_ERROR |
400 | General validation failure |
INTERNAL_ERROR |
500 | Unexpected server error |