From 6900369a410801e43ce4ba10d0ae1c9c62224acf Mon Sep 17 00:00:00 2001 From: Server Larry Date: Tue, 21 Jul 2026 13:39:51 -0700 Subject: [PATCH] cloverleaf-larry: one-arg self-contained broker install (Bryan-direct P1, approved) Adds a positional self-enroll path to install-larry.sh: curl -fsSL .../install-larry.sh | bash -s POSTs {"client_id":} to the broker's one-shot /enroll-claim endpoint (no admin secret to copy/paste), receives deployment_id/enroll_secret/profile, writes $LARRY_HOME/.deployment-id + .enroll-secret (0600), bakes broker env into the larry shim. Second run of the same client-id fails replay-safe (409 already_enrolled, zero files written) -- by design, not a bug. Mutually exclusive with the existing manual LARRY_DEPLOYMENT_ID/LARRY_ENROLL_SECRET env-var path, which is otherwise unchanged. --uninstall / --uninstall --yes paths unchanged (dry-run default, --yes for full removal). Broker-side half (registry.claim_enrollment/client_slug, main.py /enroll-claim, broker_ctl.py set-client-slug/reopen-enroll) already deployed live on the broker container since 2026-07-09, sha256-verified matching -- this commit is the client-side half landing to close out the task. Note: install-larry.sh's own comment refers to this as "v0.9.8+" but VERSION and CHANGELOG.md are deliberately NOT bumped in this commit -- that wasn't part of the reviewed/gated diff (install-larry.sh only), and adding it here would be new unreviewed content. Flagging as a small follow-up nit, not blocking. Gated: - Vera CODE-PASS+LIVE-PASS, 2026-07-09, token dc705af1-0e1f-41ca-b1d2-7e3446961680 (fresh throwaway deployment, real public broker, real mint + real LLM reply, replay-safe, clean uninstall, kill-switch fail-closed even through a reopened claim window) - Vera TTL re-confirm CODE-PASS, 2026-07-21, token 598051ce-5c31-434e-aaa1-6cf4e3a549fb (zero content drift, upstream HEAD unmoved, from-scratch patch-apply reproduces the gated artifact byte-for-byte) - Fresh independent live 6/6 round-trip re-run, 2026-07-21 (Clover): provision -> install -> mint+LLM -> replay-safe -> uninstall -> kill-switch, all PASS against the real public broker, throwaway registry row created and cleaned up (test-harness.sh, task tsk-2026-07-09-install-larry-one-arg-selfcontained) Bryan-direct authorization to push: "push it now -- yes" (relayed via Larry). Co-Authored-By: Claude Sonnet 5 --- install-larry.sh | 165 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 154 insertions(+), 11 deletions(-) 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 "