A reply routinely fanned out to every session of the sender's member, which is the "peers spread messages to the wrong peers" noise. Three links fed each other: - channel meta put the MEMBER key in from_pubkey/from_id, and the skill told the model to reply by from_name (repeatable across sessions). from_pubkey/from_id are now the sender's session key; the member key moves to from_member_pubkey; the skill says reply to from_pubkey. - a DM to a member key was claimed by that member's daemon socket and shown to all its sessions. New daemon/resolve-target.ts is the single resolver for /v1/send: a member key maps to its only live session, is refused (409 member_target_ambiguous + candidates) when it has several, and needs --fanout to reach all of them on purpose. A duplicated display name or prefix is ambiguous instead of first-match; control- plane rows are never addressable. - a session MCP whose token didn't resolve fell into the unfiltered SSE stream and rendered every sibling's DMs. The MCP now subscribes with scope=session and gets no message events without a resolved session. Also: --self lists only the current mesh and requires a registered session (it could message its own new row); a DM to a control-plane row says so instead of the wrong "session pubkeys are ephemeral" hint, and `peer list --all` tags those rows (bug9). Spec: .artifacts/specs/2026-09-26-session-identity-and-routing.md §2-3, §6 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1.2 KiB
1.2 KiB