Skip to content

Commit 887f556

Browse files
hyperpolymathclaude
andcommitted
feat(cartridge): add local-coord-mcp — localhost multi-instance coordination (#97)
New cartridge enabling multiple AI instances (Claude Code, Gemini, etc.) on the same machine to discover each other, exchange messages, and coordinate work via mutex-style task claiming. Security: loopback-only bind proven at compile time (Idris2 IsLoopback), per-session CSPRNG tokens, explicit federation opt-out (uninhabited IsFederated type). Port 7745. First cartridge to use Nickel (.ncl) as source-of-truth manifest. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 957d1cb commit 887f556

11 files changed

Lines changed: 1806 additions & 0 deletions

File tree

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
// SPDX-License-Identifier: PMPL-1.0-or-later
2+
// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
3+
= local-coord-mcp
4+
:toc:
5+
:toc-placement: preamble
6+
7+
Localhost multi-instance coordination cartridge for BoJ Server.
8+
9+
== Purpose
10+
11+
Enables multiple AI instances (Claude Code windows, Gemini, Copilot, etc.)
12+
running on the same machine to discover each other, exchange messages, and
13+
coordinate work — preventing duplicated effort and enabling intentional
14+
multi-window workflows.
15+
16+
== Security Model
17+
18+
=== Loopback-Only Binding (Compile-Time Guaranteed)
19+
20+
The Idris2 ABI (`SafeLocalCoord.idr`) provides an `IsLoopback` dependent type
21+
with exactly two inhabitants: `IsIPv4Loop` ("127.0.0.1") and `IsIPv6Loop`
22+
("::1"). The `BindConfig` record requires this proof — there is no code path
23+
that can construct a bind configuration for `0.0.0.0` or any LAN/external
24+
address. This is a **compile-time guarantee**, not a runtime check.
25+
26+
The Zig FFI and adapter honour this by hardcoding `127.0.0.1:7745`.
27+
28+
=== Session Tokens
29+
30+
Every peer receives a 128-bit CSPRNG token on registration. All subsequent
31+
calls must carry this token. A message from an unregistered process is rejected
32+
before dispatch.
33+
34+
=== No Federation
35+
36+
This cartridge explicitly does NOT participate in the Umoja federation network.
37+
The `FederationPolicy` type has a single constructor (`LocalOnly`) and the
38+
`IsFederated` type is uninhabited for `LocalOnly` — federation is impossible,
39+
not just disabled.
40+
41+
== Tools
42+
43+
[cols="1,3"]
44+
|===
45+
| Tool | Description
46+
47+
| `coord_register`
48+
| Register this instance. Returns a hybrid peer ID (e.g. `claude-7f3a`) and a session token.
49+
50+
| `coord_list_peers`
51+
| List all active peers on this machine with their IDs, kinds, and states.
52+
53+
| `coord_send`
54+
| Send a direct message to a peer or broadcast to all (`target: "*"`).
55+
56+
| `coord_receive`
57+
| Poll this peer's inbox for the next message.
58+
59+
| `coord_claim_task`
60+
| Mutex-style task claiming. First peer to claim wins; others are told who holds it.
61+
62+
| `coord_status`
63+
| Set this peer's current work status, visible to other peers.
64+
|===
65+
66+
== Architecture
67+
68+
----
69+
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
70+
│ Claude Code │ │ Claude Code │ │ Gemini │
71+
│ Window 1 │ │ Window 2 │ │ Window 3 │
72+
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
73+
│ MCP │ MCP │ MCP
74+
└────────┬────────┴────────┬────────┘
75+
│ │
76+
┌────────▼─────────────────▼────────┐
77+
│ MCP Bridge (main.js) │
78+
│ tools/call → local-coord-mcp │
79+
└────────────────┬──────────────────┘
80+
81+
┌────────────────▼──────────────────┐
82+
│ mod.js (Deno adapter) │
83+
│ POST http://127.0.0.1:7745/... │
84+
└────────────────┬──────────────────┘
85+
86+
┌────────────────▼──────────────────┐
87+
│ local_coord_adapter.zig │
88+
│ REST on 127.0.0.1:7745 │
89+
│ ↓ calls ↓ │
90+
│ local_coord_ffi.zig │
91+
│ Peer registry, message fan-out, │
92+
│ task claim mutex │
93+
└──────────────────────────────────┘
94+
Idris2 ABI proofs:
95+
IsLoopback, ValidPort,
96+
PeerState transitions,
97+
ClaimResult semantics
98+
----
99+
100+
== Peer Identity (Hybrid Model)
101+
102+
Peers are identified by a human-readable prefix + 4-char hex suffix:
103+
104+
- `claude-7f3a` — Claude Code instance
105+
- `gemini-b2c1` — Gemini instance
106+
- `copilot-4e9d` — Copilot instance
107+
- `custom-a1b2` — Other tool
108+
109+
== Port Assignment
110+
111+
| Port | Service |
112+
|------|---------|
113+
| 7745 | local-coord-mcp REST |
114+
115+
== Files
116+
117+
[cols="1,2"]
118+
|===
119+
| Path | Purpose
120+
121+
| `abi/LocalCoord/SafeLocalCoord.idr`
122+
| Loopback proof, port safety, session tokens, peer identity, federation opt-out
123+
124+
| `abi/LocalCoord/Protocol.idr`
125+
| Message types, task claiming, peer lifecycle, authenticated message wrapper
126+
127+
| `abi/local-coord-mcp.ipkg`
128+
| Idris2 package definition
129+
130+
| `ffi/local_coord_ffi.zig`
131+
| Peer registry, message fan-out, claim mutex, CSPRNG tokens
132+
133+
| `adapter/local_coord_adapter.zig`
134+
| REST server on 127.0.0.1:7745
135+
136+
| `cartridge.ncl`
137+
| Nickel source-of-truth manifest
138+
139+
| `cartridge.json`
140+
| Generated from Nickel — consumed by MCP bridge
141+
142+
| `mod.js`
143+
| Deno adapter for MCP tool dispatch
144+
|===
145+
146+
== Build
147+
148+
[source,bash]
149+
----
150+
# FFI tests
151+
cd ffi && zig build test
152+
153+
# Shared library
154+
cd ffi && zig build lib
155+
156+
# Adapter binary
157+
cd adapter && zig build
158+
159+
# Regenerate JSON manifest from Nickel
160+
nickel export --format json < cartridge.ncl > cartridge.json
161+
----
162+
163+
== Usage Example
164+
165+
[source]
166+
----
167+
# Window 1 (Claude Code)
168+
> coord_register(client_kind: "claude")
169+
→ { peer_id: "claude-7f3a", token: "abc123..." }
170+
171+
> coord_list_peers(token: "abc123...")
172+
→ [{ id: "claude-7f3a", kind: "claude", state: "active" },
173+
{ id: "gemini-b2c1", kind: "gemini", state: "active" }]
174+
175+
> coord_claim_task(token: "abc123...", task: "audit-boj-server")
176+
→ { result: "granted" }
177+
178+
> coord_send(token: "abc123...", target: "*", message: "I'm auditing boj-server, take something else")
179+
→ { sent: 1 }
180+
181+
# Window 2 (Gemini)
182+
> coord_receive(token: "def456...")
183+
→ { from: "claude-7f3a", message: "I'm auditing boj-server, take something else" }
184+
185+
> coord_claim_task(token: "def456...", task: "audit-boj-server")
186+
→ { result: "held", holder: "claude-7f3a" }
187+
188+
> coord_claim_task(token: "def456...", task: "audit-hypatia")
189+
→ { result: "granted" }
190+
----

0 commit comments

Comments
 (0)