#!/usr/bin/env sh
# get.hilo.team one-liner:   curl -fsSL https://get.hilo.team | sh
#
# Downloads the latest release (built by release/make-release.sh, published by
# release/publish-release.sh), VERIFIES its sha256 against the manifest, lands the code
# in a DURABLE code dir, and runs the installer from there. Works with a PRIVATE code
# repo — it fetches release tarballs, not the repo.
#
# TRUST MODEL — read this. This bootstrap trusts the CHANNEL over TLS, NOT the signing key.
# The sha256 it checks comes from the same manifest it just fetched, so it only catches a
# corrupted download, not a malicious one: anyone who can write the bucket rewrites the
# manifest, the tarball, AND this very script together. That is inherent to `curl | sh` —
# the script itself arrives from the channel (like rustup, homebrew, etc.). The root of
# trust for INSTALL is therefore Cloudflare's control of get.hilo.team + TLS. The pinned
# ed25519 key protects UPDATES, not this first fetch: it lives in the installed node's own
# code (config.OFFICIAL_UPDATE_PUBKEY) and so survives a later bucket compromise. For a
# stronger install anchor, use the git-clone / agent-paste path against the private repo
# (GitHub auth is the anchor there). docs/RELEASING.md has the full threat model.
#
# The code dir matters: the installer registers a service pointing at wherever the code
# sits, and the self-updater swaps THAT dir in place — so the extraction must never be
# installed from a temp path (it would be deleted out from under the running service).
#
# Env: HILO_CODE_DIR (default ~/hilo — where the code lives; NOT the data dir ~/.hilo)
#      HILO_RELEASE_BASE (default https://get.hilo.team/releases)
#      HILO_MANIFEST (default <HILO_RELEASE_BASE>/manifest.json when HILO_RELEASE_BASE is
#      set, else https://get.hilo.team/manifest.json)
# Installer flags pass through:  curl -fsSL https://get.hilo.team | sh -s -- --admin "Your Name"
set -e

CODE_DIR="${HILO_CODE_DIR:-$HOME/hilo}"
if [ -n "${HILO_RELEASE_BASE:-}" ]; then
  BASE="$HILO_RELEASE_BASE"
  MANIFEST="${HILO_MANIFEST:-$BASE/manifest.json}"
else
  BASE="https://get.hilo.team/releases"
  MANIFEST="${HILO_MANIFEST:-https://get.hilo.team/manifest.json}"
fi
command -v python3 >/dev/null 2>&1 || { echo "hilo: python3 is required" >&2; exit 1; }

fetch() {  # fetch URL -> file (curl or wget; supports file://)
  if command -v curl >/dev/null 2>&1; then curl -fsSL "$1" -o "$2"
  else wget -qO "$2" "$1"; fi
}
sha_of() {
  if command -v shasum >/dev/null 2>&1; then shasum -a 256 "$1" | awk '{print $1}'
  else sha256sum "$1" | awk '{print $1}'; fi
}
jval() { python3 -c 'import json,sys;print(json.load(open(sys.argv[1])).get(sys.argv[2],""))' "$1" "$2"; }

# an existing install keeps its code dir — the SELF-UPDATER owns upgrades (backup +
# verify + health-check + rollback); re-extracting over live code would bypass all of it
if [ -e "$CODE_DIR" ]; then
  if [ -f "$CODE_DIR/hilo_node/__init__.py" ]; then
    echo "hilo: an install already exists at $CODE_DIR." >&2
    echo "hilo: to upgrade it, run:  hilo-node update" >&2
    echo "hilo: to install a second node elsewhere:  HILO_CODE_DIR=/other/dir + HILO_HOME=/other/home" >&2
  else
    echo "hilo: $CODE_DIR exists and is not a hilo install — refusing to touch it." >&2
    echo "hilo: set HILO_CODE_DIR to an empty/new path." >&2
  fi
  exit 1
fi

TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
echo "hilo: reading $MANIFEST"
fetch "$MANIFEST" "$TMP/manifest.json"
VER="$(jval "$TMP/manifest.json" version)"
URL="$(jval "$TMP/manifest.json" tarball_url)"
SHA="$(jval "$TMP/manifest.json" sha256)"
[ -n "$VER" ] && [ -n "$URL" ] && [ -n "$SHA" ] || { echo "hilo: bad manifest" >&2; exit 1; }
# the tarball URL comes from the fetched manifest (remote-controlled) — same scheme wall
# as the node updater (update.py _require_safe_url): https or file (local tests) only
case "$URL" in
  https://*|file://*) ;;
  *) echo "hilo: refusing tarball over insecure scheme: $URL" >&2; exit 1 ;;
esac

echo "hilo: downloading $VER"
fetch "$URL" "$TMP/hilo.tar.gz"
GOT="$(sha_of "$TMP/hilo.tar.gz")"
[ "$GOT" = "$SHA" ] || { echo "hilo: checksum mismatch — refusing (expected $SHA, got $GOT)" >&2; exit 1; }

mkdir -p "$TMP/src"
tar -xzf "$TMP/hilo.tar.gz" -C "$TMP/src"
# the release tarball is FLAT (hilo_node/ at root); tolerate a wrapped one too
INSTALL="$(find "$TMP/src" -maxdepth 3 -path '*/install/install.sh' | head -1)"
[ -n "$INSTALL" ] || { echo "hilo: installer not found in the release" >&2; exit 1; }

# land the code somewhere durable BEFORE installing: the service plist/unit and the
# self-updater both point at this dir for the life of the install
ROOT="$(dirname "$(dirname "$INSTALL")")"
PARENT="$(dirname "$CODE_DIR")"
mkdir -p "$PARENT"
# mv can fail mid-copy across filesystems, leaving a PARTIAL $CODE_DIR — clear it before
# the copy fallback or cp -R would nest the tree INSIDE it. Safe: we refused any
# pre-existing $CODE_DIR above, so whatever sits there now is our own debris.
if ! mv "$ROOT" "$CODE_DIR" 2>/dev/null; then
  rm -rf "$CODE_DIR"
  cp -R "$ROOT" "$CODE_DIR"
fi
echo "hilo: code → $CODE_DIR"

echo "hilo: installing $VER"
sh "$CODE_DIR/install/install.sh" "$@"
