feat(iso): wire Colibri OOTB defaults + opt-in Mother MCP link

Workstream C of the next ISO rebuild.

C1 — Auto-spawn lit up out of the box:
  provider.env now ships COLIBRI_AUTOSPAWN_PI="YES", so colibri#137 fires on
  the booted image once a DeepSeek key is present (pulled by Join Hive, A).

C2 — External MCP registry staged:
  /usr/local/etc/colibri/external-mcp.json shipped as {"servers":{}} at the
  path colibri-mcp reads by default. Empty = mother off by default.

C3 — Opt-in "Enable Mother Link" (clawdie-enable-mother + desktop entry):
  Direction is "our Pi calls mother's tools" — colibri-mcp dials OUT to mother
  over SSH-stdio and proxies mother's tools to the Pi via its external-call
  path. The toggle:
   - provisions an SSH identity for the colibri service account
     (/var/db/colibri/.ssh — the daemon and its Pi run as `colibri`),
   - writes the mother entry into external-mcp.json (ssh -i <key> ... mother),
   - upserts COLIBRI_MCP_EXTERNAL_CALL=1 into provider.env,
   - restarts the daemon and prints colibri's pubkey to authorize on mother.

provider.env.sample documents the new toggles. sh -n clean on all scripts;
the empty default and the emitted mother entry validate as JSON and match the
ExternalMcpRegistry {servers:{command,args,env}} shape.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Sam & Claude 2026-06-21 18:53:41 +02:00
parent 0deaae2586
commit a788d99967
4 changed files with 141 additions and 0 deletions

View file

@ -1649,6 +1649,12 @@ EOF
"${MOUNT_POINT}/usr/local/share/applications/Clawdie Join Hive.desktop"
install -m 0644 "${LIVE_SESSION_DIR}/clawdie-join-hive.desktop" \
"${MOUNT_POINT}/home/clawdie/Desktop/Join Hive.desktop"
install -m 0755 "${LIVE_SESSION_DIR}/clawdie-enable-mother.sh" \
"${MOUNT_POINT}/usr/local/bin/clawdie-enable-mother"
install -m 0644 "${LIVE_SESSION_DIR}/clawdie-enable-mother.desktop" \
"${MOUNT_POINT}/usr/local/share/applications/Clawdie Enable Mother.desktop"
install -m 0644 "${LIVE_SESSION_DIR}/clawdie-enable-mother.desktop" \
"${MOUNT_POINT}/home/clawdie/Desktop/Enable Mother Link.desktop"
mkdir -p "${MOUNT_POINT}/usr/local/share/clawdie-iso/bootstrap"
install -m 0644 "${LIVE_SESSION_DIR}/bootstrap.html" \
"${MOUNT_POINT}/usr/local/share/clawdie-iso/bootstrap/index.html"

View file

@ -0,0 +1,9 @@
[Desktop Entry]
Type=Application
Version=1.0
Name=Enable Mother Link
Comment=Let this agent's Pi call mother's tools over MCP (opt-in)
Exec=xfce4-terminal --title "Enable Mother Link" --geometry=90x28 --command "/usr/local/bin/clawdie-enable-mother"
Icon=network-transmit-receive
Terminal=false
Categories=System;Utility;

View file

@ -0,0 +1,102 @@
#!/bin/sh
# Enable the opt-in MCP link to "mother".
#
# Direction: this agent's Pi calls mother's tools. colibri-mcp dials OUT to
# mother over SSH-stdio and proxies mother's MCP tools to the Pi via its
# external-call path. Off by default; this script turns it on.
#
# The colibri daemon (and the Pi it spawns) run as the `colibri` user, so the
# outbound SSH uses an identity in the colibri home (/var/db/colibri/.ssh).
# Run this in a visible terminal so the operator can copy the public key.
set -eu
PROVIDER_ENV="/usr/local/etc/colibri/provider.env"
EXTERNAL_MCP="/usr/local/etc/colibri/external-mcp.json"
COLIBRI_HOME="/var/db/colibri"
SSH_KEY="${COLIBRI_HOME}/.ssh/id_ed25519"
finish() {
echo ""
echo "Press Enter to close."
read -r _
exit "${1:-0}"
}
have() {
command -v "$1" >/dev/null 2>&1
}
if ! have mdo; then
echo "ERROR: mdo is required to update the colibri service configuration."
finish 1
fi
echo "========================================"
echo " Clawdie — Enable Mother Link"
echo "========================================"
echo ""
# 1. Mother's reachable address + remote MCP command.
printf " Mother SSH target (user@tailscale-ip-or-name): "
read -r MOTHER_HOST
if [ -z "${MOTHER_HOST:-}" ]; then
echo " Cancelled: no mother target entered."
finish 0
fi
printf " Mother MCP command [colibri-mcp]: "
read -r MOTHER_CMD
[ -n "${MOTHER_CMD:-}" ] || MOTHER_CMD="colibri-mcp"
# 2. Ensure the colibri service account has an SSH key for the outbound link.
echo ""
echo "[1/4] Ensuring colibri SSH identity..."
mdo -u root sh -c '
set -eu
home="$1"; key="$2"
install -d -o colibri -g colibri -m 0700 "${home}/.ssh"
if [ ! -f "$key" ]; then
ssh-keygen -t ed25519 -N "" -f "$key" -C "colibri@$(hostname)" >/dev/null
chown colibri:colibri "$key" "${key}.pub"
chmod 0600 "$key"; chmod 0644 "${key}.pub"
fi
' sh "$COLIBRI_HOME" "$SSH_KEY"
# 3. Write the mother entry into the external MCP registry.
echo "[2/4] Registering mother in ${EXTERNAL_MCP}..."
printf '%s\n' "{
\"servers\": {
\"mother\": {
\"command\": \"ssh\",
\"args\": [\"-i\", \"${SSH_KEY}\", \"-o\", \"BatchMode=yes\", \"-o\", \"StrictHostKeyChecking=accept-new\", \"${MOTHER_HOST}\", \"${MOTHER_CMD}\"],
\"env\": {}
}
}
}" | mdo -u root sh -c 'cat > "$1"; chmod 0644 "$1"' sh "$EXTERNAL_MCP"
# 4. Allow external MCP calls: upsert COLIBRI_MCP_EXTERNAL_CALL=1 into provider.env.
echo "[3/4] Enabling external MCP calls..."
mdo -u root sh -c '
set -eu
f="$1"
tmp="$(mktemp)"
grep -v "^COLIBRI_MCP_EXTERNAL_CALL=" "$f" >"$tmp" 2>/dev/null || :
printf "COLIBRI_MCP_EXTERNAL_CALL=\"1\"\n" >>"$tmp"
cat "$tmp" >"$f"
rm -f "$tmp"
chmod 0600 "$f"
' sh "$PROVIDER_ENV"
# 5. Restart the daemon so the Pi inherits the new env + registry.
echo "[4/4] Restarting colibri daemon..."
mdo -u root service colibri_daemon restart >/dev/null 2>&1 ||
echo " WARNING: restart failed; run: mdo -u root service colibri_daemon restart"
echo ""
echo "Mother link configured. Authorize this key on mother (restrict it to the"
echo "MCP command, e.g. command=\"${MOTHER_CMD}\",restrict in authorized_keys):"
echo ""
mdo -u root cat "${SSH_KEY}.pub" 2>/dev/null || echo " (could not read ${SSH_KEY}.pub)"
echo ""
echo "Once authorized on mother, the Pi can call mother's tools via colibri-mcp."
finish 0

View file

@ -112,9 +112,25 @@ cat > "${ETC_DIR}/provider.env" <<'EOF'
# provider keys and Vaultwarden bootstrap credentials here after boot.
VAULT_SERVER="https://vault.smilepowered.org"
BW_SERVER="https://vault.smilepowered.org"
# Auto-spawn one Pi agent on daemon startup once a DeepSeek key is present
# (the Operator Image OOTB flow). The daemon sources this file, so the spawned
# Pi inherits the provider keys set here.
COLIBRI_AUTOSPAWN_PI="YES"
EOF
chmod 0600 "${ETC_DIR}/provider.env" 2>/dev/null || true
# External MCP server registry — empty by default. The "Enable Mother Link"
# action (clawdie-enable-mother) adds a server entry here; colibri-mcp reads it
# when launched with COLIBRI_MCP_EXTERNAL_CALL=1. Path matches colibri-mcp's
# default COLIBRI_MCP_EXTERNAL_CONFIG.
cat > "${ETC_DIR}/external-mcp.json" <<'EOF'
{
"servers": {}
}
EOF
chmod 0644 "${ETC_DIR}/external-mcp.json" 2>/dev/null || true
cat > "${ETC_DIR}/provider.env.sample" <<'EOF'
# Optional provider keys and Vaultwarden bootstrap credentials for
# colibri_daemon. The ISO already stages provider.env with the non-secret
@ -138,6 +154,14 @@ BW_SERVER="https://vault.smilepowered.org"
# Optional endpoints/models:
# DEEPSEEK_ENDPOINT="https://api.deepseek.com/chat/completions"
# DEEPSEEK_MODEL="deepseek-chat"
#
# Behavior toggles (non-secret):
# COLIBRI_AUTOSPAWN_PI="YES" # auto-spawn one Pi on daemon startup
# COLIBRI_PI_BINARY="pi" # Pi executable name/path
# COLIBRI_AUTOSPAWN_PI_ARGS="--mode json" # Pi argv
# COLIBRI_MCP_EXTERNAL_CALL="1" # allow Pi (via colibri-mcp) to call
# # external MCP servers; set by
# # clawdie-enable-mother
EOF
cat > "${ETC_DIR}/README.iso" <<'EOF'