5e9bf4e777
Resolve a message sender's endpoint id to a human handle using a new
control-plane directory endpoint.
- ControlPlane.fetchDirectory(): signed GET /api/directory, mapped to
DirectoryEntry { signPub, endpoint, handle, role }. The server's endpoint
matches endpointID(signPub) byte for byte.
- busService keeps an endpoint -> handle Map, loaded once after a session
opens and refreshed after createRoom (where the ACL is already refreshed).
Exposes a pure displayName(endpoint) resolver: handle when known, the
session user's own handle for their messages, short id fallback otherwise.
- Resilience: loadDirectory never throws. A missing endpoint (404 on older
clusters) or a transient error leaves the map empty and the UI falls back to
the short id, so the chat keeps working exactly as before.
- ChatPanel renders displayName(msg.sender) in the message header and derives
the avatar initials from the handle; the raw endpoint stays in a title
tooltip for debugging.
- types: Message.sender comment updated (this is the "phase 2" readable name).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>