feat(serve): opt-in etcd-lease presence for the daemon's live sessions#487
Merged
Conversation
The mu-daemon slice of lease-backed dialogue presence (push-mailbox spec §1; completes the trio: mu#482 server read side, agent_tools#48 cc watch holder). One lease per daemon + one reconciliation task: every TTL/3 it keepalives and syncs /mu/dialogue/v1/peers/ against the LIVE session registry (mu:<daemon_id>:<session_id> per session + a daemon key) — no lifecycle hooks, so every create/remove path (workers included) is covered on the next tick, and a crashed daemon's keys vanish with its single lease. Rehydrated read-only ghosts are excluded via a new Sessions::live_session_ids() (inner + workers only). Strictly opt-in per operator requirement (adopting mu must not require etcd): spawned only when events_dir is set (production; same hermeticity posture as the supervisor mailbox) AND [dialogue.presence] enabled=true is present in the config mu-core carries opaquely (mu#485). No cargo feature: reqwest is already in the dep tree via rmcp's reqwest transport and base64 is a workspace dep, so the compile-cost delta is nil — config-gating alone keeps the adoption barrier flat. Fail-open: etcd trouble degrades to activity-derived presence and retries; sessions are never affected. Verified live against etcd 3.6.5 + the production dialogue server: mu serve start -> daemon peer visible as presence=lease on the channel; kill -> liveness claim dropped at lease expiry.
sahuagin
approved these changes
Jul 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The mu-daemon slice of lease-backed dialogue presence — completes the trio (mu#482 server read side, agent_tools#48 cc watch holder):
mu serveregisters its live sessions (mu:<daemon_id>:<session_id>) plus a daemon key under one etcd lease, so mu peers show uppresence:"lease"on the dialogue channel and are addressable by broadcast without ever saying/polling. Crash → the single lease expires → all the daemon's keys vanish. The lease IS the liveness proof.Design: a reconciliation task (every TTL/3: keepalive + diff the keyspace against
Sessions::live_session_ids(), a new accessor coveringinner+workersand deliberately excluding rehydrated read-only ghosts) — no per-lifecycle hooks, so every create/remove path including spawned workers is covered on the next tick.Adoption barrier (operator requirement)
Strictly opt-in, no cargo feature needed:
events_diris set (production; same hermeticity posture as the supervisor mailbox — tests spawn nothing) and[dialogue.presence] enabled = trueis in the config (carried opaquely by mu-core since fix(mu-core): tolerate the [dialogue] config section owned by mu-dialogue #485).Testing
just cigreen.mu servestart → daemon peer visible aspresence:"lease"viaagent dialogue peers; kill → liveness claim dropped at lease expiry.🤖 Generated with Claude Code
https://claude.ai/code/session_019YY9xpjK6mWLJDMaYnvC6T