Skip to content

feat(gateway): resolve durable profile identity at connection setup#111311

Merged
steipete merged 1 commit into
mainfrom
claude/identity-presence-enrichment
Jul 19, 2026
Merged

feat(gateway): resolve durable profile identity at connection setup#111311
steipete merged 1 commit into
mainfrom
claude/identity-presence-enrichment

Conversation

@steipete

Copy link
Copy Markdown
Contributor

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

  • Connection setup resolves the authenticated email once via 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).
  • Presence now projects user: { id: profileId, email, name?, avatarUrl? }avatarUrl points at the authenticated /api/users/:id/avatar endpoint when an avatar exists. Clients group by user.id opaquely, so no client changes.
  • Chat and agent prompt attribution stamp sender: { id: profileId, name? } via one shared projection helper (gateway-client-identity.ts), with email-only fallback preserved.
  • Store failure falls back to today's email-only identity with one warn log — profile storage can never block login.
  • Accepted tradeoff (commented): profile edits become visible on reconnect; no live re-resolution or new events.

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.
  • Structured autoreview (codex/gpt-5.6-sol, xhigh): clean, "patch is correct".
  • Production diff: 8 files, ~70 prod insertions.

@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime size: M maintainer Maintainer-authored PR labels Jul 19, 2026
@steipete
steipete marked this pull request as ready for review July 19, 2026 08:29

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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`,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

@steipete
steipete force-pushed the claude/identity-presence-enrichment branch from ffcb619 to 345df1c Compare July 19, 2026 08:33

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines 347 to +349
...(authenticatedUserId
? { user: { id: authenticatedUserId, email: authenticatedUserId } }
? {
user: authenticatedUserProfile

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

@steipete
steipete merged commit 2e8b042 into main Jul 19, 2026
118 checks passed
@steipete
steipete deleted the claude/identity-presence-enrichment branch July 19, 2026 08:41
@steipete

Copy link
Copy Markdown
Contributor Author

Merged via squash.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gateway Gateway runtime maintainer Maintainer-authored PR size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant