v0.9.1: on upgrade to broker-mode, WIPE the now-obsolete local credentials
An install switching TO broker-mode (the v0.9.0 default) carried long-lived Anthropic/OAuth credentials from the pre-broker era. Broker-mode authenticates via short-lived broker tokens and never uses them — they are a pure security liability on the box, acutely so on a PHI box. On the next self-update the agent now cleans them up automatically: - Secure-deletes $LARRY_HOME/.api-key and .oauth.json (reuses the uninstall-larry.sh shred -u -z -n3 -> overwrite -> rm logic). - Strips the ANTHROPIC_API_KEY / CLAUDE_CODE_OAUTH_TOKEN LINES from $LARRY_HOME/.env and from ~/.bashrc, ~/.bash_profile, ~/.profile (backup first); every other line is kept. - Idempotent (.broker-cred-wiped marker, written only after a run that removed something); silent no-op when clean. - Hard-guarded on LARRY_AUTH_MODE=broker: does NOT fire under the apikey escape hatch (which legitimately still needs the key). Only the two Anthropic/OAuth vars are touched (LARRY_* / GITEA_TOKEN are still needed in broker mode). - Prints a reminder to ALSO revoke at the source (local deletion != server revocation), per the decommission / kill-switch docs. Fires at the broker-resolution block (after self_update synced a fresh lib/broker.sh, before the fail-closed preflight). New functions in lib/broker.sh: _broker_wipe_obsolete_credentials, _broker_strip_cred_lines_from_env, _broker_strip_cred_lines_from_rc. VERSION + MANIFEST regenerated. Tested: 31/31 assertions pass across the upgrade-wipe, apikey-non-wipe, clean-no-op, idempotency, dangerous-path-guard, and selective-line-strip paths. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ea9f4c2399
commit
2b578f5058
36
CHANGELOG.md
36
CHANGELOG.md
@ -4,6 +4,42 @@ All notable changes to `cloverleaf-larry` / `larry-anywhere` are recorded here.
|
||||
Versioning is loose-semver; bumps trigger the in-process self-update on every
|
||||
running client via `LARRY_BASE_URL` + `MANIFEST`.
|
||||
|
||||
## v0.9.1 — 2026-05-31
|
||||
|
||||
**Upgrade to broker-mode now WIPES the now-obsolete local Anthropic/OAuth
|
||||
credentials it carried from the pre-broker era.**
|
||||
|
||||
When an existing install self-updates and `LARRY_AUTH_MODE` resolves to `broker`
|
||||
(the v0.9.0 default), the long-lived credentials baked in before the broker
|
||||
pivot are unused by broker-mode (which authenticates via short-lived broker
|
||||
tokens) and are a pure security liability on the box — acutely so on a PHI box.
|
||||
On the next self-update the agent now cleans them up automatically:
|
||||
|
||||
- **Secure-deletes** `$LARRY_HOME/.api-key` and `$LARRY_HOME/.oauth.json`
|
||||
(reuses the `uninstall-larry.sh` `shred -u -z -n3` → overwrite → rm logic via
|
||||
`lib/broker.sh` `_broker_secure_delete`, since on a PHI box these are sensitive).
|
||||
- **Strips the credential LINES** (`ANTHROPIC_API_KEY` / `CLAUDE_CODE_OAUTH_TOKEN`,
|
||||
with optional `export`/leading whitespace) from `$LARRY_HOME/.env` and from
|
||||
`~/.bashrc`, `~/.bash_profile`, `~/.profile` — keeping every other line; a
|
||||
timestamped `.broker-upgrade.<ts>.bak` is written before any rc/.env rewrite.
|
||||
- Logs clearly what was wiped, file by file, with the secure-delete method used.
|
||||
- **Idempotent:** writes `$LARRY_HOME/.broker-cred-wiped` only after a run that
|
||||
actually removed something, so the rc-rewrite path isn't re-attempted every
|
||||
launch; if nothing obsolete is present it no-ops silently and leaves no marker
|
||||
(so a later `apikey`→`broker` flip still triggers cleanup).
|
||||
- **Does NOT fire when `LARRY_AUTH_MODE=apikey`** — the escape hatch legitimately
|
||||
still needs its key. Hard-guarded on `LARRY_AUTH_MODE=broker` inside the
|
||||
function; only the two Anthropic/OAuth vars are touched (broker-mode still uses
|
||||
`LARRY_*` and `GITEA_TOKEN`, so those rc lines are left intact).
|
||||
- Prints a reminder that the operator must ALSO revoke the keys at the source
|
||||
(local deletion ≠ server revocation), consistent with the decommission /
|
||||
kill-switch docs.
|
||||
|
||||
Fires in `larry.sh` at the broker-resolution block (after `self_update` synced a
|
||||
fresh `lib/broker.sh`, before the fail-closed preflight). New functions in
|
||||
`lib/broker.sh`: `_broker_wipe_obsolete_credentials`,
|
||||
`_broker_strip_cred_lines_from_env`, `_broker_strip_cred_lines_from_rc`.
|
||||
|
||||
## v0.9.0 — 2026-05-31
|
||||
|
||||
**Broker mode is the DEFAULT — the remote kill-switch is wired into every
|
||||
|
||||
8
MANIFEST
8
MANIFEST
@ -23,7 +23,7 @@
|
||||
# scripts/make-manifest.sh and bump VERSION.
|
||||
|
||||
# Top-level scripts
|
||||
larry.sh 55c6fb42f09c923e6a8f5a8dfa9dbdd8fb5a6012dd5ac6707f87e853abcec234
|
||||
larry.sh 25d81d239b268635bff6704b55f473dc795af844b7a77cf6b24ba7e214c25786
|
||||
larry-tunnel.sh 6b050e4eeab15669f4858eaf3b807f168f211ced07815db9521bc40a093f6aaa
|
||||
larry-auth.sh a220cdf7878569dc3028951ee57fc8d5e706a8ca5c6aa45347b58facb386f831
|
||||
larry-rollback.sh 91b5e9aa6c79266bf306dcfba4ca791c07971bd6924d67a779037531648aa6d0
|
||||
@ -31,9 +31,9 @@ install-larry.sh 072a036ad5bbf80e866cfd2dd74de50f8defd69a3f835032579b0cb9d421ad5
|
||||
uninstall-larry.sh c53ad2d8354c7adeb243b541f027f3f481e4a8661eecfd7af14d7ca53cfcaad9
|
||||
|
||||
# Metadata
|
||||
VERSION 9d8c94f1ad3ea96b1e2ac4914fda4cb93c76b4a3e0d8cc6dd8976d6c0b227d15
|
||||
VERSION 179a5390966e85c2071a87c1b31de13df67460665196f6529f8c4986842f81e5
|
||||
MANUAL.md 5ff54d6d5fae826f8b3da1eb3be6476076bb15f9b1417a4de285e59ea37e1b1f
|
||||
CHANGELOG.md b3c48567452b4302b1167a3a9fb11f02964312a2f03ec393f8d35c9a1d392d90
|
||||
CHANGELOG.md 934007dc1b08b6c90120f009e3cc7870815e7b251fdf8f6629aa4c004c866017
|
||||
|
||||
# Agent personas (system-prompt overlays)
|
||||
agents/larry.md 0a1ef737e7fc133ab35be09f79c3a4df33de814e0404b69b950932d0c8a01be1
|
||||
@ -55,7 +55,7 @@ lib/oauth.sh 04a93376f88fe53cc1c86a5dbe577735c60375dadd4f2fda55b921ef3cddf22b
|
||||
# v0.9.0: broker client — the remote kill-switch (default rail). Enroll+mint a
|
||||
# short-lived token, route LLM calls through the broker, fail-closed heartbeat,
|
||||
# best-effort PHI wipe on disable.
|
||||
lib/broker.sh fe05f7b349d68239b3683b1f8c3139358f586cb583c907638483f1d3389959c4
|
||||
lib/broker.sh 8ec200439329ca0f93003d0976589f427de29450bbc8afab0fe32838e862bdce
|
||||
|
||||
# Secure SSH with ControlMaster (password hidden from Larry-the-LLM)
|
||||
lib/ssh-helper.sh 18df1f1f1936c930ba0197c0e0b4bd89c027500de99b56067b620ca9144f6e9e
|
||||
|
||||
13
larry.sh
13
larry.sh
@ -99,7 +99,7 @@ set -o pipefail
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Config
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
LARRY_VERSION="0.9.0"
|
||||
LARRY_VERSION="0.9.1"
|
||||
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
@ -1733,6 +1733,17 @@ if [ "$LARRY_AUTH_MODE" = "broker" ] && [ "$LARRY_NO_API" != "1" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# UPGRADE-TO-BROKER CREDENTIAL CLEANUP (v0.9.1): an install switching TO broker
|
||||
# mode carried long-lived Anthropic/OAuth credentials from the pre-broker era.
|
||||
# Broker mode never uses them (it mints short-lived broker tokens) — they're a
|
||||
# pure security liability on the box, acutely so on a PHI box. Secure-delete
|
||||
# .api-key/.oauth.json and strip the key/oauth LINES from .env + the shell rc
|
||||
# files, keeping everything else. Idempotent (no-ops once clean); fires ONLY in
|
||||
# broker mode (the apikey escape hatch legitimately still needs its key, so this
|
||||
# is guarded inside the function on LARRY_AUTH_MODE=broker). Prints a reminder to
|
||||
# ALSO revoke at the source. See lib/broker.sh _broker_wipe_obsolete_credentials.
|
||||
_broker_wipe_obsolete_credentials
|
||||
|
||||
# Resolve a deployment id: explicit env wins; else derive a stable per-box id so
|
||||
# "install larry on a box" auto-enrolls under a named deployment in the dashboard.
|
||||
if [ -z "$LARRY_DEPLOYMENT_ID" ]; then
|
||||
|
||||
145
lib/broker.sh
145
lib/broker.sh
@ -327,6 +327,151 @@ _broker_phi_wipe() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# ── Upgrade-to-broker credential cleanup (v0.9.1) ────────────────────────────
|
||||
# When an existing install switches TO broker-mode, the long-lived Anthropic
|
||||
# credentials it carried from the pre-broker (apikey/oauth) era are UNUSED by
|
||||
# broker-mode (which authenticates via short-lived broker tokens) and are a pure
|
||||
# security liability on the box — most acutely on a PHI box. This wipes them.
|
||||
#
|
||||
# WHAT IT WIPES (only when LARRY_AUTH_MODE resolved to "broker"):
|
||||
# - $LARRY_HOME/.api-key — secure-deleted (it's a whole-file secret)
|
||||
# - $LARRY_HOME/.oauth.json — secure-deleted (whole-file OAuth refresh token)
|
||||
# - $LARRY_HOME/.env — the ANTHROPIC_API_KEY / CLAUDE_CODE_OAUTH_TOKEN
|
||||
# LINES are stripped; the rest of .env is KEPT.
|
||||
# - ~/.bashrc, ~/.bash_profile, ~/.profile — exported ANTHROPIC_API_KEY /
|
||||
# CLAUDE_CODE_OAUTH_TOKEN lines stripped (backup
|
||||
# first); other lines kept.
|
||||
#
|
||||
# IDEMPOTENT: a marker ($LARRY_HOME/.broker-cred-wiped) is written after a run
|
||||
# that found something, so the (potentially destructive) rc-rewrite path is not
|
||||
# re-attempted every launch. If NOTHING obsolete is present it no-ops silently
|
||||
# and does NOT write the marker (so a later apikey→broker flip still cleans up).
|
||||
#
|
||||
# GUARD: the caller MUST only invoke this when LARRY_AUTH_MODE is "broker". In
|
||||
# apikey mode the key is legitimately still needed — do NOT call this there.
|
||||
#
|
||||
# NOT a substitute for revocation: local deletion ≠ server revocation. It prints
|
||||
# the same "revoke at the source" reminder as uninstall-larry.sh / the
|
||||
# decommission checklist.
|
||||
|
||||
# _broker_strip_cred_lines_from_env FILE — remove only the ANTHROPIC_API_KEY and
|
||||
# CLAUDE_CODE_OAUTH_TOKEN assignment/export lines from a key=value .env, keeping
|
||||
# every other line. Backs up first. Echoes "stripped" | "none" | "FAILED".
|
||||
# Matches: ANTHROPIC_API_KEY=… , export ANTHROPIC_API_KEY=… , and the OAuth twin
|
||||
# (with optional leading whitespace), never a substring of another var name.
|
||||
_broker_strip_cred_lines_from_env() {
|
||||
local f="$1"
|
||||
[ -f "$f" ] || { echo "absent"; return 0; }
|
||||
local re='^[[:space:]]*(export[[:space:]]+)?(ANTHROPIC_API_KEY|CLAUDE_CODE_OAUTH_TOKEN)='
|
||||
grep -qE "$re" "$f" 2>/dev/null || { echo "none"; return 0; }
|
||||
local ts; ts="$(date +%Y%m%d-%H%M%S 2>/dev/null || echo bak)"
|
||||
cp -p "$f" "$f.broker-upgrade.$ts.bak" 2>/dev/null \
|
||||
|| { _broker_log_err "could not back up $f — leaving it UNCHANGED for safety"; echo "FAILED"; return 1; }
|
||||
if grep -vE "$re" "$f" > "$f.broker-tmp" 2>/dev/null && mv "$f.broker-tmp" "$f" 2>/dev/null; then
|
||||
echo "stripped"; return 0
|
||||
fi
|
||||
rm -f "$f.broker-tmp" 2>/dev/null || true
|
||||
_broker_log_err "could not rewrite $f — remove the credential line(s) by hand"
|
||||
echo "FAILED"; return 1
|
||||
}
|
||||
|
||||
# _broker_strip_cred_lines_from_rc FILE — same idea for a shell profile: strip
|
||||
# only the exported ANTHROPIC_API_KEY / CLAUDE_CODE_OAUTH_TOKEN lines, keep the
|
||||
# rest, backup first. (Mirrors uninstall-larry.sh's rc-strip, but scoped to the
|
||||
# two Anthropic/OAuth vars only — broker-mode still legitimately uses LARRY_* and
|
||||
# GITEA_TOKEN, so we must NOT strip those here.) Echoes stripped|none|FAILED.
|
||||
_broker_strip_cred_lines_from_rc() {
|
||||
local f="$1"
|
||||
[ -f "$f" ] || { echo "absent"; return 0; }
|
||||
local re='^[[:space:]]*(export[[:space:]]+)?(ANTHROPIC_API_KEY|CLAUDE_CODE_OAUTH_TOKEN)='
|
||||
grep -qE "$re" "$f" 2>/dev/null || { echo "none"; return 0; }
|
||||
local ts; ts="$(date +%Y%m%d-%H%M%S 2>/dev/null || echo bak)"
|
||||
cp -p "$f" "$f.broker-upgrade.$ts.bak" 2>/dev/null \
|
||||
|| { _broker_log_err "could not back up $f — leaving it UNCHANGED for safety"; echo "FAILED"; return 1; }
|
||||
if grep -vE "$re" "$f" > "$f.broker-tmp" 2>/dev/null && mv "$f.broker-tmp" "$f" 2>/dev/null; then
|
||||
echo "stripped"; return 0
|
||||
fi
|
||||
rm -f "$f.broker-tmp" 2>/dev/null || true
|
||||
_broker_log_err "could not rewrite $f — remove the credential line(s) by hand"
|
||||
echo "FAILED"; return 1
|
||||
}
|
||||
|
||||
# _broker_wipe_obsolete_credentials — the upgrade-to-broker cleanup entry point.
|
||||
# Call ONLY when LARRY_AUTH_MODE == "broker". Secure-deletes the whole-file
|
||||
# secrets and strips the key/oauth lines from .env + the shell rc files. No-ops
|
||||
# (and prints nothing) when there is nothing obsolete to remove.
|
||||
_broker_wipe_obsolete_credentials() {
|
||||
[ "${LARRY_AUTH_MODE:-}" = "broker" ] || return 0 # hard guard: broker only
|
||||
local lh="${LARRY_HOME:-$HOME/.larry}"
|
||||
|
||||
# Same dangerous-path guard family as _broker_phi_wipe (don't operate on / etc).
|
||||
local norm; norm="$(printf '%s' "$lh" | sed 's:/*$::')"
|
||||
case "$norm" in
|
||||
""|"/"|"/root"|"/home"|"/Users"|"/usr"|"/etc"|"/var"|"/bin"|"/tmp")
|
||||
_broker_log_err "credential cleanup skipped: LARRY_HOME ('$lh') resolves to a dangerous path."; return 1 ;;
|
||||
esac
|
||||
|
||||
# Detect whether any obsolete credential is actually present. If not, no-op and
|
||||
# leave NO marker (so a later apikey→broker flip still triggers cleanup).
|
||||
local rc_re='^[[:space:]]*(export[[:space:]]+)?(ANTHROPIC_API_KEY|CLAUDE_CODE_OAUTH_TOKEN)='
|
||||
local present=0 rc
|
||||
[ -f "$lh/.api-key" ] && present=1
|
||||
[ -f "$lh/.oauth.json" ] && present=1
|
||||
[ -f "$lh/.env" ] && grep -qE "$rc_re" "$lh/.env" 2>/dev/null && present=1
|
||||
for rc in "${HOME:-}/.bashrc" "${HOME:-}/.bash_profile" "${HOME:-}/.profile"; do
|
||||
[ -n "$rc" ] && [ -f "$rc" ] && grep -qE "$rc_re" "$rc" 2>/dev/null && { present=1; break; }
|
||||
done
|
||||
[ "$present" = "1" ] || return 0
|
||||
|
||||
_broker_log_err "── upgrade to broker-mode: wiping now-obsolete local Anthropic/OAuth credentials ──"
|
||||
_broker_log_err " broker-mode authenticates via short-lived broker tokens; these baked credentials"
|
||||
_broker_log_err " are unused and a security liability on this box. Removing them now."
|
||||
|
||||
local m
|
||||
# 1. Whole-file secrets — secure-delete (reuses uninstall-larry.sh shred logic).
|
||||
if [ -f "$lh/.api-key" ]; then
|
||||
m="$(_broker_secure_delete "$lh/.api-key")"
|
||||
_broker_log_err " .api-key: $m"
|
||||
fi
|
||||
if [ -f "$lh/.oauth.json" ]; then
|
||||
m="$(_broker_secure_delete "$lh/.oauth.json")"
|
||||
_broker_log_err " .oauth.json: $m"
|
||||
fi
|
||||
# 2. .env — strip only the two cred LINES, keep the rest of the file.
|
||||
if [ -f "$lh/.env" ]; then
|
||||
m="$(_broker_strip_cred_lines_from_env "$lh/.env")"
|
||||
case "$m" in
|
||||
stripped) _broker_log_err " .env: stripped ANTHROPIC_API_KEY/CLAUDE_CODE_OAUTH_TOKEN line(s) (rest kept; .bak written)" ;;
|
||||
none) : ;;
|
||||
*) _broker_log_err " .env: $m" ;;
|
||||
esac
|
||||
fi
|
||||
# 3. Shell profiles — strip exported key/oauth lines (backup first), keep rest.
|
||||
for rc in "${HOME:-}/.bashrc" "${HOME:-}/.bash_profile" "${HOME:-}/.profile"; do
|
||||
[ -n "$rc" ] || continue
|
||||
m="$(_broker_strip_cred_lines_from_rc "$rc")"
|
||||
case "$m" in
|
||||
stripped) _broker_log_err " $rc: stripped exported key/oauth line(s) (rest kept; .bak written)" ;;
|
||||
none|absent) : ;;
|
||||
*) _broker_log_err " $rc: $m" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 4. Idempotency marker — written only because we found+acted on something.
|
||||
: > "$lh/.broker-cred-wiped" 2>/dev/null || true
|
||||
date -u +%Y-%m-%dT%H:%M:%SZ > "$lh/.broker-cred-wiped" 2>/dev/null || true
|
||||
|
||||
# 5. Revocation reminder — local deletion ≠ server revocation (decommission §6,
|
||||
# kill-switch design). The operator MUST also revoke at the source.
|
||||
_broker_log_err "REMINDER: local deletion does NOT revoke these credentials at the source."
|
||||
_broker_log_err " Also REVOKE them now so a copy that already egressed cannot be used:"
|
||||
_broker_log_err " 1. Anthropic Console → Settings → API Keys: DELETE the key for this box."
|
||||
_broker_log_err " 2. Anthropic Console → account security / Connected apps: REVOKE the"
|
||||
_broker_log_err " Claude-Code OAuth grant ('sign out everywhere'). See the decommission checklist."
|
||||
_broker_log_err "── credential cleanup complete ──"
|
||||
return 0
|
||||
}
|
||||
|
||||
# _broker_log_err — route through larry's err() if present, else stderr. Never
|
||||
# logs a token or secret.
|
||||
_broker_log_err() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user