groove-dev 0.12.4 → 0.12.6
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.
- package/README.md +122 -3
- package/node_modules/@groove-dev/daemon/src/firstrun.js +8 -18
- package/node_modules/@groove-dev/daemon/src/index.js +3 -2
- package/node_modules/@groove-dev/daemon/src/journalist.js +0 -1
- package/node_modules/@groove-dev/daemon/src/rotator.js +0 -1
- package/package.json +1 -1
- package/packages/daemon/src/firstrun.js +8 -18
- package/packages/daemon/src/index.js +3 -2
- package/packages/daemon/src/journalist.js +0 -1
- package/packages/daemon/src/rotator.js +0 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ npm i -g groove-dev
|
|
|
12
12
|
groove start
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
The GUI opens at `http://localhost:31415`. On a VPS? GROOVE detects it and tells you exactly what to do.
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
@@ -61,6 +61,125 @@ GROOVE auto-detects monorepo workspaces (npm, pnpm, lerna) and lets you spawn ea
|
|
|
61
61
|
- **Task negotiation** — duplicate roles get assigned non-overlapping work
|
|
62
62
|
- **Knock protocol** — agents signal before shared/destructive actions
|
|
63
63
|
|
|
64
|
+
## Remote Access
|
|
65
|
+
|
|
66
|
+
Run GROOVE on a VPS and manage your agents from anywhere. No ports exposed to the internet. No tokens. No custom auth code. Zero attack surface.
|
|
67
|
+
|
|
68
|
+
### How It Works
|
|
69
|
+
|
|
70
|
+
GROOVE never opens ports to the public internet. Instead, it uses battle-tested transport layers — SSH tunnels and WireGuard (Tailscale) — to keep your daemon private.
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
Your laptop Your VPS
|
|
74
|
+
┌──────────┐ SSH tunnel ┌──────────────────┐
|
|
75
|
+
│ Browser │ ◄────────────────► │ GROOVE daemon │
|
|
76
|
+
│ localhost│ encrypted │ 127.0.0.1:31415 │
|
|
77
|
+
└──────────┘ └──────────────────┘
|
|
78
|
+
Zero open ports
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The daemon always binds to `127.0.0.1`. Nothing reaches it from the public internet. Your SSH keys handle auth. Your browser connects to `localhost` on your machine, and the tunnel forwards traffic securely to the VPS.
|
|
82
|
+
|
|
83
|
+
### Setup
|
|
84
|
+
|
|
85
|
+
**On your VPS:**
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npm i -g groove-dev # install
|
|
89
|
+
groove start # start the daemon
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**On your laptop:**
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npm i -g groove-dev # install (one-time)
|
|
96
|
+
groove connect user@your-server-ip # open the GUI
|
|
97
|
+
groove disconnect # close when done
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
That's it. The GUI opens in your browser automatically.
|
|
101
|
+
|
|
102
|
+
GROOVE auto-detects your environment — VS Code Remote, plain SSH, or local — and tells you exactly what to do. SSH config aliases work too: `groove connect my-vps`.
|
|
103
|
+
|
|
104
|
+
### Tailscale / LAN Access
|
|
105
|
+
|
|
106
|
+
For multi-device access (phone, tablet, other machines on your network):
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
groove start --host tailscale # auto-detects your Tailscale IP
|
|
110
|
+
groove start --host 192.168.1.5 # explicit IP
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Open `http://<ip>:31415` from any device on the same network. Tailscale handles auth via WireGuard.
|
|
114
|
+
|
|
115
|
+
### What's Blocked
|
|
116
|
+
|
|
117
|
+
GROOVE will reject any attempt to expose the daemon directly to the internet:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
groove start --host 0.0.0.0 # REJECTED — not allowed
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
This is by design. Direct exposure requires custom auth, rate limiting, TLS management — attack surface we refuse to create. SSH and WireGuard solve this better than we ever could.
|
|
124
|
+
|
|
125
|
+
### Federation (Preview)
|
|
126
|
+
|
|
127
|
+
Pair GROOVE daemons across machines with Ed25519 key exchange. The security layer is built — cross-server agent coordination (typed contracts, federated registry) is coming soon.
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
groove federation pair 100.64.1.5 # pair two daemons
|
|
131
|
+
groove federation list # see paired peers
|
|
132
|
+
groove federation status # show keypair + peers
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Every cross-server message is signed with Ed25519 keys generated during a pairing ceremony. The receiving daemon verifies the signature before accepting any contract. Replay attacks are rejected (5-minute timestamp window). Tampered payloads are rejected. Unknown senders are rejected.
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
Server A Server B
|
|
139
|
+
┌──────────────┐ signed contract ┌──────────────┐
|
|
140
|
+
│ GROOVE daemon│ ◄────────────────►│ GROOVE daemon│
|
|
141
|
+
│ Ed25519 key │ verify + audit │ Ed25519 key │
|
|
142
|
+
└──────────────┘ └──────────────┘
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Contracts are typed data (method, path, input/output schema) — not freeform text. No surface for prompt injection. Full audit trail on both sides.
|
|
146
|
+
|
|
147
|
+
### Audit Log
|
|
148
|
+
|
|
149
|
+
Every state-changing operation is logged to `.groove/audit.log`:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
groove audit # view recent entries
|
|
153
|
+
groove audit -n 50 # last 50 entries
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
2:14:32 PM agent.spawn id=a1 role=backend provider=claude-code
|
|
158
|
+
2:14:35 PM agent.spawn id=a2 role=frontend provider=claude-code
|
|
159
|
+
2:33:12 PM agent.rotate oldId=a1 newId=a3 role=backend
|
|
160
|
+
2:45:00 PM federation.pair peerId=f63dc52b14b9 peerHost=100.64.1.5
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Append-only, `0600` permissions, auto-rotates at 5MB. When team auth is added, every entry will include who performed the action.
|
|
164
|
+
|
|
165
|
+
### Security Model
|
|
166
|
+
|
|
167
|
+
| Threat | Defense |
|
|
168
|
+
|--------|---------|
|
|
169
|
+
| Remote attackers | Zero open ports. Daemon binds to private interface only. |
|
|
170
|
+
| Network eavesdroppers | SSH (tunnel) or WireGuard (Tailscale) encryption. |
|
|
171
|
+
| Spoofed federation contracts | Ed25519 signature verification on every message. |
|
|
172
|
+
| Replay attacks | 5-minute timestamp window. Reject old/future contracts. |
|
|
173
|
+
| Malformed peer data | Public key validation at pairing time. Peer IDs restricted to hex. |
|
|
174
|
+
| Path traversal | Peer IDs sanitized. No filesystem access across servers. |
|
|
175
|
+
| Privilege escalation | No auth code to exploit. Transport layer handles all access control. |
|
|
176
|
+
|
|
177
|
+
**What we explicitly don't defend against:** Compromised SSH keys, root access to VPS, malicious AI provider responses (out of scope — we're a process manager).
|
|
178
|
+
|
|
179
|
+
**The principle:** "There's nothing to attack" is better than "we have a security system and here's why it's good." GROOVE has zero auth code. The transport layer does all the work.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
64
183
|
## Works With Everything
|
|
65
184
|
|
|
66
185
|
| Provider | Auth | Models |
|
|
@@ -76,7 +195,7 @@ Works in any terminal, any IDE, any OS. Technical and non-technical users alike.
|
|
|
76
195
|
|
|
77
196
|
## The GUI
|
|
78
197
|
|
|
79
|
-
Open
|
|
198
|
+
Open the dashboard after starting the daemon (local or remote):
|
|
80
199
|
|
|
81
200
|
- **Agent Tree** — visual node graph with Bezier spline connections, role badges, live status
|
|
82
201
|
- **Chat** — instruct agents, query without disrupting, continue completed agents, streaming text
|
|
@@ -99,7 +218,7 @@ GROOVE routes tasks to the cheapest model that can handle them. Planners get Opu
|
|
|
99
218
|
|
|
100
219
|
```
|
|
101
220
|
┌──────────────────────────────────────────────┐
|
|
102
|
-
│ GROOVE DAEMON (:31415)
|
|
221
|
+
│ GROOVE DAEMON (:31415) │
|
|
103
222
|
│ │
|
|
104
223
|
│ Registry · Introducer · Lock Manager │
|
|
105
224
|
│ Journalist · Rotator · Adaptive · Indexer │
|
|
@@ -22,7 +22,7 @@ export function isFirstRun(grooveDir) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
// Show welcome banner on every startup
|
|
25
|
-
export function printWelcome(port, host = '127.0.0.1') {
|
|
25
|
+
export function printWelcome(port, host = '127.0.0.1', firstRun = false) {
|
|
26
26
|
const providers = listProviders();
|
|
27
27
|
const installed = providers.filter((p) => p.installed);
|
|
28
28
|
const notInstalled = providers.filter((p) => !p.installed);
|
|
@@ -53,6 +53,7 @@ export function printWelcome(port, host = '127.0.0.1') {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
console.log('');
|
|
56
|
+
|
|
56
57
|
const isRemote = host !== '127.0.0.1';
|
|
57
58
|
|
|
58
59
|
// Detect environment
|
|
@@ -63,17 +64,12 @@ export function printWelcome(port, host = '127.0.0.1') {
|
|
|
63
64
|
const isServer = !isVSCode && (isSSH || isHeadless);
|
|
64
65
|
|
|
65
66
|
if (isRemote) {
|
|
66
|
-
|
|
67
|
-
console.log(` GUI: http://${host}:${port}`);
|
|
68
|
-
console.log(` Host: ${host} (network-accessible)`);
|
|
67
|
+
console.log(` Open: http://${host}:${port}`);
|
|
69
68
|
} else if (isVSCode) {
|
|
70
|
-
|
|
71
|
-
console.log(` GUI: http://localhost:${port}`);
|
|
69
|
+
console.log(` Open: http://localhost:${port}`);
|
|
72
70
|
console.log(` VS Code forwards this port automatically.`);
|
|
73
71
|
} else if (isServer) {
|
|
74
|
-
// Plain SSH / headless — need groove connect
|
|
75
72
|
const sshUser = process.env.SUDO_USER || process.env.USER || 'user';
|
|
76
|
-
|
|
77
73
|
let serverIp = '';
|
|
78
74
|
const sshConn = process.env.SSH_CONNECTION || '';
|
|
79
75
|
if (sshConn) {
|
|
@@ -92,20 +88,14 @@ export function printWelcome(port, host = '127.0.0.1') {
|
|
|
92
88
|
}
|
|
93
89
|
}
|
|
94
90
|
serverIp = serverIp || '<your-server-ip>';
|
|
95
|
-
|
|
96
|
-
console.log(` Daemon running on port ${port}`);
|
|
97
|
-
console.log('');
|
|
98
|
-
console.log(' To open the GUI, open a terminal on your Mac/PC and run:');
|
|
99
|
-
console.log('');
|
|
91
|
+
console.log(' Open the GUI from your Mac/PC:');
|
|
100
92
|
console.log(` npx groove-dev connect ${sshUser}@${serverIp}`);
|
|
101
93
|
} else {
|
|
102
|
-
|
|
103
|
-
console.log(` GUI: http://localhost:${port}`);
|
|
94
|
+
console.log(` Open: http://localhost:${port}`);
|
|
104
95
|
}
|
|
105
96
|
|
|
106
|
-
console.log(
|
|
107
|
-
console.log(
|
|
108
|
-
console.log(' GitHub: https://github.com/grooveai-dev/groove');
|
|
97
|
+
console.log(` Stop: groove stop (or Ctrl+C)`);
|
|
98
|
+
console.log(` Docs: https://docs.groovedev.ai`);
|
|
109
99
|
console.log('');
|
|
110
100
|
}
|
|
111
101
|
|
|
@@ -89,7 +89,8 @@ export class Daemon {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
// First-run detection
|
|
92
|
-
|
|
92
|
+
this._firstRun = isFirstRun(this.grooveDir);
|
|
93
|
+
if (this._firstRun) {
|
|
93
94
|
this.config = runFirstTimeSetup(this.grooveDir);
|
|
94
95
|
} else {
|
|
95
96
|
this.config = loadConfig(this.grooveDir);
|
|
@@ -224,7 +225,7 @@ export class Daemon {
|
|
|
224
225
|
writeFileSync(resolve(this.grooveDir, 'daemon.port'), String(this.port));
|
|
225
226
|
writeFileSync(resolve(this.grooveDir, 'daemon.host'), this.host);
|
|
226
227
|
|
|
227
|
-
printWelcome(this.port, this.host);
|
|
228
|
+
printWelcome(this.port, this.host, this._firstRun);
|
|
228
229
|
|
|
229
230
|
// Start background services
|
|
230
231
|
this.journalist.start();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "groove-dev",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.6",
|
|
4
4
|
"description": "Open-source agent orchestration layer for AI coding tools. GUI dashboard, multi-agent coordination, zero cold-start (Journalist), infinite sessions (adaptive context rotation), AI Project Manager, Quick Launch. Works with Claude Code, Codex, Gemini CLI, Ollama.",
|
|
5
5
|
"license": "FSL-1.1-Apache-2.0",
|
|
6
6
|
"author": "Groove Dev <hello@groovedev.ai> (https://groovedev.ai)",
|
|
@@ -22,7 +22,7 @@ export function isFirstRun(grooveDir) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
// Show welcome banner on every startup
|
|
25
|
-
export function printWelcome(port, host = '127.0.0.1') {
|
|
25
|
+
export function printWelcome(port, host = '127.0.0.1', firstRun = false) {
|
|
26
26
|
const providers = listProviders();
|
|
27
27
|
const installed = providers.filter((p) => p.installed);
|
|
28
28
|
const notInstalled = providers.filter((p) => !p.installed);
|
|
@@ -53,6 +53,7 @@ export function printWelcome(port, host = '127.0.0.1') {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
console.log('');
|
|
56
|
+
|
|
56
57
|
const isRemote = host !== '127.0.0.1';
|
|
57
58
|
|
|
58
59
|
// Detect environment
|
|
@@ -63,17 +64,12 @@ export function printWelcome(port, host = '127.0.0.1') {
|
|
|
63
64
|
const isServer = !isVSCode && (isSSH || isHeadless);
|
|
64
65
|
|
|
65
66
|
if (isRemote) {
|
|
66
|
-
|
|
67
|
-
console.log(` GUI: http://${host}:${port}`);
|
|
68
|
-
console.log(` Host: ${host} (network-accessible)`);
|
|
67
|
+
console.log(` Open: http://${host}:${port}`);
|
|
69
68
|
} else if (isVSCode) {
|
|
70
|
-
|
|
71
|
-
console.log(` GUI: http://localhost:${port}`);
|
|
69
|
+
console.log(` Open: http://localhost:${port}`);
|
|
72
70
|
console.log(` VS Code forwards this port automatically.`);
|
|
73
71
|
} else if (isServer) {
|
|
74
|
-
// Plain SSH / headless — need groove connect
|
|
75
72
|
const sshUser = process.env.SUDO_USER || process.env.USER || 'user';
|
|
76
|
-
|
|
77
73
|
let serverIp = '';
|
|
78
74
|
const sshConn = process.env.SSH_CONNECTION || '';
|
|
79
75
|
if (sshConn) {
|
|
@@ -92,20 +88,14 @@ export function printWelcome(port, host = '127.0.0.1') {
|
|
|
92
88
|
}
|
|
93
89
|
}
|
|
94
90
|
serverIp = serverIp || '<your-server-ip>';
|
|
95
|
-
|
|
96
|
-
console.log(` Daemon running on port ${port}`);
|
|
97
|
-
console.log('');
|
|
98
|
-
console.log(' To open the GUI, open a terminal on your Mac/PC and run:');
|
|
99
|
-
console.log('');
|
|
91
|
+
console.log(' Open the GUI from your Mac/PC:');
|
|
100
92
|
console.log(` npx groove-dev connect ${sshUser}@${serverIp}`);
|
|
101
93
|
} else {
|
|
102
|
-
|
|
103
|
-
console.log(` GUI: http://localhost:${port}`);
|
|
94
|
+
console.log(` Open: http://localhost:${port}`);
|
|
104
95
|
}
|
|
105
96
|
|
|
106
|
-
console.log(
|
|
107
|
-
console.log(
|
|
108
|
-
console.log(' GitHub: https://github.com/grooveai-dev/groove');
|
|
97
|
+
console.log(` Stop: groove stop (or Ctrl+C)`);
|
|
98
|
+
console.log(` Docs: https://docs.groovedev.ai`);
|
|
109
99
|
console.log('');
|
|
110
100
|
}
|
|
111
101
|
|
|
@@ -89,7 +89,8 @@ export class Daemon {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
// First-run detection
|
|
92
|
-
|
|
92
|
+
this._firstRun = isFirstRun(this.grooveDir);
|
|
93
|
+
if (this._firstRun) {
|
|
93
94
|
this.config = runFirstTimeSetup(this.grooveDir);
|
|
94
95
|
} else {
|
|
95
96
|
this.config = loadConfig(this.grooveDir);
|
|
@@ -224,7 +225,7 @@ export class Daemon {
|
|
|
224
225
|
writeFileSync(resolve(this.grooveDir, 'daemon.port'), String(this.port));
|
|
225
226
|
writeFileSync(resolve(this.grooveDir, 'daemon.host'), this.host);
|
|
226
227
|
|
|
227
|
-
printWelcome(this.port, this.host);
|
|
228
|
+
printWelcome(this.port, this.host, this._firstRun);
|
|
228
229
|
|
|
229
230
|
// Start background services
|
|
230
231
|
this.journalist.start();
|