groove-dev 0.27.59 → 0.27.61
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/node_modules/@groove-dev/cli/package.json +1 -1
- package/node_modules/@groove-dev/daemon/package.json +1 -1
- package/node_modules/@groove-dev/daemon/src/api.js +73 -56
- package/node_modules/@groove-dev/daemon/src/conversations.js +78 -35
- package/node_modules/@groove-dev/daemon/src/journalist.js +1 -0
- package/node_modules/@groove-dev/daemon/src/process.js +17 -7
- package/node_modules/@groove-dev/daemon/src/providers/base.js +4 -0
- package/node_modules/@groove-dev/daemon/src/providers/claude-code.js +63 -0
- package/node_modules/@groove-dev/daemon/src/providers/codex.js +55 -0
- package/node_modules/@groove-dev/daemon/src/providers/gemini.js +53 -0
- package/node_modules/@groove-dev/daemon/src/providers/groove-network.js +1 -1
- package/node_modules/@groove-dev/daemon/src/providers/index.js +16 -1
- package/node_modules/@groove-dev/daemon/src/providers/local.js +44 -0
- package/node_modules/@groove-dev/daemon/src/providers/ollama.js +44 -0
- package/node_modules/@groove-dev/daemon/src/rotator.js +4 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-B3AqeyS4.css +1 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-DWao9glo.js +8614 -0
- package/node_modules/@groove-dev/gui/dist/index.html +2 -2
- package/node_modules/@groove-dev/gui/package.json +1 -1
- package/node_modules/@groove-dev/gui/src/components/chat/chat-view.jsx +3 -2
- package/node_modules/@groove-dev/gui/src/components/chat/model-picker.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/components/layout/status-bar.jsx +13 -7
- package/node_modules/@groove-dev/gui/src/components/network/activity-chart.jsx +245 -0
- package/node_modules/@groove-dev/gui/src/components/network/compute-header.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/components/network/network-health.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/components/network/network-status.jsx +5 -5
- package/node_modules/@groove-dev/gui/src/components/network/node-details.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/components/ui/update-modal.jsx +70 -0
- package/node_modules/@groove-dev/gui/src/stores/groove.js +66 -6
- package/node_modules/@groove-dev/gui/src/views/network.jsx +99 -17
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/api.js +73 -56
- package/packages/daemon/src/conversations.js +78 -35
- package/packages/daemon/src/journalist.js +1 -0
- package/packages/daemon/src/process.js +17 -7
- package/packages/daemon/src/providers/base.js +4 -0
- package/packages/daemon/src/providers/claude-code.js +63 -0
- package/packages/daemon/src/providers/codex.js +55 -0
- package/packages/daemon/src/providers/gemini.js +53 -0
- package/packages/daemon/src/providers/groove-network.js +1 -1
- package/packages/daemon/src/providers/index.js +16 -1
- package/packages/daemon/src/providers/local.js +44 -0
- package/packages/daemon/src/providers/ollama.js +44 -0
- package/packages/daemon/src/rotator.js +4 -0
- package/packages/gui/dist/assets/index-B3AqeyS4.css +1 -0
- package/packages/gui/dist/assets/index-DWao9glo.js +8614 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/components/chat/chat-view.jsx +3 -2
- package/packages/gui/src/components/chat/model-picker.jsx +1 -1
- package/packages/gui/src/components/layout/status-bar.jsx +13 -7
- package/packages/gui/src/components/network/activity-chart.jsx +245 -0
- package/packages/gui/src/components/network/compute-header.jsx +1 -1
- package/packages/gui/src/components/network/network-health.jsx +1 -1
- package/packages/gui/src/components/network/network-status.jsx +5 -5
- package/packages/gui/src/components/network/node-details.jsx +1 -1
- package/packages/gui/src/components/ui/update-modal.jsx +70 -0
- package/packages/gui/src/stores/groove.js +66 -6
- package/packages/gui/src/views/network.jsx +99 -17
- package/default/fix-beta-endpoint-deployment.md +0 -68
- package/default/groovedev-beta-auth-endpoint.md +0 -166
- package/default/security-review-prompt.md +0 -98
- package/node_modules/@groove-dev/gui/dist/assets/index-BrfCzrxJ.css +0 -1
- package/node_modules/@groove-dev/gui/dist/assets/index-BycOlqLx.js +0 -8614
- package/packages/gui/dist/assets/index-BrfCzrxJ.css +0 -1
- package/packages/gui/dist/assets/index-BycOlqLx.js +0 -8614
|
@@ -9,11 +9,11 @@ import { ScrollArea } from '../components/ui/scroll-area';
|
|
|
9
9
|
import { cn } from '../lib/cn';
|
|
10
10
|
import { NodeToggle } from '../components/network/node-toggle';
|
|
11
11
|
import { ComputeHeader } from '../components/network/compute-header';
|
|
12
|
-
import {
|
|
12
|
+
import { ActivityChart } from '../components/network/activity-chart';
|
|
13
13
|
import { ActivityStream } from '../components/network/activity-stream';
|
|
14
14
|
import { NetworkHealth } from '../components/network/network-health';
|
|
15
|
-
import { HEX } from '../lib/theme-hex';
|
|
16
|
-
import { Globe, Download, Check, AlertCircle, Loader2, Trash2, ArrowUpCircle } from 'lucide-react';
|
|
15
|
+
import { HEX, hexAlpha } from '../lib/theme-hex';
|
|
16
|
+
import { Globe, Download, Check, AlertCircle, Loader2, Trash2, ArrowUpCircle, Zap } from 'lucide-react';
|
|
17
17
|
|
|
18
18
|
const REQUIREMENTS = [
|
|
19
19
|
'Python 3.10 or higher',
|
|
@@ -312,6 +312,99 @@ function NetworkHeader() {
|
|
|
312
312
|
);
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
+
function IdleHero() {
|
|
316
|
+
const node = useGrooveStore((s) => s.networkNode);
|
|
317
|
+
const networkStatus = useGrooveStore((s) => s.networkStatus);
|
|
318
|
+
const startNetworkNode = useGrooveStore((s) => s.startNetworkNode);
|
|
319
|
+
const [pending, setPending] = useState(false);
|
|
320
|
+
|
|
321
|
+
const hardware = node.hardware || {};
|
|
322
|
+
const activeNodes = (networkStatus.nodes || []).filter((n) => n.status === 'active').length;
|
|
323
|
+
const activeSessions = networkStatus.activeSessions || 0;
|
|
324
|
+
|
|
325
|
+
async function handleStart() {
|
|
326
|
+
setPending(true);
|
|
327
|
+
try { await startNetworkNode(); }
|
|
328
|
+
catch { /* toasted in store */ }
|
|
329
|
+
setPending(false);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return (
|
|
333
|
+
<div className="flex flex-col h-full">
|
|
334
|
+
<NetworkHeader />
|
|
335
|
+
<div
|
|
336
|
+
className="flex-1 flex items-center justify-center bg-surface-0"
|
|
337
|
+
style={{ background: `radial-gradient(ellipse at 50% 40%, ${hexAlpha(HEX.accent, 0.06)} 0%, transparent 70%) ${HEX.surface0}` }}
|
|
338
|
+
>
|
|
339
|
+
<div className="max-w-lg w-full px-6 flex flex-col items-center text-center">
|
|
340
|
+
<div
|
|
341
|
+
className="w-24 h-24 rounded-full flex items-center justify-center mb-6 mx-auto"
|
|
342
|
+
style={{
|
|
343
|
+
background: hexAlpha(HEX.accent, 0.08),
|
|
344
|
+
border: `1px solid ${hexAlpha(HEX.accent, 0.15)}`,
|
|
345
|
+
boxShadow: `0 0 40px ${hexAlpha(HEX.accent, 0.1)}`,
|
|
346
|
+
}}
|
|
347
|
+
>
|
|
348
|
+
<Globe size={56} className="text-accent" strokeWidth={1.25} />
|
|
349
|
+
</div>
|
|
350
|
+
|
|
351
|
+
<h2 className="text-xl font-semibold text-text-0 font-sans text-center">
|
|
352
|
+
Join the Groove Network
|
|
353
|
+
</h2>
|
|
354
|
+
|
|
355
|
+
<p className="text-sm text-text-2 font-sans text-center leading-relaxed mt-2 max-w-sm mx-auto">
|
|
356
|
+
Contribute your compute to power decentralized AI inference. Your hardware runs model shards alongside other nodes in the network.
|
|
357
|
+
</p>
|
|
358
|
+
|
|
359
|
+
<div className="mt-8 w-full">
|
|
360
|
+
<div className="text-2xs font-mono text-text-3 uppercase tracking-widest mb-2 text-left">Your Hardware</div>
|
|
361
|
+
<div className="grid grid-cols-3 gap-2">
|
|
362
|
+
<div className="bg-surface-1 rounded-sm border border-border-subtle px-4 py-3">
|
|
363
|
+
<div className="text-2xs font-mono text-text-4 uppercase tracking-wider">Device</div>
|
|
364
|
+
<div className="text-sm font-mono text-text-0 mt-1 truncate">{hardware.device || 'auto'}</div>
|
|
365
|
+
</div>
|
|
366
|
+
<div className="bg-surface-1 rounded-sm border border-border-subtle px-4 py-3">
|
|
367
|
+
<div className="text-2xs font-mono text-text-4 uppercase tracking-wider">Memory</div>
|
|
368
|
+
<div className="text-sm font-mono text-text-0 mt-1 truncate">{hardware.memory || '—'}</div>
|
|
369
|
+
</div>
|
|
370
|
+
<div className="bg-surface-1 rounded-sm border border-border-subtle px-4 py-3">
|
|
371
|
+
<div className="text-2xs font-mono text-text-4 uppercase tracking-wider">GPU</div>
|
|
372
|
+
<div className="text-sm font-mono text-text-0 mt-1 truncate">{hardware.gpu || 'None'}</div>
|
|
373
|
+
</div>
|
|
374
|
+
</div>
|
|
375
|
+
</div>
|
|
376
|
+
|
|
377
|
+
<div className="mt-6 w-full max-w-sm mx-auto">
|
|
378
|
+
<Button variant="primary" size="lg" onClick={handleStart} disabled={pending} className="w-full">
|
|
379
|
+
{pending ? (
|
|
380
|
+
<><Loader2 size={14} className="animate-spin" /> Connecting…</>
|
|
381
|
+
) : (
|
|
382
|
+
<><Zap size={14} /> Start Contributing</>
|
|
383
|
+
)}
|
|
384
|
+
</Button>
|
|
385
|
+
</div>
|
|
386
|
+
|
|
387
|
+
<div className="mt-4 text-center text-2xs font-mono text-text-3">
|
|
388
|
+
{activeNodes > 0 || activeSessions > 0 ? (
|
|
389
|
+
<span>
|
|
390
|
+
<span className="text-accent">{activeNodes}</span> node{activeNodes !== 1 ? 's' : ''} online · {activeSessions} active session{activeSessions !== 1 ? 's' : ''}
|
|
391
|
+
</span>
|
|
392
|
+
) : (
|
|
393
|
+
<span>Checking network status…</span>
|
|
394
|
+
)}
|
|
395
|
+
</div>
|
|
396
|
+
|
|
397
|
+
{node.nodeId && (
|
|
398
|
+
<div className="mt-6 text-center text-2xs font-mono text-text-4">
|
|
399
|
+
Node identity: {node.nodeId.length > 14 ? `${node.nodeId.slice(0, 6)}…${node.nodeId.slice(-4)}` : node.nodeId}
|
|
400
|
+
</div>
|
|
401
|
+
)}
|
|
402
|
+
</div>
|
|
403
|
+
</div>
|
|
404
|
+
</div>
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
|
|
315
408
|
export default function NetworkView() {
|
|
316
409
|
const fetchNetworkNodeStatus = useGrooveStore((s) => s.fetchNetworkNodeStatus);
|
|
317
410
|
const fetchNetworkStatus = useGrooveStore((s) => s.fetchNetworkStatus);
|
|
@@ -341,18 +434,7 @@ export default function NetworkView() {
|
|
|
341
434
|
}
|
|
342
435
|
|
|
343
436
|
if (!nodeActive) {
|
|
344
|
-
return
|
|
345
|
-
<div className="flex flex-col h-full">
|
|
346
|
-
<NetworkHeader />
|
|
347
|
-
<ScrollArea className="flex-1">
|
|
348
|
-
<div className="flex items-center justify-center min-h-full px-6 py-12">
|
|
349
|
-
<div className="w-full max-w-md">
|
|
350
|
-
<NodeToggle />
|
|
351
|
-
</div>
|
|
352
|
-
</div>
|
|
353
|
-
</ScrollArea>
|
|
354
|
-
</div>
|
|
355
|
-
);
|
|
437
|
+
return <IdleHero />;
|
|
356
438
|
}
|
|
357
439
|
|
|
358
440
|
return (
|
|
@@ -361,10 +443,10 @@ export default function NetworkView() {
|
|
|
361
443
|
|
|
362
444
|
<ComputeHeader />
|
|
363
445
|
|
|
364
|
-
<div className="flex-1 min-h-0 flex flex-col" style={{ background:
|
|
446
|
+
<div className="flex-1 min-h-0 flex flex-col" style={{ background: HEX.surface3, gap: '1px' }}>
|
|
365
447
|
<div className="min-h-0 flex-1 grid" style={{ gridTemplateColumns: '3fr 1.5fr', gap: '0 1px' }}>
|
|
366
448
|
<div className="min-w-0 min-h-0 overflow-hidden bg-surface-1">
|
|
367
|
-
<
|
|
449
|
+
<ActivityChart />
|
|
368
450
|
</div>
|
|
369
451
|
<div className="min-w-0 min-h-0 overflow-hidden bg-surface-1">
|
|
370
452
|
<NetworkHealth />
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
# BUG: POST /api/beta/validate returns 404 — not deployed
|
|
2
|
-
|
|
3
|
-
From: Groove UI Team
|
|
4
|
-
To: groovedev.ai Team
|
|
5
|
-
Date: 2026-04-18
|
|
6
|
-
Priority: BLOCKING
|
|
7
|
-
|
|
8
|
-
## The Problem
|
|
9
|
-
|
|
10
|
-
The beta invite code validation endpoint is returning 404 from nginx. The route handler was built (routes/beta.js, mounted in server.js) but it's not reachable in production.
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
curl -s https://groovedev.ai/api/beta/validate \
|
|
14
|
-
-H 'Content-Type: application/json' \
|
|
15
|
-
-d '{"code":"GROOVE-NET-ALPHA-001","machineId":"test"}'
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
Returns:
|
|
19
|
-
```html
|
|
20
|
-
<center>nginx/1.24.0 (Ubuntu)</center>
|
|
21
|
-
404
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
This is an nginx 404, not an app-level 404. The request never reaches the Node.js server.
|
|
25
|
-
|
|
26
|
-
## What Should Happen
|
|
27
|
-
|
|
28
|
-
The endpoint should return:
|
|
29
|
-
```json
|
|
30
|
-
{"valid": true, "expiresAt": "2026-07-18T00:00:00Z", "features": ["network-node", "network-consumer"], "message": "Welcome to the Groove Network beta"}
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
Or for invalid codes:
|
|
34
|
-
```json
|
|
35
|
-
{"valid": false, "message": "Invalid invite code"}
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Likely Causes
|
|
39
|
-
|
|
40
|
-
1. Nginx isn't proxying /api/beta/* to the Node.js app — needs a location block or the existing proxy_pass doesn't cover this path
|
|
41
|
-
2. The Node.js server isn't running or was restarted without the new routes
|
|
42
|
-
3. The route is mounted at a different path than /api/beta/validate
|
|
43
|
-
|
|
44
|
-
## What to Check
|
|
45
|
-
|
|
46
|
-
1. Nginx config — does it proxy /api/* to the Node.js backend? If routes are whitelisted individually, add /api/beta/*
|
|
47
|
-
2. Is the Node.js server running? Check with: `pm2 status` or `systemctl status groovedev` (whatever process manager is used)
|
|
48
|
-
3. Did server.js get deployed with the beta route mount? Verify the deployed code includes: `app.use('/api/beta', betaRoutes)` or equivalent
|
|
49
|
-
4. Test locally on the server: `curl http://localhost:<app-port>/api/beta/validate -H 'Content-Type: application/json' -d '{"code":"test","machineId":"test"}'` — if this works, it's purely an nginx issue
|
|
50
|
-
|
|
51
|
-
## Impact
|
|
52
|
-
|
|
53
|
-
Every Groove app trying to activate a beta invite code fails. We have a hardcoded fallback for 5 ALPHA codes, but any codes generated through the admin portal (like GROOVE-NET-SWIFT-899) are completely broken until this is fixed. Beta testers cannot activate.
|
|
54
|
-
|
|
55
|
-
## Expected Nginx Config
|
|
56
|
-
|
|
57
|
-
Something like this should work (adjust upstream port):
|
|
58
|
-
```nginx
|
|
59
|
-
location /api/beta/ {
|
|
60
|
-
proxy_pass http://127.0.0.1:3000;
|
|
61
|
-
proxy_set_header Host $host;
|
|
62
|
-
proxy_set_header X-Real-IP $remote_addr;
|
|
63
|
-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
64
|
-
proxy_set_header X-Forwarded-Proto $scheme;
|
|
65
|
-
}
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
Or if there's already a catch-all /api/ proxy, make sure it's not being overridden by a more specific location block.
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
# Groove Network Beta — Auth Endpoint Setup
|
|
2
|
-
|
|
3
|
-
Instructions for setting up the invite code validation endpoint on groovedev.ai.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Endpoint
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
POST https://groovedev.ai/api/beta/validate
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Request
|
|
14
|
-
|
|
15
|
-
```json
|
|
16
|
-
{
|
|
17
|
-
"code": "GROOVE-NET-ALPHA-001",
|
|
18
|
-
"machineId": "sha256-of-hostname-and-mac"
|
|
19
|
-
}
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
- `code` — the invite code the user entered
|
|
23
|
-
- `machineId` — hashed machine identifier so you can track activations per device (the daemon already generates this for credential encryption)
|
|
24
|
-
|
|
25
|
-
## Response — Valid Code
|
|
26
|
-
|
|
27
|
-
```json
|
|
28
|
-
{
|
|
29
|
-
"valid": true,
|
|
30
|
-
"expiresAt": "2026-07-18T00:00:00Z",
|
|
31
|
-
"features": ["network-node", "network-consumer"],
|
|
32
|
-
"message": "Welcome to the Groove Network beta"
|
|
33
|
-
}
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
- `expiresAt` — when this code stops working (null = never expires)
|
|
37
|
-
- `features` — which beta features this code unlocks (future-proofs for gating individual features)
|
|
38
|
-
- `message` — optional message shown to the user on activation
|
|
39
|
-
|
|
40
|
-
## Response — Invalid Code
|
|
41
|
-
|
|
42
|
-
```json
|
|
43
|
-
{
|
|
44
|
-
"valid": false,
|
|
45
|
-
"message": "Invalid invite code"
|
|
46
|
-
}
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
HTTP 200 for both valid and invalid — don't leak info through status codes.
|
|
50
|
-
|
|
51
|
-
## Response — Rate Limited
|
|
52
|
-
|
|
53
|
-
```json
|
|
54
|
-
{
|
|
55
|
-
"valid": false,
|
|
56
|
-
"message": "Too many attempts, try again later"
|
|
57
|
-
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
HTTP 429. Rate limit: 5 attempts per IP per hour.
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
## Code Management
|
|
65
|
-
|
|
66
|
-
### Database Schema (or KV store, whatever groovedev.ai uses)
|
|
67
|
-
|
|
68
|
-
```sql
|
|
69
|
-
CREATE TABLE beta_codes (
|
|
70
|
-
code TEXT PRIMARY KEY,
|
|
71
|
-
created_at TIMESTAMP DEFAULT NOW(),
|
|
72
|
-
expires_at TIMESTAMP,
|
|
73
|
-
max_uses INTEGER DEFAULT 1,
|
|
74
|
-
used_count INTEGER DEFAULT 0,
|
|
75
|
-
features JSONB DEFAULT '["network-node", "network-consumer"]',
|
|
76
|
-
notes TEXT
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
CREATE TABLE beta_activations (
|
|
80
|
-
id SERIAL PRIMARY KEY,
|
|
81
|
-
code TEXT REFERENCES beta_codes(code),
|
|
82
|
-
machine_id TEXT,
|
|
83
|
-
activated_at TIMESTAMP DEFAULT NOW(),
|
|
84
|
-
ip_address TEXT
|
|
85
|
-
);
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
### Generating Codes
|
|
89
|
-
|
|
90
|
-
Codes should be human-friendly, uppercase, with dashes. Format: `GROOVE-NET-<WORD>-<3 DIGITS>`
|
|
91
|
-
|
|
92
|
-
Examples:
|
|
93
|
-
- GROOVE-NET-ALPHA-001
|
|
94
|
-
- GROOVE-NET-BETA-042
|
|
95
|
-
- GROOVE-NET-EARLY-777
|
|
96
|
-
|
|
97
|
-
Generate a batch to start:
|
|
98
|
-
|
|
99
|
-
```sql
|
|
100
|
-
INSERT INTO beta_codes (code, expires_at, max_uses, notes) VALUES
|
|
101
|
-
('GROOVE-NET-ALPHA-001', '2026-07-18', 1, 'Rok - testing'),
|
|
102
|
-
('GROOVE-NET-ALPHA-002', '2026-07-18', 1, 'Tommy'),
|
|
103
|
-
('GROOVE-NET-ALPHA-003', '2026-07-18', 1, 'Reserved'),
|
|
104
|
-
('GROOVE-NET-ALPHA-004', '2026-07-18', 1, 'Reserved'),
|
|
105
|
-
('GROOVE-NET-ALPHA-005', '2026-07-18', 1, 'Reserved');
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
### Validation Logic
|
|
109
|
-
|
|
110
|
-
```
|
|
111
|
-
1. Look up code in beta_codes
|
|
112
|
-
2. If not found → { valid: false }
|
|
113
|
-
3. If expires_at < NOW() → { valid: false, message: "Code expired" }
|
|
114
|
-
4. If used_count >= max_uses → { valid: false, message: "Code already used" }
|
|
115
|
-
5. Insert into beta_activations
|
|
116
|
-
6. Increment used_count
|
|
117
|
-
7. Return { valid: true, expiresAt, features, message }
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### Admin
|
|
121
|
-
|
|
122
|
-
You'll want a way to:
|
|
123
|
-
- Generate new codes (one-off or batch)
|
|
124
|
-
- Revoke a code (delete or set expires_at to past)
|
|
125
|
-
- See who activated (query beta_activations)
|
|
126
|
-
- Set max_uses > 1 for codes you want to share more broadly
|
|
127
|
-
|
|
128
|
-
A simple admin page at groovedev.ai/admin/beta or even just direct DB access works for now.
|
|
129
|
-
|
|
130
|
-
---
|
|
131
|
-
|
|
132
|
-
## How the Daemon Uses This
|
|
133
|
-
|
|
134
|
-
The daemon (packages/daemon/src/api.js) calls this endpoint when a user submits an invite code:
|
|
135
|
-
|
|
136
|
-
```javascript
|
|
137
|
-
// POST /api/beta/activate handler
|
|
138
|
-
const response = await fetch('https://groovedev.ai/api/beta/validate', {
|
|
139
|
-
method: 'POST',
|
|
140
|
-
headers: { 'Content-Type': 'application/json' },
|
|
141
|
-
body: JSON.stringify({ code, machineId: daemon.getMachineId() })
|
|
142
|
-
});
|
|
143
|
-
const result = await response.json();
|
|
144
|
-
|
|
145
|
-
if (result.valid) {
|
|
146
|
-
config.set('networkBeta.unlocked', true);
|
|
147
|
-
config.set('networkBeta.code', code);
|
|
148
|
-
config.set('networkBeta.features', result.features);
|
|
149
|
-
config.set('networkBeta.expiresAt', result.expiresAt);
|
|
150
|
-
daemon.broadcast({ type: 'config:updated' });
|
|
151
|
-
}
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
Offline fallback: if the daemon can't reach groovedev.ai, it falls back to a hardcoded allowlist (the 5 ALPHA codes). This way beta testers aren't locked out by network issues. The hardcoded list gets removed when the server endpoint is live.
|
|
155
|
-
|
|
156
|
-
Re-validation: on daemon startup, if networkBeta.unlocked is true, optionally re-validate the stored code against the server. If the code was revoked or expired, lock the feature. Don't block startup on this — do it async.
|
|
157
|
-
|
|
158
|
-
---
|
|
159
|
-
|
|
160
|
-
## Security Notes
|
|
161
|
-
|
|
162
|
-
- Never log the full code in server logs — log first 10 chars only
|
|
163
|
-
- Rate limit by IP, not by code (prevents enumeration)
|
|
164
|
-
- Codes are not passwords — they're single-use invites. No hashing needed, but don't expose the full list in any public API
|
|
165
|
-
- The machineId is a hash, not PII — safe to store
|
|
166
|
-
- HTTPS only, obviously
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
# Security Review: Groove Network Integration
|
|
2
|
-
|
|
3
|
-
You are auditing the Groove Network feature — a decentralized LLM inference network integrated into the Groove desktop app. This feature is beta-gated behind invite codes. Review all network-related code for security vulnerabilities and attack vectors.
|
|
4
|
-
|
|
5
|
-
## Scope
|
|
6
|
-
|
|
7
|
-
All code added in these commits (newest first):
|
|
8
|
-
- daemon: switch network from relay to signal service
|
|
9
|
-
- gui: handle signal_connected and matched WS events in network feed
|
|
10
|
-
- daemon: spawn Python from venv so msgpack/torch are available
|
|
11
|
-
- daemon: use python3.12 for brew Python
|
|
12
|
-
- network: wire install/uninstall endpoints with progress broadcast
|
|
13
|
-
- network: gate view on network package install
|
|
14
|
-
- beta: validate invite codes against groovedev.ai with offline fallback
|
|
15
|
-
- settings: hide groove-network provider from Settings UI
|
|
16
|
-
- provider: default claude-code to Opus 4.6
|
|
17
|
-
- network: add beta-gated Groove Network integration
|
|
18
|
-
|
|
19
|
-
## Files to Review
|
|
20
|
-
|
|
21
|
-
Daemon (packages/daemon/src/):
|
|
22
|
-
- api.js — search for "Network" section (~lines 3720-4200): beta gate, invite code validation, node start/stop, install/uninstall, network status, version check/update endpoints
|
|
23
|
-
- providers/groove-network.js — provider that spawns Python consumer subprocess
|
|
24
|
-
- providers/index.js — provider registration
|
|
25
|
-
- index.js — networkNode state, startup re-validation
|
|
26
|
-
- firstrun.js — networkBeta config defaults
|
|
27
|
-
|
|
28
|
-
GUI (packages/gui/src/):
|
|
29
|
-
- views/network.jsx — install gate, main network view
|
|
30
|
-
- views/settings.jsx — Early Access invite code section
|
|
31
|
-
- stores/groove.js — networkUnlocked state, install/update progress, WebSocket handlers
|
|
32
|
-
- components/network/* — NodeToggle, NetworkStatus, NodeDetails
|
|
33
|
-
- components/layout/activity-bar.jsx — conditional Globe icon
|
|
34
|
-
|
|
35
|
-
## Threat Model
|
|
36
|
-
|
|
37
|
-
The feature involves:
|
|
38
|
-
1. An invite code system validating against a remote server (groovedev.ai)
|
|
39
|
-
2. Cloning a GitHub repo to the user's machine and running a setup script
|
|
40
|
-
3. Spawning Python subprocesses that connect outbound to a signal service (signal.groovedev.ai)
|
|
41
|
-
4. WebSocket connections to an external service carrying msgpack-encoded inference data
|
|
42
|
-
5. Ethereum-style keypair stored at ~/.groove/node_key.json
|
|
43
|
-
6. Config persistence with unlocked state, codes, and paths
|
|
44
|
-
|
|
45
|
-
## Attack Vectors to Investigate
|
|
46
|
-
|
|
47
|
-
### Code Execution & Injection
|
|
48
|
-
- Can the install endpoint be tricked into cloning a malicious repo? (repo URL hardcoded or configurable?)
|
|
49
|
-
- Does setup.sh run with proper sandboxing? What if the repo contents are compromised?
|
|
50
|
-
- Are Python spawn commands safe from injection? (check all spawn() calls use array args, not shell strings)
|
|
51
|
-
- Can the deployPath config value be manipulated to point outside ~/.groove/?
|
|
52
|
-
- Is the git clone --depth 1 safe from git-specific attacks?
|
|
53
|
-
|
|
54
|
-
### Authentication & Authorization
|
|
55
|
-
- Can the beta gate be bypassed? (check all /api/network/* endpoints go through networkGate)
|
|
56
|
-
- Is the hardcoded fallback code list a risk? (codes visible in source)
|
|
57
|
-
- Can invite codes be brute-forced? (rate limiting — daemon side and server side)
|
|
58
|
-
- Is the machineId derivation stable and non-spoofable?
|
|
59
|
-
- Can a deactivated user re-activate by manipulating local config?
|
|
60
|
-
|
|
61
|
-
### Network Security
|
|
62
|
-
- Is the WebSocket connection to signal.groovedev.ai using TLS? (wss:// vs ws://)
|
|
63
|
-
- Can a MITM intercept inference data between node and signal?
|
|
64
|
-
- Is the signal URL validated? Can it be pointed to a malicious server via config manipulation?
|
|
65
|
-
- Are there SSRF risks from the network status fetch?
|
|
66
|
-
- Does the daemon properly validate responses from groovedev.ai and signal.groovedev.ai?
|
|
67
|
-
|
|
68
|
-
### Data Security
|
|
69
|
-
- Is the node keypair (~/.groove/node_key.json) properly protected? (file permissions)
|
|
70
|
-
- Is the private key ever exposed via API endpoints or logs?
|
|
71
|
-
- Are invite codes logged in full or truncated?
|
|
72
|
-
- Can the API credentials endpoint leak the beta code?
|
|
73
|
-
- Is config.networkBeta.code exposed in GET /api/config?
|
|
74
|
-
|
|
75
|
-
### Denial of Service
|
|
76
|
-
- Can the install/update endpoints be called repeatedly to exhaust disk?
|
|
77
|
-
- Is there a limit on networkEvents array growth?
|
|
78
|
-
- Can the node process be spawned multiple times simultaneously?
|
|
79
|
-
- Does the version check cache actually prevent GitHub API abuse?
|
|
80
|
-
|
|
81
|
-
### Filesystem Safety
|
|
82
|
-
- Does uninstall properly scope deletion to ~/.groove/network/?
|
|
83
|
-
- Can path traversal in deployPath escape the intended directory?
|
|
84
|
-
- Are there symlink attacks possible in the install path?
|
|
85
|
-
- Does rmSync with recursive: true risk deleting unintended files?
|
|
86
|
-
|
|
87
|
-
### Supply Chain
|
|
88
|
-
- The install clones from GitHub — is the repo URL hardcoded or injectable?
|
|
89
|
-
- Is the tag pinned or can an attacker push a malicious tag?
|
|
90
|
-
- setup.sh runs arbitrary shell — what if the repo is compromised?
|
|
91
|
-
|
|
92
|
-
## Deliverables
|
|
93
|
-
|
|
94
|
-
1. List every vulnerability found with severity (critical/high/medium/low)
|
|
95
|
-
2. For each: describe the attack, the affected file and line number, and the fix
|
|
96
|
-
3. Flag any patterns that are risky even if not immediately exploitable
|
|
97
|
-
4. Confirm which security measures are already in place and working correctly
|
|
98
|
-
5. Commit all fixes
|