diff --git a/install-larry.sh b/install-larry.sh index 0ad9018..52b893c 100755 --- a/install-larry.sh +++ b/install-larry.sh @@ -2,18 +2,45 @@ # install-larry.sh — bootstrap Larry-Anywhere on a fresh remote shell. # No root, no package install, no sudo. Writes only into $LARRY_HOME. # -# Usage: +# Usage (v0.9.8+ — ONE-ARG self-contained broker install, Bryan-approved 2026-07-09): +# curl -fsSL /install-larry.sh | bash -s +# Self-enrolls against the broker using ONLY the client-id — no manual env +# vars, no secret to paste. The broker (which the box owner already +# provisioned with `provision_broker_client.sh --open-enroll +# `) hands back the deployment-id + a fresh enrollment secret +# ONCE. A second run of the same client-id fails replay-safe (the broker +# returns 409 already_enrolled) — that is BY DESIGN, not a bug; the trust +# boundary is the single admin-opened claim window, closed either by the +# first successful claim or by the kill-switch (broker_ctl set-authorized +# false). See "ONE-ARG SELF-ENROLL" below. +# +# curl -fsSL /install-larry.sh | bash -s -- --uninstall [flags] +# Fetches uninstall-larry.sh fresh and runs it (dry-run by default, same +# as running it directly — pass --yes to actually delete). See its own +# --help for --keep-data / --keep-rc / --no-shred. +# # curl -fsSL /install-larry.sh | bash +# (no args) — existing interactive/apikey path, UNCHANGED. Also still +# accepts the original manual broker env vars below if you already have a +# secret (e.g. from the admin `enroll` flow rather than open-enroll). # # Or with explicit base URL: # LARRY_BASE_URL=https://example.com/larry-anywhere bash install-larry.sh # # Env vars: -# LARRY_HOME install location (default: $HOME/.larry) -# LARRY_BASE_URL where to fetch files from (no trailing slash) -# LARRY_BIN_DIR where to symlink the `larry` command (default: $HOME/bin) -# LARRY_GITEA_TOKEN optional Gitea PAT (read scope) for authenticated fetch -# against a PRIVATE repo. Alias: GITEA_TOKEN. Never logged. +# LARRY_HOME install location (default: $HOME/.larry) +# LARRY_BASE_URL where to fetch files from (no trailing slash) +# LARRY_BIN_DIR where to symlink the `larry` command (default: $HOME/bin) +# LARRY_GITEA_TOKEN optional Gitea PAT (read scope) for authenticated fetch +# against a PRIVATE repo. Alias: GITEA_TOKEN. Never logged. +# LARRY_BROKER_URL broker base URL. Defaults to the public broker +# (https://broker.bjnoela.com) when a arg is +# given; override for a dev/staging broker. +# LARRY_DEPLOYMENT_ID / LARRY_ENROLL_SECRET / LARRY_PROFILE +# the ORIGINAL manual broker-enrollment path (v0.9.0) — +# still supported, unchanged, for operators who already +# hold a secret from the admin `enroll` flow. Mutually +# exclusive with the positional arg. set -eu LARRY_HOME="${LARRY_HOME:-$HOME/.larry}" @@ -124,6 +151,42 @@ fetch_validate() { } # <<< fetch-safe inline <<< +# ───────────────────────────────────────────────────────────────────────────── +# v0.9.8 — CLI arg parsing: (one-arg self-enroll) | --uninstall | +# (no args, unchanged). Deliberately minimal — this installer has never had +# positional-arg parsing before, so there is no legacy behavior to preserve +# beyond "no args = today's interactive/apikey/manual-broker-env path". +# +# NOTE: SCRIPT_DIR (the local-checkout fallback path, used elsewhere by +# fetch()) is NOT YET DEFINED at this point in the script (it's computed much +# later, in the "Fetch the scripts" section) — this block runs under `set -eu` +# so any reference here MUST use the ${VAR:-} default-guard form, never a bare +# "$SCRIPT_DIR", or a plain unset-variable reference would abort the whole +# script with "unbound variable" before reaching any of our own error +# messages. The --uninstall path below relies on this guard. +# ───────────────────────────────────────────────────────────────────────────── +LARRY_CLIENT_ID="" +if [ "${1:-}" = "--uninstall" ]; then + shift + say "uninstall requested — fetching uninstall-larry.sh fresh from origin" + _un_tmp="$(mktemp 2>/dev/null || echo "/tmp/uninstall-larry.$$.sh")" + if [ -n "$LARRY_BASE_URL" ] && fetch_validate "$LARRY_BASE_URL/uninstall-larry.sh" "$_un_tmp" sh 30; then + chmod +x "$_un_tmp" 2>/dev/null || true + exec bash "$_un_tmp" "$@" + elif [ -n "${SCRIPT_DIR:-}" ] && [ -f "${SCRIPT_DIR:-}/uninstall-larry.sh" ]; then + exec bash "$SCRIPT_DIR/uninstall-larry.sh" "$@" + else + rm -f "$_un_tmp" + die "could not fetch uninstall-larry.sh from LARRY_BASE_URL=$LARRY_BASE_URL — run it directly instead: bash \$LARRY_HOME/uninstall-larry.sh $*" + fi +elif [ $# -gt 0 ]; then + case "$1" in + -*) die "unknown flag: $1 (usage: install-larry.sh [] | install-larry.sh --uninstall [flags])" ;; + *) LARRY_CLIENT_ID="$1"; shift + [ $# -eq 0 ] || die "unexpected extra argument(s) after client-id '$LARRY_CLIENT_ID': $*" ;; + esac +fi + # ───────────────────────────────────────────────────────────────────────────── # Detect platform # ───────────────────────────────────────────────────────────────────────────── @@ -446,12 +509,92 @@ else esac fi +# ───────────────────────────────────────────────────────────────────────────── +# v0.9.8 — ONE-ARG SELF-ENROLL (client-id argument drives EVERYTHING: broker +# URL, deployment-id lookup, profile, AND the enrollment secret itself — zero +# manual env vars, matching Bryan's approved 2026-07-09 design). +# +# Calls the broker's one-shot POST /enroll-claim {"client_id": ""} — no +# secret in the request. Only succeeds for a deployment the broker owner +# already provisioned + left claim-window-open via: +# provision_broker_client.sh --client "