fix(iso): remove real IPs from image, install mother key for daemon user, de-obfuscate docs

Three blockers fixed from review of fix/ootb-mother-mcp:

1. Real Tailscale IP removed from image/repo.
   - external-mcp.json uses "mother" host alias (resolved by SSH config).
   - Key path: /var/db/colibri/.ssh/mother-mcp (daemon user home).
   - The real IP lives only on the offline seed (ssh/config), never in
     the repo or the shipped image.

2. Cross-user key access fixed.
   - The daemon runs as colibri (home /var/db/colibri), not clawdie.
   - Seed importer now installs SSH material to both clawdie AND
     colibri homes (same seed material, same key, separate ~/.ssh).
   - build.sh dev convenience also copies to both homes.
   - clawdie-live-seed.README.txt already documents the seed layout.

3. Doc fully de-obfuscated.
   - All m0th3r/c0l1br1/n0d3_r3g1st3r → mother/colibri/node_register.
   - All real IPs → <mother-tailscale-ip> placeholder.
   - Removed Step 2 (manual external MCP) + Step 3 (register) — both
     are now baked into the ISO.
   - Removed trailing "colibri-mcp" remote command from examples
     (hardened wrapper rejects non-allowlisted commands).
This commit is contained in:
Sam & Claude 2026-06-24 11:19:21 +02:00
parent 3fd3bc7560
commit dee76991de
4 changed files with 55 additions and 56 deletions

View file

@ -1743,6 +1743,11 @@ EOF
mkdir -p "${MOUNT_POINT}/home/clawdie/.ssh"
cp "${_mother_key_src}" "${MOUNT_POINT}/home/clawdie/.ssh/mother-mcp"
chmod 0600 "${MOUNT_POINT}/home/clawdie/.ssh/mother-mcp"
# Also install for the daemon user (colibri) — same key, different home.
mkdir -p "${MOUNT_POINT}/var/db/colibri/.ssh"
cp "${_mother_key_src}" "${MOUNT_POINT}/var/db/colibri/.ssh/mother-mcp"
chown -R colibri:colibri "${MOUNT_POINT}/var/db/colibri/.ssh" 2>/dev/null || true
chmod 0600 "${MOUNT_POINT}/var/db/colibri/.ssh/mother-mcp"
echo " Staged mother SSH key for USB→mother connectivity."
fi
chmod 0755 \

View file

@ -8,10 +8,10 @@ sent to mother, and stored in PostgreSQL `mother_hive.hive_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 |
| Host | IP (Tailscale) | User for MCP | Role |
| ---------------------- | ------------------------ | ------------ | ----------------------------------------------- |
| `mother` (OSA) | `<mother-tailscale-ip>` | `colibri` | Mother — runs PostgreSQL, external MCP servers |
| `clawdie-usb` (USB) | `<node-tailscale-ip>` | `clawdie` | Operator workstation — sends hw-probe to mother |
## How it works
@ -23,8 +23,8 @@ sent to mother, and stored in PostgreSQL `mother_hive.hive_nodes`.
│ │ external-mcp.json (baked): │
│ │ "mother": { │
│ │ "command": "ssh", │
│ │ "args": ["-i", "~/.ssh/mother-mcp",
│ │ "colibri@100.72.229.63"]
│ │ "args": ["-i", "/var/db/colibri/.ssh/mother-mcp",
│ │ "mother"]
│ │ } │
│ │ │
│ │ spawns persistent SSH child (no remote command) │
@ -32,7 +32,7 @@ sent to mother, and stored in PostgreSQL `mother_hive.hive_nodes`.
│ │ │ │
│ │ clawdie-hw-probe → JSON → │ │
│ │ colibri_external_mcp_call_tool( │ │
│ │ server="m0th3r", tool="n0d3_r3g1st3r", ...) ──────┤ │
│ │ server="mother", tool="node_register", ...) ──────┤ │
│ │ │ │
└────┼─────────────────────────────────────────────────────────┘ │
│ │
@ -66,58 +66,41 @@ 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'
cat > ~/.ssh/mother-mcp << 'KEY'
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACB3aHk0cmUteTB1LXIzYWRpbmctdGgxcwAAAAh3aHk0cmUteTB1LXIzYWRp
bmctdGgxcwAAAAtzc2gtZWQyNTUxOQAAACB3aHk0cmUteTB1LXIzYWRpbmctdGgxcwAAAA
<your-private-key-here>
-----END OPENSSH PRIVATE KEY-----
KEY
chmod 600 ~/.ssh/m0th3r-mcp
chmod 600 ~/.ssh/mother-mcp
# SSH config — use Tailscale IP so it works without DNS
# SSH config — use Tailscale IP so it works without DNS.
# The Host alias "mother" must match the external-mcp.json entry
# baked into the ISO. The real IP lives only on the seed, never in the repo.
cat > ~/.ssh/config << 'SSH'
Host m0th3r
HostName 100.72.229.63
User c0l1br1
IdentityFile ~/.ssh/m0th3r-mcp
Host mother
HostName <mother-tailscale-ip>
User colibri
IdentityFile ~/.ssh/mother-mcp
IdentitiesOnly yes
StrictHostKeyChecking accept-new
SSH
chmod 600 ~/.ssh/config
# Test the connection:
ssh m0th3r 'tools' 2>&1 | head -5
ssh mother '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)
## Step 2: External MCP calls + registry (baked into ISO)
```bash
# === ON USB, as clawdie ===
`COLIBRI_MCP_EXTERNAL_CALL=1` and the mother `external-mcp.json` entry
are pre-configured in the ISO image by `scripts/stage-colibri-iso.sh`.
No manual steps are needed — the daemon picks up both on first boot.
# 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)
external-mcp.json is now baked into the ISO image by
`scripts/stage-colibri-iso.sh` with the mother server pre-configured.
No manual step is needed — the daemon picks it up on first boot.
To add additional external MCP servers post-install, edit
`/usr/local/etc/colibri/external-mcp.json` and restart the daemon.
## Step 4: Install clawdie-hw-probe (on USB)
## Step 3: Install clawdie-hw-probe (on USB)
```bash
# === ON USB, as clawdie ===
@ -130,8 +113,8 @@ 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 \
# Option B — scp from mother:
# scp colibri@mother:/home/clawdie/ai/clawdie-iso/live/operator-session/clawdie-hw-probe \
# /tmp/ && sudo install -m 755 /tmp/clawdie-hw-probe /usr/local/bin/
# Test:
@ -139,7 +122,7 @@ 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)
## Step 4: Restart daemon + verify (on USB)
```bash
# === ON USB, as clawdie ===
@ -177,10 +160,10 @@ echo "$HW_JSON" | python3.11 -m json.tool | head -15
# The 0.12 daemon collects hw-probe at autospawn time and passes it to agents
# via CLAWDIE_HW_PROFILE env var. For manual testing, pipe JSON-RPC directly:
printf '{"jsonrpc":"2.0","method":"tools/call","id":1,"params":{"name":"node_register","arguments":{"hostname":"%s","hw_profile":%s}}}\n' \
"$(hostname)" "$HW_JSON" | ssh m0th3r 'cat - | /usr/local/bin/node-register-mcp'
"$(hostname)" "$HW_JSON" | ssh mother 'cat - | /usr/local/bin/node-register-mcp'
# 4. Verify on mother
ssh m0th3r 'sudo -u postgres psql -d mother_hive \
ssh mother 'sudo -u postgres psql -d mother_hive \
-c "SELECT hostname, status, capabilities FROM hive_nodes;"'
# Expected: both osa.smilepowered.org and clawdie-usb listed
```
@ -193,7 +176,7 @@ Once the daemon is restarted with `COLIBRI_AUTOSPAWN=YES` and
1. **zot autospawns** — DeepSeek API key from seed partition
2. **Daemon collects hw-probe** — runs `clawdie-hw-probe` at spawn time, passes result
to zot as `CLAWDIE_HW_PROFILE` env var (zot doesn't run hw-probe itself in 0.12)
3. **zot reads env var and calls mother**: `colibri_external_mcp_call_tool(server="m0th3r", tool="node_register", arguments={hostname:..., hw_profile:...})`
3. **zot reads env var and calls mother**: `colibri_external_mcp_call_tool(server="mother", tool="node_register", arguments={hostname:..., 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
@ -203,10 +186,10 @@ Once the daemon is restarted with `COLIBRI_AUTOSPAWN=YES` and
| 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` |
| `ssh mother` hangs | Tailscale not up | `sudo tailscale up` on USB |
| `Permission denied (publickey)` | Key not in authorized_keys on mother | Verify: `cat /var/db/colibri/.ssh/authorized_keys` on mother |
| `Permission denied (publickey)` | Key permissions wrong on USB | `chmod 600 /var/db/colibri/.ssh/mother-mcp` (daemon user) or `~/.ssh/mother-mcp` (clawdie user) |
| `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'` |
| Daemon starts, external tools visible, but calls fail | SSH key path wrong in external-mcp.json | Baked path: `/var/db/colibri/.ssh/mother-mcp` |
| `error: unrecognized subcommand` | SSH wrapper getting non-allowlisted command | Wrapper only allows `""` (stdio) and `"tools"`; `ssh mother tools` is correct |

View file

@ -44,6 +44,11 @@ SEED_MOUNT="${SEED_MOUNT:-/mnt/clawdie-seed}"
SEED_LOG="${SEED_LOG:-/var/log/clawdie-live-seed.log}"
SEED_USER="${SEED_USER:-clawdie}"
SEED_USER_HOME="${SEED_USER_HOME:-/home/clawdie}"
# Daemon user — the colibri_daemon runs as colibri and needs outbound SSH
# material (mother-mcp key + config + known_hosts) installed to its home so
# external MCP SSH connections to mother work.
SEED_DAEMON_USER="${SEED_DAEMON_USER:-colibri}"
SEED_DAEMON_HOME="${SEED_DAEMON_HOME:-/var/db/colibri}"
# Where imported agent payloads are staged. Runtime consumption (loading a soul
# into the agent workspace cwd, launching the chosen harness) reads from here.
SEED_IMPORT_ROOT="${SEED_IMPORT_ROOT:-/var/db/clawdie/seed}"
@ -348,6 +353,10 @@ _seed_activate_agent() {
# connectivity, delivered via the offline seed rather than baked in the image.
if [ -d "${_dir}/ssh" ]; then
_seed_install_ssh_material "${_dir}/ssh" "${_user}" "${_home}"
# Also install for the daemon user (colibri) — the daemon spawns the
# external-MCP SSH connection to mother, not the operator (clawdie).
# Same seed material, same private key, separate ~/.ssh directory.
_seed_install_ssh_material "${_dir}/ssh" "${SEED_DAEMON_USER}" "${SEED_DAEMON_HOME}"
fi
_seed_log "activated agent '${_agent}' for user ${_user} (home ${_home})"

View file

@ -133,9 +133,11 @@ EOF
chmod 0600 "${ETC_DIR}/provider.env" 2>/dev/null || true
# External MCP server registry. The mother server entry is pre-configured so
# the daemon connects to mother OOTB; the SSH key, known_hosts, and config
# come from the CLAWDIESEED seed partition (see clawdie-live-seed.README.txt).
# Without the seed, the connection fails gracefully — the daemon keeps running.
# the daemon (running as user colibri) connects to mother OOTB via the
# "mother" SSH config alias. The SSH key, known_hosts, and Host definition
# for "mother" come from the CLAWDIESEED seed partition and are installed
# to /var/db/colibri/.ssh/ by the importer (see clawdie-live-seed.README.txt).
# Without the seed the connection fails gracefully — the daemon keeps running.
# Path matches colibri-mcp's default COLIBRI_MCP_EXTERNAL_CONFIG.
cat > "${ETC_DIR}/external-mcp.json" <<'EOF'
{
@ -143,9 +145,9 @@ cat > "${ETC_DIR}/external-mcp.json" <<'EOF'
"mother": {
"command": "ssh",
"args": [
"-i", "/home/clawdie/.ssh/mother-mcp",
"-i", "/var/db/colibri/.ssh/mother-mcp",
"-o", "StrictHostKeyChecking=accept-new",
"colibri@100.72.229.63"
"mother"
],
"env": {}
}