Luigit
repositories / bugabinga.net

bugabinga.net

personal infrastructure for bugabinga!

owned by admin

services/pufferpanel/README.md

Raw
Rendered preview

PufferPanel

Panel/daemon are deployed rootlessly on Klops, with loopback health verified. Public HTTPS at https://games.bugabinga.net is deployed and returns HTTP 200 through the dedicated Unix ingress. Latest verified rollout is fork revision b9bd1f7, image registry.bugabinga.net/pufferpanel:fork-b9bd1f7, digest sha256:72480e7645dfcf59f51306f77d08c32080f8ea7aebfcd528d397a5fae61b197a. Vanilla Steam Valheim valheim1 is deployed with public UDP 2456–2457 over IPv4 and IPv6, forwarding through WireGuard-only container bindings. Friend assignments and scheduled game-consistent encrypted backups remain pending implementation/deployment. Initial selection is Valheim, Minecraft Java, and vanilla Terraria, reachable with ordinary game clients. Game passwords/allowlists restrict play; public network reachability does not imply unrestricted game access.

Public ingress

games.bugabinga.net is configured for HTTPS ingress through public Caddy, a dedicated rootless Caddy gateway, and a Unix socket at /data/pufferpanel-ingress/http.sock. The gateway is HTTP-only on private pufferpanel.network, proxies all paths including WebSockets to pufferpanel:8080, preserves HTTPS forwarded-proto, has no TCP listener, admin endpoint, TLS state, Podman socket, credentials, or game-data mount. The panel remains bound to 127.0.0.1:8082; SFTP remains unexposed. Deployment creates a pufferpanel-owned shared socket directory with narrow ACL access for the configured public Caddy user, persistent container SELinux labeling, then narrowly adds its read-only mount to Caddy's deployed Quadlet without replacing unrelated fields. Live checks confirm HTTP 200 from the proxy user over Unix and from public HTTPS, separate confined gateway/public-Caddy process labels, read-only public-Caddy socket mount, and continued loopback-only panel TCP binding. Both PufferPanel user units, Podman socket, and registry auto-update timer are active. Gateway/public-Caddy confinement does not establish panel confinement: the panel currently uses --security-opt label=disable. Caddy's /data/sites mount intentionally omits relabeling: CI-owned shared-label files otherwise prevent rootless restarts.

Mail and registration

just generate pufferpanel-mail provisions dedicated games@bugabinga.net mailbox through Mail-in-a-Box at SSH alias box. Private SMTP credentials reside in ignored keys/pufferpanel-email.env, mode 0600; never commit or publish them. Deployment transfers that file to /home/pufferpanel/.config/pufferpanel-email.env for Quadlet environment injection, without storing credential values in OpenTofu inputs/state. The Quadlet enables public self-registration; new accounts have login scope only. Email ownership is not verified, and failed welcome-email delivery does not prevent signup. Mailbox creation, TLS SMTP authentication/submission, and receipt in its inbox were verified independently of panel deployment. Native PufferPanel signup/email behavior still needs live verification. Owner administrator creation through the native CLI, public HTTPS password login, and administrator API access were verified. Generated owner credentials reside in ignored keys/pufferpanel-owner.env, mode 0600; never commit or publish them. Live owner-account WebAuthn remains unverified.

Authentication verification

The previous image omitted Secure from manually issued authentication cookies behind HTTP ingress, despite configured secure cookies. Released native fix honors configured Secure or direct TLS and emits actual logout-cookie deletion, preserving cookie scope, lifetime, and HttpOnly distinctions. Public HTTPS login and administrator API returned HTTP 200; both authentication cookies now carry Secure. Logout returned HTTP 204 with cookie deletion; replaying the old session returned HTTP 401. Only pufferpanel.service was restarted; gateway and public-Caddy container IDs/start times remained unchanged. Focused regressions fail against the old source; patched race tests pass ten repetitions. Full exact-tree release gate passed: frontend tests/lint/build, Ace byte verification, uncached normal/race Go suites, vet, and Actionlint. Verified candidate tree is 5367e7452888015904b8465e1343699626f4d47b. Security commit e85fd63558e272d6b5dbc783a7ac629e1073b065 is pushed to fork origin/trunk, so scheduled maintenance inherits the fix; no upstream submission was made.

Initial game exposure profiles

  • Valheim Steam backend: UDP 2456–2457, shared password; optional platform-ID allowlist.
  • Minecraft Java: TCP 25565, online-mode=true, account-name/UUID allowlist.
  • Vanilla Terraria: TCP 7777, shared password.

RCON/admin endpoints remain private; panel users do not control relay/firewall rules. Valheim Crossplay can instead use its relay backend; initial direct-port profile assumes Steam clients. Valheim's profile is deployed; Minecraft and Terraria remain researched profiles, not provisioned servers. Valheim remains private/unadvertised (-public 0); join through games.bugabinga.net:2456. Relay and Klops use measured tunnel MTU 1392, with relay TCP MSS caps 1352/1332 for IPv4/IPv6 across all forwarded services. Both-family HTTPS, SSH reachability, and full-size tunnel probes passed after this fix; sustained Valheim gameplay remains to be confirmed. Graceful-stop world and definition backup was taken before the binding migration; this does not replace scheduled offsite backups. Consistent offsite backups require validated graceful game shutdown/save handling, encryption, retention, and restore tests. Private EU-jurisdiction R2 bucket bugabinga-games-backups exists in account 4a54eb05dec2f951ea1746d9a0e944af; public r2.dev access is disabled and no custom domain is attached. S3 endpoint is https://4a54eb05dec2f951ea1746d9a0e944af.eu.r2.cloudflarestorage.com. Encrypted Restic repository is initialized under the bucket's pufferpanel/ prefix. Storage-only backup/dump round-trip and full restic check --read-data passed; game-consistent snapshots, schedule, retention, and game restore tests remain pending. Bucket-scoped account-token credentials and Restic password reside in ignored keys/pufferpanel-backup.env, mode 0600. The token grants object read/write only on this EU bucket; desktop OAuth credentials must never be copied to Klops. Preserve an independent recovery copy of the Restic password; losing it makes stored data unrecoverable. Provisioning used the mise-managed Cloudflare cf CLI with account_api_tokens:create and an additional workers-r2.write OAuth grant. Bucket-item-only OAuth grants returned is_selectable: false; adding that R2-management grant enabled delegation without broadening the resulting service token. Cloudflare skills and authenticated Pi MCP are installed globally; /reload loads their tools into an existing Pi session.

Native branding

just nugu generates assets/pufferpanel/Nugu.tar from external dotfiles/nugu/pufferpanel.nu, services/pufferpanel/theme/, and original assets/bugabinga.min.svg. The archive contains native PufferPanel manifest.json, theme.css, and original logo. Palette values are generated, not hand-authored in deployment assets. The Quadlet selects Nugu, sets company name to bugabinga Games, and mounts /data/pufferpanel/theme read-only at native web theme directory. Deployment uploads the archive; its hash triggers redeployment when branding changes. No PufferPanel application source or compiled frontend modification is needed.

Light/dark palettes support OS auto mode and explicit overrides. Upstream CSS retains layout, high-contrast mode, font accessibility controls, and responsive behavior. Logo CSS uses a generated image/svg+xml data URI because native tar-loaded asset blobs lack SVG MIME metadata. The SVG bytes remain identical to original asset.

services/pufferpanel/test-branding.sh validates archive contents, mode controls, and logo identity. services/pufferpanel/test-deploy.sh validates rendered panel and ingress Quadlet contracts plus gateway Caddy adaptation. bash services/pufferpanel/test-ingress-runtime.sh NEW_OS_TEMP_DIRECTORY verifies stock Caddy's required rootless capability, private-network DNS, Unix HTTP, forwarded HTTPS, host filtering, read-only root, and exact gateway mount count. It uses isolated Podman storage, stops its containers, and retains test evidence in the supplied new OS temporary directory. Cross-user ACL/SELinux access was verified live on Klops through public HTTPS with both gateway and public Caddy confined. Local browser proof covers login page at desktop/mobile widths with both auto schemes and explicit overrides. Browser proof does not establish game runtime compatibility or owner-account WebAuthn login; live deployment is independently verified above.

# PufferPanel

Panel/daemon are deployed rootlessly on Klops, with loopback health verified.
Public HTTPS at `https://games.bugabinga.net` is deployed and returns HTTP 200 through the dedicated Unix ingress.
Latest verified rollout is fork revision `b9bd1f7`, image `registry.bugabinga.net/pufferpanel:fork-b9bd1f7`, digest `sha256:72480e7645dfcf59f51306f77d08c32080f8ea7aebfcd528d397a5fae61b197a`.
Vanilla Steam Valheim `valheim1` is deployed with public UDP 2456–2457 over IPv4 and IPv6, forwarding through WireGuard-only container bindings.
Friend assignments and scheduled game-consistent encrypted backups remain pending implementation/deployment.
Initial selection is Valheim, Minecraft Java, and vanilla Terraria, reachable with ordinary game clients.
Game passwords/allowlists restrict play; public network reachability does not imply unrestricted game access.

## Public ingress

`games.bugabinga.net` is configured for HTTPS ingress through public Caddy, a dedicated rootless Caddy gateway, and a Unix socket at `/data/pufferpanel-ingress/http.sock`.
The gateway is HTTP-only on private `pufferpanel.network`, proxies all paths including WebSockets to `pufferpanel:8080`, preserves HTTPS forwarded-proto, has no TCP listener, admin endpoint, TLS state, Podman socket, credentials, or game-data mount.
The panel remains bound to `127.0.0.1:8082`; SFTP remains unexposed.
Deployment creates a `pufferpanel`-owned shared socket directory with narrow ACL access for the configured public Caddy user, persistent container SELinux labeling, then narrowly adds its read-only mount to Caddy's deployed Quadlet without replacing unrelated fields.
Live checks confirm HTTP 200 from the proxy user over Unix and from public HTTPS, separate confined gateway/public-Caddy process labels, read-only public-Caddy socket mount, and continued loopback-only panel TCP binding.
Both PufferPanel user units, Podman socket, and registry auto-update timer are active.
Gateway/public-Caddy confinement does not establish panel confinement: the panel currently uses `--security-opt label=disable`.
Caddy's `/data/sites` mount intentionally omits relabeling: CI-owned shared-label files otherwise prevent rootless restarts.

## Mail and registration

`just generate pufferpanel-mail` provisions dedicated `games@bugabinga.net` mailbox through Mail-in-a-Box at SSH alias `box`.
Private SMTP credentials reside in ignored `keys/pufferpanel-email.env`, mode 0600; never commit or publish them.
Deployment transfers that file to `/home/pufferpanel/.config/pufferpanel-email.env` for Quadlet environment injection, without storing credential values in OpenTofu inputs/state.
The Quadlet enables public self-registration; new accounts have login scope only.
Email ownership is not verified, and failed welcome-email delivery does not prevent signup.
Mailbox creation, TLS SMTP authentication/submission, and receipt in its inbox were verified independently of panel deployment.
Native PufferPanel signup/email behavior still needs live verification.
Owner administrator creation through the native CLI, public HTTPS password login, and administrator API access were verified.
Generated owner credentials reside in ignored `keys/pufferpanel-owner.env`, mode 0600; never commit or publish them.
Live owner-account WebAuthn remains unverified.

## Authentication verification

The previous image omitted `Secure` from manually issued authentication cookies behind HTTP ingress, despite configured secure cookies.
Released native fix honors configured Secure or direct TLS and emits actual logout-cookie deletion, preserving cookie scope, lifetime, and HttpOnly distinctions.
Public HTTPS login and administrator API returned HTTP 200; both authentication cookies now carry `Secure`.
Logout returned HTTP 204 with cookie deletion; replaying the old session returned HTTP 401.
Only `pufferpanel.service` was restarted; gateway and public-Caddy container IDs/start times remained unchanged.
Focused regressions fail against the old source; patched race tests pass ten repetitions.
Full exact-tree release gate passed: frontend tests/lint/build, Ace byte verification, uncached normal/race Go suites, vet, and Actionlint.
Verified candidate tree is `5367e7452888015904b8465e1343699626f4d47b`.
Security commit `e85fd63558e272d6b5dbc783a7ac629e1073b065` is pushed to fork `origin/trunk`, so scheduled maintenance inherits the fix; no upstream submission was made.

## Initial game exposure profiles

- Valheim Steam backend: UDP 2456–2457, shared password; optional platform-ID allowlist.
- Minecraft Java: TCP 25565, `online-mode=true`, account-name/UUID allowlist.
- Vanilla Terraria: TCP 7777, shared password.

RCON/admin endpoints remain private; panel users do not control relay/firewall rules.
Valheim Crossplay can instead use its relay backend; initial direct-port profile assumes Steam clients.
Valheim's profile is deployed; Minecraft and Terraria remain researched profiles, not provisioned servers.
Valheim remains private/unadvertised (`-public 0`); join through `games.bugabinga.net:2456`.
Relay and Klops use measured tunnel MTU 1392, with relay TCP MSS caps 1352/1332 for IPv4/IPv6 across all forwarded services.
Both-family HTTPS, SSH reachability, and full-size tunnel probes passed after this fix; sustained Valheim gameplay remains to be confirmed.
Graceful-stop world and definition backup was taken before the binding migration; this does not replace scheduled offsite backups.
Consistent offsite backups require validated graceful game shutdown/save handling, encryption, retention, and restore tests.
Private EU-jurisdiction R2 bucket `bugabinga-games-backups` exists in account `4a54eb05dec2f951ea1746d9a0e944af`; public r2.dev access is disabled and no custom domain is attached.
S3 endpoint is `https://4a54eb05dec2f951ea1746d9a0e944af.eu.r2.cloudflarestorage.com`.
Encrypted Restic repository is initialized under the bucket's `pufferpanel/` prefix.
Storage-only backup/dump round-trip and full `restic check --read-data` passed; game-consistent snapshots, schedule, retention, and game restore tests remain pending.
Bucket-scoped account-token credentials and Restic password reside in ignored `keys/pufferpanel-backup.env`, mode 0600.
The token grants object read/write only on this EU bucket; desktop OAuth credentials must never be copied to Klops.
Preserve an independent recovery copy of the Restic password; losing it makes stored data unrecoverable.
Provisioning used the mise-managed Cloudflare `cf` CLI with `account_api_tokens:create` and an additional `workers-r2.write` OAuth grant.
Bucket-item-only OAuth grants returned `is_selectable: false`; adding that R2-management grant enabled delegation without broadening the resulting service token.
Cloudflare skills and authenticated Pi MCP are installed globally; `/reload` loads their tools into an existing Pi session.

## Native branding

`just nugu` generates `assets/pufferpanel/Nugu.tar` from external `dotfiles/nugu/pufferpanel.nu`, `services/pufferpanel/theme/`, and original `assets/bugabinga.min.svg`.
The archive contains native PufferPanel `manifest.json`, `theme.css`, and original logo.
Palette values are generated, not hand-authored in deployment assets.
The Quadlet selects `Nugu`, sets company name to `bugabinga Games`, and mounts `/data/pufferpanel/theme` read-only at native web theme directory.
Deployment uploads the archive; its hash triggers redeployment when branding changes.
No PufferPanel application source or compiled frontend modification is needed.

Light/dark palettes support OS auto mode and explicit overrides.
Upstream CSS retains layout, high-contrast mode, font accessibility controls, and responsive behavior.
Logo CSS uses a generated `image/svg+xml` data URI because native tar-loaded asset blobs lack SVG MIME metadata.
The SVG bytes remain identical to original asset.

`services/pufferpanel/test-branding.sh` validates archive contents, mode controls, and logo identity.
`services/pufferpanel/test-deploy.sh` validates rendered panel and ingress Quadlet contracts plus gateway Caddy adaptation.
`bash services/pufferpanel/test-ingress-runtime.sh NEW_OS_TEMP_DIRECTORY` verifies stock Caddy's required rootless capability, private-network DNS, Unix HTTP, forwarded HTTPS, host filtering, read-only root, and exact gateway mount count.
It uses isolated Podman storage, stops its containers, and retains test evidence in the supplied new OS temporary directory.
Cross-user ACL/SELinux access was verified live on Klops through public HTTPS with both gateway and public Caddy confined.
Local browser proof covers login page at desktop/mobile widths with both auto schemes and explicit overrides.
Browser proof does not establish game runtime compatibility or owner-account WebAuthn login; live deployment is independently verified above.