Skip to content

feat: daemon + serve HTTP surface (v3.3.0)#65

Merged
kengio merged 2 commits into
mainfrom
v3.3-daemon
Jun 4, 2026
Merged

feat: daemon + serve HTTP surface (v3.3.0)#65
kengio merged 2 commits into
mainfrom
v3.3-daemon

Conversation

@kengio
Copy link
Copy Markdown
Contributor

@kengio kengio commented Jun 4, 2026

What

Adds the daemon + serve HTTP surface — the v3.3 foundation for the OneBrain WebUI (step 1 + step 2 of the daemon-serve build sequence). The CLI gains a persistent engine and a read-only vault JSON API, serving an external dist directory (no embedded assets — keeps the binary lean).

Commands

  • onebrain daemon start | stop | status — detached self-respawning engine (PID file, session-leader liveness)
  • onebrain serve [--dir <dist>] [--port N] [--host <addr>] [--open] — foreground HTTP surface; Jupyter-style ?token= URL

HTTP surface (one local listener, 127.0.0.1 by default)

  • GET /api/configonebrain.yml as JSON
  • GET /api/vault/tree — recursive folder/file listing
  • GET /api/vault/file?path= — one note's content + rev (for future conflict detection)
  • Static SPA fallback (placeholder page when no dist mounted)

serve and daemon __run share one run_server; only the shutdown trigger differs (Ctrl-C vs SIGTERM).

Security

  • Per-session 128-bit token (/dev/urandom), required on every /api/* (Authorization: Bearer or X-OneBrain-Token); constant-time compare
  • 3-layer path-traversal guard.., absolute paths, and symlink-escape all rejected inside the vault root
  • No vault → 503, never falls back to serving / (caught in review: a vault_root="/" fallback had exposed /etc/passwd)
  • Token never written to the daemon log; run dir 0700, log 0600

Review & verification

  • 3-round review (min standard): R1 found the vault=/ release blocker + 11 other issues → all fixed; R2 verified 12 fixes; R3 = SHIP
  • Verified live on the real ob-1 vault (1,761 entries): tree/config/file return real data; error matrix correct — no-token→401, traversal→400, absolute→400, missing→404, dir→400, valid→200
  • Local CI mirror green: cargo fmt --check clean · cargo clippy -D warnings clean · all workspace tests pass

Deferred (step 2b)

PUT /api/vault/file + conflict detection · /api/search · /api/chat SSE · PWA SW/manifest · daemon-aware serve reuse · TimeoutLayer (needs tower-http timeout feature) · tree dotfile/.DS_Store filtering

🤖 Generated with Claude Code

kengio added 2 commits June 5, 2026 00:14
Self-respawn-detached daemon: `start` spawns a hidden `daemon __run` child via
setsid (+ chdir, stdio redirected to ~/.onebrain/run/daemon.log); PID file with
a session-leader identity probe; SIGTERM park loop; structured tracing→log. Sync
for now — the tokio/axum HTTP server arrives in step 2.

- 14 tests (dependency-injected pure core + assert_cmd lifecycle); clippy + fmt clean
- 3-round review applied: PID-recycling identity guard (getpgid==pid), single
  log FD (tracing→stderr→log), lifecycle integration test, read_pid bounds guard,
  chdir("/"), known-limitation docs (concurrent-start TOCTOU)
…vault read API (v3.3 step 2)

The daemon `__run` and a new foreground `onebrain serve [--dir] [--port] [--open] [--host]`
share one tokio/axum HTTP server on 127.0.0.1: a static SPA (ServeDir + index.html
fallback + per-session token injected into the page) plus a read-only JSON API —
GET /api/config, /api/vault/tree, /api/vault/file?path= — behind a per-session token.
`__run` is now async (replacing the step-1 park loop) and shuts down gracefully on
SIGTERM; `serve` on Ctrl-C.

Security (from a 3-round review): a daemon with no real vault returns 503 (never
falls back to `/`, so the API can't leak the host filesystem); 3-layer path-traversal
guard; constant-time token compare; the token is never written to the log (log 0600,
run dir 0700); 10 MB file cap; FS work moved off the async runtime via spawn_blocking;
filesystem/config errors mapped to 4xx (no errno leak).

- deps: tokio (rt-multi-thread/macros/net/signal/time) + axum 0.8 + tower + tower-http (fs)
- 38 server tests incl. a live TCP bind/serve/graceful-shutdown integration test
- step 2b deferred (noted in code): PUT+conflict, /api/search, /api/extensions, /api/chat SSE, PWA service-worker, daemon-aware serve reuse
@kengio kengio merged commit 16c2080 into main Jun 4, 2026
6 checks passed
@kengio kengio deleted the v3.3-daemon branch June 4, 2026 23:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant