# mediabox-bootstrap Everything the media box (`10.0.1.20`, hostname `mediabox`) needs after the Ubuntu 24.04 autoinstall has laid down the base system. The autoinstall USB is deliberately thin: sshd, a key, DHCP, Docker CE, `/srv`, and a first-boot unit that clones **this** repo and runs `bootstrap.sh`. That split exists so the interesting parts stay in git, reviewable and re-runnable, instead of frozen onto a USB stick that nobody can diff. Full context, decisions and the phase-by-phase runbook live in the workspace repo at `projects/mediabox-linux/`. --- ## Layout ``` bootstrap.sh orchestrator — runs the stages, fails soft scripts/ 00-preflight.sh asserts hardware/BIOS state. Changes nothing. 10-secrets.sh idempotent ADD-ONLY seeder for /srv/secrets/stacks.env 20-cifs.sh the 6 NAS mounts, named exactly as the DB stores them 30-nvidia.sh NVIDIA 580 + container toolkit 40-shell-mcp.sh builds and starts the native shell-mcp 50-plex.sh Plex against the MIGRATED data dir. No claim token. 60-media-relocate.sh optional, post-soak: move the 337 GB preview cache migration/ remap.sql, migrate-db.sh, gen-preferences.ps1, README.md shell-mcp/ amd64 build of the MCP server (server.py, Dockerfile, docker-compose.yml, pinned requirements.txt) plex/docker-compose.yml Plex service definition secrets/stacks.env.example placeholder master env ``` ## Running it ```bash sudo /srv/mediabox-bootstrap/bootstrap.sh # all stages sudo /srv/mediabox-bootstrap/scripts/20-cifs.sh # one stage sudo /srv/mediabox-bootstrap/scripts/20-cifs.sh --verify ``` Every stage is idempotent. Re-running is the normal way to use this, not an emergency measure. --- ## Design rules **Nothing may leave the box unreachable.** `sshd` is up before any of this runs and no stage may compromise that. `bootstrap.sh` catches stage failures and continues; the systemd unit declares `SuccessExitStatus=0 1` so a bad stage can never wedge boot. There is no keyboard attached to this machine. **Secrets never ride on removable media.** The CIFS credentials file is created *empty* by the autoinstall and filled in post-boot over the MCP. **No claim token, ever.** The migration preserves `MachineIdentifier`, so this is the existing server rather than a new one. Claiming would discard shared users and every client registration. **One secrets file per host.** This box has its own `/srv/secrets/stacks.env`, using the same `[OPERATOR]` / `[VALUES]` / `[MANIFEST]` tiering as arrsstack, but it is not shared or mounted from anywhere. Canonical copies live in Vaultwarden. **The seeder only ever adds.** `10-secrets.sh` will never overwrite an existing key, change its value, or move it. If `KEY=` is present it is skipped entirely. Verified against a pre-populated file: existing values, unrelated keys, the operator tier and existing manifest lines all survive byte-for-byte, and a second run is a no-op. --- ## Notes that will save you an evening **shell-mcp was originally built on arm64.** It builds clean on amd64 — the whole Python dependency tree resolves to prebuilt manylinux x86_64 wheels, so no compiler is needed. Two real changes were required: - **Dependencies are now pinned.** The original installed `mcp starlette uvicorn` unpinned. `starlette` has since gone 1.x. Unpinned installs are tolerable in a hand-run build and dangerous inside a first-boot script. - **Port and description.** It listens on 8103 (not 8085) so NPM proxy host 42 can simply be repointed, and the tool description says *media box*, not *arrsstack* — otherwise every session starts with the wrong idea of which host it is touching. **The GTX 1070 is Pascal, and NVIDIA branch 580 is the last one that supports it.** There will be no 590 for this card. `30-nvidia.sh` installs 580 explicitly and pins against newer branches. Never use `ubuntu-drivers autoinstall` here — it will cheerfully install a branch that drops the card. And it must be the proprietary module, not `-open`: the open kernel modules need Turing or newer. **Secure Boot must stay off.** It is off today. A BIOS update resets ASUS defaults and turns it back on, at which point the DKMS module needs interactive MOK enrollment at a console this box does not have. `00-preflight.sh` checks for this. **`nofail` on every CIFS mount is not optional.** Without it, an unreachable NAS drops a headless box to an emergency shell waiting for a root password on a keyboard that is not plugged in. **`autoEmptyTrash` is the most dangerous default here.** The NAS account is read-write. If a mount is missing when Plex scans, Plex sees an empty library and will act on that. `50-plex.sh` disables it before any library is added. **Plex's PUID/PGID must equal the CIFS `uid=`/`gid=`.** Both are `3000` (`media`). If they ever drift, every file is permission-denied.