-- ── The schema fragment ─────────────────────────────────────────────────── -- -- Core replays this file on EVERY boot, statement by statement, immediately -- after its own schema.sql and before it seeds defaults (MODULE_API.md §2.6). -- -- There is no migration runner anywhere in this project. A module's schema is -- not a sequence of changes to apply once — it is a statement of what the tables -- should look like, written so that running it against a database that already -- matches does nothing. Every CREATE carries IF NOT EXISTS; **changing a table -- is an ALTER below the CREATE, never an edit to the CREATE**, because -- `CREATE TABLE IF NOT EXISTS` does nothing at all when the table is already -- there and an edited column would reach fresh installs only. -- -- Every table here is prefixed `rust_`, which is this module's id and the only -- prefix it may create under. -- -- ── Two tables, and the split between them is the whole design ──────────── -- -- `rust_servers` is CONFIGURATION: rows an operator writes, from Admin → Rust. -- `rust_server_state` is OBSERVED STATE: rows this module writes from what a -- sidecar reported. They are separate tables rather than columns on one because -- they have different writers, different lifetimes and different audiences — -- and because a purge of observed state while keeping the configuration is a -- thing an operator will eventually want. -- -- Teardown is `purge.sql`, which no boot ever runs. -- ── The configured servers ──────────────────────────────────────────────── -- -- One row per Rust game server, and therefore one row per sidecar: the bridge is -- one server to one sidecar, on that server's own host (R8). A community running -- six servers has six rows here, each with its own base URL and its own token. -- -- `id` is the operator's own slug and is what every URL under `/rust/servers/` -- carries. It is deliberately NOT auto-increment: it appears in links people -- share, and a row rebuilt after a mistake should be able to keep its address. -- -- `sidecar_token_enc` holds the sidecar's shared secret **encrypted at rest** -- through `ctx.secretBox` (MODULE_API.md §2.3), like every other secret this -- platform stores. It is write-only in the API: the admin surface accepts a new -- value and never returns the stored one, so a compromised admin session cannot -- read back the credential that reaches the game host. -- -- `protocol` records the wire version this row was configured against. It is -- stored rather than assumed because a fleet is upgraded one host at a time, and -- an operator needs to see WHICH server disagrees rather than that one does. CREATE TABLE IF NOT EXISTS rust_servers ( id VARCHAR(64) NOT NULL PRIMARY KEY, name VARCHAR(120) NOT NULL, sidecar_base_url VARCHAR(255) NOT NULL, sidecar_token_enc TEXT NULL, protocol INT UNSIGNED NOT NULL DEFAULT 1, enabled TINYINT(1) NOT NULL DEFAULT 1, sort_order INT NOT NULL DEFAULT 0, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ); -- ── What each server last said about itself ─────────────────────────────── -- -- One row per configured server, replaced whole each time this module reads a -- sidecar. It is the table that lets the site render while every game server is -- off, which is the point of the sidecar holding a store at all. -- -- `updated_at` carries no `ON UPDATE CURRENT_TIMESTAMP`, deliberately. That -- clause fires only when an UPDATE actually CHANGES a value, so a writer sending -- the same numbers back — which is exactly what a quiet server looks like — -- would leave the timestamp frozen at the first write and the row would look -- stale while nothing was wrong. The writer sets the column explicitly instead. -- -- `boot_id` is the game process's own identity, not the sidecar's and not the -- plugin's. It changes when the world started over and at no other time, which -- is what makes it the thing to watch: a reconnect of either bridge component -- loses nothing, and a game restart loses everything an event put in the world. -- -- `raw` keeps the whole frame. This module indexes the columns it serves and -- stores the rest verbatim, so a protocol version that adds a field needs no -- migration here — the same dumb-forwarder property the sidecar has, one hop -- further along. CREATE TABLE IF NOT EXISTS rust_server_state ( server_id VARCHAR(64) NOT NULL PRIMARY KEY, reachable TINYINT(1) NOT NULL DEFAULT 0, online TINYINT(1) NOT NULL DEFAULT 0, players INT UNSIGNED NOT NULL DEFAULT 0, max_players INT UNSIGNED NOT NULL DEFAULT 0, hostname VARCHAR(191) NULL, level VARCHAR(120) NULL, seed BIGINT NULL, world_size INT UNSIGNED NULL, boot_id VARCHAR(64) NULL, save_created_at VARCHAR(32) NULL, protocol INT UNSIGNED NULL, raw LONGTEXT NULL, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_rust_server_state_server FOREIGN KEY (server_id) REFERENCES rust_servers (id) ON DELETE CASCADE );