diff --git a/docs/PLAN-MOTHER-MCP-VAULT-KEYS.md b/docs/PLAN-MOTHER-MCP-VAULT-KEYS.md new file mode 100644 index 0000000..5fd470d --- /dev/null +++ b/docs/PLAN-MOTHER-MCP-VAULT-KEYS.md @@ -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: + 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@ 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 + → if exists: bw edit item notes="$PUBKEY" + → if not: bw create item --name "$(hostname)" --notes "$PUBKEY" --collectionid +``` + +### 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 `) +- [ ] 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@ 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 | diff --git a/docs/README.md b/docs/README.md index dfba82d..d1aba7c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -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 |