From fcf3143b39446ee962efaa1ff499c4c78ee5722a Mon Sep 17 00:00:00 2001 From: mwiegand Date: Wed, 13 May 2026 14:19:57 +0200 Subject: [PATCH] docs: add server hostname cvar design spec --- .../2026-05-13-server-hostname-design.md | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 docs/superpowers/specs/2026-05-13-server-hostname-design.md diff --git a/docs/superpowers/specs/2026-05-13-server-hostname-design.md b/docs/superpowers/specs/2026-05-13-server-hostname-design.md new file mode 100644 index 0000000..730fb0e --- /dev/null +++ b/docs/superpowers/specs/2026-05-13-server-hostname-design.md @@ -0,0 +1,77 @@ +# Server Hostname (Source `hostname` cvar) Design + +**Goal:** Allow users to set the L4D2 server name (`hostname` cvar) that players see in the server browser and MOTD, with an ephemeral auto-generated fallback. + +**Architecture:** A new `hostname VARCHAR(128)` column on the `servers` table. Empty string means "auto-generate at deploy time." The fallback is resolved ephemerally in `initialize_server` — computed fresh from `user.username + server.name` on each deploy, never persisted. Explicit overrides are stored and emitted verbatim. + +--- + +## Model + +Add one column to `Server` in `l4d2web/models.py`: + +```python +hostname: Mapped[str] = mapped_column(String(128), default="", nullable=False) +``` + +Default `""` means auto-generate. Non-empty means explicit override. + +## Behavior + +| `hostname` value | Deploy result | +|---|---| +| `""` (empty) | Emit `hostname " "` — computed fresh each deploy, never written to DB | +| `"My Server"` | Emit `hostname "My Server"` verbatim | +| User clears the field | Resets to `""`, next deploy auto-generates | + +The fallback is ephemeral — `initialize_server` resolves it in-memory for the spec YAML. The DB row stays empty. This means renames auto-propagate to the hostname on the next deploy without manual updates. + +## Spec Payload + +`build_server_spec_payload()` gains an optional `resolved_hostname: str = ""` keyword parameter. When non-empty, a `hostname "..."` line is inserted into the config array, before the `rcon_password` line (so rcon remains last-wins). + +`initialize_server()` resolves the hostname: + +```python +with session_scope() as db: + user = db.get(User, server.user_id) +resolved = server.hostname or f"{user.username} {server.name}" +``` + +## UI + +On `server_detail.html`, a new row in the info `
` block, placed after the RCON password row: + +``` +Hostname: [ _______________ ] [Save] + Leave empty for auto: "alice alpha" +``` + +- Input `name="hostname"`, `maxlength="128"` +- `value="{{ server.hostname }}"` (empty when not set) +- `placeholder="{{ user.username }} {{ server.name }}"` (previews auto-generated value) +- Form submits to `POST /servers/` — same endpoint as the rename form +- No hostname field in the create-server modal; new servers always start with `hostname=""` + +## Routes + +**`POST /servers/`** (update_server_form) — unchanged signature; just also saves `request.form.get("hostname", "")` to `server.hostname`. + +**`POST /servers`** (create_server) — unchanged; `hostname` defaults to `""` from the model default. + +## Files Touched + +| File | Change | +|---|---| +| `l4d2web/models.py` | Add `hostname` column to `Server` | +| `l4d2web/alembic/versions/0011_server_hostname.py` | Migration — `ADD COLUMN hostname VARCHAR(128) NOT NULL DEFAULT ''` | +| `l4d2web/routes/server_routes.py` | `update_server_form` saves `hostname` from form | +| `l4d2web/services/l4d2_facade.py` | `build_server_spec_payload` accepts `resolved_hostname=`, emits `hostname "..."` line. `initialize_server` resolves fallback. | +| `l4d2web/templates/server_detail.html` | Hostname form row in info `
` | +| `l4d2web/tests/test_servers.py` | Tests for create default, update, clear | +| `l4d2web/tests/test_l4d2_facade.py` | Tests for hostname in spec, fallback resolution | + +## Open / Closed + +- **Explicit vs ephemeral:** Explicit overrides persist; empty means auto at deploy time. No toggle, no "locked" mode needed in v1. +- **No hostname in create modal:** Simplifies the form. Hostname is configured post-creation on the detail page.