Skip to content

Repository files navigation

KetherServerDaemon

Daemon for managing Left 4 Dead 2 workshop and custom maps: installs VPKs, keeps a JSON registry, watches the addons folder, syncs with a remote backend, and exposes a local HTTP API plus interactive REPL.

Requirements

  • Rust stable (see rust-toolchain.toml)
  • Network access for Steam Workshop downloads and optional backend sync

Build

cargo build --release

Binary: target/release/KetherServerDaemon

Configuration

Configuration is loaded from config.toml (or path in KETHER_CONFIG). If the file does not exist, a commented default file is created on first startup. Environment variables override file values:

VariableDescription
KETHER_CONFIGPath to config TOML
KETHER_L4D2_SERVER_DIRL4D2 server root (addons at {dir}/left4dead2/addons)
KETHER_REGISTRY_PATHJSON map registry file
KETHER_BACKEND_API_URLRemote sync API base URL (website-server: http://127.0.0.1:3001/api)
KETHER_BACKEND_API_KEYShared bearer token for backend sync and inbound API (must match website-server [server_daemon].sync_api_key)
KETHER_LOCAL_API_BINDLocal HTTP API bind address (default 127.0.0.1:8080)
KETHER_SYNC_INTERVAL_SECSBackend sync interval
KETHER_LOG_LEVELtrace, debug, info, warn, error
KETHER_MAX_DOWNLOAD_SIZE_BYTESMax download size (default 1GB)
KETHER_MAX_EXTRACTION_SIZE_BYTESMax ZIP extraction size
KETHER_MAX_EXTRACTION_FILE_COUNTMax files per archive
KETHER_L4D2CENTER_INDEX_URLL4D2Center catalog index URL
KETHER_MAP_UPDATE_CHECK_INTERVAL_DAYSShared workshop/L4D2Center update check interval in days (default 3; first check waits one full interval after startup)
KETHER_WORKSHOP_UPDATE_CHECK_ENABLEDEnable periodic workshop update checks (default true)
KETHER_WORKSHOP_UPDATE_AUTO_APPLYAuto-download workshop updates when found (default true)
KETHER_L4D2CENTER_UPDATE_CHECK_ENABLEDEnable periodic L4D2Center update checks (default true)
KETHER_L4D2CENTER_UPDATE_AUTO_APPLYAuto-download L4D2Center updates when found (default false)

REPL commands

CommandDescription
ls / mapsList installed maps
i <url|workshop_id> [name]Install map
rm <id>Uninstall map
u [id] [--check] [--force]Check or update workshop maps
d [u|U]Discover local VPKs (u = refresh metadata)
compactPrune orphaned registry entries and reindex IDs
info <id>Show map details
modify <id> <field> <value>Edit registry field
S / stopStop daemon

HTTP API

Default bind: http://127.0.0.1:8080

All /api/* routes require Authorization: Bearer <backend_api_key> when backend_api_key is configured. /health remains unauthenticated. A non-loopback local_api_bind is rejected at startup unless this key is set.

MethodPathDescription
GET/healthHealth check
GET/api/mapsList maps
GET/api/maps/updates/availablePending + in-progress updates ({ available, in_progress })
GET/api/maps/{id}Get map
PATCH/api/maps/{id}Modify map field
POST/api/maps/installInstall from URL or workshop ID
POST/api/maps/uninstall/{id}Uninstall map
POST/api/maps/workshop/updateUpdate workshop maps (check_only, force)
POST/api/maps/discoverScan addons directory
POST/api/maps/compactCompact registry

Responses use { "success": true, "data": ... } or { "success": false, "error": "..." }.

Backend sync (website-server)

The daemon pushes its map registry to the website backend and polls for pending updates:

MethodPath (relative to backend_api_url)Auth
POST/registry/syncAuthorization: Bearer <backend_api_key>
GET/registry/updatessame Bearer token

Example config.toml when website-server runs on port 3001:

backend_api_url = "http://127.0.0.1:3001/api"backend_api_key = "your-shared-secret"

The public website reads installed maps from GET http://127.0.0.1:3001/api/maps (no auth).

Cross-host website-server access

When website-server runs on another host, bind the daemon to an address reachable from that host and configure a shared inbound API key:

local_api_bind = "0.0.0.0:8080"backend_api_key = "a-long-random-secret"

Set the same value as website-server's [server_daemon].sync_api_key, and set its daemon_url to this daemon's reachable HTTP address. The daemon API is plain HTTP, so firewall port 8080 to the website-server's source IP only. Prefer a private VPN/tunnel or a TLS reverse proxy for traffic crossing an untrusted network.

Systemd

Example user unit: .config/systemd/user/ksd.service

Tests

cargo test
cargo clippy
cargo fmt --check

About

L4D2 Server-side daemon for managing installed custom maps

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages