left4me/README: describe symlink delivery + reactor scope after the reshape

Rewrite "What this bundle does" to reflect the post-migration model:
- Target-side symlinks table for the 6 static artifacts (sudoers, sysctl,
  2 hardening drop-ins, 4 libexec helpers, sbin wrapper)
- Reactor-emitted units section (per-host shape: web/server@ units, slices,
  cpuset drop-ins)
- bw files{} for the templated env files
- Action chains section covering the full deploy lifecycle
- Reference to the design doc for rationale

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
CroneKorkN 2026-05-15 19:49:11 +02:00
parent ae4bfc8db3
commit a95a7e20e2
Signed by: cronekorkn
SSH key fingerprint: SHA256:v0410ZKfuO1QHdgKBsdQNF64xmTxOF8osF1LIqwTcVw

View file

@ -36,36 +36,74 @@ from defaults. None of these need to be declared per-node.
## What this bundle does ## What this bundle does
- Creates system user `left4me` (uid/gid 980, home `/var/lib/left4me`, The bundle delivers to `ovh.left4me` a mix of:
mode 0755) — same uid hosts the web app, gameservers, and the
script-overlay sandbox unit (which drops privileges via systemd-run ### Target-side symlinks into the left4me checkout
with a fully hardened transient service).
- Drops privileged helpers under `/usr/local/libexec/left4me/` After `git_deploy:/opt/left4me/src` (root-owned — left4me cannot rewrite
(`left4me-systemctl`, `left4me-journalctl`, `left4me-overlay`, its own deployment artifacts at runtime), ckn-bw creates symlinks from
`left4me-script-sandbox`) plus a tight sudoers file (validated with canonical on-host paths into the checkout:
`visudo -cf` before install).
- `git_deploy`s the left4me repo to `/opt/left4me/src` (root-owned — | On-host path | Source in checkout |
the source tree is read-only at runtime so left4me cannot rewrite |---|---|
its own future hardening drop-ins / unit files under | `/etc/sudoers.d/left4me` | `deploy/files/etc/sudoers.d/left4me` |
`/opt/left4me/src/deploy/`). Builds a venv at `/var/lib/left4me/.venv` | `/etc/sysctl.d/99-left4me.conf` | `deploy/files/etc/sysctl.d/99-left4me.conf` |
and installs `l4d2host` + `l4d2web` non-editably (`pip install` copies | `/etc/systemd/system/left4me-web.service.d/10-hardening.conf` | `deploy/files/etc/systemd/system/left4me-web.service.d/10-hardening.conf` |
source to a left4me-writable tempdir first, since setuptools writes | `/etc/systemd/system/left4me-server@.service.d/10-hardening.conf` | `deploy/files/etc/systemd/system/left4me-server@.service.d/10-hardening.conf` |
egg-info into the source dir during the wheel build). Then runs | `/usr/local/libexec/left4me/{left4me-overlay,left4me-systemctl,left4me-journalctl,left4me-script-sandbox}` | `deploy/scripts/libexec/*` |
`alembic upgrade head` and `flask seed-script-overlays` and enables | `/usr/local/sbin/left4me` | `deploy/scripts/sbin/left4me` |
`left4me-web.service`. Developer machines keep `pip install -e` via
direnv for fast iteration; only the production install model differs. The hardening drop-ins and sudoers are the application's own security
Runtime mutable state lives under `/var/lib/left4me/` (venv, steamcmd, knowledge — they live in the left4me repo and are version-controlled there.
game installations, overlays); `/opt/left4me/` stays as a root-owned The privileged helpers are also application code. The symlink pattern
deploy-artifact root. lets bw manage placement without duplicating content.
- Emits four systemd units via `systemd/units` metadata (consumed by
`bundles/systemd/`): Design rationale:
- `left4me-web.service` — gunicorn on `127.0.0.1:8000` (TLS terminates upstream). `left4me/docs/superpowers/specs/2026-05-15-deployment-responsibility-design.md`.
- `left4me-server@.service` — per-instance srcds template, started on
demand by the web app via the `left4me-systemctl` helper. ### Reactor-emitted units (per-host shape)
- `l4d2-game.slice` / `l4d2-build.slice` — cgroup slices for the
perf-baseline (CPU/IO weights, memory caps). Via `systemd/units` metadata in `metadata.py` (consumed by `bundles/systemd/`):
- `left4me-web.service` — gunicorn on `127.0.0.1:8000`; worker/thread
counts from `web.env.mako`. TLS terminates upstream.
- `left4me-server@.service` — per-instance srcds template; `SocketBindAllow=`
ranges from metadata.
- `l4d2-game.slice` / `l4d2-build.slice` — cgroup slices with per-host
`AllowedCPUs=` from `left4me/system_cpus`.
- `system.slice.d/99-left4me-cpuset.conf` + `user.slice.d/99-left4me-cpuset.conf`
— host CPU-set drop-ins, same source.
### bw `files{}` — templated env files
- `host.env.mako``/etc/left4me/host.env`
- `web.env.mako``/etc/left4me/web.env`
- `sandbox-resolv.conf``/etc/left4me/sandbox-resolv.conf`
### Action chains — deploy lifecycle
- `git_deploy``pip_install` (non-editable; setuptools writes egg-info to
a left4me-writable tempdir) → `alembic_upgrade``seed_overlays` + web restart.
- Idempotent gates: `chmod-sudoers` (0440 root:root), `chmod-scripts` (0755 root:root).
- Post-git-deploy reloads: `systemctl daemon-reload`, `sysctl --system`.
- Post-apply self-test: `verify-hardening-dropins` (asserts the drop-ins are
loaded by the live units before declaring apply done).
### System user
`left4me` (uid/gid 980, home `/var/lib/left4me`, mode 0755) — the same uid
hosts the web app, gameservers, and the script-overlay sandbox unit (which
drops privileges via systemd-run with a fully hardened transient service).
Runtime mutable state lives under `/var/lib/left4me/`; `/opt/left4me/`
stays as a root-owned deploy-artifact root.
### nftables / nginx / monitoring
- Contributes uid-based DSCP/priority marks for srcds UDP egress to - Contributes uid-based DSCP/priority marks for srcds UDP egress to
`nftables/output` (via `defaults`). `nftables/output` (via `defaults`).
- `derived_from_domain` reactor emits the corresponding `nginx/vhosts`,
`letsencrypt/domains`, and `monitoring/services/left4me-web` (HTTPS
health check).
## Gotchas ## Gotchas