Bake Sunshine remote admin + add one-command configured ISO builder
- Sunshine flatpak in first-boot list; autostarts with the console account's session (bc250-sunshine-autostart, honors CONSOLE_KID_USER) - bc250-personalize: one-shot display names from /etc/bc250-console.conf - build-console.sh + console.env(.example): fill in usernames/passwords/ display names, get a fully personalized ISO — no post-install steps beyond per-account logins. Generated files git-ignored (credentials) - Docs: Moonlight is the remote-admin path; RDP marked blocked by the mutter cursor-metadata screencast crash on GFX1013 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,3 +3,8 @@ __pycache__/
|
|||||||
release-preview/
|
release-preview/
|
||||||
/.bluebuild-scripts_*
|
/.bluebuild-scripts_*
|
||||||
/Containerfile
|
/Containerfile
|
||||||
|
|
||||||
|
# build-console.sh generated files (contain credentials/machine-specific)
|
||||||
|
/console.env
|
||||||
|
/iso-config.generated.toml
|
||||||
|
/files/console/etc/bc250-console.conf
|
||||||
|
|||||||
@@ -1,5 +1,20 @@
|
|||||||
# BC-250 Console — local build → ISO
|
# 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
|
Custom recipe: `recipes/bc250-console-gnome.yml` (Bazzite GNOME + cyan-skillfish
|
||||||
governor + SSH/RDP remote admin + BC-250 ttm kargs). Build path is **local**
|
governor + SSH/RDP remote admin + BC-250 ttm kargs). Build path is **local**
|
||||||
(no CI, no registry push, no cosign needed).
|
(no CI, no registry push, no cosign needed).
|
||||||
|
|||||||
+10
-1
@@ -13,7 +13,16 @@ kid's password any time: `sudo passwd kid`.
|
|||||||
| | Command / client | Works when |
|
| | Command / client | Works when |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| SSH (terminal) | `ssh parent@<console-address>` | Always (console is never asleep) |
|
| SSH (terminal) | `ssh parent@<console-address>` | Always (console is never asleep) |
|
||||||
| RDP (see the screen) | Any RDP client → `<console-address>: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://<console-address>: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.
|
||||||
|
|
||||||
`<console-address>`: the LAN IP, or (recommended) the Tailscale name/IP so it
|
`<console-address>`: the LAN IP, or (recommended) the Tailscale name/IP so it
|
||||||
works from anywhere. Never port-forward 22/3389 on the router.
|
works from anywhere. Never port-forward 22/3389 on the router.
|
||||||
|
|||||||
@@ -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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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
|
## Accounts
|
||||||
|
|
||||||
The ISO installs **unattended** and bakes two users from
|
Build with [`build-console.sh`](build-console.sh) + your `console.env` to
|
||||||
[`iso-config.toml`](iso-config.toml): `parent` (admin/wheel) and `kid`
|
bake real usernames, passwords, and display names directly into the ISO
|
||||||
(standard), both with a placeholder password to change on first login.
|
(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
|
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`.
|
Big Picture autostart follows `CONSOLE_KID_USER` in `/etc/bc250-console.conf`.
|
||||||
|
|
||||||
|
|||||||
Executable
+117
@@ -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 <<EOF
|
||||||
|
# GENERATED by build-console.sh from console.env — git-ignored, do not commit.
|
||||||
|
[[customizations.user]]
|
||||||
|
name = "$PARENT_USER"
|
||||||
|
password = "$PARENT_PASSWORD"
|
||||||
|
groups = ["wheel"]
|
||||||
|
|
||||||
|
[[customizations.user]]
|
||||||
|
name = "$KID_USER"
|
||||||
|
password = "$KID_PASSWORD"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
mkdir -p files/console/etc
|
||||||
|
cat > files/console/etc/bc250-console.conf <<EOF
|
||||||
|
# GENERATED by build-console.sh — which accounts get the console treatment.
|
||||||
|
CONSOLE_KID_USER=$KID_USER
|
||||||
|
CONSOLE_KID_DISPLAY="${KID_DISPLAY:-}"
|
||||||
|
CONSOLE_PARENT_USER=$PARENT_USER
|
||||||
|
CONSOLE_PARENT_DISPLAY="${PARENT_DISPLAY:-}"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- image ----
|
||||||
|
if ! docker info >/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"
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -13,7 +13,16 @@ kid's password any time: `sudo passwd kid`.
|
|||||||
| | Command / client | Works when |
|
| | Command / client | Works when |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| SSH (terminal) | `ssh parent@<console-address>` | Always (console is never asleep) |
|
| SSH (terminal) | `ssh parent@<console-address>` | Always (console is never asleep) |
|
||||||
| RDP (see the screen) | Any RDP client → `<console-address>: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://<console-address>: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.
|
||||||
|
|
||||||
`<console-address>`: the LAN IP, or (recommended) the Tailscale name/IP so it
|
`<console-address>`: the LAN IP, or (recommended) the Tailscale name/IP so it
|
||||||
works from anywhere. Never port-forward 22/3389 on the router.
|
works from anywhere. Never port-forward 22/3389 on the router.
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ modules:
|
|||||||
|
|
||||||
- type: script
|
- type: script
|
||||||
snippets:
|
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
|
- dconf update # compile the local.d/gdm.d console defaults into the dconf dbs
|
||||||
|
|
||||||
- type: default-flatpaks@v1
|
- type: default-flatpaks@v1
|
||||||
@@ -39,6 +39,7 @@ modules:
|
|||||||
- com.heroicgameslauncher.hgl # Heroic: Epic + GOG
|
- com.heroicgameslauncher.hgl # Heroic: Epic + GOG
|
||||||
- net.retrodeck.retrodeck # RetroDECK: retro emulation
|
- net.retrodeck.retrodeck # RetroDECK: retro emulation
|
||||||
- org.prismlauncher.PrismLauncher # Minecraft (Java; sign in with Microsoft account)
|
- 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
|
- type: systemd
|
||||||
system:
|
system:
|
||||||
@@ -47,6 +48,7 @@ modules:
|
|||||||
- sshd.service
|
- sshd.service
|
||||||
- tailscaled.service # idle until 'sudo tailscale up'
|
- tailscaled.service # idle until 'sudo tailscale up'
|
||||||
- bc250-gfn-install.service # GeForce NOW beta from NVIDIA's flatpak repo (first boot)
|
- 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:
|
masked:
|
||||||
- hhd.service # Handheld Daemon crash-loops on BC-250
|
- hhd.service # Handheld Daemon crash-loops on BC-250
|
||||||
# Always-on console: hard-block every suspend path (the box must
|
# Always-on console: hard-block every suspend path (the box must
|
||||||
|
|||||||
Reference in New Issue
Block a user