|
| 1 | +<!-- SPDX-License-Identifier: MPL-2.0 --> |
| 2 | +<!-- Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk> --> |
| 3 | + |
| 4 | +# 16. mTLS + ed25519 federation stop-gap — spike |
| 5 | + |
| 6 | +Date: 2026-05-24 |
| 7 | + |
| 8 | +## Status |
| 9 | + |
| 10 | +Accepted (2026-05-24) — chosen over ADR-0015 because it closes the |
| 11 | +more user-visible survey gap (Ruflo as the only comparator with a |
| 12 | +cross-host story) and is additive: loopback bus and Idris2-verified |
| 13 | +intra-host ABI stay untouched. Positioned as the v1 transport; |
| 14 | +ADR-0010 (DID + ML-DSA-87 + ML-KEM-1024 + federated quarantine) |
| 15 | +remains the v2 upgrade path. |
| 16 | + |
| 17 | +**Implementation plan:** phased rollout, each phase its own PR off |
| 18 | +`main` after this ADR lands. |
| 19 | + |
| 20 | +| Phase | Scope | Estimated | |
| 21 | +|---|---|---| |
| 22 | +| 1 | Identity foundation — ed25519 keypair, pubkey export, `known_peers.toml` parser, `coord-tui --print-pubkey` flag. No transport. | ~1 day | |
| 23 | +| 2 | Envelope sign/verify on the loopback bus (round-trip validation before any wire work). | ~1 day | |
| 24 | +| 3 | TLS listener on `:7746` + mTLS handshake gated by `known_peers.toml`. | ~1-2 days | |
| 25 | +| 4 | New `coord_add_peer` / `coord_remove_peer` / `coord_list_remote_peers` tools — bridge + cartridge.json sync. | ~0.5 day | |
| 26 | +| 5 | Idris2 `Federation.idr` — envelope-boundary signature soundness obligation. | ~1 day | |
| 27 | +| 6 | Tests — two-instance round-trip, signature tampering, replay protection. | ~0.5 day | |
| 28 | + |
| 29 | +## Context |
| 30 | + |
| 31 | +The multi-agent MCP survey identified `ruvnet/ruflo` as the only |
| 32 | +comparator shipping a real cross-host story (mTLS + ed25519 federation |
| 33 | +across machines). `local-coord-mcp` is currently localhost-only |
| 34 | +(`127.0.0.1:7745`) — closing this gap is one of the two outstanding |
| 35 | +items from PR #143's spike. |
| 36 | + |
| 37 | +ADR-0010 already proposes the *ambitious* answer: DID-based identity, |
| 38 | +ML-DSA-87 signing, ML-KEM-1024 key exchange, federated quarantine. It |
| 39 | +sits at "Proposed (RFC)" status, tracked under epic #87 item 3, and is |
| 40 | +a substantial cross-cartridge build effort. |
| 41 | + |
| 42 | +This spike evaluates a deliberately **smaller** stop-gap: ship |
| 43 | +Ruflo-equivalent cross-host federation using mTLS + ed25519 *now*, |
| 44 | +without blocking on the post-quantum work in ADR-0010. The two are |
| 45 | +compatible: this becomes the v1 transport, ADR-0010 becomes the v2 |
| 46 | +upgrade path. |
| 47 | + |
| 48 | +## What the change does |
| 49 | + |
| 50 | +1. **Transport.** Add a TLS-terminating endpoint alongside the loopback |
| 51 | + bus — `https://<host>:7746/coord/federated/inbox` for inbound |
| 52 | + federated envelopes. Loopback bus (`:7745`) stays the default for |
| 53 | + intra-machine traffic and is untouched. |
| 54 | +2. **Identity.** Each peer generates an ed25519 keypair on first run |
| 55 | + (stored under `~/.cache/coord-tui/peer.key`). The public key is the |
| 56 | + peer's federated identity; the private key signs outbound envelopes. |
| 57 | + No DID method, no PKI hierarchy — leaf-only trust. |
| 58 | +3. **Peer trust.** A new file `~/.config/coord-tui/known_peers.toml` |
| 59 | + maps peer-id → `{host, port, pubkey}`. Federation only works between |
| 60 | + peers that have explicitly added each other (manual key exchange via |
| 61 | + `coord-tui --print-pubkey` and a shared channel). No discovery; no |
| 62 | + registry. |
| 63 | +4. **mTLS.** Both ends of the federated link present client certs |
| 64 | + derived from their ed25519 identity keys (SPKI-pinned, not |
| 65 | + CA-signed). Connection fails closed if the presented pubkey doesn't |
| 66 | + match `known_peers.toml`. |
| 67 | +5. **Envelope wrapping.** Outbound coord messages destined for a remote |
| 68 | + peer are signed (ed25519 over the canonical envelope bytes), wrapped |
| 69 | + in `{from, to, signature, payload}`, sent over the mTLS connection. |
| 70 | + Receiver verifies signature against `known_peers.toml`, then injects |
| 71 | + into the local bus as if it had arrived locally. |
| 72 | +6. **New tools.** `coord_add_peer(peer_id, host, port, pubkey)`, |
| 73 | + `coord_remove_peer(peer_id)`, `coord_list_remote_peers()`. No tool |
| 74 | + schema change to the existing `coord_send` / `coord_claim_task` |
| 75 | + surface — federation is transport-layer. |
| 76 | + |
| 77 | +## Cost |
| 78 | + |
| 79 | +| Surface | Work | |
| 80 | +|---|---| |
| 81 | +| Zig adapter | TLS listener (use `std.crypto.tls`), ed25519 sign/verify (`std.crypto.sign.Ed25519` — already available), known_peers parser, envelope wrap/unwrap. ~400-600 LOC. | |
| 82 | +| Idris2 ABI | New `Federation.idr` — but only proof obligations on the *envelope* boundary (signature verification soundness), not on the transport. ~150 lines; reuses existing `SafeHTTP` patterns. Class (J) believe_me may be unavoidable for `std.crypto.tls` opaque primitives — same axiom posture as the existing `Char`/`String` primitives. | |
| 83 | +| Bridge | New 3 `coord_*` tool entries in `tools.js` + cartridge.json; dispatcher routes them to the backend. No envelope-shape changes. | |
| 84 | +| coord-tui | `--print-pubkey` flag, `~/.config/coord-tui/known_peers.toml` reader, optional `coord-add-peer` shell helper. | |
| 85 | +| Tests | Round-trip test on two backend instances on different ports; signature-tampering rejection test; replay-protection (envelope timestamp + nonce). | |
| 86 | + |
| 87 | +Estimated: 4-6 working days end-to-end. |
| 88 | + |
| 89 | +## Benefit |
| 90 | + |
| 91 | +- Closes the survey's "cross-host transport" gap directly: matches |
| 92 | + Ruflo's posture, doesn't claim more. |
| 93 | +- Additive — the existing loopback bus and Idris2-verified intra-host |
| 94 | + ABI are untouched. No proof regression. |
| 95 | +- Sets up ADR-0010 as a clean v2 upgrade (swap ed25519→ML-DSA-87, |
| 96 | + add ML-KEM, add DID resolution) once that work is prioritised. |
| 97 | + Wire format is the same shape; only the cryptographic primitives |
| 98 | + change. |
| 99 | +- Real user-visible feature — agents on Jonathan's workstation can |
| 100 | + coordinate with agents on a build server, a sandbox VM, or a |
| 101 | + teammate's machine. |
| 102 | + |
| 103 | +## Risks / open questions |
| 104 | + |
| 105 | +- **Trust bootstrap is manual.** Exchanging pubkeys via a shared |
| 106 | + channel is the right answer for a v1 (it's how SSH known_hosts |
| 107 | + works), but it does mean federation has a per-peer setup cost. ADR- |
| 108 | + 0010's DID + cartridge-index resolution removes this; v1 doesn't. |
| 109 | +- **No federated quarantine.** Tier-2+ envelopes from a remote peer |
| 110 | + hit the *local* master for review — no upstream supervision model. |
| 111 | + This is a feature gap relative to ADR-0010, not a bug, but worth |
| 112 | + naming. |
| 113 | +- **Crypto agility.** ed25519 in a post-quantum world has a finite |
| 114 | + shelf life. Estate standards already commit to ML-DSA-87. This |
| 115 | + spike's stop-gap framing is honest about that: v1 lasts until ADR- |
| 116 | + 0010 lands, not forever. CHANGELOG and README should say so. |
| 117 | +- **Idris2 transport-layer proofs.** TLS internals are opaque to the |
| 118 | + Idris2 backend-assurance harness — same axiomatisation posture as |
| 119 | + the existing 5 `believe_me` sites. Net new principled assumption, |
| 120 | + not a regression. |
| 121 | + |
| 122 | +## Verdict |
| 123 | + |
| 124 | +Smaller scope than ADR-0015, lower architectural risk than ADR-0010, |
| 125 | +and directly closes the only survey gap where `local-coord-mcp` |
| 126 | +clearly trails a comparator. The "this is explicitly a stop-gap" |
| 127 | +framing keeps the door open for ADR-0010 without making it a |
| 128 | +blocker. |
| 129 | + |
| 130 | +**Recommendation:** Prefer this over ADR-0015 if the goal is "ship a |
| 131 | +visible new capability that the survey identified as missing". |
| 132 | +Combine with ADR-0015 only if file-locks are a stated user need; |
| 133 | +otherwise ADR-0015 is gold-plating relative to the survey's actual |
| 134 | +findings. |
| 135 | + |
| 136 | +## Out of scope |
| 137 | + |
| 138 | +- DID identity (ADR-0010, v2 upgrade). |
| 139 | +- Post-quantum crypto (ADR-0010, v2 upgrade). |
| 140 | +- Discovery / registry (deliberate — manual known_peers keeps the |
| 141 | + trust story legible). |
| 142 | +- Federated quarantine semantics (would land alongside DID work). |
| 143 | +- Wire format negotiation for v1↔v2 transition — addressed when |
| 144 | + ADR-0010 implementation starts. |
0 commit comments