diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 03faf1f..0000000 --- a/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# NEVER commit the cosign private key — it goes in the SIGNING_SECRET GitHub secret. -cosign.key -/.bluebuild-scripts_* diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md deleted file mode 100644 index 88923c3..0000000 --- a/DEVELOPMENT.md +++ /dev/null @@ -1,103 +0,0 @@ -# Development setup - -What the 29 projects in `~/Documents/Development` need on the new machine, mapped against what -the image already provides. Scanned 2026-07 from each project's manifests, Dockerfiles, and -setup scripts. Run [scripts/dev-setup.sh](scripts/dev-setup.sh) after first boot to install the -toolchain layer in one shot. - -## Already in the image — install nothing - -| Need | Provided by | Used by | -|---|---|---| -| Docker CE + compose, Podman, skopeo | Bazzite DX base | requester, serverTransfer, ispy, keyWord, encoderPro, movieScanner, kidstube, Docker-Registry-Browser, smartSpeaker… | -| NVIDIA driver (610+ open) + container toolkit + CDI | base (nvidia image) | AIVoice, ispy, keyWord, encoderPro, smartSpeaker whisper, superBossMonster art pipeline | -| qemu/KVM + virt tooling | DX base | BC250 VM testing | -| android-tools (adb) | DX base | denon-bluetooth btsnoop captures | -| Steam + Proton | Bazzite base | BG4_UltraWide_fix | -| VS Code, git, Homebrew, just | DX base | everything | -| BlueZ/bluetoothd, rsync, openssh | Fedora base | denon-bluetooth, serverTransfer | -| Godot (current stable), Android Studio | first-boot Flatpaks | superBossMonster/FFCardGame, native-angular | - -AIVoice's `SETUP.md` warning about snap Docker / Docker Desktop is moot here: Bazzite DX ships -native docker-ce, which is exactly what it wants. Its -`nvidia-ctk runtime configure --runtime=docker` step is the same one in the README's GPU -section. - -## Toolchains — mise (all per-version needs found in the scan) - -No project commits `.nvmrc`/`.python-version`/`.tool-versions`, so versions below come from -Dockerfiles and build configs. `dev-setup.sh` installs mise and these versions: - -- **Node 22** global default (requester, movieScanner); projects also reference 18 - (Docker-Registry-Browser, movieMetaTagger) and 20 (FFCardGame server, kidstube) — installed - side-by-side, pin per-project with `mise use node@18` in the project dir (writes - `mise.toml` — worth committing so this guesswork ends). -- **Python 3.12** (serverTransfer, movieMetaTagger, movieScanner, nameFixer4000, BG4 tools) and - **3.11** floor (FRScanner). Fedora 44's system python is newer — don't rely on it for these. -- **JDK 21** (villager-logic-fix, Minecraft-Village-Warriors release target), **JDK 17** - (native-angular android-host), **JDK 25** (Minecraft-Village-Warriors toolchain — Gradle can - also auto-provision it). Gradle itself always via each repo's wrapper. -- **.NET 10 SDK** (requester; also rebuilds the BG4 BepInEx plugin). -- **Go** and **Rust** — present on the old machine (`~/go`, `~/.cargo`); no scanned project pins - a version, so latest. - -## Native builds — use a distrobox, not the host - -Compilers and C library headers don't belong on the atomic host. One Fedora distrobox covers -every native-build need the scan found: - -```bash -distrobox create dev --image registry.fedoraproject.org/fedora:44 -distrobox enter dev -sudo dnf install -y gcc gcc-c++ make cmake pkgconf python3-devel \ - gtk4-devel \ # native-angular linux-host (CMake + GTK4) - zbar \ # DVDPhotoMatic (pyzbar) - libsndfile mediainfo-libs \ # FRScanner audio / movieMetaTagger pymediainfo - rtl-sdr rtl-sdr-devel \ # FRScanner RTL-SDR + Cython builds - mkvtoolnix ffmpeg-free \ # movieMetaTagger mkvpropedit; ffmpeg fallback - python3-tkinter python3-dbus \ # denon-bluetooth GUI - flashrom # BC250 BIOS flashing (CH347 over USB) -``` - -Native node modules (kidstube's better-sqlite3, FFCardGame's bcrypt) build fine inside the -distrobox too — or in their Docker builds, which is how they ship anyway. - -## Host-level odds and ends - -- **RTL-SDR udev rules** (FRScanner): the dongle permissions must live on the *host* — - `dev-setup.sh` writes `/etc/udev/rules.d/20-rtlsdr.rules` (`/etc` is mutable and survives - updates). -- **ffmpeg on the host** (audioSticher, AIVoice host venv, encoderPro host runs): Bazzite - ships ffmpeg — verify with `ffmpeg -version`; if a project needs full codecs beyond it, - `brew install ffmpeg` wins on PATH. -- **Pinned Godot binaries**: superBossMonster hardcodes `~/godot/Godot_v4.3-stable_linux.x86_64` - and FFCardGame ships its own 4.2 binary. Keep the `~/godot/` directory from the old machine - (plus export templates in `~/.local/share/godot/`); the Flatpak is only "current stable". -- **gh CLI**: `brew install gh` (in `dev-setup.sh`) — used across projects. -- **BlueBuild CLI** (BC250 and this repo): the installer one-liner in the README. -- **API keys**: ANTHROPIC_API_KEY (FFCardGame tools, DVDPhotoMatic), TMDB/OMDB - (movieScanner, nameFixer4000, DVDPhotoMatic), YouTube Data v3 (kidstube) — carried in - per-project `.env` files; bring them over with your home directory. - -## Per-project cheat sheet - -| Project | Needs beyond the image | -|---|---| -| AIVoice | py3.12 venv (mise), ffmpeg, GPU containers (covered) | -| AIVoices_HA, personal-landing, cardscraper, audioSticher, teamsTrick | nothing / stdlib python | -| BC250 | BlueBuild CLI, flashrom (distrobox) | -| BG4_UltraWide_fix | .NET 10, py3.12, Steam/Proton (covered) | -| denon-bluetooth | distrobox tkinter+dbus, adb (covered) | -| Docker-Registry-Browser | Node 18 | -| DVDPhotoMatic | finished project — py3.12, zbar (distrobox), webcam if ever revived; OpenSCAD flatpak for the jig files if needed | -| encoderPro, ispy, keyWord, movieScanner, nameFixer4000, serverTransfer | Docker (+GPU) only | -| FFCardGame | Godot 4.2 binary, Node 20, python+anthropic | -| FRScanner | py3.11+, rtl-sdr distrobox + host udev rules | -| kidstube | Node 20 (+distrobox for native build outside Docker) | -| Minecraft mods (×2) | JDK 21/25 via mise | -| movieMetaTagger | py3.12, Node 18, mkvtoolnix/mediainfo (distrobox) | -| native-angular | Node 20+, JDK 17, Android Studio (flatpak), GTK4 distrobox | -| requester | .NET 10, Node 22, postgres via compose | -| smartSpeaker | Docker (covered); Pi hardware is remote | -| superBossMonster | Godot 4.3 binary + templates, ComfyUI in GPU container | -| teamsTrick | X11 session note: KDE on Bazzite defaults to Wayland — run it in an X11 session or expect XTest to fail | diff --git a/README.md b/README.md index f5aa614..e43c9fb 100644 --- a/README.md +++ b/README.md @@ -1,189 +1,165 @@ # main-desktop -Custom [Bazzite DX](https://github.com/ublue-os/bazzite-dx) image for my desktop, defined with -[BlueBuild](https://blue-build.org). **There is no published image** — clone this repo and build -it yourself; `bluebuild switch` rebases the running system straight onto the local build. +**Ubuntu 26.04 LTS** on my desktop (Ryzen 7 9800X3D, RTX 5090, 49" 5120x1440). This replaces the +Bazzite setup, which lives on in this repo's git history. -- **Base:** `ghcr.io/ublue-os/bazzite-dx-nvidia` — Bazzite DX, KDE Plasma, **NVIDIA open kernel - modules**. There is no separate `-open` DX image anymore: DX NVIDIA is open-driver-only, which - is exactly what the RTX 5090 (Blackwell) requires. Current stable ships driver **610.x** (open). -- **Pinned** to Fedora 44 via `image-version: stable-44` in - [recipes/recipe.yml](recipes/recipe.yml). No surprise major upgrades; bump to `stable-45` - deliberately. -- **This image layers almost nothing.** The base already includes Steam and the gaming stack, - Flatpak, Docker CE + CLI, Podman, VS Code, Homebrew, **and nvidia-container-toolkit with - automatic CDI setup** (`ublue-nvctk-cdi.service` regenerates `/etc/cdi/nvidia.yaml` each boot). - What this recipe adds: a declarative first-boot Flatpak set (and a dormant signing policy). - That's it — by design. +This is plain Ubuntu with no custom image: apt handles the system and dev tools, Flathub handles +the desktop apps, and three scripts do the rest. -Why BlueBuild instead of a raw Containerfile: the whole custom layer is "install these Flatpaks -on first boot", which the `default-flatpaks` module expresses declaratively — and -`bluebuild switch` gives a one-command build-and-rebase with no registry involved. - -## Getting onto this image (fresh desktop rebuild) - -1. Install stock **Bazzite DX (NVIDIA)** from the official ISO at - [bazzite.gg](https://bazzite.gg) and boot into it. -2. Install the BlueBuild CLI and switch: - - ```bash - git clone https://git.lazypugs.com/ckoch/main-desktop.git && cd main-desktop - podman run --pull always --rm ghcr.io/blue-build/cli:latest-installer | bash - bluebuild switch recipes/recipe.yml # add --reboot to reboot when done - ``` - - `switch` builds the image locally, stores it as an oci-archive under `/etc/bluebuild/`, and - rebases `rpm-ostree` onto it. First build pulls the multi-GB base — give it time and disk - (~25 GB free). -3. Reboot. Done — the first-boot service then installs the Flatpak list below (needs a few - minutes on first login; check with `flatpak list --system`). - -Alternatively, `bluebuild generate-iso recipes/recipe.yml` can produce installer media with the -custom image baked in, skipping step 1's stock install. - -**Updating later** (pulls the newest Bazzite `stable-44` base plus any recipe changes): - -```bash -cd main-desktop && git pull && bluebuild update recipes/recipe.yml --reboot -``` - -Roll back anytime with `rpm-ostree rollback` (or pick the previous deployment in the boot menu). - -Note on signing: local oci-archive rebases are inherently unsigned (`ostree-unverified-image`), -so cosign doesn't apply here. The recipe keeps the `signing` module and `cosign.pub` so that -publishing signed images to a registry later is just a `bluebuild build --push` away. - -## Install-time checklist - -1. **Driver sanity:** `nvidia-smi` reports driver **≥ 580** (currently 610.x) and the RTX 5090 is - listed. Confirm open kernel modules: `modinfo -F license nvidia` should say `Dual MIT/GPL` - (the proprietary module says `NVIDIA`). Blackwell only works on the open modules. -2. **Deployment:** `rpm-ostree status` shows the booted deployment is the local build - (`ostree-unverified-image:oci-archive:/etc/bluebuild/...`) with the expected base version. -3. **Flatpaks:** all installed (`flatpak list --system` — 22 apps from the list below). The - first-boot install is sequential and multi-GB; on slow links it can take 15+ minutes after - first login. Progress: `journalctl -b --grep flatpak -f`. -4. **Docker group:** run `ujust dx-group` (adds you to `docker` and friends), then log out/in. -5. **GPU in containers:** run the one-liner test in the next section. - -## GPU in containers (CUDA on the 5090) - -`nvidia-container-toolkit` is already in the image and a boot service generates the CDI spec -automatically. CDI is the shipped mechanism: - -```bash -# Podman — works out of the box: -podman run --rm --device nvidia.com/gpu=all docker.io/nvidia/cuda:12.8.0-base-ubuntu24.04 nvidia-smi - -# Docker — CDI device syntax also works on current Docker CE: -docker run --rm --device nvidia.com/gpu=all nvidia/cuda:12.8.0-base-ubuntu24.04 nvidia-smi -``` - -If you want the classic `docker run --gpus all` syntax, that needs a one-time runtime hookup -(this writes to `/etc/docker/daemon.json`, which is mutable and survives updates): - -```bash -sudo nvidia-ctk runtime configure --runtime=docker -sudo systemctl restart docker -``` - -## First-boot Flatpaks - -Installed system-wide from Flathub by the `default-flatpaks` module. All IDs verified against -Flathub (2026-07). - -**Official / verified listings:** Bambu Studio, Bitwarden, Discord, Firefox, Thunderbird, -FreeCAD, GIMP, HandBrake, LibreOffice, Moonlight, PCSX2 (`net.pcsx2.PCSX2` — the official -build), Podman Desktop, Prism Launcher, Warzone 2100, XIVLauncher (`dev.goats.xivlauncher`). - -**Community / unverified listings — know what you're running:** - -| App | ID | Note | +| Script | Where it runs | What it does | |---|---|---| -| Android Studio | `com.google.AndroidStudio` | Community packaging of Google's IDE (not verified) | -| GitHub Desktop | `io.github.shiftey.Desktop` | Community Linux fork; the "shiftey" spelling is the real ID | -| MakeMKV | `com.makemkv.MakeMKV` | Community packaging of the proprietary app | -| GeForce NOW | `io.github.hmlendea.geforcenow-electron` | **Unofficial** Electron wrapper, not an NVIDIA product | -| Spotify | `com.spotify.Client` | Community packaging of the proprietary client | -| VLC | `org.videolan.VLC` | No verified badge on Flathub (still the VideoLAN-maintained build) | -| Godot | `org.godotengine.Godot` | No verified badge on Flathub | +| [scripts/backup.sh](scripts/backup.sh) | Bazzite, **before wiping** | Archives your home folder to server-marvin and reads every archive back to check it | +| [scripts/setup.sh](scripts/setup.sh) | Fresh Ubuntu | Adds repos, the NVIDIA driver, Docker, Steam, Flatpaks, dev tools and mounts, all in one pass | +| [scripts/restore.sh](scripts/restore.sh) | Ubuntu, after setup + reboot | Unpacks the backup and leaves out the Fedora/KDE bits that would fight Ubuntu | -**GeForce NOW on the 32:9 monitor:** the service only streams 16:9 (and some 21:9) aspect -ratios, so expect pillarboxing at 5120x1440 — that's a service limitation, not a config problem. +All three are safe to re-run. -## Development setup +## Why leave Bazzite -All 29 projects in `~/Documents/Development` were scanned for toolchain needs — the full -mapping (what the image covers, mise versions, the native-build distrobox, hardware udev rules) -is in [DEVELOPMENT.md](DEVELOPMENT.md). After first boot, run -[scripts/dev-setup.sh](scripts/dev-setup.sh) to set it all up. +The read-only base made ordinary things into projects. VS Code had to be layered with rpm-ostree, +Node and .NET came from Homebrew, and Claude desktop ran inside an Ubuntu distrobox because it +only ships for Ubuntu/Debian. On Ubuntu, each of those is an `apt install`. -## Post-install steps (manual by design — don't automate these) +## The move, in order -### PIA VPN — native WireGuard, not the app +### 1. Back up (on Bazzite) -The PIA desktop app installs into `/usr` and breaks on the read-only filesystem. Skip it: - -1. Log in at PIA's [OpenVPN config generator](https://www.privateinternetaccess.com/pages/ovpn-config-generator) - (or generate a WireGuard config via their API/support flow) and download configs for your - preferred regions. -2. KDE **System Settings → Network → Connections → Add (+) → Import VPN connection…**, pick the - downloaded file, and enter your PIA credentials. -3. This is plain NetworkManager — it lives in `/etc`, survives every update, and gets a - connect/disconnect toggle in the system tray. - -### Microsoft Teams — PWA via Edge - -No native Linux client. Install **Microsoft Edge specifically** (corporate conditional -access / Intune compatibility): `flatpak install flathub com.microsoft.Edge`, sign in to -, then menu → **Apps → Install this site as an app**. - -### Claude desktop — PWA - -No official Linux app. Open in Edge (or any Chromium browser) and use -**Install this site as an app** the same way. - -### AnythingLLM — not on Flathub - -No Flatpak exists (verified 2026-07: zero Flathub search hits). Options: the official desktop -AppImage from [anythingllm.com](https://anythingllm.com/desktop) (works fine on atomic distros — -keep it in `~/Apps` and use e.g. Gear Lever to integrate it), or run the server edition in a -container: `docker run -p 3001:3001 mintplexlabs/anythingllm` (add -`--device nvidia.com/gpu=all` for 5090 acceleration). - -### Proton-GE - -Already handled by Bazzite: use the preinstalled **ProtonUp-Qt** GUI (or browse `ujust` — run -`ujust` with no arguments to list recipes). Don't install anything extra. - -## Node / toolchains — not in the image - -Node, npm, etc. are deliberately **not layered**. Use per-project versions instead: +From this repo's folder: ```bash -brew install mise # Homebrew ships in the base image -mise use node@22 # per-project .mise.toml; `mise use -g node@22` for a global default +scripts/backup.sh # -> /var/mnt/server-marvin-personal/desktop-backup-/ ``` -(Plain `brew install node` also works if you just want one global Node.) +Close Steam, Firefox, Discord and Claude first. The script refuses to write to the disk that's about +to be wiped. It's about **360 GB**, so expect around 2 hours on gigabit, including the read-back +check. -## Display note +| Archive | Size | Contents | +|---|---|---| +| `home.tar.zst` | ~120 GB | Everything in `~` except caches, Trash, container images, and the three below. Includes `Desktop/Becky_backup` (63 GB), all Flatpak app data (`~/.var/app`), Lutris games in `~/Games` | +| `xlcore.tar.zst` | ~124 GB | FFXIV install, because Square's patch servers make a redownload take hours | +| `comfyui.tar.zst` | ~115 GB | ComfyUI minus its venv (mostly models) | +| `steam-saves.tar.zst` | ~5 GB | Steam `userdata` + Proton `compatdata` (saves and prefixes). Game files on the home drive (216 GB) redownload | -The 49" 32:9 (5120x1440) ultrawide needs nothing at the image layer. Post-install: set scale and -refresh rate in KDE System Settings → Display; KDE's window tiling (Meta+drag, or a tiler like -Polonium) is worth setting up at this width. VMs are remote via Remmina — there is intentionally -no local hypervisor tooling in this image. +`manifest/` holds reference lists: Flatpaks, VS Code extensions, Steam games, the old fstab and +Flatpak overrides. The archives contain `~/.ssh` and `~/.gnupg`, so the backup folder is as +sensitive as your home folder. + +**Not backed up, but not lost:** the Data drive (SteamLibrary, `emu/`) isn't touched by the +install. + +### 2. Install Ubuntu 26.04 LTS + +> **⚠ Two identical 1 TB NVMe drives.** Install to the **Samsung SSD 990 PRO**. Never pick the +> **Samsung SSD 970 EVO Plus**: that's the "Data" drive with your second Steam library, and it's +> full, so nothing on it is backed up. `nvme0`/`nvme1` can swap between boots, so go by the model +> name the installer shows. + +- "Erase disk and install Ubuntu" on the 990 PRO is fine. +- Make the user **`ckoch`**. The first user gets UID 1000, which matches the file ownership on the + Data drive. +- The "third-party drivers" checkbox doesn't matter; setup.sh installs the NVIDIA driver either way. +- Prefer KDE? Install **Kubuntu 26.04** instead. The scripts don't care which desktop you run. + +### 3. Set up + +```bash +sudo apt install -y git +git clone https://git.lazypugs.com/ckoch/main-desktop.git ~/Documents/git/main-desktop +cd ~/Documents/git/main-desktop && scripts/setup.sh +``` + +It asks for your sudo password once and for the server-marvin SMB username/password (stored +root-only in `/etc/samba/credentials-marvin`). Flatpaks take most of the time. **Reboot** when it +finishes. + +What it installs: + +- **NVIDIA driver:** Ubuntu's recommended **open-kernel** branch (595 today) with the prebuilt, + signed modules, so there's no DKMS. The script stops if it ends up with a non-open driver, + because Blackwell only runs on the open modules. It also installs 32-bit GL for Steam/Proton. +- **Gaming:** Steam (`steam-installer` + `steam-devices` for controllers), Lutris, GameMode, + MangoHud. ProtonUp-Qt (Flatpak) for Proton-GE. The Xbox Bluetooth fix (`disable_ertm`) + carries over from Bazzite. +- **Containers:** Docker CE from Docker's repo (not the snap), with `--gpus all` wired up through + nvidia-container-toolkit. Also Podman. You're added to `docker` and `kvm`. +- **Apps:** the 27 Flatpaks in [flatpaks.txt](flatpaks.txt), picked from the 42 that were on + Bazzite (the rest are listed at the bottom of that file, commented out, if you want one back). + Ubuntu's Firefox snap is removed in favor of the Flatpak so your old profile restores + untouched. A systemd timer updates Flatpaks daily. +- **Dev:** VS Code, Claude desktop (Anthropic's apt repo), Claude Code CLI, and + `build-essential`/cmake, so no dev distrobox needed. **No Node or .NET on the host** by + choice — LudosData and landingPage build in Docker. To add them later: + `sudo add-apt-repository ppa:jdxcode/mise && sudo apt install mise dotnet-sdk-10.0`. +- **Mounts:** the Data drive at **`/mnt/data`** and server-marvin at + **`/mnt/server-marvin-personal`** (automounted). + +### 4. Restore + +```bash +scripts/restore.sh # newest backup on server-marvin +ONLY="home steam-saves" scripts/restore.sh # skip the big ones if you want +``` + +First it pulls the Flatpak NVIDIA GL runtime (only possible once the driver is loaded after the +reboot). Without it, Flatpak apps render in software. + +It deliberately skips a few things: + +- Fedora's `.bashrc` and `.bash_profile` +- `~/.local/bin` (old pip `--user` tools such as rembg) +- KDE's GNOME-settings database and generated GTK themes +- Bazzite's autostart items + +Everything is still in the archives, e.g. +`tar -I zstd -xf home.tar.zst -C ~ ./.config/some-app`. + +Old configs full of `/var/home/ckoch/...` paths keep working through a `/var/home → /home` +symlink. Claude Code's per-project memory and history are renamed to the new paths. + +## Check it worked + +```bash +nvidia-smi # RTX 5090, driver 595.x +modinfo -F license nvidia # "Dual MIT/GPL" = open modules +docker run --rm --gpus all nvidia/cuda:12.8.0-base-ubuntu24.04 nvidia-smi +flatpak list --app | wc -l # 27 +findmnt /mnt/data /mnt/server-marvin-personal +claude --version && code --version +``` + +Podman's `--device nvidia.com/gpu=all` needs a CDI spec. The toolkit normally generates one at +boot; if Podman says the device is unknown, run +`sudo nvidia-ctk cdi generate --output=/etc/cdi/nvidia.yaml`. + +## By hand after restore + +- **Steam:** sign in, then **Settings → Storage → Add drive → `/mnt/data/SteamLibrary`**. Games + there come back without downloading. +- **ComfyUI:** `cd ~/ComfyUI && python3 -m venv venv && venv/bin/pip install -r requirements.txt` + (plus any custom nodes' requirements). +- **Display:** Settings → Displays. The monitor does **5120x1440 @ 240 Hz**; Bazzite was running it + at 120. Turn on variable refresh rate there too. Ubuntu's built-in window tiling works well at + this width. +- **PIA VPN:** the official Linux app works on Ubuntu (it couldn't on Bazzite's read-only + `/usr`). Download the installer from PIA and run it. + +## Coming from Bazzite: what moved + +| Was | Now | +|---|---| +| `rpm-ostree` layering / `ujust` | `apt` | +| Homebrew `node@24`, `dotnet` | Nothing on the host — those projects build in Docker | +| Claude desktop in a distrobox | Native `claude-desktop` package | +| `/var/home/ckoch` | `/home/ckoch` (+ compatibility symlink) | +| `/run/media/ckoch/Data` | `/mnt/data` | +| `/var/mnt/server-marvin-personal` | `/mnt/server-marvin-personal` | +| Image updates + rollback | Software Updater / `sudo apt update && sudo apt upgrade`; Flatpaks auto-update daily. No rollback by default; `sudo apt install timeshift` if you miss it | ## Maintenance -- **Change the Flatpak list / packages:** edit [recipes/recipe.yml](recipes/recipe.yml), commit, - and re-run `bluebuild switch`. (Removing an app from the list does not uninstall it from the - machine; `flatpak uninstall` it once by hand.) -- **Jump Fedora majors:** change `image-version: stable-44` → `stable-45` in the recipe when - ready, then `bluebuild switch`. -- **Reclaim build disk:** `bluebuild prune` cleans build caches. -- **Publishing later:** if this ever moves to a registry + signed rebases, the pieces are in - place — `cosign.pub` is committed, the signing module is in the recipe, and - `bluebuild build --push --registry --registry-namespace ` does the rest (note: - as of CLI v0.9.36, local `build` runs bake the signing policy for `localhost` — a registry - push should be done from CI or with a `bluebuild generate`-based two-step). +- **Add or remove an app:** edit [flatpaks.txt](flatpaks.txt) and re-run `scripts/setup.sh`. + Removing a line doesn't uninstall anything; `flatpak uninstall ` once by hand. +- **Next LTS (28.04):** Ubuntu offers the upgrade in Software Updater once 28.04.1 is out. The + upgrader disables third-party repos (Docker, NVIDIA toolkit, etc.). Turn them back on in + Software & Updates → Other Software afterwards. diff --git a/cosign.pub b/cosign.pub deleted file mode 100644 index 9e4fbad..0000000 --- a/cosign.pub +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEog2z/j7tTbopTjEMcisWFqbIQ/cD -WZP/m/ImJktzg0YZtSexbJxd1ltayXtl/6c3roGuPT+w1qZ+PuooR2BKkA== ------END PUBLIC KEY----- diff --git a/flatpaks.txt b/flatpaks.txt new file mode 100644 index 0000000..75d0d5c --- /dev/null +++ b/flatpaks.txt @@ -0,0 +1,66 @@ +# Flathub apps installed system-wide by scripts/setup.sh — one ID per line, '#' comments. +# Picked from everything installed on the Bazzite box (scanned 2026-09-21, chosen 2026-09-23). +# Restoring ~/.var/app brings each app's data back with it (logins, profiles, saves). +# Removing a line doesn't uninstall the app; `flatpak uninstall ` it once by hand. + +# -- Internet / chat -- +org.mozilla.firefox # replaces Ubuntu's Firefox snap (setup.sh removes the snap) +org.mozilla.thunderbird_esr +com.discordapp.Discord +com.bitwarden.desktop + +# -- Media -- +com.spotify.Client # community packaging of the proprietary client +org.videolan.VLC +fr.handbrake.ghb +com.makemkv.MakeMKV # community packaging + +# -- Create / CAD / print -- +org.gimp.GIMP +org.freecad.FreeCAD +com.bambulab.BambuStudio +org.godotengine.Godot # DCC projects target 4.7; Flathub tracks current stable +org.mapeditor.Tiled + +# -- Dev -- +com.sublimemerge.App + +# -- Games / launchers -- +com.heroicgameslauncher.hgl +net.davidotek.pupgui2 # ProtonUp-Qt — Proton-GE for Steam/Lutris (Bazzite shipped this) +dev.goats.xivlauncher # FFXIV; game files live in ~/.xlcore +com.moonlight_stream.Moonlight +io.github.hmlendea.geforcenow-electron # unofficial GeForce NOW wrapper; 16:9/21:9 only +org.prismlauncher.PrismLauncher +com.unfolding_machines.pum_lite # PUM Lite — solo RPG / story tool + +# -- Emulators -- +net.pcsx2.PCSX2 # official PCSX2 build — do not swap for community forks +org.DolphinEmu.dolphin-emu +app.xemu.xemu +io.mgba.mGBA +com.snes9x.Snes9x + +# -- Games -- +net.wz2100.wz2100 + +# --------------------------------------------------------------------------------------- +# Installed on Bazzite, deliberately left behind. Uncomment to bring one back — +# its data is still in the backup's home.tar.zst under .var/app/. +# +# com.github.IsmaelMartinez.teams_for_linux # Teams +# com.ktechpit.whatsie # WhatsApp +# org.signal.Signal +# io.github.hakuneko.HakuNeko +# net.mkiol.SpeechNote # 3.9 GB of speech models +# org.blender.Blender +# org.libreoffice.LibreOffice # Ubuntu ships LibreOffice as debs anyway +# org.photoqt.PhotoQt +# com.google.AndroidStudio +# io.github.shiftey.Desktop # GitHub Desktop +# io.podman_desktop.PodmanDesktop +# org.gnome.Calculator # Ubuntu has its own +# net.longturn.freeciv21 +# org.develz.Crawl # ~/.crawl saves are still in the backup +# net.sourceforge.atanks +# org.frozen_bubble.frozen-bubble diff --git a/recipes/recipe.yml b/recipes/recipe.yml deleted file mode 100644 index 050ebda..0000000 --- a/recipes/recipe.yml +++ /dev/null @@ -1,56 +0,0 @@ -# yaml-language-server: $schema=https://schema.blue-build.org/recipe-v1.json -# Custom desktop image: Bazzite DX (KDE) + NVIDIA open kernel modules (RTX 5090 / Blackwell). -# -# The base image already ships: Steam + gaming stack, Flatpak, Docker CE + CLI, -# Podman, VS Code, Homebrew, the NVIDIA 610+ open driver, AND nvidia-container-toolkit -# with automatic CDI generation (ublue-nvctk-cdi.service). Nothing GPU/dev-related -# needs to be layered here — this recipe only adds signature-verification policy -# and the declarative first-boot Flatpak set. -name: main-desktop -description: Bazzite DX NVIDIA (open, KDE) with my first-boot Flatpak set, signed. - -base-image: ghcr.io/ublue-os/bazzite-dx-nvidia -# Pinned to Fedora 44. Bump deliberately (stable-45, ...) when ready to jump majors. -image-version: stable-44 -# Deterministic tags regardless of where the build runs (local builds would -# otherwise only produce a local- tag; rebase refs rely on :latest). -alt-tags: - - latest - - stable-44 - -modules: - - type: default-flatpaks - configurations: - - scope: system - notify: true - install: - # -- Official / verified Flathub listings -- - - com.bambulab.BambuStudio - - com.bitwarden.desktop - - com.discordapp.Discord - - com.moonlight_stream.Moonlight - - dev.goats.xivlauncher - - fr.handbrake.ghb - - io.podman_desktop.PodmanDesktop - - net.pcsx2.PCSX2 # official PCSX2 build — do not swap for community forks - - net.wz2100.wz2100 - - org.freecad.FreeCAD # current ID; old org.freecadweb.FreeCAD is dead - - org.gimp.GIMP - - org.libreoffice.LibreOffice - - org.mozilla.Thunderbird - - org.mozilla.firefox - - org.prismlauncher.PrismLauncher - # -- Community / unverified listings (see README) -- - - com.google.AndroidStudio # native-angular android-host (SDK 35 + NDK) - - com.makemkv.MakeMKV - - com.spotify.Client - - io.github.hmlendea.geforcenow-electron # unofficial wrapper; 16:9/21:9 streams only - - io.github.shiftey.Desktop # GitHub Desktop Linux fork; "shiftey" is correct - - org.godotengine.Godot - - org.videolan.VLC - - # Installs the cosign public key + container signature-verification policy. - # Unused in the current build-locally model (`bluebuild switch` rebases from a - # local oci-archive, unsigned) — kept so publishing to a registry later only - # requires a push, not a recipe change. Requires cosign.pub at repo root. - - type: signing diff --git a/scripts/backup.sh b/scripts/backup.sh new file mode 100755 index 0000000..ca287db --- /dev/null +++ b/scripts/backup.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash +# Run on the Bazzite box BEFORE wiping it. Archives your home folder to server-marvin as +# tar.zst files, then reads every archive back to prove it's intact. +# +# scripts/backup.sh # -> /var/mnt/server-marvin-personal/desktop-backup- +# scripts/backup.sh /some/other/dir # any directory NOT on the disk you're about to wipe +# +# Re-running skips archives that already finished (FORCE=1 redoes them). +# Close Steam, Firefox, Discord and Claude first — files that change mid-read get a warning. +set -euo pipefail + +SHARE=/var/mnt/server-marvin-personal +DEST="${1:-$SHARE/desktop-backup-$(date +%F)}" + +step() { printf '\n\033[1;34m== %s ==\033[0m\n' "$*"; } +warn() { printf '\033[1;33mwarning:\033[0m %s\n' "$*" >&2; } +die() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; } + +# -- Make sure the backup lands somewhere that survives the reinstall ------------------ +if [[ $DEST == "$SHARE"/* ]]; then + ls "$SHARE" >/dev/null 2>&1 || true # poke the automount + mountpoint -q "$SHARE" || die "$SHARE isn't mounted — is server-marvin up?" +fi +mkdir -p "$DEST" +disk_of() { findmnt -n -o SOURCE -T "$1" | sed 's/\[.*//'; } +[[ $(disk_of "$DEST") != "$(disk_of "$HOME")" ]] \ + || die "$DEST is on the same disk as your home — that's the disk Ubuntu will wipe" +echo "Backing up $HOME -> $DEST" +df -h "$DEST" | tail -1 + +# -- Archive helper --------------------------------------------------------------------- +archive() { # archive + local name=$1 out="$DEST/$1.tar.zst"; shift + step "$name" + if [[ -s $out && -z ${FORCE:-} ]]; then echo "already done, skipping"; return; fi + local rc=0 + tar --create --file="$out.partial" --use-compress-program='zstd -T0 -3' \ + --directory="$HOME" \ + --checkpoint=100000 --checkpoint-action='ttyout= %{%H:%M:%S}t %T%*\r' \ + "$@" || rc=$? + echo + # GNU tar exits 1 when a file changed while it was being read (an app was still open). + # Everything else still made it in, so keep the archive; anything above 1 is fatal. + (( rc <= 1 )) || die "tar failed on $name (exit $rc)" + (( rc == 0 )) || warn "$name: some files changed while being read — close apps and FORCE=1 to redo if it matters" + mv "$out.partial" "$out" + ls -lh "$out" | awk '{print " " $5}' +} + +# Big, self-contained folders get their own archive so they can be restored (or skipped) +# separately. Everything else goes in home.tar.zst. +archive home \ + --anchored \ + --exclude=./.cache \ + --exclude=./.local/share/Trash \ + --exclude=./.local/share/Steam \ + --exclude=./.steam \ + --exclude=./.local/share/containers \ + --exclude=./.local/share/flatpak \ + --exclude=./.local/share/baloo \ + --exclude='./.var/app/*/cache' \ + --exclude='./.claude.json.tmp.*' \ + --exclude=./.xlcore \ + --exclude=./ComfyUI \ + --no-anchored \ + --exclude=node_modules \ + --exclude=__pycache__ \ + . + +# FFXIV game install (~124 GB) — Square's patch servers make a redownload take hours. +[[ -d $HOME/.xlcore ]] && archive xlcore ./.xlcore + +# ComfyUI minus its venv (rebuilt on Ubuntu); this is ~110 GB of models. +[[ -d $HOME/ComfyUI ]] && archive comfyui \ + --anchored --exclude=./ComfyUI/venv --exclude=./ComfyUI/.venv \ + --no-anchored --exclude=__pycache__ \ + ./ComfyUI + +# Steam: only saves and settings. Games get redownloaded; the ones on the Data drive stay put. +archive steam-saves \ + ./.local/share/Steam/userdata \ + ./.local/share/Steam/steamapps/compatdata + +# -- Reference info for the rebuild ------------------------------------------------------- +step "manifest" +m="$DEST/manifest"; mkdir -p "$m" +flatpak list --app --columns=application,origin,installation > "$m/flatpaks.txt" 2>/dev/null || true +code --list-extensions > "$m/vscode-extensions.txt" 2>/dev/null || true +brew leaves > "$m/brew-leaves.txt" 2>/dev/null || true +rpm-ostree status > "$m/rpm-ostree-status.txt" 2>/dev/null || true +cp /etc/fstab "$m/fstab" +cp -r "$HOME/.local/share/flatpak/overrides" "$m/flatpak-overrides-user" 2>/dev/null || true +cp -r /var/lib/flatpak/overrides "$m/flatpak-overrides-system" 2>/dev/null || true +lsblk -f > "$m/disks.txt" +ls "$HOME/.local/share/Steam/steamapps/common" > "$m/steam-games-home.txt" 2>/dev/null || true +ls /run/media/"$USER"/Data/SteamLibrary/steamapps/common > "$m/steam-games-data-drive.txt" 2>/dev/null || true +ls "$m" + +# -- Prove every archive reads back cleanly ---------------------------------------------- +if [[ -z ${SKIP_VERIFY:-} ]]; then + for f in "$DEST"/*.tar.zst; do + step "verify $(basename "$f")" + tar --use-compress-program=zstd --list --file="$f" > /dev/null || die "$f is corrupt — rerun with FORCE=1" + echo "ok" + done +fi + +step "done" +du -sh "$DEST"/*.tar.zst +echo "Backup is in: $DEST" +echo "Safe to wipe the 990 PRO once you've eyeballed that list." diff --git a/scripts/dev-setup.sh b/scripts/dev-setup.sh deleted file mode 100755 index 8fc20b5..0000000 --- a/scripts/dev-setup.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash -# One-shot developer toolchain setup for the main-desktop image. -# Run as your user after first boot (safe to re-run). See DEVELOPMENT.md for the why. -set -euo pipefail - -echo "== Homebrew tools ==" -brew install mise gh cosign - -echo "== Toolchains (mise) ==" -mise use -g node@22 python@3.12 go@latest rust@latest java@temurin-21 dotnet@10 -mise install node@18 node@20 python@3.11 java@temurin-17 java@temurin-25 - -echo "== RTL-SDR udev rules (FRScanner) ==" -if [ ! -f /etc/udev/rules.d/20-rtlsdr.rules ]; then - sudo tee /etc/udev/rules.d/20-rtlsdr.rules >/dev/null <<'EOF' -# Generic RTL2832U (rtl-sdr) dongles — allow non-root access -SUBSYSTEM=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="2838", MODE="0666", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="2832", MODE="0666", TAG+="uaccess" -EOF - sudo udevadm control --reload-rules -fi - -echo "== Native-build distrobox ==" -if ! distrobox list 2>/dev/null | grep -q '^.*| *dev '; then - distrobox create --yes dev --image registry.fedoraproject.org/fedora:44 - distrobox enter dev -- sudo dnf install -y gcc gcc-c++ make cmake pkgconf python3-devel \ - gtk4-devel zbar libsndfile mediainfo-libs rtl-sdr rtl-sdr-devel mkvtoolnix \ - python3-tkinter python3-dbus flashrom -fi - -echo "== Done ==" -echo "Remaining manual bits (see DEVELOPMENT.md):" -echo " - copy ~/godot/ (pinned 4.2/4.3 binaries) and ~/.local/share/godot/ from old machine" -echo " - bring per-project .env files (API keys)" -echo " - ujust dx-group (docker group), then re-login" diff --git a/scripts/restore.sh b/scripts/restore.sh new file mode 100755 index 0000000..74210ac --- /dev/null +++ b/scripts/restore.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash +# Run on Ubuntu AFTER setup.sh and a reboot. Unpacks the Bazzite backup into your home, +# minus the Fedora/KDE-specific bits that would fight a fresh Ubuntu desktop. +# +# scripts/restore.sh # newest desktop-backup-* on server-marvin +# scripts/restore.sh /path/to/backup # a specific backup folder +# ONLY="home steam-saves" scripts/restore.sh # just some archives +# +# Everything is still in the archives, so anything skipped here can be pulled out later: +# tar -I zstd -xf home.tar.zst -C ~ ./.config/some-app +set -euo pipefail + +SHARE=/mnt/server-marvin-personal + +step() { printf '\n\033[1;34m== %s ==\033[0m\n' "$*"; } +die() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; } + +[[ $EUID -ne 0 ]] || die "run as your user, not with sudo" +ls "$SHARE" >/dev/null 2>&1 || true # poke the automount +SRC="${1:-$(ls -d "$SHARE"/desktop-backup-* 2>/dev/null | sort | tail -1)}" +[[ -n $SRC && -d $SRC ]] || die "no backup found — pass the folder, or check $SHARE is mounted" +ONLY=${ONLY:-home steam-saves xlcore comfyui} +echo "Restoring from $SRC: $ONLY" + +pgrep -x steam >/dev/null && die "quit Steam first" + +# Flatpak apps need a GL extension matching the NVIDIA driver, and flatpak can only see the +# driver version once it's loaded — i.e. now, after the reboot. Without this they render in software. +step "Flatpak NVIDIA runtime" +sudo flatpak update -y --noninteractive + +# Old configs (Heroic, Lutris, XIVLauncher, Proton prefixes, ...) hold absolute +# /var/home/ckoch/... paths. One symlink makes all of them resolve on Ubuntu. +[[ -e /var/home ]] || sudo ln -s /home /var/home + +# Left out of home.tar.zst on purpose: +skip=( + --anchored + # Fedora's shell dotfiles; Ubuntu's are better and setup.sh already added what's needed + --exclude=./.bashrc --exclude=./.bash_profile --exclude=./.bash_logout --exclude=./.profile + --exclude=./.bashrc.d --exclude=./.zshrc --exclude=./.zprofile + # .NET tool shims built against Homebrew's SDK (setup.sh reinstalled dotnet-ef) + --exclude=./.dotnet + # pip --user installs for Fedora's Python, and launchers for the old distroboxes + --exclude=./.local/bin --exclude='./.local/lib/python*' + --exclude='./.local/share/applications/claude*' + --exclude='./.local/share/applications/war1gus*' + --exclude=./.local/share/applications/code.desktop + # desktop state that would clobber Ubuntu's: GNOME's settings db, keyring, default apps, + # and KDE-generated GTK themes that would recolor GNOME apps + --exclude=./.config/dconf --exclude=./.local/share/keyrings --exclude=./.config/mimeapps.list + --exclude=./.config/gtk-3.0 --exclude=./.config/gtk-4.0 + --exclude=./.config/gtkrc --exclude=./.config/gtkrc-2.0 --exclude=./.gtkrc-2.0 + # Bazzite leftovers + --exclude=./.config/bazzite --exclude=./.config/autostart/sb-key-notify.desktop + # old SMB password file (setup.sh stored the credentials in /etc/samba) + --exclude=./.smbcredentials +) + +for name in $ONLY; do + f="$SRC/$name.tar.zst" + step "$name" + [[ -f $f ]] || { echo "not in backup, skipping"; continue; } + args=() + [[ $name == home ]] && args=("${skip[@]}") + tar --extract --file="$f" --use-compress-program=zstd --directory="$HOME" \ + --checkpoint=100000 --checkpoint-action='ttyout= %{%H:%M:%S}t %T%*\r' \ + "${args[@]}" + echo "ok" +done + +# Claude Code keys memory/history by folder path; /var/home/ckoch is now /home/ckoch. +if [[ -d ~/.claude/projects ]]; then + for d in ~/.claude/projects/-var-home-*; do + [[ -d $d ]] || continue + new=~/.claude/projects/${d##*/-var} + [[ -e $new ]] || mv "$d" "$new" + done + [[ -f ~/.claude.json ]] && sed -i 's#"/var/home/#"/home/#g' ~/.claude.json +fi + +step "Done — a few things to finish by hand" +cat < Storage > Add drive > /mnt/data/SteamLibrary + (games on the Data drive come back without downloading; home-drive games redownload). +- ComfyUI: rebuild its venv — + cd ~/ComfyUI && python3 -m venv venv && venv/bin/pip install -r requirements.txt +- Old pip --user tools (rembg etc.) weren't restored: pipx install "rembg[cli]" if you want it. +- Log out and back in so every app picks up the restored settings. +EOF diff --git a/scripts/setup.sh b/scripts/setup.sh new file mode 100755 index 0000000..dfd83e8 --- /dev/null +++ b/scripts/setup.sh @@ -0,0 +1,226 @@ +#!/usr/bin/env bash +# Fresh Ubuntu 26.04 LTS -> my desktop, in one pass. Run as your user; it sudos as needed. +# Safe to re-run: each step checks before it changes anything. Reboot when it finishes, +# then run scripts/restore.sh. +set -euo pipefail + +REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +# Second NVMe (970 EVO Plus, label "Data"): SteamLibrary + emulator files. Never formatted. +DATA_UUID=29db4bd3-b743-417d-aa39-4da4acc9df3f +DATA_MNT=/mnt/data +# server-marvin SMB share — the backup lives here. \040 is fstab's escaped space. +MARVIN_SHARE='//192.168.1.193/Personal\040Files\040-\040ckoch' +MARVIN_MNT=/mnt/server-marvin-personal +MARVIN_CREDS=/etc/samba/credentials-marvin + +step() { printf '\n\033[1;34m== %s ==\033[0m\n' "$*"; } +warn() { printf '\033[1;33mwarning:\033[0m %s\n' "$*" >&2; } +die() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; } +apt_install() { sudo DEBIAN_FRONTEND=noninteractive apt-get install -y "$@"; } + +[[ $EUID -ne 0 ]] || die "run as your user, not with sudo" +. /etc/os-release +[[ $ID == ubuntu ]] || die "expected Ubuntu, found $ID" +[[ $VERSION_ID == 26.04 ]] || warn "written for 26.04; this is $VERSION_ID — carrying on" +CODENAME=${UBUNTU_CODENAME:-$VERSION_CODENAME} + +# Ask for the password once and keep sudo alive for the whole run (Flatpaks take a while). +sudo -v +while true; do sudo -n true; sleep 50; kill -0 "$$" 2>/dev/null || exit; done 2>/dev/null & +keepalive=$! +trap 'kill "$keepalive" 2>/dev/null' EXIT + +# --------------------------------------------------------------------------------------- +step "Apt repositories" +sudo apt-get update +apt_install curl gpg ca-certificates software-properties-common +for c in universe multiverse restricted; do sudo add-apt-repository -y -n "$c"; done +sudo dpkg --add-architecture i386 # Steam + 32-bit game libraries +sudo install -d -m 0755 /etc/apt/keyrings + +# Docker CE — Docker's own repo, not the snap or Ubuntu's docker.io +if [[ ! -f /etc/apt/sources.list.d/docker.sources ]]; then + sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc + sudo chmod a+r /etc/apt/keyrings/docker.asc + sudo tee /etc/apt/sources.list.d/docker.sources >/dev/null </dev/null +fi + +# VS Code — Microsoft's repo (was rpm-ostree-layered on Bazzite) +if [[ ! -f /etc/apt/sources.list.d/vscode.sources ]]; then + curl -fsSL https://packages.microsoft.com/keys/microsoft.asc \ + | sudo gpg --dearmor --yes -o /usr/share/keyrings/microsoft.gpg + sudo tee /etc/apt/sources.list.d/vscode.sources >/dev/null </dev/null +fi + +# Claude desktop — Anthropic's repo (ran inside an Ubuntu distrobox on Bazzite) +if [[ ! -f /etc/apt/sources.list.d/claude-desktop.list ]]; then + key=/usr/share/keyrings/claude-desktop-archive-keyring.asc + sudo curl -fsSLo "$key" https://downloads.claude.ai/claude-desktop/key.asc + if ! gpg --show-keys --with-colons "$key" 2>/dev/null \ + | grep -q '^fpr:::::::::31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE:'; then + sudo rm -f "$key"; die "Claude desktop signing key has the wrong fingerprint — not adding the repo" + fi + echo "deb [arch=amd64,arm64 signed-by=$key] https://downloads.claude.ai/claude-desktop/apt/stable stable main" \ + | sudo tee /etc/apt/sources.list.d/claude-desktop.list >/dev/null +fi + +sudo apt-get update +sudo DEBIAN_FRONTEND=noninteractive apt-get full-upgrade -y + +# --------------------------------------------------------------------------------------- +step "Packages" +# steam-installer refuses to install non-interactively until its license is accepted. +echo 'steam-installer steam/question select I AGREE' | sudo debconf-set-selections +echo 'steam-installer steam/license note ' | sudo debconf-set-selections + +apt_install \ + build-essential cmake pkg-config git git-lfs wget jq zstd unzip p7zip-full rsync htop \ + python3-venv python3-pip pipx ffmpeg cifs-utils flatpak \ + steam-installer steam-devices gamemode mangohud lutris vulkan-tools \ + docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin \ + nvidia-container-toolkit \ + code claude-desktop + +# --------------------------------------------------------------------------------------- +step "NVIDIA driver (open kernel modules — the only kind the RTX 5090 supports)" +apt_install ubuntu-drivers-common +installed_driver() { + { dpkg-query -W -f='${Status} ${Package}\n' 'nvidia-driver-*' 2>/dev/null || true; } \ + | awk '/^install ok installed / {print $4}' | sort -V | tail -1 +} +if [[ $(installed_driver) != *-open ]]; then + ubuntu-drivers devices 2>/dev/null | grep -E 'model|driver' || true + # Picks Ubuntu's recommended branch plus its prebuilt, signed kernel modules (no DKMS). + sudo ubuntu-drivers install +fi +driver=$(installed_driver) +[[ $driver == nvidia-driver-*-open ]] \ + || die "got '${driver:-no driver}', but the 5090 needs an -open driver — check 'ubuntu-drivers devices'" +branch=${driver#nvidia-driver-}; branch=${branch%-open} +apt_install "libnvidia-gl-$branch:i386" # 32-bit GL/Vulkan for Steam and Proton +echo "using $driver" + +# --------------------------------------------------------------------------------------- +step "Docker, GPU containers, Claude's Cowork VM" +sudo usermod -aG docker "$USER" # docker without sudo (after reboot) +getent group kvm >/dev/null && sudo usermod -aG kvm "$USER" # Cowork needs /dev/kvm + vhost-vsock +echo vhost_vsock | sudo tee /etc/modules-load.d/vhost_vsock.conf >/dev/null +if ! grep -q nvidia /etc/docker/daemon.json 2>/dev/null; then + sudo nvidia-ctk runtime configure --runtime=docker # enables `docker run --gpus all` + sudo systemctl restart docker +fi + +# --------------------------------------------------------------------------------------- +step "Flatpak apps" +sudo flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo +mapfile -t apps < <(sed -e 's/#.*//' -e 's/[[:space:]]//g' -e '/^$/d' "$REPO/flatpaks.txt") +sudo flatpak install -y --noninteractive --system flathub "${apps[@]}" +# Firefox's read-only peek at landingPage, as set by hand on Bazzite +flatpak override --user org.mozilla.firefox --filesystem="$HOME/Documents/git/landingPage:ro" + +# Firefox/Thunderbird come from Flathub instead, so restoring ~/.var/app brings the old +# profiles back untouched. Drop Ubuntu's snaps and the debs that pull them back in. +for s in firefox thunderbird; do + if snap list "$s" >/dev/null 2>&1; then sudo snap remove --purge "$s"; fi + if dpkg -s "$s" >/dev/null 2>&1; then sudo apt-get remove -y "$s"; fi +done +xdg-settings set default-web-browser org.mozilla.firefox.desktop 2>/dev/null || true + +# Keep Flatpaks updated daily, like Bazzite did. Starts next boot. +sudo tee /etc/systemd/system/flatpak-update.service >/dev/null <<'EOF' +[Unit] +Description=Update system Flatpaks +Wants=network-online.target +After=network-online.target + +[Service] +Type=oneshot +ExecStart=/usr/bin/flatpak update --system --noninteractive --assumeyes +EOF +sudo tee /etc/systemd/system/flatpak-update.timer >/dev/null <<'EOF' +[Unit] +Description=Update system Flatpaks daily + +[Timer] +OnBootSec=10min +OnUnitActiveSec=1d +Persistent=true + +[Install] +WantedBy=timers.target +EOF +sudo systemctl daemon-reload +sudo systemctl enable flatpak-update.timer + +# --------------------------------------------------------------------------------------- +step "Claude Code CLI" +# No Node or .NET on the host by choice — LudosData and landingPage build in Docker. +# If you want them back: sudo apt install mise dotnet-sdk-10.0 (mise needs ppa:jdxcode/mise). +command -v claude >/dev/null || [[ -x ~/.local/bin/claude ]] || curl -fsSL https://claude.ai/install.sh | bash + +# --------------------------------------------------------------------------------------- +step "Data drive + server-marvin share" +sudo mkdir -p "$DATA_MNT" "$MARVIN_MNT" +if ! grep -q "$DATA_UUID" /etc/fstab; then + echo "UUID=$DATA_UUID $DATA_MNT ext4 defaults,nofail,x-gvfs-show,x-gvfs-name=Data 0 2" \ + | sudo tee -a /etc/fstab >/dev/null +fi +sudo blkid -U "$DATA_UUID" >/dev/null || warn "Data drive ($DATA_UUID) not found — fstab entry is nofail, so boot is fine" + +if [[ ! -f $MARVIN_CREDS ]]; then + read -rp "server-marvin SMB username: " smb_user + read -rsp "server-marvin SMB password: " smb_pass; echo + sudo install -d -m 0755 /etc/samba + printf 'username=%s\npassword=%s\n' "$smb_user" "$smb_pass" \ + | sudo install -m 600 /dev/stdin "$MARVIN_CREDS" + unset smb_pass +fi +if ! grep -q "$MARVIN_MNT" /etc/fstab; then + echo "$MARVIN_SHARE $MARVIN_MNT cifs credentials=$MARVIN_CREDS,uid=$(id -u),gid=$(id -g),iocharset=utf8,vers=3.0,x-systemd.automount,_netdev,nofail 0 0" \ + | sudo tee -a /etc/fstab >/dev/null +fi +sudo systemctl daemon-reload +sudo mount -a || warn "a mount failed — check 'sudo mount -a' (is server-marvin up? right password?)" + +# --------------------------------------------------------------------------------------- +step "Small carry-overs from Bazzite" +# Xbox controllers over Bluetooth (Bazzite set this as a kernel argument) +echo 'options bluetooth disable_ertm=1' | sudo tee /etc/modprobe.d/bluetooth-xbox.conf >/dev/null + +step "Done" +cat <