feat(gateway): resolve durable profile identity at connection setup#111311
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ffcb619bc4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| : {}), | ||
| ...(authenticatedUserProfile.hasAvatar | ||
| ? { | ||
| avatarUrl: `/api/users/${authenticatedUserProfile.profileId}/avatar`, |
There was a problem hiding this comment.
Make profile avatar URLs usable with Tailscale auth
For a tokenless Control UI session authenticated through Tailscale Serve, this advertises an avatar URL that the browser cannot fetch. The avatar handler requires authorizeScopedGatewayHttpRequestOrReply (src/gateway/user-profiles-http.ts:34), which reaches authorizeHttpGatewayConnect; that function explicitly selects the HTTP auth surface with Tailscale forwarded-header authentication disabled (src/gateway/auth.ts:605-612). Since an <img> request cannot reuse the WebSocket credentials and this session has no shared-secret bearer token, /api/users/:id/avatar returns 401, so every Tailscale-authenticated user with an avatar gets a broken image.
Useful? React with 👍 / 👎.
ffcb619 to
345df1c
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 345df1cdb1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ...(authenticatedUserId | ||
| ? { user: { id: authenticatedUserId, email: authenticatedUserId } } | ||
| ? { | ||
| user: authenticatedUserProfile |
There was a problem hiding this comment.
Render the new presence identity in bundled clients
For users relying on the bundled Control UI, this populated user object has no visible effect: ui/src/api/types.ts:291-307 omits user from PresenceEntry, and ui/src/pages/nodes/view-inventory.ts:441-455 renders presence-only clients from host, device metadata, and roles without reading the profile name or avatar. Consequently the advertised names and avatars do not appear in the repository's presence-driven UI; the client type and renderer need to consume these fields.
Useful? React with 👍 / 👎.
|
Merged via squash.
|
What Problem This Solves
Presence identity (#111179) and prompt attribution (#111207) key on the raw authenticated email, because nothing durable existed when they landed. Emails change; durable history keyed on them fractures. The profile store (#111224) provides stable ids — this PR flips identity to them at the single place identity enters the system.
Part of the multiplayer-identity program: #111133 (final wiring PR).
Why This Change Was Made
ensureProfileForEmail(first login lazily creates the profile — this is the login path by design) and carries a prepared{ profileId, displayName, hasAvatar }snapshot on connection state (hot-path rule: resolve once, no per-request lookups).user: { id: profileId, email, name?, avatarUrl? }—avatarUrlpoints at the authenticated/api/users/:id/avatarendpoint when an avatar exists. Clients group byuser.idopaquely, so no client changes.sender: { id: profileId, name? }via one shared projection helper (gateway-client-identity.ts), with email-only fallback preserved.Identity remains strictly optional: no identity → no store access, byte-identical behavior.
User Impact
Names and avatars appear in presence-driven UI for identified teammates; transcript attribution survives email changes. Identity-less gateways unchanged.
Evidence
node scripts/run-vitest.mjs src/gateway/server/ws-connection/message-handler.post-connect-health.test.ts src/gateway/server.chat.gateway-server-chat-b.test.ts— green (37 + 87 tests): stable profile id across connections for one email, avatar-on-reconnect, store-failure fallback, optional identity, durable chat attribution.src/gateway/server/ws-connection.test.ts(17),src/state/user-profiles.test.ts(11, untouched) — green.