An ordered first-run checklist for deploying on osa (or any new mother), covering the things that can only be validated against a live PostgreSQL + FreeBSD host: - build 0.12 on FreeBSD from current main + ci-checks (Linux binaries won't run) - record any pre-existing node-register before install - post-install integrity: installed node-register is the hardened hive_nodes version (grep -c "E'" == 0; grep hive_nodes > 0) — not the injectable copy - schema migrated in place (usb_nodes renamed, not duplicated; node_type present) - peer auth works; pg_hba peer rule present AND precedes generic local rules - external-mcp has all three servers (jq-merge preserved existing) - SSH forced-command wrapper rejects non-allowlisted commands - daemon env + service live; key hygiene (private key → seed only) Captures the operational risks flagged during the mother-infra review.
9.4 KiB
Mother Node MCP Setup
What this is
The mother node (OSA) runs the Colibri MCP host for USB operator node coordination. USB nodes send hardware profiles via SSH → MCP → PostgreSQL. This directory contains the scripts and schema for that infrastructure.
Files
| File | Purpose |
|---|---|
setup-mother.sh |
Idempotent deploy — run once as root, safe to re-run |
mother_schema.sql |
PostgreSQL schema (hive_nodes, build_queue, audit_log + triggers) |
node-register-mcp |
MCP tool: receive hw-probe JSON → UPSERT into hive_nodes |
geodesic-dome-mcp |
MCP tool: geodesic dome wireframe + structural BOM |
build-colibri.sh |
MCP tool: build a colibri crate (branch-allowlisted) |
colibri-mcp-ssh |
SSH forced-command wrapper (allowlisted: "", "tools" only) |
Architecture
USB node Mother (osa)
──────── ────────────
clawdie-hw-probe PostgreSQL (mother_hive)
│ ▲
▼ │
colibri-daemon │ INSERT / UPSERT
│ (external MCP) │
▼ │
ssh colibri@mother (no cmd) │
│ (mother-mcp key) │
▼ │
colibri-mcp-ssh ◄─ authorized_keys command="..."
│ allowlist: "" or "tools"
▼
colibri-mcp ──► node-register-mcp ──► psql -v :'variable' (heredoc)
(parameterized UPSERT, no shell interpolation of SQL)
The USB's colibri-mcp spawns ssh -i ~/.ssh/mother-mcp colibri@mother
with NO remote command. SSH is restricted via command= + restrict to
run only colibri-mcp-ssh, which delegates to colibri-mcp in stdio MCP
mode. The node_register tool on mother receives the hw-probe JSON and
UPSERTs it into PostgreSQL using psql -v :'variable' quoting in a
heredoc — the JSON blob is a bound variable, never interpolated into SQL.
Security properties
- colibri-mcp-ssh: allowlists
SSH_ORIGINAL_COMMANDto""(stdio MCP mode) or"tools"(one-shot discovery). All other values are rejected. This prevents callers from passing arbitrary colibri-mcp flags through the forced-command boundary. - build-colibri.sh: branch is validated against an allowlist (
main, semverv*tags, plusCOLIBRI_BUILD_ALLOW_BRANCHEScolon-separated extras). Features are validated against^[A-Za-z0-9_,-]+$. Cargo args use a shell array — no unquoted string concatenation. - node-register-mcp:
psql -v :'variable'quoting in a heredoc (not-c). The JSON blob is dollar-quoted by psql; shell never interpolates it into SQL. - mother-mcp key: NOT baked into images, NOT reused for Forgejo.
Lives on seed partition (
CLAWDIESEED/colibri/ssh/mother-mcp). One key per trust domain — blast radius is MCP-over-SSH only.
Setup (one-time)
On mother, as root:
# Build colibri from current main first, then:
cd /home/clawdie/ai/colibri
sudo ./packaging/mother/setup-mother.sh
# The script prints the mother-mcp private key — copy it for USB nodes.
What setup-mother.sh does
- Installs colibri binaries from
target/release - Installs MCP server scripts
- jq-merges mother servers into
/usr/local/etc/colibri/external-mcp.json - Creates
colibriOS user + SSH authorized_keys withcommand=wrapper - Generates mother-mcp keypair (prints private key for seed placement)
- Configures PostgreSQL peer auth for colibri on mother_hive
- Runs
mother_schema.sql(idempotent) - Updates colibri daemon env (
COLIBRI_AUTOSPAWN,COLIBRI_MCP_EXTERNAL_CALL) - Restarts
colibri_daemon
Idempotent — running again is a no-op.
First-run checklist (osa)
Run through this the first time you deploy on osa (or any new mother).
Most of it is setup-mother.sh; the checks around it catch the things that
can't be validated until a real PostgreSQL + FreeBSD host is in front of you.
1. Build 0.12 on FreeBSD from current main (provenance).
Binaries built on Linux will not run on osa. Build and gate on the host:
cd /home/clawdie/ai/colibri
git fetch origin && git checkout main && git merge --ff-only origin/main
./scripts/ci-checks.sh # fmt, clippy -D warnings, tests — must be green
cargo build --workspace --release
2. Before installing — note any pre-existing (possibly unsafe) tool.
An older node-register-mcp may already sit in /usr/local/bin from an earlier
copy. setup-mother.sh overwrites it, but record what was there:
ls -l /usr/local/bin/node-register-mcp 2>/dev/null || echo "(none installed yet)"
3. Run the canonical setup. Copy the printed private key straight to the node seed (step 8) — do not leave it in shell scrollback or a logged session.
sudo ./packaging/mother/setup-mother.sh
4. The installed node-register-mcp is the hardened version.
The safe tool uses parameterized psql -v :'var' against hive_nodes; the old
one used E'${...}' string interpolation (SQL-injectable). Verify:
grep -c "E'" /usr/local/bin/node-register-mcp # expect 0
grep -c hive_nodes /usr/local/bin/node-register-mcp # expect > 0
5. The schema migrated, data preserved. On an existing DB, usb_nodes
should be renamed to hive_nodes (not duplicated), with the node_type column:
sudo -u postgres psql -d mother_hive -c "SELECT to_regclass('public.usb_nodes')" # expect (null)
sudo -u postgres psql -d mother_hive -c "SELECT to_regclass('public.hive_nodes')" # expect hive_nodes
sudo -u postgres psql -d mother_hive -c "\d hive_nodes" | grep node_type # column present
6. Peer auth works for the colibri role. This is what node-register-mcp
relies on (no password):
sudo -u colibri psql -d mother_hive -c "SELECT hostname, node_type, status FROM hive_nodes;"
If it errors with an auth failure, confirm the peer rule landed and precedes
any generic local all all line (pg_hba is first-match):
HBA=$(sudo -u postgres psql -tAc 'SHOW hba_file'); sudo grep -n 'mother_hive .*colibri .*peer' "$HBA"
7. External MCP registry has all three servers (jq-merge preserved any existing entries):
jq '.servers | keys' /usr/local/etc/colibri/external-mcp.json
# expect: build-colibri (mother-build) / geodesic-dome / node-register present
8. The SSH forced-command boundary allowlists. A non-allowlisted command must be rejected, not run:
ssh colibri@localhost 'rm -rf /' # expect: rejected (exit 1), nothing runs
ssh colibri@localhost tools # expect: MCP tool list
9. Daemon picked up the env and is live:
grep -E 'COLIBRI_AUTOSPAWN|COLIBRI_MCP_EXTERNAL_CALL' /usr/local/etc/colibri/provider.env
service colibri_daemon status
10. End-to-end (optional, with a real DEEPSEEK key present): register a
sample node and confirm the derive_capabilities() trigger populated
capabilities — see the Verification section below.
11. Key hygiene: the mother-mcp private key from step 3 is on the node seed
(CLAWDIESEED/colibri/ssh/mother-mcp) and nowhere else persistent. See
Key management.
Key management
The mother-mcp key is NOT baked into images. It lives:
- On mother:
/var/db/colibri/.ssh/mother-mcp+.pub(auto-generated by setup-mother.sh) - On USB nodes: placed on the seed partition at
CLAWDIESEED/colibri/ssh/mother-mcp(the live-seed importer picks it up at first boot and installs it at~/.ssh/mother-mcp)
One key per trust domain. The mother-mcp key is not reused for Forgejo or other services — its blast radius is limited to MCP over SSH.
USB-side external-mcp.json
The USB node registers mother as an external MCP server. Note: NO remote command — the forced-command wrapper handles entry:
{
"servers": {
"mother": {
"command": "ssh",
"args": [
"-i", "/home/clawdie/.ssh/mother-mcp",
"-o", "StrictHostKeyChecking=accept-new",
"colibri@100.72.229.63"
],
"env": {}
}
}
}
The colibri-daemon spawner pipes JSON-RPC on stdin/stdout. SSH connects
with an empty remote command; the forced-command wrapper starts
colibri-mcp in stdio MCP mode automatically.
Verification
# On mother: check the external MCP tools are live
colibri-mcp tools 2>&1 | grep external
# On mother: test node_register with a sample hw-probe
sudo clawdie-hw-probe 2>/dev/null | \
jq '{jsonrpc:"2.0",method:"tools/call",id:1,
params:{name:"node_register",
arguments:{hostname:"test-node",hw_profile:.}}}' | \
/usr/local/bin/node-register-mcp
# Check PostgreSQL
sudo -u colibri psql -d mother_hive -c \
"SELECT hostname, status, capabilities FROM hive_nodes;"
# From a USB node with the mother-mcp key:
ssh colibri@mother tools | grep node_register
Adding a USB node
- Copy the mother-mcp private key to the USB node's seed partition
(
CLAWDIESEED/colibri/ssh/mother-mcp) - On the USB, install
external-mcp.jsonas shown above - On the USB, install
clawdie-hw-probefrom clawdie-iso - Restart
colibri_daemon— autospawn runs hw-probe and registers the node with mother
See docs/USB-MOTHER-MCP-CONNECTION.md in clawdie-iso for the full
USB-side procedure.