groove-dev 0.27.8 → 0.27.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/node_modules/@groove-dev/daemon/src/api.js +460 -25
  2. package/node_modules/@groove-dev/daemon/src/index.js +7 -0
  3. package/node_modules/@groove-dev/daemon/src/introducer.js +72 -4
  4. package/node_modules/@groove-dev/daemon/src/journalist.js +66 -11
  5. package/node_modules/@groove-dev/daemon/src/process.js +67 -7
  6. package/node_modules/@groove-dev/daemon/src/registry.js +1 -1
  7. package/node_modules/@groove-dev/daemon/src/repo-import.js +541 -0
  8. package/node_modules/@groove-dev/daemon/src/rotator.js +28 -1
  9. package/node_modules/@groove-dev/daemon/src/supervisor.js +2 -1
  10. package/node_modules/@groove-dev/daemon/src/tunnel-manager.js +504 -0
  11. package/node_modules/@groove-dev/daemon/src/validate.js +13 -0
  12. package/node_modules/@groove-dev/daemon/test/journalist.test.js +5 -4
  13. package/node_modules/@groove-dev/daemon/test/rotator.test.js +4 -1
  14. package/node_modules/@groove-dev/gui/dist/assets/index-BE6lYcd7.css +1 -0
  15. package/node_modules/@groove-dev/gui/dist/assets/index-zdzOLAZM.js +677 -0
  16. package/node_modules/@groove-dev/gui/dist/index.html +2 -2
  17. package/node_modules/@groove-dev/gui/src/app.css +14 -0
  18. package/node_modules/@groove-dev/gui/src/app.jsx +13 -0
  19. package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +130 -1
  20. package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +2 -2
  21. package/node_modules/@groove-dev/gui/src/components/agents/agent-mdfiles.jsx +43 -1
  22. package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +141 -1
  23. package/node_modules/@groove-dev/gui/src/components/dashboard/fleet-panel.jsx +3 -3
  24. package/node_modules/@groove-dev/gui/src/components/dashboard/intel-panel.jsx +4 -4
  25. package/node_modules/@groove-dev/gui/src/components/dashboard/routing-chart.jsx +3 -3
  26. package/node_modules/@groove-dev/gui/src/components/dashboard/team-burn-panel.jsx +1 -1
  27. package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +4 -4
  28. package/node_modules/@groove-dev/gui/src/components/layout/app-shell.jsx +7 -1
  29. package/node_modules/@groove-dev/gui/src/components/layout/breadcrumb-bar.jsx +26 -8
  30. package/node_modules/@groove-dev/gui/src/components/layout/command-palette.jsx +14 -4
  31. package/node_modules/@groove-dev/gui/src/components/layout/status-bar.jsx +46 -11
  32. package/node_modules/@groove-dev/gui/src/components/marketplace/repo-card.jsx +64 -0
  33. package/node_modules/@groove-dev/gui/src/components/marketplace/repo-import.jsx +363 -0
  34. package/node_modules/@groove-dev/gui/src/components/marketplace/repo-nuke-dialog.jsx +68 -0
  35. package/node_modules/@groove-dev/gui/src/components/pro/pro-gate.jsx +22 -0
  36. package/node_modules/@groove-dev/gui/src/components/pro/upgrade-card.jsx +48 -0
  37. package/node_modules/@groove-dev/gui/src/components/settings/quick-connect.jsx +129 -0
  38. package/node_modules/@groove-dev/gui/src/components/settings/remote-server-card.jsx +243 -0
  39. package/node_modules/@groove-dev/gui/src/components/settings/server-dialog.jsx +192 -0
  40. package/node_modules/@groove-dev/gui/src/components/ui/approval-modal.jsx +63 -0
  41. package/node_modules/@groove-dev/gui/src/components/ui/toast.jsx +1 -1
  42. package/node_modules/@groove-dev/gui/src/lib/edition.js +4 -0
  43. package/node_modules/@groove-dev/gui/src/lib/electron.js +17 -0
  44. package/node_modules/@groove-dev/gui/src/lib/status.js +1 -0
  45. package/node_modules/@groove-dev/gui/src/stores/groove.js +139 -6
  46. package/node_modules/@groove-dev/gui/src/views/dashboard.jsx +38 -39
  47. package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +82 -0
  48. package/node_modules/@groove-dev/gui/src/views/settings.jsx +66 -0
  49. package/node_modules/@groove-dev/gui/vite.config.js +3 -0
  50. package/package.json +7 -2
  51. package/packages/daemon/src/api.js +460 -25
  52. package/packages/daemon/src/index.js +7 -0
  53. package/packages/daemon/src/introducer.js +72 -4
  54. package/packages/daemon/src/journalist.js +66 -11
  55. package/packages/daemon/src/process.js +67 -7
  56. package/packages/daemon/src/registry.js +1 -1
  57. package/packages/daemon/src/repo-import.js +541 -0
  58. package/packages/daemon/src/rotator.js +28 -1
  59. package/packages/daemon/src/supervisor.js +2 -1
  60. package/packages/daemon/src/tunnel-manager.js +504 -0
  61. package/packages/daemon/src/validate.js +13 -0
  62. package/packages/gui/dist/assets/index-BE6lYcd7.css +1 -0
  63. package/packages/gui/dist/assets/index-zdzOLAZM.js +677 -0
  64. package/packages/gui/dist/index.html +2 -2
  65. package/packages/gui/node_modules/.vite/deps/@codemirror_lang-html.js +3 -3
  66. package/packages/gui/node_modules/.vite/deps/@codemirror_lang-javascript.js +2 -2
  67. package/packages/gui/node_modules/.vite/deps/@codemirror_lang-markdown.js +3 -3
  68. package/packages/gui/node_modules/.vite/deps/@codemirror_lang-python.js +5 -5
  69. package/packages/gui/node_modules/.vite/deps/@radix-ui_react-dialog.js +3 -3
  70. package/packages/gui/node_modules/.vite/deps/@radix-ui_react-scroll-area.js +1 -1
  71. package/packages/gui/node_modules/.vite/deps/@radix-ui_react-tabs.js +5 -5
  72. package/packages/gui/node_modules/.vite/deps/@radix-ui_react-tooltip.js +3 -3
  73. package/packages/gui/node_modules/.vite/deps/_metadata.json +53 -53
  74. package/packages/gui/node_modules/.vite/deps/{chunk-WYSQD5ZG.js → chunk-DH7AESXW.js} +2 -2
  75. package/packages/gui/node_modules/.vite/deps/{chunk-KXLIKZFX.js → chunk-GFE3G4IN.js} +133 -133
  76. package/packages/gui/node_modules/.vite/deps/chunk-GFE3G4IN.js.map +7 -0
  77. package/packages/gui/node_modules/.vite/deps/{chunk-3LBP22MX.js → chunk-LKZVMLRH.js} +6 -6
  78. package/packages/gui/node_modules/.vite/deps/{chunk-J6DMOQWP.js → chunk-MCVDVNE5.js} +2 -2
  79. package/packages/gui/node_modules/.vite/deps/{chunk-3Q7HT7ZF.js → chunk-SPKVQGZX.js} +6 -6
  80. package/packages/gui/src/app.css +14 -0
  81. package/packages/gui/src/app.jsx +13 -0
  82. package/packages/gui/src/components/agents/agent-config.jsx +130 -1
  83. package/packages/gui/src/components/agents/agent-feed.jsx +2 -2
  84. package/packages/gui/src/components/agents/agent-mdfiles.jsx +43 -1
  85. package/packages/gui/src/components/agents/spawn-wizard.jsx +141 -1
  86. package/packages/gui/src/components/dashboard/fleet-panel.jsx +3 -3
  87. package/packages/gui/src/components/dashboard/intel-panel.jsx +4 -4
  88. package/packages/gui/src/components/dashboard/routing-chart.jsx +3 -3
  89. package/packages/gui/src/components/dashboard/team-burn-panel.jsx +1 -1
  90. package/packages/gui/src/components/layout/activity-bar.jsx +4 -4
  91. package/packages/gui/src/components/layout/app-shell.jsx +7 -1
  92. package/packages/gui/src/components/layout/breadcrumb-bar.jsx +26 -8
  93. package/packages/gui/src/components/layout/command-palette.jsx +14 -4
  94. package/packages/gui/src/components/layout/status-bar.jsx +46 -11
  95. package/packages/gui/src/components/marketplace/repo-card.jsx +64 -0
  96. package/packages/gui/src/components/marketplace/repo-import.jsx +363 -0
  97. package/packages/gui/src/components/marketplace/repo-nuke-dialog.jsx +68 -0
  98. package/packages/gui/src/components/pro/pro-gate.jsx +22 -0
  99. package/packages/gui/src/components/pro/upgrade-card.jsx +48 -0
  100. package/packages/gui/src/components/settings/quick-connect.jsx +129 -0
  101. package/packages/gui/src/components/settings/remote-server-card.jsx +243 -0
  102. package/packages/gui/src/components/settings/server-dialog.jsx +192 -0
  103. package/packages/gui/src/components/ui/approval-modal.jsx +63 -0
  104. package/packages/gui/src/components/ui/toast.jsx +1 -1
  105. package/packages/gui/src/lib/edition.js +4 -0
  106. package/packages/gui/src/lib/electron.js +17 -0
  107. package/packages/gui/src/lib/status.js +1 -0
  108. package/packages/gui/src/stores/groove.js +139 -6
  109. package/packages/gui/src/views/dashboard.jsx +38 -39
  110. package/packages/gui/src/views/marketplace.jsx +82 -0
  111. package/packages/gui/src/views/settings.jsx +66 -0
  112. package/packages/gui/vite.config.js +3 -0
  113. package/integrations/FEDERATION_PLAN.md +0 -583
  114. package/integrations/VOICE_PLAN.md +0 -232
  115. package/node_modules/@groove-dev/gui/dist/assets/index-CwmR3-HY.css +0 -1
  116. package/node_modules/@groove-dev/gui/dist/assets/index-DiCjVtQL.js +0 -652
  117. package/packages/gui/dist/assets/index-CwmR3-HY.css +0 -1
  118. package/packages/gui/dist/assets/index-DiCjVtQL.js +0 -652
  119. package/packages/gui/node_modules/.vite/deps/chunk-KXLIKZFX.js.map +0 -7
  120. package/test-slack.mjs +0 -28
  121. /package/packages/gui/node_modules/.vite/deps/{chunk-WYSQD5ZG.js.map → chunk-DH7AESXW.js.map} +0 -0
  122. /package/packages/gui/node_modules/.vite/deps/{chunk-3LBP22MX.js.map → chunk-LKZVMLRH.js.map} +0 -0
  123. /package/packages/gui/node_modules/.vite/deps/{chunk-J6DMOQWP.js.map → chunk-MCVDVNE5.js.map} +0 -0
  124. /package/packages/gui/node_modules/.vite/deps/{chunk-3Q7HT7ZF.js.map → chunk-SPKVQGZX.js.map} +0 -0
@@ -1,583 +0,0 @@
1
- # Multi-Server Federation Plan — Cross-Instance Agent Communication
2
-
3
- > Status: Planning complete, not yet started
4
- > Created: 2026-04-12
5
- > Owner: Rok
6
-
7
- ---
8
-
9
- ## Current State
10
-
11
- Already built:
12
- - federation.js — Ed25519 keypair per daemon, peer pairing via HTTP key exchange, signed contracts with 5-min replay protection, peer storage on disk
13
- - connect.js — SSH tunnel to remote daemon (groove connect user@host)
14
- - CLI commands: groove federation pair/unpair/list/status
15
- - API endpoints: /api/federation/* (status, peers, pair, unpair, contract send/receive)
16
-
17
- Not built:
18
- - Agent-to-agent communication across instances
19
- - WebSocket bridge between paired daemons
20
- - Ambassador agent role
21
- - IP whitelisting on bridge port
22
- - Nonce-based replay protection
23
- - Ephemeral (job-scoped) connections
24
- - GUI for any federation features
25
-
26
- ---
27
-
28
- ## Vision
29
-
30
- Two or more Groove instances coordinate through ambassador agents. A user spawns an ambassador, it opens an ephemeral bridge to a paired peer, and agents on both sides work together as if they were on the same machine. The connection is job-scoped — it opens when work starts and closes when work finishes. Security is layered: IP whitelist first, then Ed25519 signatures, then nonces, all over Tailscale encryption.
31
-
32
- ---
33
-
34
- ## The Ambassador Model
35
-
36
- ### How It Works
37
-
38
- 1. Pairing (already built): `groove federation pair 100.64.0.2` — both daemons exchange Ed25519 keys, become trusted peers. This is a one-time setup. The peer's IP is stored and becomes the whitelist entry.
39
-
40
- 2. Spawning an ambassador: User picks "ambassador" role in the Spawn Wizard and selects a paired peer. The ambassador agent gets injected context: remote daemon's agent registry, remote project map, and a message channel to the remote side.
41
-
42
- 3. Bridge opens: An ephemeral WebSocket connection opens between the two daemons on the bridge port (31416). Both sides verify IP whitelist, then Ed25519 handshake.
43
-
44
- 4. Agents coordinate: Local and remote agents exchange messages, status updates, scope claims, and project map syncs through the bridge. The ambassador relays and translates.
45
-
46
- 5. Job completes: All linked agents finish. Bridge auto-closes. WebSocket torn down, port released.
47
-
48
- ### Use Cases
49
-
50
- - API server (VPS) + frontend (local): backend and frontend agents coordinate the build together, sharing API contracts, types, and integration points
51
- - Monorepo split: one Groove instance per service, ambassadors coordinate cross-service changes
52
- - Staging + production: agent on staging validates changes before ambassador signals production instance to deploy
53
- - Team collaboration: two developers on different machines, each running Groove, coordinate through federation
54
-
55
- ---
56
-
57
- ## Security Architecture
58
-
59
- ### Layered Defense
60
-
61
- ```
62
- Incoming connection to :31416
63
- |
64
- [Layer 1: IP whitelist] — not on list? TCP RST, logged. Done.
65
- |
66
- [Layer 2: Tailscale encryption] — WireGuard tunnel, end-to-end
67
- |
68
- [Layer 3: WebSocket upgrade]
69
- |
70
- [Layer 4: Ed25519 hello verification] — bad signature? close.
71
- |
72
- [Layer 5: Nonce + timestamp check] — replay? reject.
73
- |
74
- [Layer 6: JSON schema validation] — malformed/oversized? reject.
75
- |
76
- [Layer 7: Route to agent inbox]
77
- |
78
- [Layer 8: Agent applies scope + safety rules]
79
- ```
80
-
81
- ### Layer 1 — IP Whitelist (First Gate)
82
-
83
- The bridge port checks source IP against the peer allowlist before any protocol logic, any crypto, any parsing. If you're not on the list, connection is dropped at TCP level.
84
-
85
- - Allowlist source: federation/peers/*.json (each peer file stores host IP)
86
- - Each `federation pair` adds one IP, each `federation unpair` removes one
87
- - Non-whitelisted IPs never reach the WebSocket layer
88
- - Bridge port appears closed to non-whitelisted IPs (port scan sees nothing)
89
- - All rejections logged to audit
90
-
91
- ```
92
- allowedIPs = federation.getPeers().map(p => p.host)
93
- if sourceIP not in allowedIPs:
94
- drop connection (TCP RST)
95
- audit.log('bridge.rejected', { sourceIP, reason: 'not_whitelisted' })
96
- return
97
- ```
98
-
99
- Eliminates: rogue pairing, port scanning, brute force, flooding from unknown IPs.
100
-
101
- ### Layer 2 — Tailscale (Transport Encryption)
102
-
103
- Primary transport: Tailscale (WireGuard). Provides:
104
- - End-to-end encryption between machines
105
- - Stable IPs (100.64.x.x) that don't change
106
- - NAT traversal (no port forwarding needed)
107
- - Machine identity verification
108
-
109
- Fallback transports:
110
- - SSH tunnel (groove connect) — already encrypted
111
- - For non-Tailscale static IPs: TLS required on bridge WebSocket (wss://)
112
- - bridge.js refuses unencrypted connections to non-Tailscale, non-localhost targets
113
-
114
- ### Layer 3 — Ed25519 Signatures (Message Authentication)
115
-
116
- Every bridge message is signed with the sender daemon's Ed25519 private key (already built in federation.js). Receiving daemon verifies with the stored public key.
117
-
118
- Handles: compromised peer forgery (attacker would need the private key).
119
-
120
- ### Layer 4 — Nonce + Timestamp (Replay Protection)
121
-
122
- Every message includes:
123
- - nonce: UUID v4, unique per message
124
- - timestamp: ISO 8601, must be within 5-minute window
125
-
126
- Receiving daemon:
127
- - Tracks seen nonces in memory (Set, pruned every 10 minutes)
128
- - Rejects duplicate nonces
129
- - Rejects messages outside the 5-minute timestamp window
130
- - Combined with signature, eliminates replay attacks entirely
131
-
132
- ### Layer 5 — Input Validation
133
-
134
- - JSON schema validation on every received message
135
- - Max message size: 1MB
136
- - Max JSON depth: 10 levels
137
- - No prototype pollution (__proto__, constructor, prototype keys rejected)
138
- - Payload is text only — no executable code, no serialized objects
139
- - Bridge never passes payloads to eval, new Function, or child_process
140
-
141
- ### Layer 6 — Scope and Approval
142
-
143
- - Remote peers can only send messages, not execute commands
144
- - Messages go to agent inbox — the agent decides what to do
145
- - Agent's own scope restrictions and CLAUDE.md safety rules apply
146
- - Dangerous cross-instance actions (task handoffs, file syncs) require local user approval
147
- - Message type allowlist: only hello, agent.status, message, coordinate, sync, heartbeat
148
-
149
- ### Additional Measures
150
-
151
- - Rate limiting: 100 messages/min per peer on bridge
152
- - Anomaly detection: flag sudden spikes in message volume, auto-pause connection
153
- - Key rotation: `groove federation rotate-keys` generates new keypair, invalidates old, all peers must re-pair
154
- - Audit trail: every cross-daemon event logged (send, receive, reject, connect, disconnect)
155
-
156
- ---
157
-
158
- ## IP Whitelist — Multi-Peer Mesh
159
-
160
- Scales naturally with each pairing:
161
-
162
- ```
163
- Server A (laptop): allowlist = [B: 100.64.0.2, C: 100.64.0.3]
164
- Server B (api-vps): allowlist = [A: 100.64.0.1, C: 100.64.0.3]
165
- Server C (frontend-vps): allowlist = [A: 100.64.0.1, B: 100.64.0.2]
166
- ```
167
-
168
- Each server only talks to servers it has explicitly paired with. Peer-to-peer, no central hub. Adding a new server: `groove federation pair <new-ip>` on both sides.
169
-
170
- ### Dynamic IP Handling
171
-
172
- | IP Type | Stability | Recommendation |
173
- |---------|-----------|----------------|
174
- | Tailscale (100.64.x.x) | Stable, tied to machine identity | Primary path, recommended |
175
- | Static VPS | Stable | Works perfectly |
176
- | Dynamic home IP | Changes | Use Tailscale, or re-pair when IP changes |
177
-
178
- ---
179
-
180
- ## Ephemeral Connections — Job Mode
181
-
182
- Connections are tied to a job, not a peer relationship. The pairing (key exchange + IP whitelist) is permanent, but the live WebSocket is temporary.
183
-
184
- ### Connection Lifecycle
185
-
186
- ```
187
- User spawns ambassador with peer + task
188
- -> bridge.open(peerId, { jobId, ttl, agents })
189
- -> IP whitelist check
190
- -> WebSocket connects, Ed25519 handshake
191
- -> Agents work, messages flow
192
- -> Job completes (all linked agents done)
193
- -> bridge.close(peerId, jobId)
194
- -> WebSocket torn down
195
- ```
196
-
197
- ### Auto-Close Triggers
198
-
199
- Any of these kills the connection:
200
-
201
- | Trigger | Default | Description |
202
- |---------|---------|-------------|
203
- | Job complete | Always | All linked agents complete/crash/killed |
204
- | Idle timeout | 15 min | No messages in either direction |
205
- | TTL expiry | 2 hours | Hard cap, even if agents still running |
206
- | Manual disconnect | User action | GUI button or CLI command |
207
- | Daemon shutdown | Always | Graceful close with signed goodbye |
208
-
209
- ### Keep-Alive Mode (Opt-In)
210
-
211
- For persistent coordination (always-on servers), user sets keepAlive: true when spawning. This disables idle timeout and extends TTL to 24h with auto-renew. Default is ephemeral.
212
-
213
- ---
214
-
215
- ## Protocol Design — WebSocket Bridge
216
-
217
- ### Message Envelope
218
-
219
- Every message through the bridge:
220
-
221
- ```json
222
- {
223
- "type": "message",
224
- "nonce": "550e8400-e29b-41d4-a716-446655440000",
225
- "timestamp": "2026-04-12T20:30:00.000Z",
226
- "from": { "daemon": "abc123", "agent": "frontend-1" },
227
- "to": { "daemon": "def456", "agent": "backend-3" },
228
- "payload": { ... },
229
- "signature": "base64-ed25519-signature"
230
- }
231
- ```
232
-
233
- ### Message Types
234
-
235
- | Type | Direction | Description |
236
- |------|-----------|-------------|
237
- | hello | Bidirectional | Initial handshake with agent registry + job metadata |
238
- | goodbye | Bidirectional | Graceful close with reason |
239
- | heartbeat | Bidirectional | Keepalive, every 30s, dead peer at 90s |
240
- | agent.status | Broadcast | Agent spawned/completed/crashed/killed on remote |
241
- | message | Routed | Direct agent-to-agent message |
242
- | coordinate | Routed | Scope negotiation, task handoff, shared decision |
243
- | sync.registry | Request/Response | Full agent registry exchange |
244
- | sync.projectmap | Request/Response | Journalist project map exchange |
245
-
246
- ### Connection Flow
247
-
248
- ```
249
- Daemon A Daemon B
250
- | |
251
- |--- TCP connect to :31416 ---------->|
252
- | [IP whitelist check]
253
- |<-- TCP accept ---------------------|
254
- | |
255
- |--- WS upgrade -------------------->|
256
- |<-- WS accept ----------------------|
257
- | |
258
- |--- hello (signed, with registry) ->|
259
- |<-- hello (signed, with registry) --|
260
- | |
261
- |--- message (A:frontend -> B:backend) -->|
262
- |<-- message (B:backend -> A:frontend) ---|
263
- | |
264
- |--- heartbeat --------------------->|
265
- |<-- heartbeat ----------------------|
266
- | |
267
- |--- goodbye (job complete) -------->|
268
- |<-- goodbye (ack) ------------------|
269
- |--- TCP close --------------------->|
270
- ```
271
-
272
- ---
273
-
274
- ## Daemon Architecture
275
-
276
- ### New Files
277
-
278
- #### bridge.js — Ephemeral WebSocket Bridge Manager
279
-
280
- ```
281
- class Bridge {
282
- constructor(daemon)
283
- - connections: Map<jobId, Connection>
284
- - seenNonces: Set (pruned every 10 min)
285
- - bridgeServer: WebSocket.Server on :31416
286
-
287
- open(peerId, { jobId, linkedAgents, ttl, keepAlive })
288
- - Validate peer is paired
289
- - Enforce transport security (Tailscale/localhost/wss)
290
- - Connect WebSocket to peer's bridge port
291
- - Send signed hello with job metadata + agent registry
292
- - Start idle timer + TTL timer
293
- - Return connection handle
294
-
295
- close(jobId)
296
- - Send signed goodbye
297
- - Close WebSocket
298
- - Clean up timers
299
- - Audit log
300
-
301
- send(jobId, message)
302
- - Add nonce (UUID v4) + timestamp
303
- - Sign with Ed25519
304
- - Validate message size (<1MB)
305
- - Route through connection
306
- - Update lastActivity
307
-
308
- receive(rawMessage)
309
- - Verify source IP is whitelisted
310
- - Verify Ed25519 signature
311
- - Check nonce (reject duplicate)
312
- - Check timestamp (reject >5min)
313
- - Validate JSON schema
314
- - Route to target agent's inbox
315
- - Emit event for GUI WebSocket
316
-
317
- _onAgentStatusChange(agentId, status)
318
- - Broadcast to all connections that include this agent
319
- - If all linked agents done, trigger close
320
-
321
- _checkIdle(jobId)
322
- - If lastActivity > idleTimeout, close
323
-
324
- _enforceTransportSecurity(peerHost)
325
- - Tailscale range (100.64.0.0/10): allow ws://
326
- - localhost/127.0.0.1: allow ws://
327
- - All others: require wss:// or reject
328
-
329
- _pruneNonces()
330
- - Remove nonces older than 10 minutes
331
- - Runs on 5-minute timer
332
- }
333
- ```
334
-
335
- ### Modified Files
336
-
337
- #### federation.js
338
-
339
- - Add peer IP to allowlist management
340
- - Add bridge lifecycle hooks (onPeerPaired, onPeerUnpaired)
341
- - Add key rotation support
342
- - Add nonce tracking helpers
343
-
344
- #### api.js — New Endpoints
345
-
346
- | Method | Path | Description |
347
- |--------|------|-------------|
348
- | GET | /api/federation/bridge/status | Bridge connection state per peer |
349
- | POST | /api/federation/bridge/open | Open bridge to peer (for ambassador spawn) |
350
- | POST | /api/federation/bridge/close | Close bridge connection |
351
- | POST | /api/federation/bridge/message | Send message to remote agent |
352
- | GET | /api/federation/bridge/inbox/:agentId | Pending messages for an agent |
353
- | GET | /api/federation/remote-agents | List agents on all peers |
354
- | GET | /api/federation/remote-agents/:peerId | List agents on a specific peer |
355
-
356
- #### index.js
357
-
358
- - Initialize Bridge after Federation
359
- - Wire bridge port listener (31416)
360
- - Pass bridge reference to ProcessManager for ambassador spawning
361
-
362
- #### introducer.js
363
-
364
- - Inject remote agent context into ambassador agents
365
- - Include remote project map in agent briefing
366
- - Add cross-instance team awareness
367
-
368
- #### registry.js
369
-
370
- - Support "remote" agent entries (ghost agents from peers)
371
- - Remote agents are read-only, updated via bridge sync
372
-
373
- #### process.js
374
-
375
- - Ambassador role spawning with federation context
376
- - Wire agent status changes to bridge broadcasts
377
-
378
- ### Port Allocation
379
-
380
- | Port | Binding | Purpose |
381
- |------|---------|---------|
382
- | 31415 | 127.0.0.1 | Main daemon (GUI, API, WebSocket, local agents) |
383
- | 31416 | Tailscale interface / 0.0.0.0 with IP whitelist | Bridge (peer-to-peer federation) |
384
-
385
- ---
386
-
387
- ## GUI Design
388
-
389
- ### A. Spawn Wizard — Federation Section
390
-
391
- Appears when at least one peer is paired:
392
-
393
- ```
394
- Federation
395
- [x] Connect to peer: [dropdown: my-vps (100.64.0.2)]
396
- Remote agents: backend-3, frontend-2, planner-1
397
- Mode: (x) Ambassador ( ) Direct link
398
-
399
- Ambassador: Full bridge — relays between local and remote teams
400
- Direct link: 1:1 channel to a specific remote agent
401
- ```
402
-
403
- ### B. Agent Config Panel — Federation Tab
404
-
405
- New tab alongside Chat, Config, Monitor, Files:
406
-
407
- ```
408
- Federation
409
- Peer: my-vps (100.64.0.2) [Connected]
410
- Role: Ambassador
411
- Job: job-a1b2c3
412
- Bridge uptime: 14m
413
- Messages: 47 sent / 52 received
414
- Last activity: 12s ago
415
-
416
- Remote Team:
417
- backend-3 (running) — packages/api/src/**
418
- frontend-2 (running) — packages/web/src/**
419
- planner-1 (completed)
420
-
421
- [Send Message] [Sync Project Map] [Disconnect]
422
- ```
423
-
424
- ### C. Agent Tree View
425
-
426
- Remote agents appear as ghost nodes — dimmed, with a network icon badge. Connected via dashed edges to the local ambassador.
427
-
428
- ```
429
- [planner-1] ----> [frontend-1]
430
- | |
431
- | [ambassador-1] - - - - [remote:backend-3]
432
- | - - - - [remote:frontend-2]
433
- v
434
- [backend-1]
435
- ```
436
-
437
- Clicking a remote ghost node shows read-only status in the detail panel.
438
-
439
- ### D. Status Bar
440
-
441
- When federation bridge is active:
442
-
443
- ```
444
- Connected | 3/4 agents | Federation: 1 peer (3 remote agents)
445
- ```
446
-
447
- ### E. Dashboard — Federation Card
448
-
449
- New card in the KPI strip:
450
-
451
- | Metric | Value |
452
- |--------|-------|
453
- | Peers connected | 1 |
454
- | Remote agents | 3 |
455
- | Messages exchanged | 99 |
456
- | Bridge uptime | 14m |
457
- | Active jobs | 1 |
458
-
459
- ### F. Settings — Federation Section
460
-
461
- ```
462
- Federation
463
- Daemon ID: abc123
464
- Keypair: Ready (Ed25519)
465
-
466
- Paired Peers:
467
- [my-vps] 100.64.0.2 paired 2026-04-10 [Unpair]
468
- [staging] 100.64.0.3 paired 2026-04-11 [Unpair]
469
-
470
- [Pair New Server] [Rotate Keys]
471
-
472
- Bridge Settings:
473
- Default idle timeout: [15] minutes
474
- Default TTL: [2] hours
475
- Bridge port: [31416]
476
- ```
477
-
478
- ---
479
-
480
- ## Integration Registry Entry
481
-
482
- New entry in integrations-registry.json:
483
-
484
- ```json
485
- {
486
- "id": "federation",
487
- "name": "Cross-Server Federation",
488
- "description": "Connect agents across Groove instances for coordinated multi-server builds",
489
- "category": "developer",
490
- "icon": "network",
491
- "tags": ["federation", "multi-server", "cross-instance", "ambassador"],
492
- "roles": ["ambassador", "fullstack", "backend", "frontend"],
493
- "authType": "none",
494
- "envKeys": [],
495
- "setupSteps": [
496
- "Pair with a remote Groove instance: groove federation pair <ip>",
497
- "Both daemons must be on Tailscale or have static IPs",
498
- "Spawn an ambassador agent and select the paired peer"
499
- ],
500
- "requiresApproval": ["send_message", "sync_project", "coordinate_task"],
501
- "agentInstructions": "## Federation Integration\n\nYou are an ambassador agent connected to a remote Groove instance.\n\n### API\nSend message: POST http://localhost:31415/api/federation/bridge/message\nBody: {\"jobId\": \"...\", \"to\": {\"agent\": \"remote-agent-id\"}, \"payload\": {...}}\n\nCheck inbox: GET http://localhost:31415/api/federation/bridge/inbox/<your-agent-id>\n\nRemote agents: GET http://localhost:31415/api/federation/remote-agents\n\n### Rules\n- Coordinate, don't command — remote agents have their own scope and safety rules\n- Confirm with user before initiating cross-instance task handoffs\n- Keep messages concise — bridge has rate limits (100/min)"
502
- }
503
- ```
504
-
505
- ---
506
-
507
- ## CLI Additions
508
-
509
- ```
510
- groove federation pair <ip> — pair with remote daemon (existing)
511
- groove federation unpair <id> — remove peer (existing)
512
- groove federation list — list peers (existing)
513
- groove federation status — federation status (existing)
514
- groove federation rotate-keys — rotate Ed25519 keypair, invalidate old
515
- groove federation bridge status — show active bridge connections
516
- groove federation bridge close <id> — manually close a bridge connection
517
- ```
518
-
519
- ---
520
-
521
- ## Phased Rollout
522
-
523
- ### Phase 1 — Bridge + Ambassador (core)
524
-
525
- - [ ] bridge.js — WebSocket bridge with IP whitelist, Ed25519 handshake, nonce replay protection
526
- - [ ] Bridge port listener on 31416 with IP whitelist enforcement
527
- - [ ] Agent registry sync (see remote agents via bridge)
528
- - [ ] Direct agent-to-agent messaging through bridge
529
- - [ ] Ambassador role in spawn wizard
530
- - [ ] Ephemeral connection lifecycle (idle timeout, TTL, auto-close on job complete)
531
- - [ ] API endpoints for bridge management
532
- - [ ] Federation tab in agent config panel
533
-
534
- ### Phase 2 — Coordination Protocol
535
-
536
- - [ ] Cross-instance scope negotiation (remote backend claims API files, local frontend claims UI files)
537
- - [ ] Task handoff: remote agent completes, hands off to local agent with context
538
- - [ ] Shared journalist: project map exchange so both sides have full picture
539
- - [ ] Ghost nodes in agent tree view (remote agents as dimmed nodes)
540
- - [ ] Federation card on dashboard
541
-
542
- ### Phase 3 — Polish + Scale
543
-
544
- - [ ] Multi-peer mesh (3+ daemons)
545
- - [ ] Keep-alive mode for persistent coordination
546
- - [ ] Key rotation command and flow
547
- - [ ] Anomaly detection (spike in message volume, auto-pause)
548
- - [ ] Settings panel for federation config
549
- - [ ] Message history / audit viewer in GUI
550
- - [ ] Connection quality indicators
551
- - [ ] TLS fallback for non-Tailscale public IP connections
552
-
553
- ---
554
-
555
- ## Team Structure
556
-
557
- | Role | Scope | Work |
558
- |------|-------|------|
559
- | Backend | daemon/src/bridge.js (new), federation.js, api.js, index.js, process.js, introducer.js, integrations-registry.json | Bridge manager, IP whitelist, nonce tracking, API endpoints, ambassador spawning |
560
- | Frontend | gui/src/components/agents/spawn-wizard.jsx, agent-panel.jsx, gui/src/views/agents.jsx, dashboard.jsx, settings.jsx, stores/groove.js | Federation tab, ghost nodes, spawn wizard federation section, dashboard card, settings panel |
561
- | QC (fullstack) | All | Verify end-to-end bridge works, build, no regressions, security review |
562
-
563
- ---
564
-
565
- ## Key Dependencies
566
-
567
- ```
568
- # Already in use
569
- ws — WebSocket (already used by daemon)
570
- crypto — Ed25519 (already used by federation.js)
571
- minimatch — scope matching (already used by lockmanager.js)
572
-
573
- # New (if TLS fallback needed)
574
- selfsigned — generate self-signed certs for wss:// fallback
575
- ```
576
-
577
- No new heavy dependencies. The bridge is built on ws (already a dependency) and crypto (built-in Node).
578
-
579
- ---
580
-
581
- ## Summary
582
-
583
- Federation turns Groove from a single-machine orchestrator into a distributed one. The ambassador model keeps it simple — agents don't need to know about networking, they just talk to their ambassador. Security is layered: IP whitelist stops 99% of threats at the TCP level, Ed25519 + nonces handle the rest, and Tailscale encrypts the transport. Connections are ephemeral by default — open for a job, closed when done. The whole system scales by adding peers with `groove federation pair`.