Skip to content

delego 0.3.4 — single-writer daemon - #18

Merged
koishore merged 1 commit into
mainfrom
feat/single-writer-daemon
Jun 10, 2026
Merged

delego 0.3.4 — single-writer daemon#18
koishore merged 1 commit into
mainfrom
feat/single-writer-daemon

Conversation

@koishore

Copy link
Copy Markdown
Member

Adds the single-writer daemon — the spec's serialized single-writer ledger, made real. Protocol unchanged (0.3); the daemon is optional (no daemon → file-backed, exactly as before). Package → 0.3.4.

Why

Without it, a rate_limit is exact only among processes sharing one home on one host (the file lock). delego daemon makes one process the sole owner of the Firewall; every client routes through it, so the cap is exact across all clients — and the ledger has one writer by construction.

What

  • delego/daemon.pyserve() + a ThreadingUnixStreamServer with a single lock around every op (line-delimited JSON over a UDS, stdlib only, no new dependency). Hardened: Windows-guarded (needs AF_UNIX), main-thread-only signal handlers, deep listen backlog, chmod 0600, and a probe-before-unlink so a second daemon refuses to start (two writers is the one thing this prevents).
  • delego/client.pyDaemonClient (propose/resolve/decide/pending/policy/audit_tail/verify/ping) + daemon_running().
  • Paths.socket in the per-user runtime dir keyed by a hash of the home (Unix socket paths are length-limited; a deep .claude/.delego would overflow).
  • CLI: delego daemon runs it; approve/deny/pending auto-route to a live daemon (the sole writer) so a human decision can't fork state behind it.

Verification

72 tests (7 new): the headline — 16 concurrent DaemonClient.propose against rate_limit max:3 → exactly 3 allows (cross-client exactness the file lock alone can't give) — plus full approval loop over the socket, second-daemon-refuses, bad-request-doesn't-kill-connection, ping/round-trips. examples/demo.py green; existing 65 unchanged (opt-in).

Scoped out (follow-ups, noted in ROADMAP)

Routing the MCP agent surface to the daemon (next — realizes exact cross-client limits for agents in prod, not just CLI/tests); a TCP/cross-host transport; and a reserve-then-execute path (broker call outside the lock). Docs: README/ROADMAP/SECURITY updated; the landing /docs site is a separate follow-up PR.

One long-running process owns the Firewall and exposes it over a Unix domain
socket (line-delimited JSON, stdlib socketserver — no new dependency),
serializing every operation under a single lock. So every client routes
through one writer and a rate_limit is exact ACROSS ALL CLIENTS (the spec's
serialized single-writer ledger, §5/§11), not just one host's file lock.

- delego/daemon.py: serve() + the UDS server. Windows-guarded (needs AF_UNIX),
  main-thread-guarded signal handlers, deep listen backlog, chmod 0600,
  probe-before-unlink so a second daemon refuses to start.
- delego/client.py: DaemonClient (propose/resolve/decide/pending/policy/
  audit_tail/verify/ping) + daemon_running().
- Paths.socket lives in the per-user runtime dir keyed by a hash of the home
  (Unix socket paths are length-limited; a deep home would overflow).
- CLI: 'delego daemon' to run it; approve/deny/pending auto-route to a live
  daemon (the sole writer) so a human decision can't fork state behind it.
- Off by default: no daemon -> file-backed, unchanged. Protocol still 0.3;
  package 0.3.4. 7 new tests incl. 16 concurrent clients vs rate_limit max:3
  -> exactly 3 allows; 72 total green.
@koishore
koishore merged commit d8d2f3f into main Jun 10, 2026
3 checks passed
@koishore
koishore deleted the feat/single-writer-daemon branch June 10, 2026 22:13
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