diff --git a/docs/l4d2-server-cvar-reference.md b/docs/l4d2-server-cvar-reference.md new file mode 100644 index 0000000..b4a6e6f --- /dev/null +++ b/docs/l4d2-server-cvar-reference.md @@ -0,0 +1,259 @@ +# L4D2 server cvar reference + +Working notes from the 2026-05 research session on best-practice +L4D2 dedicated server settings. Sources cited inline; some findings +verified empirically on the `left4.me` Trixie test server (kernel +6.12.86). This is reference material, not a settled design. + +## Quick lookup + +| Topic | Recommended | +|---|---| +| Tickrate (stock) | 30 | +| Tickrate (competitive) | 100, requires Tickrate Enabler plugin | +| `sv_pure` | `2` (strict), or `0`/`1` for modded servers | +| `sv_cheats` | `0` (set to 1 only on private practice servers; disables VAC) | +| `sv_consistency` | `0` (allow custom campaigns) or `1` (strict for competitive) | +| `sv_alltalk` | `0` (no cross-team voice), `1` for casual / fun servers | +| `sv_lan` | `0` (internet server) | +| `sv_voiceenable` | `1` | +| `nb_update_frequency` | `0.033` (safe, no plugin), `0.014` with the SM fix plugin. **Cheat-protected — must be set via `sm_cvar`.** | +| `fps_max` | `64` for 30-tick, `0` (uncapped) for higher ticks | + +## Network rates + +L4D2 default tickrate is **30**. Rates above the corresponding +ceiling are ignored without the +[Tickrate Enabler plugin](https://github.com/SirPlease/Server4Dead-Project/tree/master/Tickrate%20Enabler). + +Rule of thumb: `sv_maxrate = tickrate × 1000`. + +### 30-tick (stock) + +``` +sv_minrate 30000 +sv_maxrate 30000 +sv_mincmdrate 30 +sv_maxcmdrate 30 +sv_minupdaterate 30 +sv_maxupdaterate 30 +net_splitpacket_maxrate 30000 +fps_max 64 +``` + +### 60-tick (requires Tickrate Enabler) + +``` +sv_minrate 60000 +sv_maxrate 60000 +sv_mincmdrate 60 +sv_maxcmdrate 60 +sv_minupdaterate 60 +sv_maxupdaterate 60 +net_splitpacket_maxrate 60000 +fps_max 128 +``` + +### 100-tick (competitive, requires Tickrate Enabler) + +``` +sv_minrate 100000 +sv_maxrate 100000 +sv_mincmdrate 100 +sv_maxcmdrate 100 +sv_minupdaterate 100 +sv_maxupdaterate 100 +net_splitpacket_maxrate 100000 +fps_max 0 +``` + +### sv_min*rate vs. sv_max*rate + +- Locking `min == max` (competitive servers do this) ensures every + client sends at the tickrate exactly. Strict — kicks clients + that dip below. +- Leaving a range (e.g. `min=10, max=30` on a 30-tick public + server) tolerates clients on weak connections or loaded CPUs. +- Setting `sv_mincmdrate=0` means *no enforced minimum* — clients + could send as few as 1-2 cmds/sec. Bad. Pick a floor that's + playable (~10 minimum). + +## Cheat-protected cvars and `sm_cvar` + +Several gameplay-affecting cvars are flagged as "cheat" in L4D2 and +**cannot be set via `server.cfg` unless `sv_cheats 1`** — which +disables VAC and gates achievements. Trying to set them from cfg +silently fails (the value stays at default). + +To set them on a real (VAC-protected) server: install SourceMod and +use `sm_cvar ` instead of ` `. SourceMod +bypasses the cheat protection for *server-side cvar writes only* +(does not grant cheat commands to players). + +Cheat-protected cvars worth knowing: +- `nb_update_frequency` — common-infected pathing/state update + rate (see below). +- `director_*` — most director cvars (AI difficulty, panic events, + pacing). +- `z_*` — most zombie-behavior cvars. + +`sm_cvar` writes go in `cfg/sourcemod/sourcemod.cfg` (auto-execed +by SM on map change) or in any cfg under `cfg/sourcemod/`. SM +re-applies these on every map change — important because +cheat-protected cvars *reset to defaults on map change* even +within the same server session. + +## `nb_update_frequency` + +Controls how often common infected and witches recalculate their +pathfinding and state. Default `0.1` (10 Hz), independent of +server tickrate. + +| Value | Effect | +|---|---| +| `0.1` (default) | Common-infected look choppy regardless of tickrate | +| `0.033` | ~30 Hz updates; smooth, safe without plugin | +| `0.024` | Lowest "safe" without plugin per community testing | +| `0.014` | ~71 Hz; clients with `cl_interp 0` see jittery commons unless the [nb_update_frequency fix plugin](https://forums.alliedmods.net/showthread.php?t=344019) is installed | + +Set via `sm_cvar nb_update_frequency 0.033` in +`cfg/sourcemod/sourcemod.cfg` (or any sm-auto-execed cfg). Without +SourceMod, you cannot reliably set this on a VAC-protected server. + +If commons still look choppy after lowering `nb_update_frequency`, +try `sm_cvar z_resolve_zombie_collision_multiplier 0.6` (default +`0.25`) — helps commons unstick from each other. Cheat-protected, +same `sm_cvar` mechanism. + +## Cvars that DO NOT exist in L4D2 (despite some guides claiming otherwise) + +These come up in older guides or are inherited from other Source +games but don't actually exist in L4D2's command set. Verified by +RCON `` returning "unknown command": + +- `net_maxcleartime` — CSGO/CS:S only. +- `z_resolve_zombie_collision_multiplier` — confirmed unknown in + current L4D2 builds (verified via RCON 2026-05-14). Some + community guides list it; it's not in the binary. + +If a guide tells you to set one of these in L4D2, the guide is +wrong or out of date. + +## Security and integrity + +``` +sv_cheats 0 +sv_pure 2 # force Steam-only files (strictest) +sv_consistency 1 # enforce file hashes for critical files + # (set 0 if hosting custom campaigns) +sv_lan 0 # internet server +``` + +Launch the server with `-secure` to enable VAC. `sv_cheats 1` +requires `-insecure` (no VAC) — only acceptable on private +practice servers. + +`sv_pure 2` breaks many workshop maps/mods. Use `sv_pure 0` or `1` +for modded servers. + +## Player limits + +``` +# Co-op / Scavenge +sv_maxplayers 4 +sv_visiblemaxplayers 4 + +# Versus +sv_maxplayers 8 +sv_visiblemaxplayers 8 +``` + +## Voice + +``` +sv_voiceenable 1 +sv_alltalk 0 # 1 = cross-team voice (casual / fun servers) +``` + +## Recommended plugins (SourceMod ecosystem) + +| Plugin | Purpose | +|---|---| +| MetaMod:Source + SourceMod | Required foundation for most of the below | +| [Tickrate Enabler](https://github.com/SirPlease/Server4Dead-Project/tree/master/Tickrate%20Enabler) | Unlock >30 tick servers | +| [Little Anti-Cheat](https://github.com/J-Tanzanite/Little-Anti-Cheat) | Aimbot / angle-cheat detection | +| SMAC | Secondary AC layer (older but still works) | +| [ZoneMod](https://github.com/SirPlease/L4D2-Competitive-Rework) | Competitive Versus ruleset (full bundle: ZoneMod + MatchMode + Confogl-style plugins) | +| `l4d2_TKStopper` | Teamkill / griefing control | +| `l4d_sb_fix` | Survivor bot behavior fixes | +| [nb_update_frequency fix](https://forums.alliedmods.net/showthread.php?t=344019) | Eliminates client-side jitter at very low `nb_update_frequency` values | + +## MetaMod:Source / SourceMod versioning + +- Stable branches are pinned in URL paths: `1.10`, `1.11`, `1.12`, + etc. There is no "latest stable" alias URL — you pick the + branch. +- Within a branch, the `mmsource-latest-linux` and + `sourcemod-latest-linux` text files contain the current build's + filename, e.g. `mmsource-1.12.0-git1219-linux.tar.gz`. Curl the + pointer file, then curl the actual tarball. +- AM bumps stable every ~2-3 years. When 1.13 (or later) is + declared stable, update the `MM_BRANCH=1.12` / `SM_BRANCH=1.12` + pins in the seeded Sourcemod overlay script. +- L4D2 has no special branch — it uses whatever the current + stable supports. L4D2's engine is so stable that SM 1.11 and + 1.12 both work. + +Watch for stable announcements at +[Metamod:Source news](https://www.sourcemm.net/) and +[SourceMod releases](https://github.com/alliedmodders/sourcemod/releases). + +## Empirically-verified kernel quirk (relevant if you tweak the helpers) + +Idmapped bind mounts on kernel 6.12 (Trixie) **do** propagate +through plain `mount --bind` re-binds. Verified end-to-end on +`left4.me` during the 2026-05-15 build-time-idmap refactor: a +sandbox process inside a re-bound idmapped mount can write files, +and those writes land on disk with the idmap-translated uid. + +This contradicts some published claims (including a generic +research-agent summary) that idmaps don't propagate through plain +re-bind on this kernel. Our use case is `mount --bind --map-users +src staging` → systemd-run with `BindPaths=staging:/overlay` (a +plain re-bind into the unit's namespace). It works. + +The `--map-users ::` direction is **on-disk uid +first**, then in-mount uid. The util-linux man page calls these +`:` which is confusing — `` means "the +filesystem's native uid" (on disk) and `` means "the uid +exposed outward through the mount." Empirically verified; do not +trust the man page's word choice. + +## Launch parameters (reference) + +Typical srcds invocation: + +``` +./srcds_run -console -game left4dead2 -secure -autoupdate \ + +maxplayers 8 -port 27015 +exec server.cfg +log on +``` + +- `-secure` enables VAC. Don't run public servers without it. +- `-autoupdate` keeps the server patched automatically. +- `+exec server.cfg` runs your config on startup. +- `-tickrate ` sets the engine tickrate (requires Tickrate + Enabler for `N > 30`). + +## Sources + +Primary references used for the recommendations above: + +- [L4D2-Competitive-Rework server.cfg](https://github.com/SirPlease/L4D2-Competitive-Rework/blob/master/cfg/server.cfg) +- [L4D2 Dedicated Server Guide (Steam Community)](https://steamcommunity.com/sharedfiles/filedetails/?id=276173458) +- [L4D2 Dedicated Server Network Tweaks (Steam Discussions)](https://steamcommunity.com/app/550/discussions/1/1839063537784156851/) +- [SirPlease/Server4Dead-Project — Tickrate Enabler](https://github.com/SirPlease/Server4Dead-Project/tree/master/Tickrate%20Enabler) +- [Valve Developer Community — L4D2 console commands](https://developer.valvesoftware.com/wiki/List_of_Left_4_Dead_2_console_commands_and_variables) +- [AlliedModders — nb_update_frequency fix (Experimental)](https://forums.alliedmods.net/showthread.php?t=344019) +- [Source Multiplayer Networking — Valve Developer Community](https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking) +- [Required Versions (SourceMod wiki)](https://wiki.alliedmods.net/Required_Versions_(SourceMod)) +- [MetaMod:Source news](https://www.sourcemm.net/) diff --git a/docs/superpowers/specs/2026-05-15-janitorial-cleanup.md b/docs/superpowers/specs/2026-05-15-janitorial-cleanup.md new file mode 100644 index 0000000..33a81dc --- /dev/null +++ b/docs/superpowers/specs/2026-05-15-janitorial-cleanup.md @@ -0,0 +1,218 @@ +# Janitorial cleanup checklist + +**Status: TODO list, not a settled design.** Collects the "do later" +items that surfaced across multiple plans and handoffs during the +2026-05-14/15 idmap + consolidation work. Each is small and +self-contained. Knock them out individually or batch them into a +single janitorial PR. None are urgent — the project works fine with +all of these still present. + +## Items + +### 1. `left4me-apply-cake` — dead code + +**What**: `deploy/files/usr/local/libexec/left4me/left4me-apply-cake` +(POSIX sh, ~47 lines) that applies/clears CAKE egress traffic +shaping via `tc`. + +**Why dead**: CAKE migrated to systemd-networkd via +`network//cake` metadata in ckn-bw's `bundles/network/`. The +service unit that invoked this helper +(`left4me-cake.service`) is also obsolete (see item 2). The script +is currently shipping to `/usr/local/libexec/left4me/` on every +`bw apply` via the install glob, but nothing on the system invokes +it. + +**Action**: delete `deploy/files/usr/local/libexec/left4me/left4me-apply-cake`. +The deploy will stop installing it on next apply. Existing +deployed copy at `/usr/local/libexec/left4me/left4me-apply-cake` on +the test server can be `sudo rm`d at the same time. + +**Verification**: +``` +sudo find /var/lib/left4me /opt/left4me /usr/local -name 'left4me-apply-cake' +# expect: empty after the rm +``` + +### 2. Obsolete systemd unit files in `deploy/files/` + +**What**: +- `deploy/files/usr/local/lib/systemd/system/left4me-cake.service` +- `deploy/files/usr/local/lib/systemd/system/left4me-nft-mark.service` +- `deploy/files/usr/local/lib/systemd/system/{left4me-web.service,left4me-server@.service,left4me-workshop-refresh.service,left4me-workshop-refresh.timer,l4d2-game.slice,l4d2-build.slice}` + +**Why dead**: ckn-bw's `systemd_units` reactor in +`bundles/left4me/metadata.py` emits these units (and slices) from +metadata. The static files in `deploy/files/usr/local/lib/systemd/system/` +are not consulted by the deploy at all. They drifted out of sync +with the reactor-emitted versions (e.g. the reactor uses +`Slice=l4d2-game.slice` with current resource caps, the static file +might not). Currently kept as "greppable reference" per the +README's table; that's been the framing since the +historical-reference era. + +**Action**: decide policy in concert with the deploy-dir-rethink +handoff. Either: +- **Delete them.** They're not the source of truth; the reactor + is. The README table loses a row but gains accuracy. +- **Keep them but stamp obsolete** somewhere visible (e.g. a + comment header in each file pointing at the reactor). + +Recommendation: delete. The reactor output is what actually ships; +the static files are a footgun (someone might edit them thinking +they matter). + +**Verification**: `find deploy/files/usr/local/lib/systemd/system -type f` +should match the README's "what's canonical" list. + +### 3. `deploy/files/etc/left4me/cake.env` + +**What**: env file referenced by the obsolete `left4me-cake.service`. + +**Why dead**: bandwidth lives in node metadata under +`network/external/cake/Bandwidth` in ckn-bw. The env file is not +read by anything live. + +**Action**: delete `deploy/files/etc/left4me/cake.env`. + +### 4. `deploy/files/usr/local/lib/left4me/nft/` + +**What**: nftables fragment for `left4me-nft-mark.service`. + +**Why dead**: the central `bundles/nftables/` bundle consumes the +rules from `bundles/left4me/`'s defaults in ckn-bw. The static +fragment isn't read. + +**Action**: delete `deploy/files/usr/local/lib/left4me/` +recursively. + +### 5. `deploy-test-server.sh`'s fate + +**What**: `deploy/deploy-test-server.sh`, the historical one-shot +bash deploy. + +**Why ambiguous**: the deploy-dir-rethink doc +(`2026-05-15-deploy-dir-rethink-design.md`) calls this out as an +open decision. Three options listed there: +- Delete entirely (git history preserves the content). +- Relocate to `docs/` as a walkthrough, mark non-executable. +- Keep as-is with a louder warning header. + +**Action**: pick one as part of the broader deploy-dir-rethink +work, or as an isolated decision now. + +### 6. `bubblewrap` references in spec docs + +**What**: `docs/superpowers/specs/2026-05-08-l4d2-script-overlays-design.md` +(if it still exists) describes the sandbox as using `bubblewrap`. +The actual implementation uses `systemd-run` with hardening +properties — no `bwrap` binary is invoked. + +**Why misleading**: someone reading the spec would go looking for +`bwrap` in the helper and not find it. + +**Action**: grep the specs for `bubblewrap` / `bwrap` and either +correct to `systemd-run` or delete the references. Drive-by +correction; no rationale needed beyond "matches reality." + +### 7. Empty / unused `_sandbox_script_dir` after build-overlay-unit refactor + +**What** (conditional on the build-overlay-unit refactor landing — +see `2026-05-15-build-overlay-unit-design.md`): if Option B in that +doc is chosen (unit fetches script from DB), the +`_sandbox_script_dir()` helper in +`l4d2web/services/overlay_builders.py` and the on-disk +`/var/lib/left4me/sandbox-scripts/` directory become unused. + +**Action**: if that refactor lands, remove the helper function and +the dir. ckn-bw can stop creating the directory. + +### 8. Legacy idmap binds on un-checked instances + +**What**: server@2's stale idmap binds (from the idmap-on-mount +era) were manually cleaned during this session's verification. +Other server instances (`left4me-server@1`, …, if any are running +or have been recently) may still have orphan binds in PID 1's +mount namespace. + +**Why**: the old helper had a `_is_mountpoint` bug that left binds +behind on stop. Our fix (`dd918ac`) cleaned the bug, but binds +created by the old version persist until manual cleanup or reboot. + +**Action**: on the test server, run: +```bash +sudo findmnt --task 1 -o TARGET | grep '/var/lib/left4me/runtime/.*/idmap/' +``` +For each result, `sudo umount` it and remove its parent +`runtime//idmap/` directory after all binds for that instance +are gone. + +Alternative: schedule a host reboot. Reboot wipes the entire mount +table and gets everything clean in one step. + +### 9. `Optimized Settings` files-overlay verification + +**What**: overlay id 8 (`Optimized Settings`, type `files`) wasn't +included in the rebuild test during the build-time-idmap +verification. We only rebuilt the 5 script overlays. + +**Why low-risk**: files overlays are populated by the web app +(uid `left4me`) directly via Python file ops, not through the +sandbox helper, so the idmap refactor doesn't touch them. But it's +worth a 30-second check. + +**Action**: in the web UI, open overlay 8's detail page. Confirm +the files list renders correctly. (Or `sudo find +/var/lib/left4me/overlays/8 -type f -uid 981` should be empty.) + +### 10. SourceMod 1.13 stable bump (calendar item, not a janitorial fix) + +**What**: the example Sourcemod-overlay script in +`examples/script-overlays/Sourcemod.sh` pins `MM_BRANCH=1.12` and +`SM_BRANCH=1.12`. When AlliedModders declares 1.13 stable, the +seeded script needs updating. + +**Why not now**: 1.12 is current stable as of 2026-05. +AlliedModders bumps stable every ~2-3 years. + +**Action**: set a calendar reminder for late 2026 / early 2027 to +check +[the SourceMod releases page](https://github.com/alliedmodders/sourcemod/releases) +and bump the branch pin if 1.13 has been declared stable. When +bumping, also test the rebuild on a non-production server first; +plugin compat across major SM versions is occasionally non-clean. + +## Suggested batching + +Items 1, 3, 4 are tiny and self-contained — bundle into a single +"delete dead cake-related artifacts" commit. + +Items 2 and 6 are deploy/spec cleanup — bundle with the broader +deploy-dir-rethink decision. + +Items 5, 7 are conditional on other decisions — handle when the +prerequisite design choices are made. + +Item 8 is an operational check, not a code change — run it once +and forget. + +Item 9 is a 30-second verification, not a change. + +Item 10 is a calendar reminder. + +## Verification (after the bundle of items 1, 2, 3, 4 lands) + +``` +# nothing references the deleted artifacts +git grep -i 'apply-cake\|cake.env\|left4me-nft-mark\|left4me-cake' deploy/ + +# the deploy artifacts are pruned +find deploy/files -type f | sort + +# bw apply still works +cd ~/Projekte/ckn-bw && bw apply ovh.left4me --interactive=no +``` + +If `bw apply` errors with a missing source path, the bundle +references one of the deleted files; fix the bundle reference +before pushing the deletion.