/** * The Reference section's enumerations. * * §1 says a Reference page is "a navigable summary plus a link to the canonical document — * never a re-specification". This file is the line between those two things, and it is * worth being explicit about where it falls: * * * The NAMES are here — every environment variable, every config key, every command, * every event kind. A reference section that cannot answer "what variables are there?" * without a click-through is a link farm. * * The SEMANTICS are not. One terse line each, saying what a thing is FOR. Shapes, * defaults that matter, interactions, and every "why" stay in the canonical document. * * Everything below is checked against its source by `scripts/checkReference.mjs`, in both * directions — a name that disappears upstream fails, and a name that appears upstream and * is missing here fails too. That is the whole reason it is safe to write names down at * all: the enumeration cannot rot into fiction without turning the build red. * * Descriptions are NOT checked, and cannot be. They are the part a human has to keep * honest, which is why they are kept short enough to re-read. */ /** `website` root `.env.example` — the file a Compose deployment actually reads. */ export const envVars = { IMAGE_TAG: 'Which published image tag to run', NODE_ENV: 'production or development — several refusals are production-only', PORT: 'The port the app listens on', INTERNAL_PORT: 'The internal-only listener, for the bot channel', UPLOAD_DIR: 'Where uploads are written', LOG_LEVEL: 'Console log level', FILE_LOG_LEVEL: 'File log level, set separately', LOG_TO_FILE: 'Whether to write a log file at all', LOG_DIR: 'Directory for the log file', LOG_FILE: 'Log file name', BRAND_NAME: 'Site name — branding is data, not a build', BRAND_SHORT_NAME: 'Short form, for tight spaces', BRAND_TAGLINE: 'One line under the name', BRAND_DESCRIPTION: 'Meta description', BRAND_CONTACT_EMAIL: 'Published contact address', BRAND_URL: 'Canonical public URL', BRAND_ACCENT_COLOR: 'Accent colour', BRAND_LOGO: 'Logo path', BRAND_HERO: 'Hero image path', BRAND_FAVICON: 'Favicon path', DB_HOST: 'Database host', DB_PORT: 'Database port', DB_NAME: 'Database name', DB_USER: 'Database user', DB_PASSWORD: 'Database password', DB_ROOT_PASSWORD: "The database container's root password", JWT_SECRET: 'Signs session tokens. Rotating it logs everyone out', SECRET_ENC_KEY: 'Encrypts secrets at rest. Required in production, and rotating it ORPHANS every stored secret', JWT_EXPIRES_IN: 'Session lifetime', COOKIE_SECURE: 'auto decides Secure per request, so HTTPS and LAN HTTP both work', COOKIE_NAME: 'Session cookie name. Changing it invalidates existing sessions', TRUST_PROXY: 'Needed behind a reverse proxy for secure cookies, real IPs and rate limiting', DEBUG_TRUST_PROXY: 'Diagnostic for the above', TOTP_CHALLENGE_TTL: 'How long a pending 2FA challenge is valid', ADMIN_USERNAME: 'First admin, created only when no users exist', ADMIN_PASSWORD: 'First admin password. Set it before the first boot, not after', CLIENT_ORIGIN: 'Dev only — the Vite origin allowed through CORS', BOT_INTERNAL_URL: 'Where the Discord bot listens', BOT_INTERNAL_KEY: 'Authenticates the site↔bot channel. Required in production EVEN IF you run no bot', NTFY_BASE_URL: 'Push notification relay base URL', }; /** `link/sidecar/src/config.rs` → the TOML the sidecar writes on first run. */ export const sidecarConfig = { 'shard.bind': 'Loopback address the game plugin dials out to', 'web.bind': 'Address the website reaches the sidecar on', 'web.auth_token': 'Shared secret the website must present. Generated on first run if blank', 'store.path': "The sidecar's own durable store", }; /** `installer` — `src/cli.rs`'s `Command`. */ export const installerCommands = { Install: 'Set up the shard side: sync the overlay, install the sidecar, register its service', Doctor: 'Diagnose an existing install', Update: 'Move to a newer bundle', Uninstall: 'Remove what install put there', }; /** `servuo-plugins/overlay/Config/Bridge.cfg` — the plugin's config, grouped for reading. */ export const bridgeCfg = { Connection: { Host: 'Sidecar address the shard dials out to', Port: 'Sidecar port', QueueCap: 'Bounded queue depth. Full means drop-oldest — never block the game', PublicConnectAddress: 'Address players connect to, published to the site', LinkUrl: 'Where in-game account linking sends a player', }, Sweeps: { StatSweepSeconds: 'Character stat sweep interval', DecaySweepSeconds: 'House decay sweep', EconomySweepSeconds: 'Economy totals sweep', ChampSweepSeconds: 'Champion spawn sweep', PageSweepSeconds: 'Staff page sweep', GuildSweepSeconds: 'Guild roster sweep', CitySweepSeconds: 'City / governor sweep', PresenceSweepSeconds: 'Who is online', HousingSweepSeconds: 'Housing sweep', }, Guilds: { GuildRosterMembersPerLine: 'Frame cap — a roster is split rather than sent oversized', GuildRosterGuildsPerTick: 'How many guilds are swept per tick', }, Points: { PointsSweepSeconds: 'Points sweep interval', PointsLeaderboardEnabled: 'Publish a leaderboard at all', PointsTopN: 'Leaderboard length', PointsSystems: 'Which point systems to include', PointsProfileEnabled: 'Show points on a character profile', PointsProfileRank: 'Show rank as well as total', }, Market: { MarketEnabled: 'Publish player vendor listings', MarketSweepSeconds: 'Market sweep interval', MarketSweepBatch: 'Vendors per sweep', MarketMaxListings: 'Cap on listings published', }, Ruleset: { RulesetEnabled: 'Publish the shard ruleset', RulesetIncludeSchedule: 'Include the event schedule with it', }, 'Town crier': { TownCrierMaxLines: 'Lines per notice', TownCrierMaxLineLength: 'Characters per line', TownCrierMaxActive: 'Concurrent notices', TownCrierMaxDurationSec: 'Longest a notice may run', }, News: { NewsMaxTitleLength: 'Title cap', NewsMaxBodyLength: 'Body cap', NewsMaxExternal: 'How many site posts are carried in-game', NewsAnnounceDurationSec: 'How long an announcement shows', }, 'Admin commands': { AdminWriteEnabled: 'Whether the site may write to the game at all. Off by default', AdminAccessFloor: 'Minimum in-game access level for admin actions', AdminBroadcastMaxLength: 'Broadcast cap', AdminReasonMaxLength: 'Reason field cap', AdminBanMaxDurationSec: 'Longest ban the site may set', }, Accounts: { SignupMode: 'How game accounts may be created', AccountCreateEnabled: 'Allow creation at all', RequireIpForCreate: 'Require a real client IP', AccountNameMaxLength: 'Account name cap', AccountPasswordMaxLength: 'Account password cap', }, }; /** * The five-rung visibility ladder, from `module-uo`'s `server/utils/shardVisibility.js`. * * This one is a SECURITY boundary, not a convenience filter, which is why it is enumerated * rather than described: a reader needs to see the whole ladder at once to reason about it. */ export const visibilityLadder = ['anonymous', 'logged_in', 'player', 'staff', 'admin']; /** Canonical documents, by the question each answers. Checked to still exist in `docs`. */ export const canonicalDocs = { 'website/ARCHITECTURE.md': 'How the website fits together — the canonical diagram', 'website/BACKEND_DESIGN.md': 'The API, schema and security contract', 'website/MODULE_SYSTEM.md': 'Why the module system is shaped this way', 'website/MODULE_API.md': 'Everything a module may do — the contract', 'website/TEAMS.md': 'Teams as a platform primitive', 'website/SHARD_VISIBILITY.md': 'The audience ladder, for administrators', 'website/THEMING_AND_NAV.md': 'Admin-configurable theme, assets and navigation', 'website/TRUSTED_DEVICES_MFA.md': 'Trusted devices and the second factor', 'link/PLAN.md': 'The sidecar design of record, the data catalog and the wire protocol', 'link/INTEGRATION.md': 'Integrating with the sidecar', 'link/v4.md': 'Protocol 4, and its cross-repository obligations', 'link/ADMIN_CONTROLS.md': 'What the site may command the game to do', 'installer/INSTALL.md': 'The operator guide for setting a shard up', 'installer/PLAN.md': "The installer's design of record", 'modules/rust-dryrun.md': 'A second module designed on paper, to test that the contract generalises', 'modules/uo/API.md': "module-uo's own API, including its audience rules", 'android/PLAN.md': 'The Android app', };