Daemon outbox now stores resolved target_spec + crypto_box ciphertext + nonce per row. Drain worker is a forwarder; no per-row resolution at drain time. Outbound routing is no longer a placeholder. Schema additions (additive, NULL allowed for legacy rows): outbox.mesh, target_spec, nonce, ciphertext, priority. v0.9.0 rows keep draining via the broadcast fallback so existing in-flight rows finish cleanly. IPC /v1/send resolves the user-friendly to (display name, hex prefix, full pubkey, @group, *, #topicId) into a broker-format target_spec at accept time. DMs encrypt via crypto_box; broadcast/topic/group base64 the plaintext. Hex prefixes (16+ chars) match against connected peers. CLI thin-client routing extends trySendViaDaemon pattern to peer list and skill list/get. Three new helpers in services/bridge/daemon-route.ts. SKILL.md gains ambient mode section: after claudemesh install, raw claude works for the daemon's attached mesh. Launch stays as the override path. Spec at .artifacts/specs/2026-05-04-v2-roadmap-completion.md orders the remaining v2.0.0 work: multi-mesh daemon (1.26), CLI-to-thin-client (1.27), mesh-to-workspace rename (1.28), HKDF identity (2.0). Released as 1.25.0 on npm. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.6 KiB
v2.0.0 Daemon Redesign — Completion Roadmap
Date: 2026-05-04 Owner: alezmad Status: in-progress (1.24.0 + 1.25.0 land most of it; remainder is two follow-up arcs)
What's done
| v2.0.0 bullet | Version | Status |
|---|---|---|
claudemesh-daemon long-lived launchd / systemd unit |
1.22.0 | ✅ Done |
| MCP server shrinks to thin daemon adapter | 1.24.0 | ✅ Done — 979 → ~200 LoC of push-pipe, daemon-required, no fallback |
claudemesh install auto-installs + starts daemon |
1.24.0 | ✅ Done |
claudemesh launch ensures daemon |
1.24.0 | ✅ Done |
| Daemon outbound routing (Sprint 4: real targets + crypto) | 1.25.0 | ✅ Done — outbox stores mesh, target_spec, nonce, ciphertext, priority; resolution + crypto_box happens at IPC accept time; drain is a forwarder |
| CLI thin-client routing for read verbs | 1.25.0 | ✅ Partial — peer list, skill list/get route through daemon when present; same trySendViaDaemon fallback shape |
Ambient mode (raw claude Just Works) |
1.25.0 | ✅ Documented + functional for the daemon's attached mesh |
What remains (in dependency order)
A. Daemon multi-mesh (the prerequisite for "ambient mode for everything")
Why it's the critical path: ambient mode today only works for the single mesh the daemon is attached to. Users with N meshes either run N daemons (different sock paths) or restart the daemon to switch. Neither is acceptable for the v2.0.0 promise.
What it takes:
- Daemon holds
Map<slug, DaemonBrokerClient>instead of one broker. - Outbox row's
meshcolumn (1.25.0 added) is the dispatch key. - IPC
/v1/sendrequiresmeshfield (or infers from target prefix<slug>:<target>). - IPC read endpoints (
/v1/peers,/v1/skills,/v1/profile) accept?mesh=<slug>or return mesh-grouped results. - SSE event payloads already include
meshslug; no change needed. - Drain worker selects broker by row's
meshcolumn. daemon upwith no--meshattaches to all joined meshes; with--mesh Xrestricts to X (legacy mode for explicit single-mesh).- Inbox dedupe keeps using
client_message_idUNIQUE; mesh column for filtering only.
Estimated effort: 1 week. ~600 LoC across run.ts, drain.ts, ipc/server.ts, plus tests for per-mesh dispatch.
Risk: medium. The single-mesh assumption is baked into a few places (peer-list response shape, skill-list response shape). Need to choose: per-mesh tagged responses (breaking) or array-of-meshes wrapped responses (additive). Recommend the latter for back-compat.
B. HKDF-derived peer keypairs (cross-machine identity)
Why it matters: today each install per machine = fresh keypair = different mesh member identity. User signs in on laptop and desktop and shows up as two different members. v2.0.0 promised "same identity across machines."
What it takes:
HKDF(account_secret, info: "claudemesh/mesh/<mesh_id>/peer", salt: <user_id>)derives a deterministic ed25519 keypair per mesh.account_secretderives from the user's authenticated session — needs broker-side endpoint to vend it on first install.- Enrollment flow changes: instead of generating a fresh keypair, derive it. Subsequent installs find the same pubkey already in
mesh.memberand skip enrollment. - Migration: existing members keep their old keypairs (they're stored in config). Only new joins use HKDF. Optional: opt-in re-enrollment for users who want cross-machine sync.
- Broker hello-sig protocol unchanged (still ed25519 sign).
Estimated effort: 2-3 weeks. Touches enrollment, broker auth, dashboard, security review.
Risk: high. Crypto change with security implications. Needs design review (account_secret distribution security, HKDF salt choice, key compromise recovery story).
C. Mesh → workspace public surface rename
Why it matters: "mesh" is internal jargon for what users experience as "a workspace." v2.0.0 calls for the rename to align UX language.
What it takes:
- All CLI verbs gain
workspacealiases (claudemesh workspace list≡claudemesh list). - Help text, docs, README, marketing site updated.
- DB tables stay
mesh_*(migration cost prohibitive; not user-visible). - Wire protocol stays
mesh_*(broker change too disruptive). - Eventually deprecate the
meshaliases (~2 minor versions later).
Estimated effort: 3-4 days. Mostly rote search/replace + new aliases.
Risk: low. Cosmetic.
D. Full CLI-to-thin-client conversion
Why it matters: today the CLI has bridge + cold-path code that duplicates ~3000 LoC of broker WS / crypto / decode logic that the daemon also has. Once daemon is multi-mesh, every verb can become "open IPC, send request, render response."
What it takes:
- Each verb: replace
withMesh(...)(which opens its own broker WS) withdaemonOnly(...)(calls IPC, errors if daemon down). - Drop
bridge/server.ts,bridge/client.ts,bridge/socket-broker.tsentirely. - Drop most of
services/broker/ws-client.tsfrom the CLI build (kept only for daemon's internal use). - CLI binary shrinks ~30-40%.
- Daemon becomes the only broker WS holder per user.
Estimated effort: 1 week. Mostly mechanical; strict typescript catches most issues.
Risk: medium. Breaks workflows where CLI is used without daemon (CI environments, headless scripts). Need to keep a --no-daemon escape hatch or document the constraint.
Recommended sequencing
1.25.0 (today): Sprint 4 outbound routing + CLI thin-client read paths + ambient mode docs
1.26.0 (next): A. Daemon multi-mesh — "ambient mode for everything"
1.27.0: D. CLI-to-thin-client conversion — drops ~3000 LoC
1.28.0: C. Mesh → workspace rename (aliases shipped, no removal yet)
2.0.0: B. HKDF identity (separate security-reviewed arc)
A → D → C → B is the right order:
- A unblocks ambient mode for multi-mesh users (highest UX value).
- D unblocks the LoC reduction the v2.0.0 promise mentioned ("3000 LoC removed").
- C is cosmetic; do it once D has stabilized.
- B is the most security-sensitive; do it last, with proper review.
Out of scope for the v2.0.0 endpoint
- Topic crypto (Sprint 5+). Topics still ship as base64 plaintext. Real per-topic encryption is a v0.3.0 operator-layer item, parallel track.
- Broker hardening for daemon idempotency (Sprint 7). Partial unique index on
(mesh_id, client_message_id) WHERE NOT NULLand themesh.client_message_dedupetable. Documented in2026-05-03-daemon-spec-broker-hardening-followups.md. launchdeprecation. 1.25.0 docs now recommend ambient mode for default cases;launchstays as the override path. Full deprecation is a 2.x decision.