Fix/pod public html bind mount (#30)

* feat(pods/admin): root admin alias, default-caps cleanup, pod rebuild script

- auth: add `root` as an admin-console route alias (alongside admin/sysop);
  still gated by $AGENTBBS_ADMINS — the name confers nothing on its own.
- pods: drop the now-redundant tuneApt apt-sandbox hack. Rootless podman keeps
  its default capability set, so apt/chown/su work without disabling the
  download sandbox.
- scripts/rebuild-pods.sh: recreate all member pods (keeps home volumes) so
  they pick up the current container profile on next `ssh pod@`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(arcade,ui): 80s arcade classics + shared menu theme

Arcade games (PRD §5.1), generalizing the sandboxed-PTY DOOM path into an
external-game registry: Space Invaders (nInvaders), Pac-Man (pacman4console),
Tetris (tint/vitetris), Moon Patrol (moon-buggy). Binaries resolve from
assets/bin, PATH, then /usr/games, so a distro install or a hand-built binary
lights each game up; missing games are skipped with a discovery hint. Installed
on the host via `scripts/fetch-assets.sh --arcade` (apt), wired into setup.sh
behind FETCH_ARCADE (default on).

UI: new shared ui.Theme.MenuItem widget (accent cursor + badge, description
shown only for the focused row) adopted by the hub and arcade menus; the hub
groups rows under Features/Sessions headers and the arcade under
DOOM/ARCADE/BUILT-IN.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Anthony Ettinger 2026-06-15 08:06:32 -07:00 committed by GitHub
parent 362b47fdde
commit e0a267e343
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 539 additions and 138 deletions

View file

@ -3,10 +3,13 @@
# ./assets — Freedoom by default (PRD §9.1). Run on the host before enabling
# the arcade's DOOM entries.
#
# scripts/fetch-assets.sh [--shareware]
# scripts/fetch-assets.sh [--shareware] [--arcade]
#
# --shareware additionally fetches the freely redistributable doom1.wad
# shareware episode.
# shareware episode.
# --arcade installs the 80s arcade classics (Space Invaders, Pac-Man, Tetris,
# Moon Patrol) the arcade plugin launches via the same sandboxed-PTY path as
# DOOM. Needs apt + sudo (Debian/Ubuntu); the menu lists whatever installs.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
@ -14,6 +17,16 @@ ASSETS="$ROOT/assets"
BUILD="$ROOT/.build"
FREEDOOM_VERSION="${FREEDOOM_VERSION:-0.13.0}"
want_shareware=0
want_arcade=0
for arg in "$@"; do
case "$arg" in
--shareware) want_shareware=1 ;;
--arcade) want_arcade=1 ;;
*) echo "!! unknown flag: $arg (use --shareware and/or --arcade)" >&2; exit 2 ;;
esac
done
mkdir -p "$ASSETS/bin" "$ASSETS/wads" "$BUILD"
# --- doom-ascii -------------------------------------------------------------
@ -47,12 +60,39 @@ else
fi
# --- Doom shareware (optional) ----------------------------------------------
if [ "${1:-}" = "--shareware" ] && [ ! -f "$ASSETS/wads/doom1.wad" ]; then
if [ "$want_shareware" = 1 ] && [ ! -f "$ASSETS/wads/doom1.wad" ]; then
echo ">> fetching Doom shareware episode"
curl -fsSL -o "$ASSETS/wads/doom1.wad" \
"https://distro.ibiblio.org/slitaz/sources/packages/d/doom1.wad"
echo ">> installed doom1.wad (shareware)"
fi
# --- Arcade classics (optional) ---------------------------------------------
# Tiny, well-packaged ncurses C programs from the distro (Debian/Ubuntu
# universe). They land in /usr/games, which the arcade plugin probes alongside
# assets/bin and PATH. The arcade menu lists whichever of these is present.
ARCADE_PKGS="ninvaders pacman4console moon-buggy tint"
if [ "$want_arcade" = 1 ]; then
if command -v apt-get >/dev/null 2>&1; then
SUDO=""
[ "$(id -u)" -ne 0 ] && SUDO="sudo"
echo ">> installing arcade classics: $ARCADE_PKGS"
$SUDO apt-get update -y
# Install individually so one missing package doesn't abort the rest.
for pkg in $ARCADE_PKGS; do
$SUDO apt-get install -y "$pkg" || echo "!! $pkg not available; skipping"
done
else
echo "!! --arcade needs apt-get (Debian/Ubuntu)." >&2
echo " On other distros, install equivalents of: $ARCADE_PKGS" >&2
fi
fi
echo ">> done. WADs:"
ls -l "$ASSETS/wads"
echo ">> arcade classics on host:"
for bin in ninvaders pacman4console moon-buggy tint vitetris; do
p="$(command -v "$bin" 2>/dev/null || true)"
[ -z "$p" ] && [ -x "/usr/games/$bin" ] && p="/usr/games/$bin"
[ -n "$p" ] && echo " $bin -> $p"
done

63
scripts/rebuild-pods.sh Executable file
View file

@ -0,0 +1,63 @@
#!/usr/bin/env bash
# Rebuild every AgentBBS member pod so it picks up the current container profile
# (e.g. the rootless-podman default capability set added for apt/chown/su/:80).
#
# It removes each pod CONTAINER but keeps that pod's named home volume
# (agentbbs-pod-<name>-home) and the host-side public_html, so member data and
# websites are untouched. Pods are recreated automatically — with the new
# profile — the next time each member runs `ssh pod@<host>`. Caddy serves
# public_html from the host, so sites stay up while a pod is briefly down.
#
# Anything a member installed into the pod's system rootfs (apt packages, etc.)
# is lost on rebuild; only /home/dev and public_html persist.
#
# Run this as the user that owns the pods. For rootless podman that's the
# AgentBBS service user (pods are per-user), not necessarily root.
#
# Usage:
# scripts/rebuild-pods.sh # list, then prompt before removing
# scripts/rebuild-pods.sh --yes # non-interactive (for cron/deploy)
# AGENTBBS_POD_ENGINE=docker scripts/rebuild-pods.sh # force engine
set -euo pipefail
ENGINE="${AGENTBBS_POD_ENGINE:-}"
if [ -z "$ENGINE" ]; then
if command -v podman >/dev/null 2>&1; then
ENGINE=podman
elif command -v docker >/dev/null 2>&1; then
ENGINE=docker
else
echo "rebuild-pods: neither podman nor docker found" >&2
exit 1
fi
fi
mapfile -t pods < <("$ENGINE" ps -a --filter 'name=agentbbs-pod-' --format '{{.Names}}' | sort)
if [ "${#pods[@]}" -eq 0 ]; then
echo "rebuild-pods: no pods found (engine: $ENGINE)"
exit 0
fi
echo "Found ${#pods[@]} pod(s) via $ENGINE:"
printf ' %s\n' "${pods[@]}"
if [ "${1:-}" != "--yes" ] && [ "${1:-}" != "-y" ]; then
printf 'Remove these containers (home volumes kept)? [y/N] '
read -r reply
case "$reply" in
y | Y | yes | YES) ;;
*)
echo "aborted"
exit 0
;;
esac
fi
for p in "${pods[@]}"; do
# No -v: named home volumes are preserved, only the container is destroyed.
"$ENGINE" rm -f "$p" >/dev/null && echo "removed $p"
done
echo
echo "Done. Each pod recreates with the new profile on its owner's next 'ssh pod@'."