# l4d2-host-lib Python host library and CLI for managing L4D2 instances. ## CLI `l4d2ctl` exposes these write commands in v1: - `install` - `initialize -f ` - `start ` - `stop ` - `delete ` It also exposes read commands used by the web app host boundary: - `status --json` - `logs --lines --follow/--no-follow` Subprocess failures are fail-fast. Raw stderr is written to stderr and the command exits with the same subprocess return code. ## Runtime Paths The host library reads `LEFT4ME_ROOT` from the environment. It defaults to `/var/lib/left4me`: - `${LEFT4ME_ROOT}/installation` - `${LEFT4ME_ROOT}/overlays/` - `${LEFT4ME_ROOT}/instances/` - `${LEFT4ME_ROOT}/runtime//{upper,work,merged}` - `${LEFT4ME_ROOT}/tmp` Overlay specs use relative refs below `${LEFT4ME_ROOT}/overlays`, for example `standard`, `competitive/base`, or `users/42/custom`. Absolute refs, `..`, empty path components, and symlink escapes outside the overlays root are rejected. ## systemd Integration `l4d2ctl start`, `stop`, `status`, and `logs` use non-interactive sudo helper commands: - `sudo -n /usr/local/libexec/left4me/left4me-systemctl ...` - `sudo -n /usr/local/libexec/left4me/left4me-journalctl ...` Deployment/config management owns the global `left4me-server@.service` unit under `/usr/local/lib/systemd/system`. The host library does not install or manage the unit file directly. ## Host Prerequisites The host library intentionally does not install or preflight runtime dependencies. The target host must provide them before running `l4d2ctl`. Validated on Debian 13 during the `ckn@10.0.4.128` smoke test: - Python 3.12+ with virtualenv/pip tooling for installing `l4d2host`. - `steamcmd` available on `PATH` and able to self-update as the runtime user. - 32-bit compatibility libraries for SteamCMD on amd64 Debian: `libc6-i386`, `lib32gcc-s1`, `lib32stdc++6`. - Kernel overlayfs (`mount -t overlay`); mount/umount go through the `left4me-overlay` privileged helper, which `nsenter`s into PID 1's mount namespace. - `systemctl --user` and `journalctl --user` available for the runtime user. - User lingering enabled when services must survive SSH sessions: `sudo loginctl enable-linger `. - `/var/lib/left4me` created and writable by the runtime user, unless `LEFT4ME_ROOT` is set to another deployment-managed root. Example Debian setup: ```bash sudo apt-get update sudo apt-get install -y \ python3 python3-venv python3-pip \ curl ca-certificates tar gzip \ util-linux \ libc6-i386 lib32gcc-s1 lib32stdc++6 sudo mkdir -p /opt/steamcmd /var/lib/left4me/{installation,overlays,instances,runtime,tmp} sudo chown -R "$USER:$USER" /opt/steamcmd /var/lib/left4me sudo loginctl enable-linger "$USER" ``` SteamCMD should be installed so the runtime user can update it. If installing from Valve's tarball, avoid symlinking `steamcmd.sh` directly because it derives its install root from `$0`. Use a wrapper instead: ```bash curl -fsSL https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz -o /tmp/steamcmd_linux.tar.gz tar -xzf /tmp/steamcmd_linux.tar.gz -C /opt/steamcmd sudo tee /usr/local/bin/steamcmd >/dev/null <<'EOF' #!/bin/sh exec /opt/steamcmd/steamcmd.sh "$@" EOF sudo chmod 755 /usr/local/bin/steamcmd chmod 755 /opt/steamcmd/steamcmd.sh /opt/steamcmd/linux32/steamcmd steamcmd +quit ``` `uv` is optional deployment tooling. Debian 13 did not provide an `uv` package during the smoke test, so install it explicitly if you want to use it for faster virtualenv/dependency setup. `l4d2ctl` does not require `uv` at runtime. ## Host-Local Read APIs These Python read APIs back the CLI read commands and remain available for host-local callers: - `get_instance_status(name)` - `stream_instance_logs(name, lines=200, follow=True)`