From f9e47ab5163c5c870af2d31d359219005263b319 Mon Sep 17 00:00:00 2001 From: Sam & Claude Date: Sun, 21 Jun 2026 18:22:45 +0200 Subject: [PATCH 1/2] feat(join-hive): capture vault creds and pull provider keys on first boot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Workstream A of the next ISO rebuild. The booted XFCE image's "Join Hive" flow now collects the 3 Vaultwarden bootstrap values and pulls the provider keys, instead of only warning when they are missing. Step [2/4] now: - If provider.env lacks BW_*, prompts for BW_CLIENTID/BW_CLIENTSECRET/ BW_PASSWORD (secret + password read with echo off) and upserts them into provider.env (root-owned 0600). Entering nothing skips — manual floor intact. - Then runs clawdie-vault-fetch against provider.env (as bootstrap and as --write-env target) to pull DEEPSEEK_API_KEY (and other agent-secrets), and restarts colibri_daemon so it loads the new keys — which triggers the Pi auto-spawn (colibri#137). Secrets never appear in process arguments: values stay in shell variables and a 0600 temp under ~/.cache/clawdie; provider.env is read/written via mdo. The upsert preserves the endpoint line and other keys (verified: special characters in the secret/password survive, no duplicate BW_* lines). provider.env stays the single secret store — the daemon's vault provisioning and the existing provider_env_has_bw_creds check already assume that. sh -n clean. Co-Authored-By: Claude Opus 4.8 --- live/operator-session/clawdie-join-hive.sh | 109 +++++++++++++++++++-- 1 file changed, 103 insertions(+), 6 deletions(-) diff --git a/live/operator-session/clawdie-join-hive.sh b/live/operator-session/clawdie-join-hive.sh index 6d00f87c..d531a519 100755 --- a/live/operator-session/clawdie-join-hive.sh +++ b/live/operator-session/clawdie-join-hive.sh @@ -18,6 +18,55 @@ have() { command -v "$1" >/dev/null 2>&1 } +# Read a value into the variable named by $1 without echoing keystrokes. +# Used for the Vaultwarden secret + password so they never appear on screen. +read_secret() { + printf '%s' "$2" + stty -echo 2>/dev/null + read -r "$1" + stty echo 2>/dev/null + echo "" +} + +# Upsert BW_CLIENTID/BW_CLIENTSECRET/BW_PASSWORD into provider.env (root-owned, +# 0600) without leaking secrets through process arguments. The values stay in +# shell variables and a 0600 temp file; provider.env is read and written via mdo +# so the unprivileged operator session can update it. +write_provider_bw() { + _cache="${HOME:-/home/clawdie}/.cache/clawdie" + mkdir -p "$_cache" 2>/dev/null + chmod 0700 "$_cache" 2>/dev/null + _tmp="$(mktemp "${_cache}/joinhive.XXXXXX")" || return 1 + chmod 0600 "$_tmp" 2>/dev/null + + if have mdo; then + mdo -u root cat "$PROVIDER_ENV" >"$_tmp" 2>/dev/null || : + else + cat "$PROVIDER_ENV" >"$_tmp" 2>/dev/null || : + fi + + # Drop any existing BW_* definitions, then append the freshly entered ones. + grep -vE '^(BW_CLIENTID|BW_CLIENTSECRET|BW_PASSWORD)=' "$_tmp" >"${_tmp}.new" 2>/dev/null || : >"${_tmp}.new" + { + printf 'BW_CLIENTID=%s\n' "$BW_CLIENTID" + printf 'BW_CLIENTSECRET=%s\n' "$BW_CLIENTSECRET" + printf 'BW_PASSWORD=%s\n' "$BW_PASSWORD" + } >>"${_tmp}.new" + mv "${_tmp}.new" "$_tmp" + chmod 0600 "$_tmp" 2>/dev/null + + if have mdo; then + mdo -u root cp "$_tmp" "$PROVIDER_ENV" && mdo -u root chmod 0600 "$PROVIDER_ENV" + _rc=$? + else + cp "$_tmp" "$PROVIDER_ENV" && chmod 0600 "$PROVIDER_ENV" + _rc=$? + fi + + rm -f "$_tmp" "${_tmp}.new" 2>/dev/null + return $_rc +} + provider_env_has_bw_creds() { _check='test -f "$1" && grep -Eq "^BW_CLIENTID=.+" "$1" && grep -Eq "^BW_CLIENTSECRET=.+" "$1" && grep -Eq "^BW_PASSWORD=.+" "$1"' @@ -64,14 +113,62 @@ if [ ! -S "$SOCKET" ]; then finish 1 fi -# 2. Vault creds. provider.env is intentionally 0600, so check via mdo when possible. -echo "[2/4] Checking vault credentials..." +# 2. Vault creds + provider keys. provider.env is intentionally 0600 (root), so +# read/write via mdo. If the 3 bootstrap values are absent, prompt for them and +# save them; then pull the provider keys (DeepSeek, etc.) from Vaultwarden. +echo "[2/4] Vault credentials..." +if ! provider_env_has_bw_creds; then + echo "" + echo " No Vaultwarden bootstrap credentials found." + echo " Enter the 3 values, or press Enter at the first prompt to skip." + echo "" + printf " BW_CLIENTID: " + read -r BW_CLIENTID + if [ -n "${BW_CLIENTID:-}" ]; then + read_secret BW_CLIENTSECRET " BW_CLIENTSECRET (hidden): " + read_secret BW_PASSWORD " BW_PASSWORD (hidden): " + if [ -n "${BW_CLIENTSECRET:-}" ] && [ -n "${BW_PASSWORD:-}" ]; then + if write_provider_bw; then + echo " Saved to ${PROVIDER_ENV} (0600)." + else + echo " ERROR: could not write ${PROVIDER_ENV}." + fi + else + echo " Skipped: secret or password was empty." + fi + unset BW_CLIENTSECRET BW_PASSWORD + else + echo " Skipped credential entry." + fi + unset BW_CLIENTID +fi + if provider_env_has_bw_creds; then - echo " provider.env contains Vaultwarden bootstrap credential fields." + echo " Pulling provider keys from Vaultwarden..." + if have clawdie-vault-fetch && have bw && have mdo; then + if mdo -u root clawdie-vault-fetch --bootstrap "$PROVIDER_ENV" --write-env "$PROVIDER_ENV"; then + echo " Provider keys updated (DeepSeek and any others present)." + echo " Restarting colibri daemon to load the new keys..." + if mdo -u root service colibri_daemon restart >/dev/null 2>&1; then + _t=0 + while [ "$_t" -lt 5 ] && [ ! -S "$SOCKET" ]; do + sleep 1 + _t=$(( _t + 1 )) + done + else + echo " WARNING: daemon restart failed." + echo " Run: mdo -u root service colibri_daemon restart" + fi + else + echo " WARNING: vault fetch did not complete (check network / credentials)." + echo " The manual wizard remains available; keys can be added later." + fi + else + echo " NOTE: clawdie-vault-fetch, bw, or mdo unavailable — skipping key pull." + fi else - echo " WARNING: provider.env is missing, unreadable, or lacks BW_CLIENTID/BW_CLIENTSECRET/BW_PASSWORD." - echo " Vault provisioning will be skipped until the operator adds them." - echo " Edit ${PROVIDER_ENV} and keep it mode 0600, then re-run." + echo " WARNING: provider.env still lacks BW_CLIENTID/BW_CLIENTSECRET/BW_PASSWORD." + echo " Vault provisioning is skipped until they are added." fi # 3. Detect capabilities -- 2.45.3 From 34259e33121d8ab58ba371f8339022fd22e5e6fe Mon Sep 17 00:00:00 2001 From: Sam & Claude Date: Sun, 21 Jun 2026 18:59:15 +0200 Subject: [PATCH 2/2] feat(join-hive): confirm the auto-spawned Pi after daemon restart MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per Hermes' review of the cred-capture flow: after the daemon restart that loads the pulled keys, poll colibri status (up to 10s) for a live agent so the operator sees confirmation that the Pi auto-spawn actually came up — instead of just "daemon restarted". Prints "Pi agent is live." or a check hint. Co-Authored-By: Claude Opus 4.8 --- live/operator-session/clawdie-join-hive.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/live/operator-session/clawdie-join-hive.sh b/live/operator-session/clawdie-join-hive.sh index d531a519..f27eaced 100755 --- a/live/operator-session/clawdie-join-hive.sh +++ b/live/operator-session/clawdie-join-hive.sh @@ -155,6 +155,19 @@ if provider_env_has_bw_creds; then sleep 1 _t=$(( _t + 1 )) done + # Confirm the auto-spawned Pi came up (colibri auto-spawn on boot). + if have colibri; then + _p=0 + while [ "$_p" -lt 10 ]; do + if colibri --socket "$SOCKET" status 2>/dev/null | grep -q '"agents":[1-9]'; then + echo " Pi agent is live." + break + fi + sleep 1 + _p=$(( _p + 1 )) + done + [ "$_p" -lt 10 ] || echo " NOTE: no Pi agent yet — check: colibri status" + fi else echo " WARNING: daemon restart failed." echo " Run: mdo -u root service colibri_daemon restart" -- 2.45.3