clawdie-iso/docs/SETUP-USB-TO-MOTHER.md
Sam & Claude 6275fee97e docs: harness-neutral cleanup + restore green markdown gate
Pi-era residue in current-tense docs/strings (CHANGELOG history left intact):
- ONBOARDING-SIMPLIFICATION: COLIBRI_AUTOSPAWN_PI -> COLIBRI_AUTOSPAWN; 'Pi
  agent' -> 'agent'.
- clawdie-join-hive.sh: user-facing 'Pi agent is live' / 'no Pi agent' ->
  harness-neutral (default agent is now zot).
- clawdie-live-seed.README.txt: COLIBRI_AUTOSPAWN_PI -> COLIBRI_AUTOSPAWN.
- stage-colibri-iso.sh provider.env.sample: the AUTOSPAWN_ARGS example showed
  '--mode json' (invalid for the zot default); note the default is
  harness-derived (zot -> rpc, pi -> --mode json).

Also restore the markdown format gate: 5 docs from the 0.12.0 work were
prettier-dirty, so ./scripts/check-format.sh was already failing on main (the
gate was red and unenforced — same pattern as the colibri build break).
prettier --write brings them to style; gate is green again. No prose changes
in those 5 — formatting only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 18:08:58 +02:00

11 KiB

Setup: USB → Mother MCP Connection

2026-06-23 | Step-by-step | Tested on OSA + 0.11 USB

Connects a booted Clawdie USB to the mother node (OSA) via MCP over SSH. After setup, clawdie-hw-probe runs on the USB, the hardware profile is sent to mother, and stored in PostgreSQL mother_hive.usb_nodes.

Hosts used in this guide

Host IP (Tailscale) User for MCP Role
osa.smilepowered.org 100.72.229.63 colibri Mother — runs PostgreSQL, external MCP servers
clawdie-usb (USB) 100.66.193.11 clawdie Operator workstation — sends hw-probe to mother

How it works

┌─ USB ────────────────────────────────────────────────────────┐
│                                                               │
│  colibri-daemon                                               │
│    │                                                          │
│    │ external-mcp.json:                                       │
│    │   "mother": {                                            │
│    │     "command": "ssh",                                    │
│    │     "args": ["-i", "~/.ssh/m0th3r-mcp",                 │
│    │              "c0l1br1@100.72.229.63",                   │
│    │              "colibri-mcp"]                              │
│    │   }                                                      │
│    │                                                          │
│    │ spawns persistent SSH child process                      │
│    │ JSON-RPC flows over stdin/stdout ──────────────────────┐ │
│    │                                                         │ │
│    │ clawdie-hw-probe → JSON →                              │ │
│    │   colibri_external_mcp_call_tool(                       │ │
│    │     server="m0th3r", tool="n0d3_r3g1st3r", ...)  ──────┤ │
│    │                                                         │ │
└────┼─────────────────────────────────────────────────────────┘ │
     │                                                            │
     │  Tailscale (WireGuard encrypted)                           │
     │                                                            │
┌────┼─────────────────────────────────────────────────────────┐ │
│    ▼              Mother (OSA)                                │ │
│                                                               │ │
│  /var/db/colibri/.ssh/authorized_keys:                        │ │
│    command="/usr/local/bin/colibri-mcp-ssh",restrict,... ◄────┘ │
│                                                               │
│  colibri-mcp-ssh → strips forced-command wrapper              │
│                  → passes "tools" subcommand to colibri-mcp    │
│                                                               │
│  PostgreSQL mother_hive.usb_nodes ← hw-probe JSON stored       │
│                                                               │
└───────────────────────────────────────────────────────────────┘

Step 1: SSH key + config (on USB)

Copy the mother-mcp private key to the USB. The public key is already authorized on mother. For production: the seed partition places this automatically. For testing: scp from OSA or copy manually.

# === ON USB, as clawdie ===

mkdir -p ~/.ssh
chmod 700 ~/.ssh

# Create the private key — replace with real key content
# This key is authorized on mother as:
#   command="/usr/local/bin/colibri-mcp-ssh",restrict,no-pty,...
cat > ~/.ssh/m0th3r-mcp << 'KEY'
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACB3aHk0cmUteTB1LXIzYWRpbmctdGgxcwAAAAh3aHk0cmUteTB1LXIzYWRp
bmctdGgxcwAAAAtzc2gtZWQyNTUxOQAAACB3aHk0cmUteTB1LXIzYWRpbmctdGgxcwAAAA
-----END OPENSSH PRIVATE KEY-----
KEY
chmod 600 ~/.ssh/m0th3r-mcp

# SSH config — use Tailscale IP so it works without DNS
cat > ~/.ssh/config << 'SSH'
Host m0th3r
    HostName 100.72.229.63
    User c0l1br1
    IdentityFile ~/.ssh/m0th3r-mcp
    IdentitiesOnly yes
    StrictHostKeyChecking accept-new
SSH
chmod 600 ~/.ssh/config

# Test the connection:
ssh m0th3r 'tools' 2>&1 | head -5
# Expected output:
#   colibri_status        Get Colibri daemon status...
#   colibri_snapshot      Get Glasspane snapshot...
#   ...

Step 2: Enable external MCP calls (on USB)

# === ON USB, as clawdie ===

# Add to provider.env
echo 'COLIBRI_MCP_EXTERNAL_CALL=1' | \
    sudo tee -a /usr/local/etc/colibri/provider.env

# Verify:
grep EXTERNAL_CALL /usr/local/etc/colibri/provider.env
# → COLIBRI_MCP_EXTERNAL_CALL=1

Step 3: Register mother as external MCP server (on USB)

# === ON USB, as clawdie ===

sudo tee /usr/local/etc/colibri/external-mcp.json << 'JSON'
{
  "servers": {
    "m0th3r": {
      "command": "ssh",
      "args": [
        "-i", "/home/clawdie/.ssh/m0th3r-mcp",
        "-o", "StrictHostKeyChecking=accept-new",
        "c0l1br1@100.72.229.63",
        "colibri-mcp"
      ],
      "env": {}
    }
  }
}
JSON

# Verify JSON syntax:
python3.11 -m json.tool /usr/local/etc/colibri/external-mcp.json > /dev/null \
    && echo "OK" || echo "INVALID JSON"

What happens at daemon startup: the daemon reads external-mcp.json, spawns ssh c0l1br1@100.72.229.63 colibri-mcp as a persistent child process, and pipes JSON-RPC over stdin/stdout. The mother-side colibri-mcp-ssh wrapper (in authorized_keys via command=) strips the SSH forced-command layer and passes subcommands directly to colibri-mcp. One SSH connection per daemon lifetime — no reconnect overhead.

Step 4: Install clawdie-hw-probe (on USB)

# === ON USB, as clawdie ===

# From the 0.12 feature branch (already committed):
# Option A — from local repo (if unshallowed):
cd /home/clawdie/ai/clawdie-iso
git fetch origin feature/0.12.0
git show origin/feature/0.12.0:live/operator-session/clawdie-hw-probe \
    | sudo tee /usr/local/bin/clawdie-hw-probe > /dev/null
sudo chmod 755 /usr/local/bin/clawdie-hw-probe

# Option B — scp from OSA:
# scp clawdie@100.72.229.63:/home/clawdie/ai/clawdie-iso/live/operator-session/clawdie-hw-probe \
#     /tmp/ && sudo install -m 755 /tmp/clawdie-hw-probe /usr/local/bin/

# Test:
sudo clawdie-hw-probe 2>/dev/null | python3.11 -m json.tool | head -10
# Expected: JSON with hostname, ram_gb, cpu_model, disks, network_interfaces...

Step 5: Restart daemon + verify (on USB)

# === ON USB, as clawdie ===

# Restart the daemon so it picks up external-mcp.json and the env var
sudo service colibri_daemon restart
# Expected: Starting colibri_daemon.
#          colibri-daemon socket ready after 1s

# Verify external MCP tools are visible
colibri-mcp tools 2>&1 | grep external
# Expected:
#   colibri_external_mcp_servers    List configured external MCP servers...
#   colibri_external_mcp_list_tools List tools exposed by...
#   colibri_external_mcp_call_tool  Call a tool on an external MCP server...

# List mother's registered tools
colibri-mcp --external-config /usr/local/etc/colibri/external-mcp.json \
    --external-call tools 2>&1 | head -10
# Expected: tools registered on mother (colibri_status, geodesic_dome, etc.)

End-to-end test

# === ON USB ===

# 1. Run hw-probe, capture JSON
HW_JSON=$(sudo clawdie-hw-probe 2>/dev/null)

# 2. View what would be sent to mother
echo "$HW_JSON" | python3.11 -m json.tool | head -15

# 3. Send to mother via MCP (once node_register tool exists on mother)
# For now: manual insert via SSH to mother
echo "$HW_JSON" | ssh m0th3r 'cat - | sudo -u postgres psql -d mother_hive \
    -c "INSERT INTO usb_nodes (hostname, hw_profile, status) \
    VALUES ('\''$(hostname)'\'', '\''$(cat)'\''::jsonb, '\''online'\'') \
    ON CONFLICT (hostname) DO UPDATE SET hw_profile = EXCLUDED.hw_profile, last_seen = now();"'

# 4. Verify on mother
ssh m0th3r 'sudo -u postgres psql -d mother_hive \
    -c "SELECT hostname, status, capabilities FROM usb_nodes;"'
# Expected: both osa.smilepowered.org and clawdie-usb listed

What happens after (future — 0.12+)

Once the daemon is restarted with COLIBRI_AUTOSPAWN=YES and COLIBRI_AUTOSPAWN_BINARY=zot:

  1. zot autospawns — DeepSeek API key from seed partition
  2. zot's first action: runs clawdie-hw-probe, captures JSON
  3. zot calls mother: colibri_external_mcp_call_tool(server="m0th3r", tool="n0d3_r3g1st3r", arguments={hw_profile: ...})
  4. Mother stores the hardware profile in PostgreSQL
  5. Capability trigger fires — derives has_gpu, ram_gb, cpu_cores, geodesic_dome_mcp etc.
  6. Mother returns capabilities — zot now knows what this node can do
  7. zot logs: "node registered — 12GB RAM, 6 cores, no GPU, geodesic_dome_mcp=true"

Troubleshooting

Symptom Likely cause Fix
ssh m0th3r hangs Tailscale not up sudo tailscale up on USB
Permission denied (publickey) Key not in authorized_keys on mother Verify: cat /var/db/c0l1br1/.ssh/authorized_keys on OSA
Permission denied (publickey) Key permissions wrong on USB chmod 600 ~/.ssh/m0th3r-mcp
daemon: open: Permission denied Log file ownership wrong chown clawdie: /var/log/colibri/daemon.log
Daemon starts but no external tools COLIBRI_MCP_EXTERNAL_CALL not set Check provider.env, restart daemon
Daemon starts, external tools visible, but calls fail SSH key path wrong in external-mcp.json Use absolute path: /home/clawdie/.ssh/m0th3r-mcp
error: unrecognized subcommand SSH wrapper getting "colibri-mcp tools" instead of "tools" Use single-word invocation: ssh m0th3r 'tools' not ssh m0th3r 'colibri-mcp tools'