git-daemon 0.1.0

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/design.md ADDED
@@ -0,0 +1,481 @@
1
+ # Git Daemon Design Document
2
+
3
+ ## Overview
4
+
5
+ **Git Daemon** is a local, background service that enables a trusted web UI (React app) to perform **privileged, local machine actions**—primarily Git operations and developer convenience actions—without granting the browser arbitrary system access.
6
+
7
+ This project includes **only the daemon**. The React UI is external and out of scope; UI references here exist solely to define integration expectations.
8
+
9
+ The daemon exposes a **localhost HTTP API** guarded by:
10
+
11
+ * **Origin allowlist** (only your UI URLs)
12
+ * **Pairing token** (per-user secret)
13
+ * **Workspace sandbox** (all filesystem actions constrained)
14
+
15
+ ---
16
+
17
+ ## Goals
18
+
19
+ * Enable “Clone locally” from a web UI using native Git credentials (SSH / credential helper).
20
+ * Provide additional local dev actions:
21
+
22
+ * `fetch`
23
+ * `status`
24
+ * open folder / terminal / VS Code
25
+ * run dependency installation (`npm i` / pnpm / yarn)
26
+ * Prevent any website other than the approved UI origins from controlling the daemon.
27
+ * Prevent the approved UI from writing outside a user-approved local workspace root.
28
+
29
+ ## Non-goals
30
+
31
+ * Acting as a GitHub API proxy or storing GitHub OAuth tokens.
32
+ * Building or shipping the React UI.
33
+ * Acting as a full Git GUI (diff viewer, staging UI, merge tools) — those are UI concerns.
34
+ * Running arbitrary shell commands. (Only explicit, whitelisted operations.)
35
+
36
+ ---
37
+
38
+ ## Integration Context (External)
39
+
40
+ These components are **not part of this project**; they are listed only to define how the daemon is used.
41
+
42
+ ### React UI (external)
43
+
44
+ * Hosted: `https://app.example.com` (Cloudflare Worker URL or custom domain)
45
+ * Local: `http://localhost:<uiPort>`
46
+ * Calls:
47
+
48
+ * GitHub API directly (optional, UI-side)
49
+ * Git Daemon API on `http://127.0.0.1:<daemonPort>`
50
+ * The UI may be hosted remotely or run locally, but **the daemon does not proxy GitHub API calls** and **does not accept GitHub tokens**.
51
+
52
+ ### Cloudflare Worker (external)
53
+
54
+ * Serves hosted UI assets and routing.
55
+ * No GitHub API proxying.
56
+ * No local operations.
57
+
58
+ ---
59
+
60
+ ## Architecture
61
+
62
+ ### Components
63
+ 1. **Git Daemon (local service)**
64
+
65
+ * Binds to `127.0.0.1` only
66
+ * Implements a small HTTP JSON API + streaming logs (SSE)
67
+ * Runs **system git** for native credentials & compatibility
68
+ * Runs **package manager installs** in sandboxed repos
69
+ * Provides OS integrations: open folder/terminal/VS Code
70
+
71
+ ---
72
+
73
+ ## Security Model
74
+
75
+ ### Threats addressed
76
+
77
+ * Malicious websites attempting to send requests to localhost services.
78
+ * DNS rebinding and host header abuse against localhost services.
79
+ * Path traversal / writing outside intended directories.
80
+ * Unintended execution via `npm install` scripts.
81
+ * CSRF-like attacks from untrusted origins.
82
+
83
+ ### Controls
84
+
85
+ #### 1) Bind to loopback only
86
+
87
+ * Listen on `127.0.0.1:<daemonPort>` (not `0.0.0.0`)
88
+ * Reject requests to non-loopback interfaces.
89
+
90
+ #### 2) Origin allowlist (hard gate)
91
+
92
+ For every request:
93
+
94
+ * Check `Origin` header (and optionally `Referer` for diagnostics).
95
+ * Allow only exact matches:
96
+
97
+ * `https://app.example.com`
98
+ * `http://localhost:<uiPort>` (optionally allow a small dev port set)
99
+
100
+ If not allowed: respond `403`.
101
+
102
+ * Require `Origin` for all requests (reject missing/empty).
103
+
104
+ #### 2b) DNS rebinding protections
105
+
106
+ * Verify remote address is loopback (`127.0.0.1`/`::1`).
107
+ * Verify `Host` is `127.0.0.1` or `localhost` (reject all others).
108
+
109
+ #### 3) Pairing token (second gate)
110
+
111
+ * Daemon generates a random secret on first run.
112
+ * UI must send `Authorization: Bearer <token>` on all non-public endpoints.
113
+ * Pairing UX options:
114
+
115
+ * **Copy/Paste code**: daemon prints pairing code; UI has input box.
116
+ * **Local confirmation page**: daemon serves `GET /pair` which user visits, clicks “Approve”, daemon issues token.
117
+ * Default to the local confirmation page; use copy/paste as a headless fallback.
118
+
119
+ Token storage:
120
+
121
+ * Stored locally in daemon config directory (OS-appropriate).
122
+ * Store a hash of the token at rest; keep a per-origin token record.
123
+ * Support rotation and revocation (per origin).
124
+ * Tokens expire after 30 days by default; allow refresh before expiry.
125
+ * UI stores token in browser storage (scoped to the UI origin).
126
+
127
+ #### 4) Workspace sandbox
128
+
129
+ * Daemon maintains a **workspaceRoot** directory.
130
+ * All filesystem paths are resolved to absolute canonical paths.
131
+ * Reject if the resolved path is outside `workspaceRoot` (including symlink escapes).
132
+ * Clone destinations must be workspace-relative or validated absolute paths inside root.
133
+ * On first run, prompt the user to select a workspace root; persist the choice in config.
134
+
135
+ #### 5) Capability gating (required)
136
+
137
+ First-time use of high-risk features requires explicit user approval:
138
+
139
+ * `open-terminal`
140
+ * `open-vscode`
141
+ * `deps/install`
142
+ Approval is remembered per (origin, repo) tuple, with an option to revoke.
143
+
144
+ #### 6) No arbitrary command execution
145
+
146
+ * Only whitelisted commands are implemented.
147
+ * Any endpoint that would accept arbitrary args must be constrained/validated.
148
+ * Validate `repoUrl` formats (SSH/HTTPS only); disallow `file://` and local paths.
149
+
150
+ #### 7) Abuse limits (recommended)
151
+
152
+ * Rate limit pairing attempts and auth failures.
153
+ * Cap request body size and path length.
154
+
155
+ ---
156
+
157
+ ## Configuration & Storage
158
+
159
+ * Use OS-specific config directories:
160
+
161
+ * macOS: `~/Library/Application Support/Git Daemon`
162
+ * Linux: `~/.config/git-daemon`
163
+ * Windows: `%APPDATA%\\Git Daemon`
164
+ * Store:
165
+
166
+ * `config.json` (workspace root, approvals, options)
167
+ * `tokens.json` (hashed tokens, per-origin records)
168
+ * `logs/` (structured logs with rotation)
169
+ * Log rotation: keep 5 files × 5MB each.
170
+
171
+ ---
172
+
173
+ ## Git & System Integration
174
+
175
+ ### Git execution strategy
176
+
177
+ Use **system `git`** via process spawn:
178
+
179
+ * Supports SSH config, credential helpers, GPG signing, etc.
180
+ * Matches user environment and reduces edge cases.
181
+
182
+ Capture output:
183
+
184
+ * Stream `stdout`/`stderr` to job logs.
185
+ * Parse where useful (e.g., status porcelain).
186
+
187
+ ### Runtime & packaging
188
+
189
+ * Plain Node.js script written in TypeScript.
190
+ * No binary packaging; run via Node (e.g., `node dist/daemon.js` or `npm run daemon`).
191
+ * Manual start only; no auto-start or OS service installation.
192
+
193
+ ### Suggested libraries (optional)
194
+
195
+ * `express` for the HTTP API.
196
+ * `cors` with a strict allowlist for Origin handling.
197
+ * `zod` or `ajv` for request validation.
198
+ * `express-rate-limit` for pairing/auth failure throttles.
199
+ * `execa` for spawning git/deps with streamed output.
200
+ * `tree-kill` or `pidtree` for cross-platform process-tree termination.
201
+ * `env-paths` for OS-correct config directories.
202
+ * `pino` + `pino-http` for structured logging.
203
+ * `rotating-file-stream` for log rotation.
204
+ * `vitest` for unit/integration testing.
205
+ * `supertest` for HTTP API testing.
206
+
207
+ ### CLI UX (local)
208
+
209
+ * Use `prompts` for interactive input flows.
210
+ * Use `ora` for status spinners and long-running task feedback.
211
+ * Use `chalk` for readable, color-coded output.
212
+ * Rationale: lightweight, simple to wire into a daemon with occasional user prompts.
213
+
214
+ ### Testing approach
215
+
216
+ * Unit tests cover pure validation, path resolution, and policy checks (origins, workspace, capabilities).
217
+ * Integration tests start the daemon on an ephemeral port and call HTTP endpoints via `supertest`.
218
+ * Use temp directories for workspace roots and repos; never touch user paths.
219
+ * Stub Git/deps commands where possible; allow a small set of opt-in, real-git tests behind a flag.
220
+ * Validate SSE streams with deterministic event sequences for job lifecycles.
221
+
222
+ ### Credential behavior (native)
223
+
224
+ * Clone defaults to SSH URL: `git@github.com:OWNER/REPO.git`
225
+ * If user prefers HTTPS and has credential helper configured, daemon can support HTTPS clone URL too—still without receiving tokens.
226
+
227
+ ---
228
+
229
+ ## API Design
230
+
231
+ ### Conventions
232
+
233
+ * JSON request/response.
234
+ * Long operations create a **job** and return `jobId`.
235
+ * Logs/updates streamed via SSE.
236
+ * All “repoPath” inputs must be inside workspace root.
237
+ * Use direct responses for short operations; jobs for long-running operations.
238
+
239
+ ### Public endpoints (no token required)
240
+
241
+ * `GET /v1/meta` → status + version/build + pairing state + capabilities
242
+ * includes a `capabilities` object for tool availability
243
+
244
+ ### Pairing endpoints
245
+
246
+ * `POST /v1/pair` → `{ step: "start"|"confirm", code? }`
247
+
248
+ * `start` returns pairing instructions (and optionally a one-time code)
249
+ * `confirm` exchanges code for `accessToken`
250
+
251
+ Meta response fields (examples):
252
+
253
+ * `version: string`
254
+ * `build: { commit?: string, date?: string }`
255
+ * `pairing: { required: boolean, paired: boolean }`
256
+ * `workspace: { configured: boolean, root?: string }`
257
+ * `capabilities.tools.pnpm.installed: boolean`
258
+ * `capabilities.tools.code.installed: boolean`
259
+ * UI uses these to adapt features (e.g., show pnpm options)
260
+
261
+ ### Job endpoints
262
+
263
+ * `GET /v1/jobs/:id` → status metadata
264
+ * `GET /v1/jobs/:id/stream` → SSE stream of log events
265
+ * `POST /v1/jobs/:id/cancel` → request cancellation
266
+
267
+ ### Git endpoints
268
+
269
+ * `POST /v1/git/clone` → job
270
+
271
+ * body: `{ repoUrl, destRelative, options?: { branch?, depth? } }`
272
+ * `POST /v1/git/fetch` → job
273
+
274
+ * body: `{ repoPath, remote?: "origin", prune?: true }`
275
+ * note: fetch updates remote tracking only; no merge/rebase is performed
276
+ * `GET /v1/git/status?repoPath=...` → structured status
277
+
278
+ * returns: `{ branch, ahead, behind, stagedCount, unstagedCount, untrackedCount, conflictsCount, clean }`
279
+
280
+ ### OS “open” endpoints
281
+
282
+ * `POST /v1/os/open` → `{ target: "folder"|"terminal"|"vscode", path }`
283
+
284
+ ### Package installation endpoint
285
+
286
+ * `POST /v1/deps/install` → job
287
+
288
+ * body: `{ repoPath, manager?: "auto"|"npm"|"pnpm"|"yarn", mode?: "auto"|"ci"|"install", safer?: boolean }`
289
+
290
+ Recommendations:
291
+
292
+ * `manager=auto` chooses based on `packageManager` field / lockfiles / installed tools.
293
+ * `safer=true` maps to flags that reduce script execution risk (e.g., `--ignore-scripts` for npm/pnpm/yarn).
294
+ * Default to `safer=true` for all runs; allow a per-repo override to enable scripts.
295
+
296
+ ---
297
+
298
+ ## Job Model
299
+
300
+ ### Job states
301
+
302
+ * `queued` → `running` → `done | error | cancelled`
303
+
304
+ ### Stream event format (SSE)
305
+
306
+ Each event is JSON:
307
+
308
+ * `{ type: "log", stream: "stdout"|"stderr", line: "..." }`
309
+ * `{ type: "progress", kind: "git"|"deps", percent?: number, detail?: string }`
310
+ * `{ type: "state", state: "running"|"done"|"error", message?: string }`
311
+
312
+ ### Cancellation
313
+
314
+ * Attempt graceful termination:
315
+
316
+ * send SIGINT/SIGTERM (platform-dependent)
317
+ * terminate the entire process tree
318
+ * mark cancelled if process exits
319
+
320
+ ### Concurrency & timeouts
321
+
322
+ * Default to one running job at a time (configurable).
323
+ * Enforce a max runtime of 1 hour per job (configurable); return a `timeout` error code.
324
+
325
+ ---
326
+
327
+ ## Dependency Manager Selection
328
+
329
+ ### Detection rules
330
+
331
+ * Daemon detects presence of tools (`pnpm`, `npm`, `yarn`) via PATH and `--version`.
332
+
333
+ ### Auto selection order (example)
334
+
335
+ 1. If `package.json.packageManager` specifies pnpm/yarn/npm → prefer that manager (if installed)
336
+ 2. Else if lockfile exists:
337
+
338
+ * `pnpm-lock.yaml` → pnpm
339
+ * `yarn.lock` → yarn
340
+ * `package-lock.json` → npm
341
+ 3. Else fallback to npm
342
+
343
+ ### Command choice
344
+
345
+ * pnpm: `pnpm install` (add `--frozen-lockfile` for CI/lockfile enforcement; add `--ignore-scripts` when `safer=true`)
346
+ * npm:
347
+
348
+ * if lockfile exists: `npm ci`
349
+ * else: `npm install`
350
+ * add `--ignore-scripts` when `safer=true`
351
+ * yarn: `yarn install` (add `--immutable` if berry is detected; add `--ignore-scripts` when `safer=true`)
352
+
353
+ ---
354
+
355
+ ## Usage Flows
356
+
357
+ ### 1) Hosted UI + daemon (normal user)
358
+
359
+ 1. User opens `https://app.example.com`
360
+ 2. UI checks daemon: `GET http://127.0.0.1:<port>/v1/meta`
361
+ 3. If missing: UI shows “Run daemon” instructions (repo clone + `npm run daemon`)
362
+ 4. Pair once (code/confirm)
363
+ 5. User clicks “Clone locally”
364
+ 6. UI calls daemon `/v1/git/clone`, streams job logs
365
+ 7. UI shows “Open in VS Code / Terminal / Folder”
366
+
367
+ ### 2) Local UI + daemon (dev)
368
+
369
+ Same, except UI origin is localhost and allowlisted.
370
+
371
+ ### Usage considerations
372
+
373
+ * Provide a "Fetch" action that does not pull; use it to update ahead/behind indicators before any merge/rebase.
374
+ * Avoid implicit pull; all merges/rebases should be explicit, user-driven actions.
375
+
376
+ ---
377
+
378
+ ## Local Setup (developer-oriented)
379
+
380
+ ### Requirements
381
+
382
+ * Git installed
383
+ * Node installed (for running daemon from repo, per your approach)
384
+ * Optional:
385
+
386
+ * VS Code CLI (`code`) in PATH for “Open VS Code”
387
+ * pnpm installed for pnpm installs
388
+
389
+ ### Typical commands
390
+
391
+ * `npm install`
392
+ * `npm run daemon`
393
+
394
+ ---
395
+
396
+ ## Example Requests (for reference)
397
+
398
+ ### Check meta
399
+
400
+ ```bash
401
+ curl -H "Origin: https://app.example.com" \
402
+ http://127.0.0.1:8787/v1/meta
403
+ ```
404
+
405
+ ### Clone
406
+
407
+ ```bash
408
+ curl -X POST \
409
+ -H "Origin: https://app.example.com" \
410
+ -H "Authorization: Bearer <TOKEN>" \
411
+ -H "Content-Type: application/json" \
412
+ -d '{"repoUrl":"git@github.com:owner/repo.git","destRelative":"owner/repo"}' \
413
+ http://127.0.0.1:8787/v1/git/clone
414
+ ```
415
+
416
+ ### Stream job logs (SSE)
417
+
418
+ ```bash
419
+ curl -N \
420
+ -H "Origin: https://app.example.com" \
421
+ -H "Authorization: Bearer <TOKEN>" \
422
+ http://127.0.0.1:8787/v1/jobs/<JOB_ID>/stream
423
+ ```
424
+
425
+ ---
426
+
427
+ ## Error Handling
428
+
429
+ * `401` missing/invalid bearer token
430
+ * `403` origin not allowed
431
+ * `404` unknown job/repo
432
+ * `409` operation not allowed (e.g., outside workspace, repo not registered)
433
+ * `422` invalid input (malformed paths, missing fields)
434
+ * `413` request too large
435
+ * `429` rate limited
436
+ * `500` unexpected failure (include safe diagnostic code)
437
+
438
+ Errors should include a stable `errorCode` and a user-safe `message`.
439
+
440
+ Recommended `errorCode` values:
441
+
442
+ * `auth_required`
443
+ * `auth_invalid`
444
+ * `origin_not_allowed`
445
+ * `rate_limited`
446
+ * `request_too_large`
447
+ * `workspace_required`
448
+ * `path_outside_workspace`
449
+ * `invalid_repo_url`
450
+ * `capability_not_granted`
451
+ * `job_not_found`
452
+ * `timeout`
453
+ * `internal_error`
454
+
455
+ ---
456
+
457
+ ## Observability & Diagnostics
458
+
459
+ * `GET /v1/diagnostics` (optional) returns:
460
+
461
+ * daemon config summary (redacted)
462
+ * recent errors and job history (limited)
463
+ * log tail and last 20 job summaries
464
+ * Structured JSON logs written to `logs/` with request/job IDs.
465
+ * Keep the last 100 jobs in memory and the last 50 on disk.
466
+ * “Copy debug bundle” flow can zip logs + redacted config + job summaries.
467
+
468
+ ---
469
+
470
+ ## Future Extensions
471
+
472
+ * Repo registry: “known repos” list for safer operations and better UX
473
+ * `git pull`, `checkout`, `branch list`, `log` endpoints
474
+ * Support multiple terminals/editors
475
+ * Signed request challenge (HMAC) in addition to bearer token
476
+
477
+ ---
478
+
479
+ ## Open Questions (implementation choices)
480
+
481
+ * How to handle first-time SSH host key verification UX
package/logo.png ADDED
Binary file