No description
Find a file
mwiegand 4552af6544
fix(l4d2-web): keep SSE log stream from pinning gunicorn threads
stream_command used a blocking proc.stdout.readline() that never woke
when the underlying journalctl was silent, so Flask never delivered
GeneratorExit on client disconnect — the worker thread and the journalctl
child both leaked permanently and pinned the gunicorn thread pool.

Switch to a select-based read loop with a 15s heartbeat tick (yielded as
""), and translate the tick to an SSE keepalive comment in the log route.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 11:18:56 +02:00
deploy refactor(l4d2-web): remove legacy external overlay type 2026-05-08 09:31:04 +02:00
docs/superpowers feat(l4d2-web): managed global map overlays with daily refresh 2026-05-08 08:05:14 +02:00
l4d2host fix(host): create ~/.steam/sdk32 and sdk64 symlinks during install 2026-05-07 02:11:27 +02:00
l4d2web fix(l4d2-web): keep SSE log stream from pinning gunicorn threads 2026-05-08 11:18:56 +02:00
.gitignore fix(deploy): make test deployments safe to rerun 2026-05-07 17:16:58 +02:00
AGENTS.md refactor(l4d2-web): remove legacy external overlay type 2026-05-08 09:31:04 +02:00
README.md refactor(l4d2-web): remove legacy external overlay type 2026-05-08 09:31:04 +02:00

left4me

left4me is a local L4D2 server management platform with two planned components:

  1. l4d2host + l4d2ctl (host library + CLI)
  2. l4d2-web-app (Flask web app for users, blueprints, servers, jobs, and logs)

Status

Implementation plans remain the source of truth for architecture and task sequencing:

  • docs/superpowers/plans/2026-04-22-l4d2-host-lib-v1.md
  • docs/superpowers/plans/2026-04-23-l4d2-web-app-v1.md

Locked v1 Decisions

  • Naming is strictly l4d2 (not l4d).
  • Host library and web app are separate components.
  • Host CLI write commands are fixed to:
    • install
    • initialize <name> -f <spec.yaml>
    • start <name>
    • stop <name>
    • delete <name>
  • Host CLI read commands are available for the web/host boundary:
    • status <name> --json
    • logs <name> --lines <n> --follow/--no-follow
  • The web app calls host operations through l4d2ctl, not direct l4d2host imports.
  • Deployment uses /var/lib/left4me for runtime state, /opt/left4me for repository contents and the virtualenv, /etc/left4me for environment files, and global units under /usr/local/lib/systemd/system.
  • Overlay handling is directory-based; the web app populates each overlay (workshop downloads, managed-global refresh).
  • No lock manager, no rollback, no preflight checks in host library.
  • CLI propagates subprocess failures via stderr and return code.
  • delete on missing instance is no-op success.
  • Blueprint model (web app):
    • user-private in v1
    • servers are live-linked to blueprint
    • no per-server overrides
    • delete blueprint blocked when linked servers exist
    • blueprint changes apply on next action
    • server can reassign blueprint anytime

Planned Repository Layout

  • l4d2host/
  • l4d2web/
  • deploy/
  • docs/superpowers/plans/

Deployment

See deploy/README.md for the Linux test deployment contract, including the runtime user, target filesystem layout, systemd units, privileged helpers, sudoers rules, admin bootstrap, and overlay reference rules.

Tech Stack (planned)

  • Python 3.12+
  • Typer, PyYAML, pytest
  • Flask, SQLAlchemy, Alembic
  • HTMX (vendored locally), custom CSS, SSE
  • systemd user units, fuse-overlayfs, steamcmd
  1. Implement l4d2host plan first.
  2. Implement l4d2web plan second.
  3. Keep tests green task-by-task (TDD flow from plans).
  4. Keep commits small and aligned with plan tasks.

Contributing Notes

  • Follow plan task order unless explicitly re-planned.
  • Keep contracts above unchanged unless the user asks to change them.
  • Update plan docs when scope or behavior changes.