Files
mediabox-bootstrap/bootstrap.sh
T
ClaudeandClaude Opus 5 7eadcdf9e8 Bootstrap tree for the media box Linux conversion
Autoinstall lays down a thin base (sshd, key, DHCP, Docker CE, /srv) and hands
off to this repo on first boot. Everything interesting stays in git so it is
reviewable and re-runnable, rather than frozen onto a USB nobody can diff.

Stages, all idempotent:
  00-preflight  asserts hardware/BIOS state, changes nothing. Catches a BIOS
                update having silently re-enabled Secure Boot, which would stop
                the NVIDIA DKMS module loading on a box with no keyboard.
  10-secrets    ADD-ONLY seeder for /srv/secrets/stacks.env. Never overwrites an
                existing key. Verified against a pre-populated file: existing
                values, unrelated keys, the operator tier and existing manifest
                lines all survive byte-for-byte; a second run is a no-op.
  20-cifs       the 8 shares Plex actually uses (Share is excluded, it is not a
                library root). \040 escaping, nofail + x-systemd.automount +
                _netdev. Managed-block rewrite verified not to duplicate or to
                drop the root fstab entry.
  30-nvidia     nvidia-driver-580 explicitly: 580 is the LAST branch supporting
                Pascal, and the -open modules need Turing+. Pins against newer
                branches. Not in late-commands because DKMS needs the installed
                kernel, not the installer's.
  40-shell-mcp  builds the native MCP locally for amd64; refuses to finish
                unless /sse returns 401 without a token.
  50-plex       run by hand: PLEX_CLAIM expires in 4 minutes. Refuses to start
                against missing mounts and disables autoEmptyTrash, which with
                read-write NAS credentials is the most dangerous default here.

shell-mcp was built on arm64 originally. It builds clean on amd64 (whole dep
tree resolves to prebuilt manylinux x86_64 wheels, no compiler needed), but
dependencies are now pinned - the original installed mcp/starlette/uvicorn
unpinned and starlette has since gone 1.x. Port moved to 8103 so NPM host 42
can simply be repointed, and the tool description now says media box rather
than arrsstack.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 18:12:44 -04:00

93 lines
3.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# =============================================================================
# mediabox-bootstrap — first-boot orchestrator
#
# Runs ONCE from mediabox-firstboot.service, but every stage is idempotent so
# you can re-run this by hand at any time:
# sudo /srv/mediabox-bootstrap/bootstrap.sh
#
# Or run a single stage:
# sudo /srv/mediabox-bootstrap/scripts/20-cifs.sh
#
# DESIGN RULE: no stage may leave the box unbootable or unreachable. Every
# stage that can fail, fails soft and logs. sshd is already up by the time
# this runs; keeping it that way is the whole safety net on a headless box.
# =============================================================================
set -uo pipefail
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
STATE_DIR="/var/lib/mediabox"
LOG="/var/log/mediabox-bootstrap.log"
mkdir -p "$STATE_DIR"
exec > >(tee -a "$LOG") 2>&1
say() { printf '\n\033[1;36m=== %s\033[0m\n' "$*"; }
ok() { printf '\033[1;32m [ok]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m [warn]\033[0m %s\n' "$*"; }
fail() { printf '\033[1;31m [FAIL]\033[0m %s\n' "$*"; }
[ "$(id -u)" -eq 0 ] || { fail "must run as root"; exit 1; }
FAILED=()
run_stage() {
local script="$1" name
name="$(basename "$script")"
say "$name"
if [ ! -x "$script" ]; then chmod +x "$script" 2>/dev/null || true; fi
if "$script"; then
ok "$name complete"
else
fail "$name exited $? — continuing (see $LOG)"
FAILED+=("$name")
fi
}
say "mediabox bootstrap starting $(date -Is)"
echo "host: $(hostname) kernel: $(uname -r) ip: $(hostname -I)"
run_stage "$REPO_DIR/scripts/00-preflight.sh"
run_stage "$REPO_DIR/scripts/10-secrets.sh"
run_stage "$REPO_DIR/scripts/20-cifs.sh"
run_stage "$REPO_DIR/scripts/30-nvidia.sh"
run_stage "$REPO_DIR/scripts/40-shell-mcp.sh"
say "bootstrap summary"
if [ ${#FAILED[@]} -eq 0 ]; then
ok "all stages completed"
touch "$STATE_DIR/firstboot.done"
else
warn "stages needing attention: ${FAILED[*]}"
warn "NOT marking first-boot done — fix and re-run $0"
fi
cat <<'NEXT'
--------------------------------------------------------------------------
REMAINING STEPS — deliberately NOT automated
--------------------------------------------------------------------------
1. Write the NAS password into the credentials file (over the MCP, never
onto the USB):
printf 'username=<nas-user>\npassword=<nas-pass>\ndomain=WORKGROUP\n' \
> /etc/cifs/korval.cred
chmod 600 /etc/cifs/korval.cred
systemctl daemon-reload
/srv/mediabox-bootstrap/scripts/20-cifs.sh --verify
2. Verify Quick Sync before deploying Plex:
vainfo --display drm --device /dev/dri/renderD128
3. Deploy Plex. PLEX_CLAIM expires in 4 minutes, so this is human-in-the-loop:
# get a fresh token from https://plex.tv/claim THEN immediately:
PLEX_CLAIM=claim-xxxxx /srv/mediabox-bootstrap/scripts/50-plex.sh
4. Repoint NPM proxy host 42 from mediabox-mcp:8103 to 10.0.1.20:8103,
then delete the mediabox-mcp container and its key on arrsstack.
5. Leave D: alone until the soak is done.
--------------------------------------------------------------------------
NEXT
exit 0