docs: plan mother MCP link — Vaultwarden pubkey exchange #141

Merged
clawdie merged 1 commit from docs/mother-mcp-vault-keys-plan into main 2026-06-21 20:36:19 +02:00
2 changed files with 163 additions and 0 deletions

View file

@ -0,0 +1,162 @@
# Plan: Mother MCP Link — Vaultwarden Pubkey Exchange
**Direction:** B — we (ISO agent) call mother via SSH. Pubkeys exchanged
through Vaultwarden, no operator copy-paste.
**Scope:** OSA acts as mother until dedicated machine exists. Shell scripts
first, colibri-vault crate later when the contract is solid.
---
## Flow
```
ISO (agent) VAULTWARDEN MOTHER (OSA)
─────────────────────────────────────── ─────────────── ─────────────────────────────
[Enable Mother clicked]
1. ssh-keygen -t ed25519
(if no key yet)
2. bw publish pubkey ──────────────► hive-pubkeys item
name: <hostname>
notes: ssh-ed25519 AAAA...
[cron: @every 5m]
3. bw list hive-pubkeys
4. rebuild authorized_keys.hive
command="colibri-mcp",restrict
one line per agent
5. update external-mcp.json ──────────────────────────────────────► ready for connections
ssh -i key colibri@<ts-ip> colibri-mcp
```
## Files
### Our side (clawdie-iso)
| File | Change | What |
|------|--------|------|
| `clawdie-enable-mother.sh` | Extend | Add keygen + vault publish BEFORE the external-mcp.json update |
| `clawdie-vault-fetch` (colibri) | Extend | Add `--publish-pubkey` mode: create/update item in hive-pubkeys |
### Mother side (OSA, new)
| File | What |
|------|------|
| `mother-sync-hive-keys.sh` | Pull all pubkeys from vault → rebuild authorized_keys.hive |
| `/etc/cron.d/mother-hive-keys` | `@every 5m` cron entry |
| sshd_config change | Add `AuthorizedKeysFile ... /var/db/colibri/.ssh/authorized_keys.hive` |
---
## Step-by-step
### A. Key generation (our side)
```
clawdie-enable-mother.sh, new step [1/3]:
if [ ! -f ~/.ssh/id_ed25519 ]; then
ssh-keygen -t ed25519 -N "" -C "colibri@$(hostname)" -f ~/.ssh/id_ed25519
fi
PUBKEY=$(cat ~/.ssh/id_ed25519.pub)
```
### B. Publish to Vaultwarden (our side)
```
clawdie-enable-mother.sh, new step [2/3]:
clawdie-vault-fetch --publish-pubkey "$PUBKEY" --collection hive-pubkeys
What this does:
- bw login (using BW_* from provider.env)
- bw get collection hive-pubkeys (create if absent)
- bw get item "$(hostname)" --collectionid <id>
→ if exists: bw edit item <id> notes="$PUBKEY"
→ if not: bw create item --name "$(hostname)" --notes "$PUBKEY" --collectionid <id>
```
### C. Update external-mcp.json (our side)
```
clawdie-enable-mother.sh, step [3/3] (existing, with new identity file):
jq --arg key "$HOME/.ssh/id_ed25519" \
'.servers.mother = {
"command": "ssh",
"args": ["-i", $key, "-o", "StrictHostKeyChecking=accept-new",
"colibri@${MOTHER_TS_IP}", "colibri-mcp"],
"env": {}
}' "$EXTERNAL_MCP" > "$tmp" && mv "$tmp" "$EXTERNAL_MCP"
```
### D. Mother sync (cron)
```
mother-sync-hive-keys.sh:
1. bw login (BW_* from provider.env)
2. COLLECTION_ID=$(bw get collection hive-pubkeys --id)
3. bw list items --collectionid $COLLECTION_ID
4. For each item:
- HOSTNAME=$(bw get item $id | jq -r '.name')
- PUBKEY=$(bw get item $id | jq -r '.notes')
- echo "command=\"colibri-mcp\",restrict,no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding $PUBKEY colibri@$HOSTNAME"
5. Write all to /var/db/colibri/.ssh/authorized_keys.hive (atomic: mktemp + mv)
6. chmod 600
Cron: @every 5m root /usr/local/sbin/mother-sync-hive-keys.sh
```
### E. sshd configuration (mother, one-time)
```
# /etc/ssh/sshd_config addition:
AuthorizedKeysFile .ssh/authorized_keys /var/db/colibri/.ssh/authorized_keys.hive
# Reload:
service sshd reload
```
---
## Security properties
| Property | How |
|----------|-----|
| Rebuild, not append | Each sync regenerates the file — deleting a vault item = revocation |
| Restriction applied by mother | `command="colibri-mcp",restrict` — not baked by publisher |
| Dedicated key file | `authorized_keys.hive` separate from operator keys |
| No shell access | `restrict` blocks everything except the forced command |
| Atomic write | `mktemp` + `mv` — no partial reads |
| TOFU on first connect | `StrictHostKeyChecking=accept-new` — auto-trust on first connection |
---
## Acceptance
- [ ] Click "Enable Mother" → keypair created if absent
- [ ] Pubkey published to Vaultwarden (verify: `bw get item <hostname>`)
- [ ] external-mcp.json updated with SSH + identity file
- [ ] Mother cron syncs within 5 minutes
- [ ] `authorized_keys.hive` contains the restricted entry
- [ ] Pi can call mother's tools via `ssh -i key colibri@<ts-ip> colibri-mcp`
- [ ] Delete vault item → next sync removes access (revocation tested)
---
## Sequencing
| Step | Repo | Content |
|------|------|---------|
| 1 | colibri | Extend `clawdie-vault-fetch` with `--publish-pubkey` |
| 2 | clawdie-iso | Extend `clawdie-enable-mother.sh` — keygen + publish |
| 3 | — | Create `mother-sync-hive-keys.sh` on OSA |
| 4 | — | Wire cron + sshd_config on OSA |
| 5 | — | End-to-end test: ISO → vault → OSA → SSH → colibri-mcp |

View file

@ -15,6 +15,7 @@ A quick-reference guide to every document in this folder.
| [`ISO-ACCEPTANCE-RUNBOOK.md`](ISO-ACCEPTANCE-RUNBOOK.md) | Post-boot acceptance commands after staging Colibri into an ISO | Codex (FreeBSD) |
| [`ISO-SERVICE-LAYOUT.md`](ISO-SERVICE-LAYOUT.md) | `rc.conf` service layout for the ISO image | All |
| [`MULTI-AGENT-HOST-PLAN.md`](MULTI-AGENT-HOST-PLAN.md) | **Current sprint**: multi-agent task-board tests + CLI surface gaps | All agents |
| [`PLAN-MOTHER-MCP-VAULT-KEYS.md`](PLAN-MOTHER-MCP-VAULT-KEYS.md) | Vaultwarden pubkey exchange for mother MCP link (direction B) | Sam & Hermes |
| [`PRIORITY-HANDOFF-ISO-SPAWN-COST.md`](PRIORITY-HANDOFF-ISO-SPAWN-COST.md) | ISO boot validation, Pi spawn path, cost mode enforcement (P2/P3 done) | All agents |
| [`TRUSS-SPAWN-ANALYSIS.md`](TRUSS-SPAWN-ANALYSIS.md) | truss trace of jail-spawn Permission Denied — root cause + fix | Debugging |
| [`VAULT-PROVISION-FIRST-PROOF.md`](VAULT-PROVISION-FIRST-PROOF.md) | First-proof runbook: vault → jail → `.env` chain (clean CLI) | Agents, Sam |