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>
This commit is contained in:
Executable
+95
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# 40-shell-mcp — build and start the NATIVE shell-mcp on this host.
|
||||
#
|
||||
# This replaces the mediabox-mcp SSH proxy that currently runs on arrsstack.
|
||||
# Once this is up and NPM proxy host 42 is repointed to 10.0.1.20:8103, the
|
||||
# proxy container and its SSH key get deleted. The public URL, the LE cert and
|
||||
# the Claude connector entry do not change.
|
||||
#
|
||||
# BUILDS LOCALLY. It does not pull an image. The arrsstack image is arm64 and
|
||||
# would not run here; and there is no registry in this homelab to pull from.
|
||||
# amd64 buildability was verified before this was written — the full Python
|
||||
# dependency tree resolves to prebuilt manylinux x86_64 wheels, so no compiler
|
||||
# is required and the build takes about a minute.
|
||||
# =============================================================================
|
||||
set -uo pipefail
|
||||
|
||||
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
SRC="$REPO_DIR/shell-mcp"
|
||||
DEST="/srv/shell-mcp"
|
||||
ENVFILE="/srv/secrets/stacks/shell-mcp.env"
|
||||
MASTER="/srv/secrets/stacks.env"
|
||||
|
||||
command -v docker >/dev/null 2>&1 || { echo " [FAIL] docker not installed"; exit 1; }
|
||||
|
||||
# --- resolve the bearer token ------------------------------------------------
|
||||
# Reuses the SAME token arrsstack's mediabox-mcp serves today, so the Claude
|
||||
# connector entry survives the cutover untouched.
|
||||
TOKEN="$(grep -E '^MEDIABOX_MCP_BEARER_TOKEN=' "$MASTER" 2>/dev/null | head -1 | cut -d= -f2-)"
|
||||
if [ -z "${TOKEN:-}" ] || [ "$TOKEN" = "FILL_ME" ]; then
|
||||
cat <<'MSG'
|
||||
[SKIP] MEDIABOX_MCP_BEARER_TOKEN is not set in /srv/secrets/stacks.env.
|
||||
|
||||
Copy the existing value from arrsstack so the connector keeps working:
|
||||
# on arrsstack
|
||||
grep '^MEDIABOX_MCP_BEARER_TOKEN=' /srv/secrets/stacks.env
|
||||
then put it in this host's /srv/secrets/stacks.env and re-run:
|
||||
sudo /srv/mediabox-bootstrap/scripts/40-shell-mcp.sh
|
||||
MSG
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# --- stage source ------------------------------------------------------------
|
||||
install -d -m 0755 "$DEST"
|
||||
install -m 0644 "$SRC/server.py" "$DEST/server.py"
|
||||
install -m 0644 "$SRC/requirements.txt" "$DEST/requirements.txt"
|
||||
install -m 0644 "$SRC/Dockerfile" "$DEST/Dockerfile"
|
||||
install -m 0644 "$SRC/docker-compose.yml" "$DEST/docker-compose.yml"
|
||||
|
||||
# --- per-stack env slice (same convention as arrsstack) ----------------------
|
||||
install -d -m 0700 /srv/secrets/stacks
|
||||
umask 077
|
||||
{
|
||||
printf 'MEDIABOX_MCP_BEARER_TOKEN=%s\n' "$TOKEN"
|
||||
printf 'TZ=%s\n' "$(grep -E '^TZ=' "$MASTER" | head -1 | cut -d= -f2- || echo America/New_York)"
|
||||
} > "$ENVFILE"
|
||||
chmod 600 "$ENVFILE"
|
||||
|
||||
# --- build & start -----------------------------------------------------------
|
||||
echo " building shell-mcp for amd64 (local build, no pull)"
|
||||
cd "$DEST" || exit 1
|
||||
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
. "$ENVFILE"
|
||||
set +a
|
||||
|
||||
if ! docker compose up -d --build; then
|
||||
echo " [FAIL] build/start failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- verify ------------------------------------------------------------------
|
||||
echo " waiting for health endpoint"
|
||||
for i in $(seq 1 30); do
|
||||
if curl -fsS -m 3 "http://127.0.0.1:8103/health" >/dev/null 2>&1; then
|
||||
echo " [ok] /health responding: $(curl -fsS -m 3 http://127.0.0.1:8103/health)"
|
||||
|
||||
# An unauthenticated /sse MUST be rejected. If this ever returns 200 the
|
||||
# box is publicly shell-able through NPM.
|
||||
code="$(curl -s -o /dev/null -w '%{http_code}' -m 5 "http://127.0.0.1:8103/sse" || true)"
|
||||
if [ "$code" = "401" ]; then
|
||||
echo " [ok] /sse rejects unauthenticated requests (401)"
|
||||
else
|
||||
echo " [FAIL] /sse returned $code without a token — DO NOT repoint NPM until fixed"
|
||||
exit 1
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
echo " [FAIL] health endpoint never came up"
|
||||
docker compose logs --tail 40 shell-mcp 2>&1 | sed 's/^/ /'
|
||||
exit 1
|
||||
Reference in New Issue
Block a user