feat(join-hive): capture vault creds and pull provider keys on first boot

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 <noreply@anthropic.com>
This commit is contained in:
Sam & Claude 2026-06-21 18:22:45 +02:00
parent 0deaae2586
commit f9e47ab516

View file

@ -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