Modules
Optional server modules and how to enable them.
Installable server modules: starter kits (economy, progression, inventory, leaderboards), integrity monitoring (server-side anticheat) and player insights (analytics). Modules are declarative bundles compiled into the binary: a manifest plus authored definitions. They never download code at runtime and never run outside code.
Lifecycle
- Catalog. The dashboard Modules page lists every embedded module with its version,
description, requested permissions, the definitions it adds, and its install state. Works
offline;
modules.jsonabsent means nothing is installed. - Preview. Installing first shows every definition and permission. Name conflicts with existing definitions are listed and the install refuses until they are resolved.
- Install. Definitions are written into the live definition tables with a
moduleownership field under the module'sid_prefix (for exampleeconomy_earn). The install is audited and shows a toast. Kit endpoints are public-key callable, so they work inplayer-hostedprojects. - Disable. Hooks stop, panels hide, and the module's endpoints answer
ENDPOINT_DISABLED. Data and settings are kept; re-enabling restores everything without data loss. - Uninstall. Requires typing the module id. Keep data converts definitions to ordinary project definitions (ownership stripped, names and records kept); delete data also removes the module's definitions, its records (ledger entries included) and its game-values items. Definitions outside the module's namespace are never touched.
- Upgrade. A catalog upgrade without new permissions applies on next install. A version that adds permissions is held: the installed version keeps running until the owner approves the new permissions on the Modules page.
Sync preservation
A package sync or editor push never deletes, overwrites or renames module-owned definitions:
batch pushes report kept_module_owned per item, single PATCH keeps them, full replacements
skip them, deletes answer 403 MODULE_MANAGED, and full game-values replaces merge owned
items back. Staged promotion skips owned rows too. Snapshots, project export and import carry
module-owned definitions verbatim, so old revisions keep working with their kits.
Permissions
Fixed list: read-player-data, write-player-data, read-analytics, flag-players,
block-requests, send-alerts. Module background actions only run when granted; a revoked
permission degrades an automatic action to flag-only and logs it.
Hooks and panels
Fixed hooks (all dispatched off the request path through a bounded channel; a full channel
drops events and counts them instead of delaying requests): endpoint.executed,
ledger.delta, request.rejected, revision.reported.
Fixed panels (Razor partials under Views/Owner/Modules/): review-queue (integrity),
player-overview, player-profile, action-log (insights).
Manifest reference
Embedded resources SboxNetworkStorage.Infrastructure.Modules.<id>.module.yml and
<id>.definitions.yml:
id: economy # ^[a-z][a-z0-9-]{0,63}$, matches the file name
version: 1.0.0 # semantic version
minServerVersion: 0.6.0
name: Economy starter kit
description: ...
permissions: [] # subset of the fixed list
hooks: [] # subset of the fixed list
panels: [] # subset of the fixed list
settings: # typed settings with defaults
- key: starting_balance
type: integer
default: "0"
description: ...
definitions.yml is a list of {kind, source} where kind is
collection, endpoint, workflow, query or game-values and source is a definition in
the existing authoring format. Names must already carry the id_ prefix and endpoints and
queries must not require a secret key; the installer stamps module: <id> and refuses
anything else. An unknown hook, permission or panel marks only that module unavailable with
the validation error; other modules load normally.
Integrity monitoring
Detectors on data-plane hooks with per-project settings (Modules page): value delta (a
watched ledger/numeric field rising more than value_delta_per_minute per player per minute),
call rate (endpoint calls per player per minute above max_calls_per_minute), revision
tampering (a reported revision id that was never published), repeated anti-rollback conflicts
and repeated direct-write attempts on endpoint-only collections (5 in 10 minutes by default).
Secret-key (dedicated-server) traffic is exempt.
Detection only observes: except for an explicit player block, the module never alters, delays
or rejects requests. Automatic action per detector: flag-only (default), flag-and-alert
(through the existing operator alert channels and suppression) or flag-and-block-writes
for block_duration_minutes. Flags record player, detector, severity, time, evidence and
state (open, dismissed, confirmed); the review queue groups them by player and every
decision is audited.
Player blocks (writes or all, optional expiry and public reason) return 403 PLAYER_BLOCKED.
Unblocking takes effect immediately.
Player insights
Overview (daily active players, new players, sessions, median session length for 1/7/30 days; day-1 and day-7 return rates) from sessions the server already records. Panels say "Not enough data yet" instead of showing placeholder figures. Player search (Steam ID or display name) and profiles (first/last seen, revision, sessions, records, ledger, flags, block state). The action log records endpoint calls (endpoint, revision, outcome, changed fields, time) with configurable retention (default 14 days, 1-90), batched writes that never block or fail game requests, drop counting, and optional per-endpoint body capture. Overview series and filtered logs export as CSV.
Latency overhead
Detection adds no more than 2 ms at p95 (measured with the recipe below). Recipe: install the
four starter kits plus integrity with defaults in a scratch project, run the scripted normal
play session (scripts/ smoke against earn/spend/grant-xp/grant/consume/craft/submit), assert
zero flags, and compare p95 endpoint latency with the integrity module enabled versus
uninstalled. Record both numbers here when measured:
- p95 without integrity: to be measured
- p95 with integrity (flag-only defaults): to be measured
- flags during normal play: to be measured (must be 0)