#!/usr/bin/env sh # Nestri host installer — https://api.nestri.io/install.sh # # This file is the source of what that URL serves, kept in the public # repository so anyone about to pipe it into a shell can read it first. # # curl -fsSL https://api.nestri.io/install.sh | sh -s -- # # What it does, in order: check this is 64-bit Linux, ask where box images # should live, download the host agent for this platform, verify it against the # published SHA256SUMS, install it to ~/.local/bin, and hand over to # the agent's own onboarding, which checks the machine, registers it with the # token and starts the agent as a systemd user service. It never asks for sudo: the agent # runs as the user who ran this. # # The token comes from the dashboard's Installation page. It registers one # machine, works once and lapses after an hour. set -eu API="${NESTRI_API:-https://api.nestri.io}" # Pinned, not "latest", so the script and the binary it installs are a pair # somebody chose. Bump when cutting a release; NESTRI_HOST_VERSION overrides it. DEFAULT_VERSION="0.1.0" VERSION="${NESTRI_HOST_VERSION:-$DEFAULT_VERSION}" BIN_DIR="${NESTRI_BIN_DIR:-$HOME/.local/bin}" say() { printf '%s\n' "$*" >&2; } die() { printf 'error: %s\n' "$*" >&2; exit 1; } TOKEN="${1:-${NESTRI_INSTALL_TOKEN:-}}" [ -n "$TOKEN" ] || die "no install token. Copy the full command from the dashboard's Installation page." # --- platform --------------------------------------------------------------- [ "$(uname -s)" = Linux ] || die "a host has to run Linux (with KVM); this is $(uname -s)." case "$(uname -m)" in x86_64|amd64) target=x86_64-unknown-linux-musl ;; *) die "no host build for $(uname -m) yet." ;; esac [ "$(id -u)" -ne 0 ] || die "run this as the user that will run boxes, not as root." # --- fetch ------------------------------------------------------------------ if command -v curl >/dev/null 2>&1; then get() { curl -fsSL "$1" -o "$2"; } elif command -v wget >/dev/null 2>&1; then get() { wget -qO "$2" "$1"; } else die "need curl or wget" fi # --- where box images go ---------------------------------------------------- # Their own device, xfs or ext4, and never `/`: box images are large, and a # filled root filesystem takes the whole machine down with it. The agent's # preflight checks this again; asking here is so the default is a good guess. tty_ok() { [ -e /dev/tty ] && (exec 3/dev/null; } BOX_STORE="${NESTRI_BOX_STORE:-}" if [ -z "$BOX_STORE" ]; then guess="$(df -P -T -x tmpfs -x devtmpfs -x overlay 2>/dev/null \ | awk 'NR>1 && ($2=="xfs"||$2=="ext4") && $7!="/" && $7!~/^\/(boot|efi)/ {print $5, $7}' \ | sort -rn | awk 'NR==1 {print $2}')" default="${guess:+$guess/nestri}" if tty_ok; then printf 'Where should box images go? (xfs or ext4, not /) [%s]: ' "${default:-none found}" >&2 read -r answer /dev/null 2>&1; then have="$(sha256sum "$TMP/$ASSET" | cut -d' ' -f1)" else have="$(shasum -a 256 "$TMP/$ASSET" | cut -d' ' -f1)" fi [ "$have" = "$want" ] || die "checksum mismatch — not installing expected $want got $have" say "Checksum OK." mkdir -p "$BIN_DIR" chmod +x "$TMP/$ASSET" mv "$TMP/$ASSET" "$BIN_DIR/nestri-host" say "Installed $BIN_DIR/nestri-host" say "" # --- onboard ---------------------------------------------------------------- # The token goes through the environment rather than argv, so it is not in # `ps` for the length of the run. export NESTRI_INSTALL_TOKEN="$TOKEN" NESTRI_BOX_STORE="$BOX_STORE" NESTRI_API="$API" if tty_ok; then exec "$BIN_DIR/nestri-host" onboard