diff --git a/.gitignore b/.gitignore index e50b2f1..e09d5e6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,8 @@ __pycache__/ release-preview/ /.bluebuild-scripts_* /Containerfile + +# build-console.sh generated files (contain credentials/machine-specific) +/console.env +/iso-config.generated.toml +/files/console/etc/bc250-console.conf diff --git a/BUILD-CONSOLE.md b/BUILD-CONSOLE.md index 7fd1456..4c1aa3f 100644 --- a/BUILD-CONSOLE.md +++ b/BUILD-CONSOLE.md @@ -1,5 +1,20 @@ # BC-250 Console — local build → ISO +## Quick path: build-console.sh + +```bash +cp console.env.example console.env # fill in usernames/passwords/display names +./build-console.sh # → iso-output/bootiso/bc250-console-gnome.iso +``` + +The script runs the whole verified pipeline below and bakes your accounts +(names, passwords, display names) and the console config into the ISO — +a fresh install needs no post-install setup beyond per-account logins +(Steam + Family View, Moonlight pairing, `sudo tailscale up`). +`console.env` is git-ignored: it contains real passwords, never commit it. + +Everything below documents the manual pipeline the script automates. + Custom recipe: `recipes/bc250-console-gnome.yml` (Bazzite GNOME + cyan-skillfish governor + SSH/RDP remote admin + BC-250 ttm kargs). Build path is **local** (no CI, no registry push, no cosign needed). diff --git a/OPERATIONS.md b/OPERATIONS.md index 5e73aaf..4ccb1dd 100644 --- a/OPERATIONS.md +++ b/OPERATIONS.md @@ -13,7 +13,16 @@ kid's password any time: `sudo passwd kid`. | | Command / client | Works when | |---|---|---| | SSH (terminal) | `ssh parent@` | Always (console is never asleep) | -| RDP (see the screen) | Any RDP client → `:3389` | Only while the kid's session is logged in | +| Moonlight (see + control the screen) | [Moonlight](https://moonlight-stream.org) → console → **Desktop** | Only while the kid's session is logged in | + +One-time Moonlight pairing: browse to `https://:47990`, +create the Sunshine admin login, then enter the PIN Moonlight shows you in +Sunshine's PIN tab. Sunshine starts automatically with the kid's session. + +> Why not RDP? GNOME Remote Desktop crashes the session on this GPU +> (mutter 50.x cursor-metadata screencast segfault on cyan-skillfish). +> `bc250-remote-setup` still configures it if you want to retry after a +> future mutter update — but Moonlight is smoother anyway. ``: the LAN IP, or (recommended) the Tailscale name/IP so it works from anywhere. Never port-forward 22/3389 on the router. diff --git a/README.md b/README.md index 77e5b02..f7f621f 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ over-the-air from this Gitea's container registry. |---|---| | GPU | `cyan-skillfish-governor-smu` (BC-250 DVFS governor, from the [filippor COPR](https://copr.fedorainfracloud.org/coprs/filippor/bazzite/)), enabled | | Memory | `ttm.pages_limit` / `ttm.page_pool_size` kargs so the GPU can use the full 16 GB shared GDDR6 | -| Remote admin | `openssh-server` (enabled) + `gnome-remote-desktop` (RDP); one-time `bc250-remote-setup` script; `tailscaled` enabled | +| Remote admin | `openssh-server` (enabled); **Sunshine** auto-serves the kid's session to Moonlight; `tailscaled` enabled. (RDP via `bc250-remote-setup` exists but is blocked by a mutter/GFX1013 crash) | | Console behavior | All suspend paths masked, no screen lock/dim, GNOME animations off — always on, always reachable | | Kid experience | Steam Big Picture autostarts for the `kid` account only | | Apps | First-boot flatpaks: ProtonUp-Qt, Heroic, RetroDECK, Prism Launcher (Minecraft) — Steam, Firefox, auto-updates already in the Bazzite base | @@ -25,9 +25,11 @@ over-the-air from this Gitea's container registry. ## Accounts -The ISO installs **unattended** and bakes two users from -[`iso-config.toml`](iso-config.toml): `parent` (admin/wheel) and `kid` -(standard), both with a placeholder password to change on first login. +Build with [`build-console.sh`](build-console.sh) + your `console.env` to +bake real usernames, passwords, and display names directly into the ISO +(see BUILD-CONSOLE.md). Without it, the ISO bakes the placeholder users +from [`iso-config.toml`](iso-config.toml): `parent` (admin/wheel) and `kid` +(standard), passwords to change on first login. The kid's account can be renamed post-install (see OPERATIONS.md) — the Big Picture autostart follows `CONSOLE_KID_USER` in `/etc/bc250-console.conf`. diff --git a/build-console.sh b/build-console.sh new file mode 100755 index 0000000..115eb67 --- /dev/null +++ b/build-console.sh @@ -0,0 +1,117 @@ +#!/usr/bin/env bash +# One-command console ISO builder. +# +# cp console.env.example console.env # fill in names/passwords +# ./build-console.sh +# +# Produces iso-output/bootiso/bc250-console-gnome.iso with the accounts, +# display names, and console behavior fully baked — no post-install steps +# beyond per-account logins (Steam, Sunshine pairing, tailscale up). +# +# Pipeline (verified on Ubuntu 24.04 + Docker Desktop + native dockerd): +# BlueBuild generate -> docker build -> throwaway registry -> skopeo into +# a containers-storage volume -> bootc-image-builder ISO (native daemon). +set -euo pipefail + +DIR="$(cd "$(dirname "$0")" && pwd)" +OUT="$(dirname "$DIR")/iso-output" +NATIVE="docker -H unix:///var/run/docker.sock" + +cd "$DIR" + +# --------------------------------------------------------------- config ---- +[ -f console.env ] || { + cp console.env.example console.env + echo "Created console.env — fill in your names/passwords, then re-run." + exit 1 +} +# shellcheck disable=SC1091 +source ./console.env +for v in KID_USER KID_PASSWORD PARENT_USER PARENT_PASSWORD; do + [ -n "${!v:-}" ] || { echo "ERROR: $v is empty in console.env"; exit 1; } +done +case "$KID_PASSWORD$PARENT_PASSWORD" in *'"'*|*'\'*) + echo 'ERROR: passwords must not contain " or \'; exit 1;; +esac + +echo ">> Building for kid='$KID_USER' parent='$PARENT_USER'" + +cat > iso-config.generated.toml < files/console/etc/bc250-console.conf </dev/null 2>&1; then + systemctl --user start docker-desktop 2>/dev/null || true + for _ in $(seq 1 24); do docker info >/dev/null 2>&1 && break; sleep 5; done + docker info >/dev/null 2>&1 || { echo "ERROR: docker daemon unavailable"; exit 1; } +fi + +echo ">> Generating Containerfile" +docker run --rm -v "$PWD":/bluebuild -w /bluebuild ghcr.io/blue-build/cli:latest \ + bluebuild generate -o Containerfile ./recipes/bc250-console-gnome.yml + +echo ">> Building image" +docker buildx build -f Containerfile -t localhost/bc250-console-gnome:latest . + +# ------------------------------------------ transfer to containers-storage - +echo ">> Staging image for bootc-image-builder" +docker network create bibnet 2>/dev/null || true +docker rm -f bc250-registry >/dev/null 2>&1 || true +docker run -d --name bc250-registry --network bibnet -p 127.0.0.1:5000:5000 registry:2 >/dev/null +docker tag localhost/bc250-console-gnome:latest localhost:5000/bc250-console-gnome:latest +docker push -q localhost:5000/bc250-console-gnome:latest +$NATIVE volume create bib-storage2 >/dev/null +$NATIVE run --rm --privileged --network host \ + -v bib-storage2:/var/lib/containers/storage \ + quay.io/skopeo/stable:latest copy --src-tls-verify=false \ + docker://127.0.0.1:5000/bc250-console-gnome:latest \ + containers-storage:localhost/bc250-console-gnome:latest + +# terra repos reference GPG keys by file:// path; bib's depsolver needs them +if [ ! -d "$OUT/../rpm-gpg-keys" ]; then + echo ">> Extracting RPM GPG keys from image" + cid=$(docker create localhost/bc250-console-gnome:latest true) + docker cp "$cid":/etc/pki/rpm-gpg "$OUT/../rpm-gpg-keys" + docker rm "$cid" >/dev/null +fi + +# ------------------------------------------------------------------ ISO ---- +echo ">> Building ISO (this is the slow part)" +mkdir -p "$OUT" +$NATIVE run --rm --privileged --security-opt label=type:unconfined_t \ + -v "$OUT":/output \ + -v "$OUT/../rpm-gpg-keys":/etc/pki/rpm-gpg:ro \ + -v "$PWD/iso-config.generated.toml":/config.toml:ro \ + -v bib-storage2:/var/lib/containers/storage \ + quay.io/centos-bootc/bootc-image-builder:latest \ + --type iso --rootfs btrfs \ + localhost/bc250-console-gnome:latest + +$NATIVE run --rm -v "$OUT":/o alpine:latest chown -R "$(id -u):$(id -g)" /o +mv "$OUT/bootiso/install.iso" "$OUT/bootiso/bc250-console-gnome.iso" +( cd "$OUT/bootiso" && sha256sum bc250-console-gnome.iso > bc250-console-gnome.iso.sha256 ) + +docker rm -f bc250-registry >/dev/null + +echo +echo ">> DONE: $OUT/bootiso/bc250-console-gnome.iso" +echo ">> Flash it, or publish the image for OTA updates:" +echo ">> docker login git.lazypugs.com && docker tag localhost/bc250-console-gnome:latest git.lazypugs.com/ckoch/bc250-console-gnome:latest && docker push git.lazypugs.com/ckoch/bc250-console-gnome:latest" diff --git a/console.env.example b/console.env.example new file mode 100644 index 0000000..d8bc26f --- /dev/null +++ b/console.env.example @@ -0,0 +1,12 @@ +# Copy to console.env, fill in, then run ./build-console.sh +# console.env is git-ignored — it holds real passwords. NEVER commit it. +# +# Passwords: avoid double quotes and backslashes in them. + +KID_USER=kid +KID_DISPLAY="Kid" +KID_PASSWORD=changeme + +PARENT_USER=parent +PARENT_DISPLAY="Parent" +PARENT_PASSWORD=changeme diff --git a/files/console/etc/xdg/autostart/bc250-sunshine.desktop b/files/console/etc/xdg/autostart/bc250-sunshine.desktop new file mode 100644 index 0000000..96835f1 --- /dev/null +++ b/files/console/etc/xdg/autostart/bc250-sunshine.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=Application +Name=Sunshine streaming host (console autostart) +Comment=Serves the console session to Moonlight for remote admin +Exec=/usr/bin/bc250-sunshine-autostart +X-GNOME-Autostart-enabled=true +NoDisplay=true diff --git a/files/console/usr/bin/bc250-personalize b/files/console/usr/bin/bc250-personalize new file mode 100644 index 0000000..eb9bd2d --- /dev/null +++ b/files/console/usr/bin/bc250-personalize @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# One-time personalization from /etc/bc250-console.conf (written at image +# build time by build-console.sh): applies friendly display names to the +# accounts created by the installer. Stamps so it never re-runs — later +# on-box renames/chfn are never overwritten. +[ -r /etc/bc250-console.conf ] && . /etc/bc250-console.conf + +apply() { # $1 = user, $2 = display name + [ -n "$2" ] || return 0 + id "$1" >/dev/null 2>&1 || return 0 + chfn -f "$2" "$1" || true +} + +apply "${CONSOLE_KID_USER:-kid}" "${CONSOLE_KID_DISPLAY:-}" +apply "${CONSOLE_PARENT_USER:-parent}" "${CONSOLE_PARENT_DISPLAY:-}" + +mkdir -p /var/lib/bc250 +touch /var/lib/bc250/.personalized diff --git a/files/console/usr/bin/bc250-sunshine-autostart b/files/console/usr/bin/bc250-sunshine-autostart new file mode 100644 index 0000000..96df4bb --- /dev/null +++ b/files/console/usr/bin/bc250-sunshine-autostart @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# Start the Sunshine streaming host in the console (kid) account's session, +# so the parent can connect with Moonlight whenever the console is in use. +# Sunshine replaces RDP as the remote-admin path: GNOME RDP crashes mutter +# on the BC-250 (cursor-metadata screencast segfault, mutter 50.3). +[ -r /etc/bc250-console.conf ] && . /etc/bc250-console.conf +[ "$(id -un)" = "${CONSOLE_KID_USER:-kid}" ] || exit 0 +flatpak info dev.lizardbyte.app.Sunshine >/dev/null 2>&1 || exit 0 +exec systemctl --user enable --now app-dev.lizardbyte.app.Sunshine.service diff --git a/files/console/usr/lib/systemd/system/bc250-personalize.service b/files/console/usr/lib/systemd/system/bc250-personalize.service new file mode 100644 index 0000000..043203e --- /dev/null +++ b/files/console/usr/lib/systemd/system/bc250-personalize.service @@ -0,0 +1,10 @@ +[Unit] +Description=One-time console account personalization (display names) +ConditionPathExists=!/var/lib/bc250/.personalized + +[Service] +Type=oneshot +ExecStart=/usr/bin/bc250-personalize + +[Install] +WantedBy=multi-user.target diff --git a/files/console/usr/share/doc/bc250-console/OPERATIONS.md b/files/console/usr/share/doc/bc250-console/OPERATIONS.md index 5e73aaf..4ccb1dd 100644 --- a/files/console/usr/share/doc/bc250-console/OPERATIONS.md +++ b/files/console/usr/share/doc/bc250-console/OPERATIONS.md @@ -13,7 +13,16 @@ kid's password any time: `sudo passwd kid`. | | Command / client | Works when | |---|---|---| | SSH (terminal) | `ssh parent@` | Always (console is never asleep) | -| RDP (see the screen) | Any RDP client → `:3389` | Only while the kid's session is logged in | +| Moonlight (see + control the screen) | [Moonlight](https://moonlight-stream.org) → console → **Desktop** | Only while the kid's session is logged in | + +One-time Moonlight pairing: browse to `https://:47990`, +create the Sunshine admin login, then enter the PIN Moonlight shows you in +Sunshine's PIN tab. Sunshine starts automatically with the kid's session. + +> Why not RDP? GNOME Remote Desktop crashes the session on this GPU +> (mutter 50.x cursor-metadata screencast segfault on cyan-skillfish). +> `bc250-remote-setup` still configures it if you want to retry after a +> future mutter update — but Moonlight is smoother anyway. ``: the LAN IP, or (recommended) the Tailscale name/IP so it works from anywhere. Never port-forward 22/3389 on the router. diff --git a/recipes/bc250-console-gnome.yml b/recipes/bc250-console-gnome.yml index eaee5b6..c7f2c9e 100644 --- a/recipes/bc250-console-gnome.yml +++ b/recipes/bc250-console-gnome.yml @@ -24,7 +24,7 @@ modules: - type: script snippets: - - chmod 0755 /usr/bin/bc250-remote-setup /usr/bin/bc250-steam-bpm-autostart /usr/bin/bc250-gfn-install + - chmod 0755 /usr/bin/bc250-remote-setup /usr/bin/bc250-steam-bpm-autostart /usr/bin/bc250-gfn-install /usr/bin/bc250-sunshine-autostart /usr/bin/bc250-personalize - dconf update # compile the local.d/gdm.d console defaults into the dconf dbs - type: default-flatpaks@v1 @@ -39,6 +39,7 @@ modules: - com.heroicgameslauncher.hgl # Heroic: Epic + GOG - net.retrodeck.retrodeck # RetroDECK: retro emulation - org.prismlauncher.PrismLauncher # Minecraft (Java; sign in with Microsoft account) + - dev.lizardbyte.app.Sunshine # game-stream host: parent remote admin via Moonlight (RDP blocked by mutter bug on GFX1013) - type: systemd system: @@ -47,6 +48,7 @@ modules: - sshd.service - tailscaled.service # idle until 'sudo tailscale up' - bc250-gfn-install.service # GeForce NOW beta from NVIDIA's flatpak repo (first boot) + - bc250-personalize.service # one-time display names from /etc/bc250-console.conf masked: - hhd.service # Handheld Daemon crash-loops on BC-250 # Always-on console: hard-block every suspend path (the box must