#!/usr/bin/env bash
# install-jain.sh — key-gated jain installer.
#
# Pulls the signed jain-sagemaker container, verifies its signature + digest, runs a
# GPU-friendly health check, and (unless --verify-only) launches the local web cockpit.
#
# This script is served ONLY to callers holding a valid installer key:
#     curl -fsSL "https://www.neverhuman.org/api/install/jain.sh?key=<KEY>" | bash
# Request a key from the jain team (jepson@veox.ai).
#
# Composed from three precedents:
#   * OS/arch detection             — jankurai-installer.sh
#   * key-gated signed-fetch shape  — veox-nht ops/install/install.sh
#   * docker pull + GPU-probe +      — jain ops/ci/smoke-image.sh
#     `jain doctor` health          + setup_docker_gpu.sh (Ubuntu docker-ce path)
#
# shellcheck disable=SC2312   # we intentionally read `command -v` in conditionals

# Re-exec under bash when this script is started by a POSIX sh. On Debian/Ubuntu
# `/bin/sh` is dash, which lacks `set -o pipefail`, arrays and [[ ]], so a plain
# `curl … | sh` would die on line one ("Illegal option -o pipefail"). Detecting no
# $BASH_VERSION, we re-run under bash — so BOTH `curl … | sh` and `curl … | bash`
# work on macOS, Ubuntu, and anywhere bash is installed. (macOS /bin/sh is bash in
# POSIX mode and DOES set $BASH_VERSION, so this never triggers there.)
if [ -z "${BASH_VERSION:-}" ]; then
  # Prefer re-execing a real local file (offline-safe, preserves args). Only when
  # run from a pipe ($0 is sh/bash/-sh) do we re-fetch the source.
  if [ -r "$0" ] && [ "$0" != sh ] && [ "$0" != -sh ] && [ "$0" != bash ] && [ "$0" != -bash ]; then
    exec bash "$0" "$@"
  fi
  _jain_url="${JAIN_INSTALLER_URL:-https://www.neverhuman.org/install-jain.sh}"
  if command -v curl >/dev/null 2>&1; then
    _jain_src="$(curl -fsSL "$_jain_url")"
  elif command -v wget >/dev/null 2>&1; then
    _jain_src="$(wget -qO- "$_jain_url")"
  else
    echo "install-jain.sh: bash is required, and re-running needs curl or wget" >&2
    exit 1
  fi
  if [ -z "$_jain_src" ]; then
    echo "install-jain.sh: failed to re-download the installer from $_jain_url" >&2
    exit 1
  fi
  exec bash -c "$_jain_src" bash "$@"
fi

set -euo pipefail

# ── Configuration (all overridable by env) ───────────────────────────────────
REGISTRY="${JAIN_REGISTRY:-image.neverhuman.org/doug/jain_small/jain-sagemaker}"
VERSION="${JAIN_VERSION:-latest}"
IMAGE="${JAIN_IMAGE:-${REGISTRY}:${VERSION}}"
# cosign public key. Default: the key EMBEDDED in this script (see embedded_cosign_key
# below) so we do NOT fetch the trust root from the same origin as the script.
# Override with a local path or URL via JAIN_COSIGN_PUBLIC_KEY.
COSIGN_PUBLIC_KEY="${JAIN_COSIGN_PUBLIC_KEY:-}"
# Optional pinned digest (sha256:...) — when set, the pulled image digest must match.
EXPECTED_DIGEST="${JAIN_EXPECTED_DIGEST:-}"
# Set to 1 (env JAIN_SKIP_COSIGN=1 or flag --skip-verify) to skip cosign signature
# verification and trust the image by digest alone. Opt-in, not recommended.
SKIP_COSIGN="${JAIN_SKIP_COSIGN:-0}"
WEB_ENV_FILE="${JAIN_WEB_ENV_FILE:-}"
WEB_PORT="${JAIN_WEB_PORT:-}"
WEB_HOST_BIND="${JAIN_WEB_HOST_BIND:-}"
WEB_CONTAINER_HOST="${JAIN_WEB_CONTAINER_HOST:-}"

VERIFY_ONLY=0

# ── Pretty output ────────────────────────────────────────────────────────────
if [[ -t 1 ]]; then
  C_RED=$'\033[0;31m'; C_GRN=$'\033[0;32m'; C_YLW=$'\033[1;33m'
  C_CYN=$'\033[0;36m'; C_NC=$'\033[0m'
else
  C_RED=''; C_GRN=''; C_YLW=''; C_CYN=''; C_NC=''
fi

step() { printf '\n%s==>%s %s\n' "$C_CYN" "$C_NC" "$1"; }
ok()   { printf '  %s✓%s %s\n' "$C_GRN" "$C_NC" "$1"; }
warn() { printf '  %s!%s %s\n' "$C_YLW" "$C_NC" "$1"; }
note() { printf '  · %s\n' "$1"; }
fail() { printf '\n%s✗ %s%s\n' "$C_RED" "$1" "$C_NC" >&2; exit 1; }

env_file_value() {
  local key="$1" default="$2" line value
  if [[ -n "$WEB_ENV_FILE" && -f "$WEB_ENV_FILE" ]]; then
    # `|| [[ -n "$line" ]]` so a final line without a trailing newline is not dropped.
    while IFS= read -r line || [[ -n "$line" ]]; do
      line="${line%%#*}"
      [[ "$line" =~ ^[[:space:]]*${key}[[:space:]]*= ]] || continue
      value="${line#*=}"
      value="${value#"${value%%[![:space:]]*}"}"
      value="${value%"${value##*[![:space:]]}"}"
      value="${value%\"}"; value="${value#\"}"
      value="${value%\'}"; value="${value#\'}"
      printf '%s\n' "$value"
      return 0
    done < "$WEB_ENV_FILE"
  fi
  printf '%s\n' "$default"
}

validate_port() {
  local name="$1" value="$2"
  if [[ ! "$value" =~ ^[0-9]+$ ]] || (( value < 1 || value > 65535 )); then
    fail "${name} must be a TCP port between 1 and 65535 (got: ${value})"
  fi
}

validate_bind_host() {
  local name="$1" value="$2"
  if [[ -z "$value" || "$value" =~ [[:space:]] ]]; then
    fail "${name} must be a non-empty host/address without whitespace"
  fi
}

load_web_defaults() {
  if [[ -n "$WEB_ENV_FILE" && ! -f "$WEB_ENV_FILE" ]]; then
    fail "JAIN_WEB_ENV_FILE does not exist: ${WEB_ENV_FILE}"
  fi
  WEB_PORT="${WEB_PORT:-$(env_file_value JAIN_WEB_PORT 4180)}"
  WEB_HOST_BIND="${WEB_HOST_BIND:-$(env_file_value JAIN_WEB_HOST_BIND 127.0.0.1)}"
  WEB_CONTAINER_HOST="${WEB_CONTAINER_HOST:-$(env_file_value JAIN_WEB_CONTAINER_HOST 0.0.0.0)}"
  validate_port JAIN_WEB_PORT "$WEB_PORT"
  validate_bind_host JAIN_WEB_HOST_BIND "$WEB_HOST_BIND"
  validate_bind_host JAIN_WEB_CONTAINER_HOST "$WEB_CONTAINER_HOST"
}

usage() {
  cat <<'EOF'
usage: install-jain.sh [--verify-only] [--version X.Y.Z] [--port PORT] [--listen HOST] [--help]

  --verify-only   Detect OS/runtime, pull, verify signature+digest and run the
                  health check, but do NOT launch the cockpit (for CI).
  --skip-verify   Skip cosign signature verification (trust the image by digest
                  only). Not recommended; also settable via JAIN_SKIP_COSIGN=1.
  --version VER   Override the image tag (default: latest).
  --port PORT     Publish the cockpit on this TCP port (default: 4180).
  --listen HOST   Host interface Docker publishes on (default: 127.0.0.1).
                  Use 0.0.0.0 only when the cockpit should be reachable off-machine.
  --container-host HOST
                  Bind host inside the container (default: 0.0.0.0).
  --env-file PATH Read non-secret web defaults from PATH.
  --help          Show this help.

Environment overrides:
  JAIN_IMAGE               full image ref (default: <registry>:<version>)
  JAIN_VERSION             image tag
  JAIN_COSIGN_PUBLIC_KEY   cosign public key (path or URL)
  JAIN_EXPECTED_DIGEST     pin the pulled image to this sha256:... digest
  JAIN_WEB_ENV_FILE        optional non-secret env file for web defaults
  JAIN_WEB_HOST_BIND       host interface Docker publishes on (default: 127.0.0.1)
  JAIN_WEB_CONTAINER_HOST  bind host inside the container (default: 0.0.0.0)
  JAIN_WEB_PORT            cockpit port on launch (default: 4180)
EOF
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --verify-only) VERIFY_ONLY=1; shift ;;
    --skip-verify|--insecure-skip-verify) SKIP_COSIGN=1; shift ;;
    --version) VERSION="${2:?--version needs a value}"; IMAGE="${JAIN_IMAGE:-${REGISTRY}:${VERSION}}"; shift 2 ;;
    --port) WEB_PORT="${2:?--port needs a value}"; shift 2 ;;
    --listen) WEB_HOST_BIND="${2:?--listen needs a value}"; shift 2 ;;
    --container-host) WEB_CONTAINER_HOST="${2:?--container-host needs a value}"; shift 2 ;;
    --env-file) WEB_ENV_FILE="${2:?--env-file needs a value}"; shift 2 ;;
    -h|--help) usage; exit 0 ;;
    *) fail "unknown argument: $1 (try --help)" ;;
  esac
done

# ── (a) Detect OS / arch, friendly-fail on unsupported ───────────────────────
detect_os_arch() {
  step "Detecting platform"
  case "$(uname -s)" in
    Darwin) OS="darwin";  OS_PRETTY="macOS" ;;
    Linux)  OS="linux";   OS_PRETTY="Linux" ;;
    *) fail "unsupported operating system: $(uname -s) (jain supports macOS and Linux)" ;;
  esac
  case "$(uname -m)" in
    x86_64|amd64)   ARCH="x86_64" ;;
    arm64|aarch64)  ARCH="aarch64" ;;
    *) fail "unsupported architecture: $(uname -m) (jain supports x86_64 and aarch64)" ;;
  esac
  ok "${OS_PRETTY} / ${ARCH}"
}

# ── (b) Detect docker OR podman; if neither, print OS-specific help + exit 0 ──
print_runtime_help() {
  step "No container runtime found"
  warn "jain runs inside a container; install Docker or Podman first, then re-run this installer."
  case "$OS" in
    darwin)
      note "macOS — install Docker Desktop:"
      note "  https://docs.docker.com/desktop/install/mac-install/"
      note "Or Podman Desktop:"
      note "  https://podman-desktop.io/downloads/macos"
      ;;
    linux)
      note "Ubuntu/Debian — install Docker Engine (docker-ce):"
      note "  sudo apt-get update"
      note "  sudo apt-get install -y ca-certificates curl gnupg"
      note "  sudo install -m 0755 -d /etc/apt/keyrings"
      note "  curl -fsSL https://download.docker.com/linux/ubuntu/gpg \\"
      note "    -o /etc/apt/keyrings/docker.asc && sudo chmod a+r /etc/apt/keyrings/docker.asc"
      note "  echo \"deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \\"
      note "    https://download.docker.com/linux/ubuntu \$(. /etc/os-release && echo \$VERSION_CODENAME) stable\" \\"
      note "    | sudo tee /etc/apt/sources.list.d/docker.list"
      note "  sudo apt-get update && sudo apt-get install -y docker-ce docker-ce-cli containerd.io"
      note "Docker install docs:  https://docs.docker.com/engine/install/ubuntu/"
      note "For NVIDIA GPU support (optional): https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html"
      note "Podman docs:          https://podman.io/docs/installation"
      ;;
  esac
  note "This is guidance, not an error — install a runtime and run the installer again."
}

detect_runtime() {
  step "Detecting container runtime"
  if command -v docker >/dev/null 2>&1; then
    RUNTIME="docker"
    ok "docker found: $(command -v docker)"
  elif command -v podman >/dev/null 2>&1; then
    RUNTIME="podman"
    ok "podman found: $(command -v podman)"
  else
    print_runtime_help
    exit 0
  fi
}

# ── (c) Pull the image ───────────────────────────────────────────────────────
pull_image() {
  step "Pulling ${IMAGE}"
  # Always pull so `:latest` (and any moving tag) actually updates; docker/podman
  # pull is idempotent and prints "up to date" when the local copy is current.
  if "$RUNTIME" pull "$IMAGE"; then
    ok "pulled ${IMAGE}"
  elif "$RUNTIME" image inspect "$IMAGE" >/dev/null 2>&1; then
    warn "could not reach the registry — using the locally cached ${IMAGE}"
  else
    fail "could not pull ${IMAGE} — check your network, registry access, and that the tag exists"
  fi
}

# ── (c) Verify signature (cosign) + digest (sha256) ──────────────────────────
image_digest() {
  # Repo digest of the pulled image, e.g. sha256:abc...
  "$RUNTIME" image inspect --format '{{ index .RepoDigests 0 }}' "$IMAGE" 2>/dev/null \
    | sed -n 's/.*@\(sha256:[0-9a-f]\{64\}\).*/\1/p' | head -n 1
}

# The jain release cosign public key, embedded so the trust root does not have to
# be fetched from the same origin as this script. Keep in sync with the key the
# release pipeline signs with (published at https://www.neverhuman.org/cosign.pub).
embedded_cosign_key() {
  cat <<'PUBKEY'
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAErPuYSXVD34JlvY6dZzxoy6PWUo+m
oP+dJFPGiEBGNcyDtGMZUTK7Y6barGy18NPPzU5m/zW9OLRdvQdPcspOqg==
-----END PUBLIC KEY-----
PUBKEY
}

# OS/arch-aware cosign install guidance (cosign is not in the default apt/dnf
# repos, so we point at the official signed release binary for the right arch).
print_cosign_install_help() {
  local cosign_arch
  case "$ARCH" in
    x86_64)  cosign_arch=amd64 ;;
    aarch64) cosign_arch=arm64 ;;
    *)       cosign_arch=amd64 ;;
  esac
  if [[ "$OS" == "darwin" ]]; then
    note "Install cosign (macOS):"
    note "  brew install cosign"
  elif command -v apt-get >/dev/null 2>&1; then
    note "Install cosign (Debian/Ubuntu — official signed binary; not in apt):"
    note "  curl -sSfL https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-${cosign_arch} -o /tmp/cosign \\"
    note "    && sudo install -m 0755 /tmp/cosign /usr/local/bin/cosign && rm -f /tmp/cosign"
  elif command -v dnf >/dev/null 2>&1 || command -v yum >/dev/null 2>&1; then
    note "Install cosign (RHEL/Fedora — official signed binary):"
    note "  curl -sSfL https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-${cosign_arch} -o /tmp/cosign \\"
    note "    && sudo install -m 0755 /tmp/cosign /usr/local/bin/cosign && rm -f /tmp/cosign"
  else
    note "Install cosign (official signed binary):"
    note "  curl -sSfL https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-${cosign_arch} -o /tmp/cosign \\"
    note "    && sudo install -m 0755 /tmp/cosign /usr/local/bin/cosign && rm -f /tmp/cosign"
    note "Docs: https://docs.sigstore.dev/cosign/system_config/installation/"
  fi
  note "Then re-run this installer."
}

# ── (c2) GPU acceleration check (informational; CPU mode is always supported) ──
# Runs `jain doctor` exactly ONCE — with --gpus all if the runtime accepts it —
# and caches the output in DOCTOR_OUT for the health check. The report reflects the
# GROUND TRUTH `cuda_available` from doctor (Docker accepting --gpus is necessary
# but not sufficient — CUDA must actually initialise inside the container). Sets
# GPU_ARGS to "--gpus all" only when the GPU will really be used.
GPU_ARGS=""
DOCTOR_OUT=""
check_gpu() {
  step "Checking GPU acceleration"

  local gpu_name=""
  if [[ "$OS" != "darwin" ]] && command -v nvidia-smi >/dev/null 2>&1; then
    # `|| gpu_name=""` so a present-but-broken nvidia-smi (driver mismatch) degrades
    # to CPU mode instead of aborting under `set -e -o pipefail`.
    gpu_name="$(nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -n 1)" || gpu_name=""
  fi

  # Run doctor once. Prefer --gpus all; fall back to CPU if the runtime rejects it
  # (e.g. NVIDIA Container Toolkit not installed) or on macOS.
  local ran_with_gpu=0
  if [[ "$OS" != "darwin" ]] && DOCTOR_OUT="$("$RUNTIME" run --rm --gpus all "$IMAGE" doctor 2>&1)"; then
    ran_with_gpu=1
  else
    if ! DOCTOR_OUT="$("$RUNTIME" run --rm "$IMAGE" doctor 2>&1)"; then
      printf '%s\n' "$DOCTOR_OUT" | sed 's/^/    /'
      fail "jain doctor failed to run"
    fi
  fi

  if printf '%s\n' "$DOCTOR_OUT" | grep -q 'cuda_available=true'; then
    GPU_ARGS="--gpus all"
    ok "GPU acceleration active${gpu_name:+ (${gpu_name})}"
  elif [[ "$OS" == "darwin" ]]; then
    GPU_ARGS=""
    ok "macOS runs in CPU mode (Docker Desktop does not pass through GPUs) — fully supported"
  elif [[ "$ran_with_gpu" == "1" ]]; then
    GPU_ARGS=""
    note "GPU visible to the container runtime${gpu_name:+ (${gpu_name})}, but CUDA did not initialise in the container — running in CPU mode (fully supported)."
  elif [[ -n "$gpu_name" ]]; then
    GPU_ARGS=""
    warn "NVIDIA GPU detected (${gpu_name}) but the container runtime can't reach it."
    note "Enable it with the NVIDIA Container Toolkit, then re-run:"
    if command -v apt-get >/dev/null 2>&1; then
      note "  sudo apt-get install -y nvidia-container-toolkit \\"
      note "    && sudo nvidia-ctk runtime configure --runtime=docker && sudo systemctl restart docker"
    elif command -v dnf >/dev/null 2>&1 || command -v yum >/dev/null 2>&1; then
      note "  sudo dnf install -y nvidia-container-toolkit \\"
      note "    && sudo nvidia-ctk runtime configure --runtime=docker && sudo systemctl restart docker"
    fi
    note "Guide: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html"
    note "jain runs in CPU mode until then (fully supported)."
  else
    GPU_ARGS=""
    note "No NVIDIA GPU driver found — jain will run in CPU mode (fully supported)."
  fi
}

verify_image() {
  step "Verifying image signature + digest"

  local digest
  digest="$(image_digest || true)"
  if [[ -n "$digest" ]]; then
    ok "image digest ${digest}"
    if [[ -n "$EXPECTED_DIGEST" ]]; then
      if [[ "$digest" == "$EXPECTED_DIGEST" ]]; then
        ok "digest matches pinned ${EXPECTED_DIGEST}"
      else
        fail "digest mismatch: got ${digest}, expected ${EXPECTED_DIGEST}"
      fi
    fi
  else
    warn "no repo digest available (image built/loaded locally?) — skipping digest pin check"
  fi

  if [[ "$SKIP_COSIGN" == "1" ]]; then
    warn "Skipping cosign signature verification (JAIN_SKIP_COSIGN=1 / --skip-verify)."
    note "You are trusting the image by digest${digest:+ ${digest}} alone."
    return 0
  fi

  if command -v cosign >/dev/null 2>&1; then
    local ref="$IMAGE"
    # Strip only the trailing :tag (single %, from the LAST colon) so a registry
    # host:port in JAIN_IMAGE isn't mangled into the digest ref.
    [[ -n "$digest" ]] && ref="${IMAGE%:*}@${digest}"

    # Resolve the key: the embedded key by default (self-verifying), or the caller's
    # path/URL. A temp file for the embedded key is cleaned up on return.
    local key_src="$COSIGN_PUBLIC_KEY" key_file="" tmp_key=""
    if [[ -z "$key_src" ]]; then
      tmp_key="$(mktemp "${TMPDIR:-/tmp}/jain-cosign.XXXXXX")"
      embedded_cosign_key >"$tmp_key"
      key_file="$tmp_key"
      key_src="embedded key"
    else
      key_file="$key_src"
    fi

    # Verify the container signature. (`verify-blob` is for loose files; a container
    # image is verified with `cosign verify` against the ed25519 public key the
    # release pipeline signs with.)
    if cosign verify --key "$key_file" "$ref" >/dev/null 2>&1; then
      ok "cosign signature verified (${key_src})"
      [[ -n "$tmp_key" ]] && rm -f "$tmp_key"
    else
      [[ -n "$tmp_key" ]] && rm -f "$tmp_key"
      fail "cosign signature verification failed for ${ref}"
    fi
  else
    warn "cosign is not installed — the image signature can't be verified."
    print_cosign_install_help
    note ""
    note "Or run WITHOUT signature verification (trusts the pulled digest only):"
    note "  curl -fsSL ${JAIN_INSTALLER_URL:-https://www.neverhuman.org/install-jain.sh} | JAIN_SKIP_COSIGN=1 sh"
    fail "cosign is required to verify the image (or set JAIN_SKIP_COSIGN=1 to skip)"
  fi
}

# ── (d) GPU-friendly health check ────────────────────────────────────────────
# Reuses $DOCTOR_OUT captured by check_gpu (no second container run). Doctor is run
# WITHOUT --require-gpu so a CPU-only box is a friendly banner, never an error. We
# assert present=true (weights available) and surface any `warning=` line as a
# reassuring "CPU mode" message.
health_check() {
  step "Health check (jain doctor)"
  printf '%s\n' "$DOCTOR_OUT" | sed 's/^/    /'
  interpret_doctor "$DOCTOR_OUT"
}

# Pure text interpretation of `jain doctor` output — factored out so the
# GPU-friendly banner logic is exercised independent of a running container.
interpret_doctor() {
  local out="$1" warning
  warning="$(printf '%s\n' "$out" | sed -n 's/.*warning=\(.*\)/\1/p' | head -n 1)"
  if [[ -n "$warning" ]]; then
    ok "Running in CPU mode (no GPU detected — this is fine)"
    note "doctor note: ${warning}"
  fi
  if printf '%s\n' "$out" | grep -q 'present=true'; then
    ok "jain model weights present"
  else
    fail "jain model weights not present (present=true not reported by doctor)"
  fi
}

# True if something is already listening on 127.0.0.1:$1 (bash /dev/tcp probe).
port_in_use() {
  (exec 3<>"/dev/tcp/127.0.0.1/$1") 2>/dev/null && { exec 3>&- 3<&-; return 0; } || return 1
}

# ── (e) Launch, or print the run command ─────────────────────────────────────
launch() {
  step "Launching jain web cockpit"

  # Already running? Point the user at it instead of force-killing a live cockpit
  # that this or another terminal is serving.
  if [[ -n "$("$RUNTIME" ps --filter 'name=^jain-cockpit$' --filter 'status=running' -q 2>/dev/null)" ]]; then
    ok "jain cockpit is already running — open http://localhost:${WEB_PORT}"
    note "to restart it:  ${RUNTIME} rm -f jain-cockpit  then re-run this installer"
    exit 0
  fi
  # A stopped, non-cleaned container of the same name would clash with --name.
  "$RUNTIME" rm -f jain-cockpit >/dev/null 2>&1 || true
  # Fail early (before the "starting" banner) if the port is taken.
  if port_in_use "$WEB_PORT"; then
    fail "port ${WEB_PORT} is already in use — free it or re-run with --port <PORT> (e.g. --port 4280)"
  fi

  local -a run_args
  run_args=(run --rm --name jain-cockpit -p "${WEB_HOST_BIND}:${WEB_PORT}:${WEB_PORT}")
  run_args+=(--env "JAIN_WEB_CONTAINER_HOST=${WEB_CONTAINER_HOST}" --env "JAIN_WEB_PORT=${WEB_PORT}")
  # shellcheck disable=SC2206   # $GPU_ARGS is an intentional word-split flag list
  [[ -n "$GPU_ARGS" ]] && run_args+=($GPU_ARGS)
  # The cockpit is the standalone `jain-web` binary (crate feat-web) — the split
  # has no `jain web` subcommand. Override the image entrypoint (which is the
  # feat-cli `jain` binary, used for doctor/serve) to run it, and point it at the
  # bundled SPA at /opt/jain/web with a writable data dir.
  run_args+=(--entrypoint /usr/local/bin/jain-web "$IMAGE" --host "$WEB_CONTAINER_HOST" --port "$WEB_PORT" --static-dir /opt/jain/web --data-dir /tmp/jain-web)

  local url="http://localhost:${WEB_PORT}"
  local accel="CPU mode"; [[ -n "$GPU_ARGS" ]] && accel="GPU-accelerated"
  local rule="══════════════════════════════════════════════════════"

  printf '\n%s  %s%s\n' "$C_GRN" "$rule" "$C_NC"
  printf '%s  jain cockpit is starting%s  (%s)\n\n' "$C_GRN" "$C_NC" "$accel"
  printf '     ➜  Open your browser at   %s%s%s\n\n' "$C_CYN" "$url" "$C_NC"
  printf '%s  %s%s\n\n' "$C_GRN" "$rule" "$C_NC"
  note "container: jain-cockpit   ·   port: ${WEB_PORT}   ·   listen on: ${WEB_HOST_BIND}"
  note "Upload a CSV and train. Press Ctrl-C here to stop the cockpit."
  note "The server is starting now — the first page load may take a few seconds…"
  printf '\n'
  exec "$RUNTIME" "${run_args[@]}"
}

# ── Main ─────────────────────────────────────────────────────────────────────
main() {
  load_web_defaults
  printf '%sjain installer%s  image=%s  verify-only=%s  web=%s:%s->%s\n' "$C_CYN" "$C_NC" "$IMAGE" "$VERIFY_ONLY" "$WEB_HOST_BIND" "$WEB_PORT" "$WEB_CONTAINER_HOST"
  detect_os_arch
  detect_runtime
  pull_image
  verify_image
  check_gpu
  health_check
  if [[ "$VERIFY_ONLY" == "1" ]]; then
    step "Verify-only complete"
    ok "image pulled, signature + digest verified, health check passed — launch skipped"
    note "to run the cockpit: curl -fsSL ${JAIN_INSTALLER_URL:-https://www.neverhuman.org/install-jain.sh} | sh"
    exit 0
  fi
  launch
}

main "$@"
