Skip to content

ayozetr/beamng-server-panel

Repository files navigation

BeamNG Server Panel

BeamNG Server Panel

A web-based administration panel for BeamMP (BeamNG.drive multiplayer) dedicated servers on Linux

Version Status Audit 2FA

Game Engine Node.js SQLite Build Docker License Redistribution


What is it?

A full web interface to manage your BeamMP server without touching the terminal. Accessible from any device on your network, or from anywhere in the world using Cloudflare Tunnel.

Forked from assetto-server-panel and kept structurally identical so the two panels feel like siblings — same auth model, same audit chain, same Docker shape. The game-specific half (server lifecycle, config editor, content catalogue, live state) is rewritten end-to-end for BeamMP's free-roam multiplayer model.


Features

📊 Real-time dashboard

Live host metrics (CPU, RAM, uptime) and BeamMP-Server status pushed via Server-Sent Events. A second row of BeamMP-specific KPI widgets carries the active map (rendered as the official BeamNG.drive screenshot under a darkening gradient with the map's display name + theme badge on top — like a small map-card preview), joins in the last 24 h (from the persistent player_events history), mods on disk (Resources/Client + Server total), and the BeamMP-Server binary version. Polled at /api/dashboard/extra every 30 s.

The Dashboard also surfaces a BeamMP-Server update notifier banner: queries the GitHub releases API once per 6 h, compares against the local BeamMP-Server --version, and shows a per-version-dismissable banner with a link to the release notes when a newer server is out. Best-effort: invisible if GitHub is unreachable or the binary doesn't expose --version.

🗺️ Visual map catalogue

Browse the 15 official BeamNG.drive levels (Acantilado, Costa Este, Costa Oeste, Italia, Utah, Johnson Valley, Isla Jungle Rock, Pista de carreras Hirochi, Automation Test Track, Polígono industrial, Centro ETK de formación de pilotos, Circuitos de demolición, Isla pequeña, Cuadrícula y Mapa de prueba v2) and any custom map mods auto-detected from Resources/Client/*.zip. The catalogue uses the official BeamNG.drive map screenshots sourced from documentation.beamng.com/official_content/levels/images/ (normalised to 800×450 q82 strip-metadata via ImageMagick mogrify) so the grid reads as the real maps rather than placeholder art.

Click a card → modal with full details (display name, level path, theme, size, description) → activate-with-restart-prompt. The Map field in Server Config is no longer a free-text input — operators pick from the catalogue and the panel writes the canonical /levels/<id>/info.json path into the TOML. Custom map mods fall back to a themed SVG illustration when there's no canonical screenshot available.

Names switch to Spanish display strings ("Costa Oeste, EE. UU.", "Centro ETK de formación de pilotos", …) when the panel is set to ES locale. The list sorts alphabetically with a locale-aware Intl.Collator so accented characters land in the right place.

🔄 Automatic map rotation

Persisted list + interval in the same Maps page. An in-process timer activates the next map every N minutes and restarts BeamMP-Server. Operators build the rotation from the same vanilla+custom catalogue; no separate config screen. Each rotation step is audit-logged.

🏎️ Live players + moderation

The Players page shows currently connected drivers (id, in-game name, optional operator nickname, ping, joined-at) plus the full connection history with join/leave counters across every session. Per-player nicknames — pencil button opens a modal to attach a real name to an in-game id; the panel renders rows as "Apodo (in-game)" everywhere from then on.

Per-row buttons: Kick (immediate, uses the live BeamMP player id), Ban (writes to the bans SQLite table with optional duration, kicks immediately, refuses re-connect via PanelBridge once the plugin gates onPlayerAuth), Whitelist (adds to the whitelist allowlist). All audit-logged.

Live state comes from the PanelBridge Lua plugin — a ~90-LOC plugin that ships in tools/beammp-plugin/PanelBridge/ and writes state.json + chat.json to disk every 5 s. The panel watches the directory with fs.watch + a safety-net poll, diffs against the in-memory map, and persists join/leave rows into a player_events table. The plugin also reads a commands.json for moderation commands the panel writes back. See docs/live-state.md.

💬 Live chat viewer + broadcast templates

A live chat feed on the Players page that polls chat.json every 5 s with ?since=<ts> for incremental rendering, 200-line ring buffer, autoscroll-when-pinned-to-bottom, Clear button that hides the current backlog without touching the server-side log. Fills the moderation gap where an admin had to ssh into the BeamMP host and tail ServerLog.txt by hand to read what players were saying.

Broadcast templates modal — five starter templates (welcome, restart-5m, restart-1m, warn, rules) prefill the broadcast field, then any tweaks before sending. Broadcasts render inline in the chat feed with kind:"broadcast" star-prefix styling so the chat view shows both sides of the conversation.

MOTD loop in Settings — periodic re-broadcast every N minutes for server rules, Discord links, or rotation reminders. Reuses the same /api/broadcast + PanelBridge command pipeline.

⏰ Cron-simple restart scheduler

Fixed time + weekday picker in Settings, persisted in panel_settings. An in-process timer fires once per minute window. Each scheduled restart is audit-logged.

📦 Mod installer

Upload BeamMP mods directly from the browser. The classifier matches BeamMP's mod model: archives with a main.lua at the root are server plugins (extracted into Resources/Server/<plugin>/), everything else is a client mod copied verbatim as .zip into Resources/Client/ without re-packing — matching BeamMP's spec (the server hands the original .zip to connecting clients, not a re-packed one).

Wrong-game uploads (AC car/track archives with data.acd / ui/ui_car.json) are rejected with a clear "this looks like an Assetto Corsa mod, use the AC panel" message. Decompression-bomb guards (50 000 entries max, 2 GB per entry, 5 GB cumulative) and zip-slip protection across all paths. Chunked upload for Cloudflare and other proxies that block large binary POSTs.

The Mods catalogue mirrors the Maps page UX: unified .car-grid of thumbnail cards, kind-based filter chips, click-to-modal with full mod details, and a per-card preview image extracted on demand from inside the .zip when the mod carries one.

⚙️ ServerConfig.toml editor

Edit the live ServerConfig.toml through a visual UI — three cards: Server identity (Name / Description / Port / MaxPlayers / MaxCars / Tags), Privacy & logging (Private / AllowGuests / InformationPacket / LogChat / Debug toggles), AuthKey (write-only rotation with eye-toggle + Reveal current value button for admins, audit-logged each reveal). Backed by an embedded line-edit TOML parser that preserves comments + key order + inline whitespace — saves substitute matching lines in place rather than re-emitting from an AST.

Each PUT validates against TOML_FIELD_SCHEMA (Port 1024-65535, MaxPlayers/MaxCars 1-1000, Name ≤ 250, Description ≤ 1000, Map ≤ 100, Tags ≤ 100, UpdateReminderTime as \d+(s|min|h|d), …), atomic-writes via temp + rename(2), rotates ServerConfig.toml.bak + timestamped .<datetime>.bak backups with CFG_BACKUPS_KEEP retention, audits the changed key names (never values — secrets never land in the audit chain), and auto-restarts the BeamMP-Server when it's currently up (mirrors the AC panel because BeamMP has no SIGHUP reload).

Export / Import lives in the same page: one-click TOML download with AuthKey redacted to <REDACTED-PASTE-NEW-KEY-HERE> for backup or cross-host migration, and upload of a saved TOML through the same _validateTomlUpdates pipeline so an operator can lift a config from one host to another without copying secrets by hand.

👥 User management

Create, edit and delete panel users. Each user has their own profile with password change and a built-in secure password generator (uses crypto.getRandomValues). The panel refuses to delete or demote the last remaining admin and revokes a user's active sessions when an admin resets their password.

🔐 Granular role permissions

The user role is gated by eight independent toggles edited from the Users page: serverControl, serverConfig, whitelistManage, playerModeration, modUpload, discordWebhook, auditView, dbBackup. Admin always passes every check. Defaults preserve the open grants from earlier deployments (server control + mod upload on; the rest off). Editing a permission takes effect on the affected user's next request — no re-login needed. New permissions added in a later release auto-backfill into pre-existing role rows with their intended defaults so upgrades don't silently demote users. Panel-user CRUD, AuthKey rotation and the permission set itself are reserved to admins by design (cannot be exposed as toggles without enabling privilege escalation).

🛡️ Security

  • Sessions: scrypt password hashing with cost params pinned in SCRYPT_PARAMS (constant-time compare), HttpOnly; SameSite=Strict cookies with 7-day TTL plus the Secure flag when the request arrived over HTTPS, automatic 401 → logout interceptor on the client.
  • Forced first-login change: seeded Admin / Admin1234! is locked into a blocking modal until the password is changed; server-side gate refuses every authenticated endpoint until the flag clears.
  • TOTP 2FA: opt-in per-user, RFC-6238 SHA-1 30-second window, QR provisioning via vendored qrcode-generator. The same setup/confirm/disable flow as the AC fork.
  • CSRF: unsafe methods require Origin/Referer to match Host. When TRUST_PROXY=1, the panel also reads X-Forwarded-Host so reverse-proxies that rewrite Host (Caddy default reverse_proxy, nginx without proxy_set_header Host, cloudflared with httpHostHeader: localhost) don't trigger false-positive rejections. Combined with SameSite=Strict cookies, cross-site requests are rejected at two layers.
  • Headers: CSP without unsafe-eval/unsafe-inline, Permissions-Policy, X-Frame-Options, Referrer-Policy on every response. HSTS auto-enabled when behind an HTTPS-terminating proxy.
  • Rate-limited login, change-password, mod uploads, server start/stop/restart, config writes, AuthKey reveals and chat broadcasts. Per-user concurrent SSE log-stream cap (6) caps file-descriptor usage. Optional TRUST_PROXY=1 to honour CF-Connecting-IP / X-Forwarded-For so the limiter sees real client IPs through Cloudflare.
  • ADMIN_TOKEN header (when configured) is compared in constant time via crypto.timingSafeEqual.
  • Mod uploads: strict zip-slip abort, archive entry-count and aggregate-size caps, wrong-game rejection signals (AC archives caught before extraction).
  • Per-user audit log of every admin action, with cursor pagination. Rows are SHA-256 hash-chained with a JSON-canonicalised payload (chain_version = 1) so a | inside a field cannot collide with a different field assignment; legacy rows still validate via tools/verify-audit.js.

📱 Responsive

Sidebar collapses into a drawer with a hamburger toggle below 900 px wide; layouts re-flow to single columns on phones. Tested on portrait phones down to 360 px.

🌍 Multilingual

Full interface available in English and Spanish. The Spanish translation covers the panel chrome + every BeamMP-specific feature, including the in-game map names ("Costa Oeste, EE. UU." / "Acantilado" / "Cuadrícula, pequeño, sencillo" — the strings BeamNG.drive itself uses in the Spanish UI). The translation dictionary is checked by tools/i18n-coverage.js, which reports any key used in code but missing from a language (or vice versa).


Quick start

git clone https://github.com/ayozetr/beamng-server-panel.git
cd beamng-server-panel
nvm use 20
npm install
cp .env.example .env   # fill in BEAMMP_SERVER_DIR and BEAMMP_BIN
npm start              # automatically pre-builds dist/ via esbuild then runs server.js

The first time you run npm start, esbuild transpiles src/*.jsx to dist/*.js. Subsequent starts re-build (it takes ~20 ms — much cheaper than transpiling in the browser on every page load).

To rebuild manually after editing JSX without restarting the server:

npm run build

Open http://<server-ip>:3000 in your browser.

⚠️ Before exposing the panel beyond localhost

The default HOST=0.0.0.0 listens on every network interface. Combine that with the wrong network config and the panel becomes reachable from places you didn't intend. Pick one of:

  1. Cloudflare Tunnel (recommended). Set HOST=127.0.0.1 and TRUST_PROXY=1 in .env. The tunnel terminates TLS and forwards traffic to localhost; no firewall ports are opened. See docs/deployment.md.
  2. LAN-only access. Leave HOST=0.0.0.0 but block port 3000 from the internet at the router / firewall. Do not set TRUST_PROXY=1 — without a real proxy in front it lets any LAN client spoof their IP in CF-Connecting-IP.
  3. Reverse proxy (nginx / Caddy / Traefik). Same shape as Cloudflare Tunnel: HOST=127.0.0.1 + TRUST_PROXY=1 + TRUST_PROXY_FROM=<proxy IP CIDRs> in .env.

The BeamMP game traffic on UDP+TCP 30814 still needs port-forwarding on your router regardless of how the panel itself is exposed — Cloudflare Tunnel doesn't proxy UDP. If the panel is publicly reachable you must change the default Admin password (the first login forces it, but a port scanner can still see the login screen).

Default credentials: Admin / Admin1234!. The first login forces a password change in a blocking modal — the rest of the panel is unreachable (server-side enforced) until the password is changed. If you ever lock yourself out, run:

UPDATE panel_users SET must_change_password = 0 WHERE username = 'Admin';

in panel.db and log in normally.


Documentation

Document Description
Install the panel Step-by-step Ubuntu install for the panel itself: prerequisites, .env, systemd, backups
Install BeamMP-Server — single-tree layout Greenfield Ubuntu install for BeamMP-Server under /srv/beammp/ with a dedicated system user
Install BeamMP-Server — split layout Alternative layout: $HOME/beamng_server/ + /srv/beamng/{cfg,content} via symlinks (for shared hosts)
Docker deployment Containerised setup with docker compose up -d, volumes, networking, reverse-proxy front-end, troubleshooting
Production deployment Systemd service, Cloudflare Tunnel, firewall, log rotation, hardened systemd unit, safe update procedure
Server lifecycle How the panel manages BeamMP-Server (spawn semantics, PID adoption, TCP health-check, locking, rate limits)
Server config /api/config reference (field schema, AuthKey handling, comment-preservation, backups, atomic writes, audit, auto-restart)
Live state (PanelBridge) Live player state via the Lua plugin (install, wire protocol, what works with vs. without it, limitations)
Authentication & users Session system, 2FA TOTP, granular role permissions, audit chain
Mod installer BeamMP's two-category mod model (client zips vs server plugins), classifier signals, chunked upload
Database SQLite schema and what gets stored (tables + columns + indices + migration list)
API reference All server endpoints with request/response shapes
Troubleshooting Common issues and how to fix them
Tools Scripts under tools/ (setup wizard, smoke test, supply-chain audit, audit-log verifier, coverage reporters)
Tested on Exact OS / Node / npm / SQLite versions the panel is known to run on
Security policy How to report vulnerabilities and what is in / out of scope
Roadmap Project status, comparison with the upstream BeamMP world, prioritised backlog
Changelog Per-release summary of every notable change since 0.1.0

The docs/beammp/ directory is a mirror of the official BeamMP docs at https://docs.beammp.com — kept locally as a reference vendor; it's not part of this panel's documentation.


Threat model

This is a single-tenant admin tool, not a multi-tenant web app. Trust assumptions:

  • Admins are fully trusted. Admin role always passes every permission check. Exclusively allowed actions: managing panel users (create / delete / change role / reset password), rotating the BeamMP AuthKey, revealing the current AuthKey in cleartext, wiping mod history, downloading the SQLite DB and reading or writing the role-permissions set itself. These are reserved by design — exposing them as toggles would let a user escalate to admin.
  • The user role is configurable per-permission, not "read-only". An admin can grant a user the ability to start/stop the BeamMP server, edit ServerConfig.toml (everything except the AuthKey), apply map changes, kick/ban players, manage the whitelist, upload mods, edit the Discord webhook, view the audit log and download DB backups — one toggle each. Trust accordingly: granting serverConfig lets the user reshape the running server; granting modUpload lets the user push Lua server plugins that run inside the BeamMP-Server process (there is no sandbox between plugins and the host).
  • Do not expose the panel to the public internet without HTTPS and credentialled access. Do not give panel accounts to anyone you would not trust with the equivalent shell-level capability. Always set TRUST_PROXY=1 when behind Cloudflare/Tunnel/reverse-proxy so rate limits, audit logs and CSRF origin matching see real client IPs and hostnames.
  • The audit log is hash-chained but deletable. Each row stores a SHA-256 of the previous row's hash, so silent edits are detectable with node tools/verify-audit.js against an external backup. Anyone with shell access to panel.db can still wipe rows entirely — keep periodic backups via /api/admin/backup if you need provable history. Every permission-gated action is recorded with the actor's username, so a per-user trail survives even when a permission set is broad.

What the panel does defend against:

  • Anonymous attackers (CSRF, brute-force on login, path traversal, decompression bombs, malformed archives, wrong-game upload signals).
  • Privilege escalation from a compromised user account — even with every toggle on, the user role cannot create/delete panel users, change another user's role or password, reveal the AuthKey in cleartext, wipe mod history, or edit the role-permissions set.
  • Stolen old SW caches (network-first navigation strategy ensures security fixes propagate without manual cache bumps).

What the panel does not defend against:

  • Malicious Lua server plugins (no sandboxing — only grant modUpload to people you trust to vet upload sources).
  • A compromised admin account (full control by design).
  • An over-permissioned user account — e.g. a user granted serverConfig can rewrite the BeamMP-Server port, MaxPlayers, Private flag and Tags; a user granted modUpload can ship arbitrary Lua that runs server-side. Defaults exist; the configured permission set is the operator's responsibility.
  • Filesystem access via the host shell or other services.

Tech stack

  • Frontend: React 18 (production CDN) + esbuild build step transpiling JSX → plain JS at startup
  • Backend: Node.js native HTTP (no Express)
  • Database: SQLite via better-sqlite3
  • Mod extraction: node-stream-zip, node-unrar-js, node-7z
  • Live state bridge: BeamMP Resources/Server/PanelBridge/ Lua plugin (read/write JSON on a 5 s tick)
  • Real-time logs: Server-Sent Events (SSE)

Tested on

The combinations below are the ones the maintainer actually runs day-to-day; the panel is reasonably portable but these are the ones that are known to work. Full version matrix and bundled-dependency lockfile excerpt in docs/tested-on.md.

Role OS Kernel Node.js npm SQLite (system) BeamMP-Server
Production Ubuntu 24.04.4 LTS (Noble Numbat) 6.8.0-117-generic v20.20.2 11.13.0 3.45.1 v3.9.2
Development CachyOS (Arch-based, rolling) 7.0.9-1-cachyos v20.20.2 11.14.1 3.53.0 (remote prod)

Bundled npm packages on both hosts (production identical, dev installs the same lockfile):

7zip-bin@5.2.0 · @resvg/resvg-js@2.6.2 · better-sqlite3@12.10.0 · dotenv@16.4.5 · node-7z@3.0.0 · node-stream-zip@1.15.0 · node-unrar-js@2.0.2 · esbuild@0.28.0

Other Linux distributions on Node 20 LTS should work without changes — the panel only depends on a POSIX filesystem, a recent SQLite, and the libraries above. Windows/macOS will likely run the panel itself but the BeamMP-Server Linux binary won't run there; for development on those platforms point the panel at a remote BeamMP host. Please open an issue if you hit something distro-specific.


License

Source-available. The full text — which is what binds you, not this summary — is in LICENSE. Read it before deploying.

Short version (informative, not legally operative — only the LICENSE file is):

  • Free to download and run anywhere, for anything lawful. Personal hobby use, private servers, friend groups, amateur clubs, educational and research use are all fine. So is operating the panel for public servers, including servers advertised on the BeamMP public listing, including commercial use (paid leagues, sponsorships, ads, donations, for-profit organizations). You don't need a separate agreement to charge for participation or run a paid event on top of a server the panel manages.

  • No redistribution. You may not republish the source code, fork it to a public repository as your own, upload it to a package registry, bundle it into another product, host it as a service for third parties to download, or otherwise hand copies to other people. If someone wants to use the panel, point them at the official repository — they can clone it themselves under their own acceptance of the LICENSE.

  • Attribution Marks are irremovable. The "Developed by ayozetr" credit in the sidebar, the project name "BeamNG Server Panel", the link to the official repository, the copyright notice, and every other identifier referring to the author or the project must stay intact in every copy you operate. This applies even to commercial deployments. A re-skin, white-label, or theme that hides the marks is a breach and terminates your rights automatically.

  • Local modifications are allowed. Patch bugs, translate, add integrations, restyle (without touching the Attribution Marks) — keep them for yourself. You may submit them upstream via pull request; you may not publish them as your own fork.

  • No affiliation with anyone. Not BeamNG GmbH, not BeamMP, not Valve, not any car / track manufacturer whose brand appears in bundled assets. The LICENSE has a long disclaimer section spelling this out.

  • No warranty, no liability. "AS IS". Each tagged release is published with good-faith effort against known vulnerabilities at the time of publication, but the author makes no ongoing warranty and accepts no liability for damages, data loss, or downtime. See LICENSE sections 7 and 8.

For licensing questions, redistribution requests, or anything else: ayozetr@proton.me. For security disclosure see SECURITY.md.

About

Web-based admin panel for BeamMP (BeamNG.drive multiplayer) dedicated servers. Mods, live chat & broadcast, map rotation, 2FA, ban management, multi-language, Docker-ready.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Sponsor this project

Packages

 
 
 

Contributors