128 lines
6.4 KiB
Markdown
128 lines
6.4 KiB
Markdown
# CouchOS
|
|
|
|
CouchOS targets the `couchos` laptop: a Debian 13 living-room Melee appliance for the `couch` user. This repository is being built remotely from a separate workstation. `couchd` is intended to bind only to Tailnet IP `100.64.0.15`, and the dashboard is intended to be served same-origin at `couch.mhny.dev`.
|
|
|
|
As of Monday, August 3, 2026:
|
|
- The main root-side blocker on the target laptop is still interactive `sudo` access. The repo contains the setup and verification logic, but it has not claimed to apply privileged changes from this remote implementation workstation.
|
|
- The TV currently advertises only `1920x1080@60.00` on `HDMI-1`. There is no verified 120 Hz mode to automate today.
|
|
- The Xbox controller was observed at `C8:3F:26:12:69:7B` initially paired and connected under `jackmhny`, but it was not trusted or bonded. After disconnecting it no longer appears in `bluetoothctl`; it must be powered on, paired, and trusted in the `couch` session.
|
|
- An HDMI PulseAudio sink is currently absent on the target laptop, so HDMI sink selection remains deferred until PulseAudio exposes one.
|
|
- Dashboard DNS already exists externally. This repo covers the dashboard files and Caddy snippet, not DNS creation.
|
|
|
|
## Layout
|
|
|
|
- `src/couchd/`: stdlib-only Python control daemon, launcher, discovery, and status probes
|
|
- `setup.sh`: idempotent root/couch setup with `--dry-run` and PASS/FAIL `--check`
|
|
- `scripts/check.sh`: local/CI verification entrypoint from a clean checkout
|
|
- `scripts/preflight.sh`: deployment evidence checks plus reboot/soak evidence capture
|
|
- `scripts/install_slippi.sh`: official launcher installer with checksum fail-closed behavior
|
|
- `scripts/backup_restore.sh`: scoped backup/restore without launcher account/session data, ROMs, replays, or tokens
|
|
- `systemd/systemd-user/`: user units installed under `/etc/systemd/user` and started from the `couch` graphical session
|
|
- `dashboard/`: same-origin static UI
|
|
- `config/`: non-secret config templates plus LightDM/logind/Caddy config
|
|
|
|
## Automated Vs Manual
|
|
|
|
Automated by this repo:
|
|
- Stable install under `/home/couch/couchos`
|
|
- LightDM autologin config for `couch` into XFCE
|
|
- logind lid-ignore on AC power
|
|
- `couchd` and display/audio user units bound to the `couch` graphical session via XFCE autostart entries
|
|
- Runtime config generation with the real `couch` UID and runtime dir
|
|
- First-apply bearer token generation with mode `0600`, preserving any existing token
|
|
- XFCE compositing off, blanking/suspend off, lock/screensaver off, notification daemon/update-notifier autostart suppressed
|
|
- Tailnet-only daemon binding, constant-time bearer auth, strict route allowlist, safe process adoption/stop logic
|
|
- Dashboard static assets and Caddy reverse-proxy snippet with server-side bearer injection
|
|
|
|
Still manual or operator-observed:
|
|
- Installing the privileged changes on the actual laptop
|
|
- Setting a real ISO path under `/home/couch`
|
|
- Confirming actual launcher fallback entries discovered on the machine
|
|
- First-run Slippi UI steps and controller profile mapping
|
|
- Confirming autologin/session behavior, five reboot controller behavior, and 30-minute gameplay/FPS observations
|
|
|
|
## `couchd`
|
|
|
|
Exposed routes:
|
|
- `GET /status`
|
|
- `POST /launch/melee`
|
|
- `POST /open/slippi`
|
|
- `POST /stop`
|
|
|
|
Implemented constraints:
|
|
- Tailscale IPv4 bind only; loopback and `0.0.0.0` are rejected
|
|
- Constant-time bearer token comparison
|
|
- Exact route allowlist with safe query-string stripping
|
|
- Nonempty action request bodies rejected
|
|
- Request body size capped
|
|
- Expected config/discovery/launch failures converted to JSON `409`; unexpected action failures return JSON `503`
|
|
- State persisted atomically and corrupt/stale state tolerated
|
|
- Existing couch-owned Slippi/Dolphin processes under allowed roots can be adopted to prevent duplicates
|
|
- Stop logic verifies PID identity and allowed roots before sending `TERM`/`KILL`
|
|
- Display status reports the active starred refresh, for example `HDMI-1 1920x1080@60.00`
|
|
- Launcher and Dolphin version probes degrade safely on missing commands or timeouts
|
|
|
|
## Setup
|
|
|
|
Root/system phase:
|
|
```bash
|
|
sudo ./setup.sh --phase root --dry-run
|
|
sudo ./setup.sh --phase root
|
|
```
|
|
|
|
Couch/user phase:
|
|
```bash
|
|
sudo ./setup.sh --phase couch --dry-run
|
|
sudo ./setup.sh --phase couch
|
|
```
|
|
|
|
Notes:
|
|
- `setup.sh` does not install Caddy. The deployed site can stay behind an existing Caddy `basicauth` block or another external public auth layer such as Cloudflare Access.
|
|
- The generated runtime config keeps `iso_path`, `launcher_shortcut`, and `xbox_bluetooth_address` null unless the operator supplies real values.
|
|
- Launcher fallback is not invented. If direct Dolphin launch is unavailable, configure only a discovered `gtk-launch` desktop entry that the target machine actually exposes.
|
|
- On first apply, `setup.sh` generates a cryptographically random bearer token at `/home/couch/.config/couchd/token` with mode `0600` and leaves an existing token untouched.
|
|
- To synchronize that token with Heavy Caddy without printing or committing it, read it locally on the laptop as root and copy the same value into the Caddy environment source used by the deployed site, then reload Caddy after updating that secret.
|
|
|
|
## Dashboard And Caddy
|
|
|
|
Use [config/caddy/couch.mhny.dev.caddy](/home/jackmhny/couchos/config/caddy/couch.mhny.dev.caddy) as the site snippet.
|
|
|
|
What this snippet assumes:
|
|
- An existing Caddy `basicauth` block, Cloudflare Access, or equivalent public auth already exists outside this repo
|
|
- The static dashboard is deployed at `/var/www/couchos`
|
|
- Caddy injects `Authorization: Bearer {env.COUCHD_BEARER_TOKEN}` server-side before proxying `/api/*` to `100.64.0.15:8765`
|
|
|
|
## Verification
|
|
|
|
Project checks:
|
|
```bash
|
|
./scripts/check.sh
|
|
```
|
|
|
|
Deployment checks:
|
|
```bash
|
|
./setup.sh --check
|
|
./scripts/preflight.sh quick
|
|
./scripts/preflight.sh five-reboots
|
|
./scripts/preflight.sh soak-30m-start
|
|
./scripts/preflight.sh soak-30m-finish
|
|
```
|
|
|
|
`five-reboots` and soak mode now record real boot IDs, controller snapshots, and honest operator-observation placeholders instead of just incrementing counters.
|
|
|
|
## Recovery
|
|
|
|
- `./scripts/backup_restore.sh backup [archive.tar.gz]`
|
|
- `./scripts/backup_restore.sh restore <archive.tar.gz>`
|
|
|
|
Backups are mode `0600` and intentionally exclude:
|
|
- launcher account/session data
|
|
- replays
|
|
- ROMs
|
|
- bearer tokens
|
|
|
|
## External References
|
|
|
|
- Slippi downloads: https://slippi.gg/downloads
|
|
- Slippi launcher publish metadata: https://github.com/project-slippi/slippi-launcher/blob/main/electron-builder.json
|