Skip to content

feat(ui): identity settings and own-identity chip#111371

Merged
steipete merged 5 commits into
mainfrom
claude/identity-settings-ui
Jul 19, 2026
Merged

feat(ui): identity settings and own-identity chip#111371
steipete merged 5 commits into
mainfrom
claude/identity-settings-ui

Conversation

@steipete

Copy link
Copy Markdown
Contributor

What Problem This Solves

The identity stack is live on shared gateways (#111179, #111207, #111224, #111225, #111311), but a person has no way to see or shape their own identity: no profile view, no display-name editing, no avatar upload, and no "this is me" anchor in the UI.

Part of the multiplayer-identity program: #111133 (settings/UI PR — the final piece).

Why This Change Was Made

  • Settings → Identity section, visible only when the connection has an identity: shows the durable profile (avatar, display name, linked emails) resolved via the users.self bootstrap RPC; edits display name (users.setDisplayName) and avatar (client-side downscale to 256×256, 512 KB cap, users.setAvatar) — self-service authorized by the write-scope + own-profile contract from feat(gateway): durable user profiles with email aliases and avatars #111224.
  • Footer identity chip (bottom-left, beside the global facepile): own avatar/initials + name, click navigates to Identity settings. Renders from a shared self-identity selector that prefers locally saved profile state over the connect-time presence snapshot, so edits reflect immediately without waiting for reconnect; avatar URLs are cache-busted on save.
  • One shared avatar primitive with the landed facepile; +21 bytes gzip startup delta over main (chip rides the existing idle-load boundary).

Hard product rule enforced and tested: no identity → no Identity section, no chip, no users.self call, byte-identical UI.

User Impact

Teammates on shared gateways can set their name and avatar, and see themselves anchored bottom-left. Identity-less gateways see zero change.

Evidence

  • Testbox focused UI suites: 176 tests (profile settings, avatar processing incl. >512 KB / >10 MB rejects, identity-absent contract, sidebar chip) + 158 tests after the chip-sync fix (name/avatar update without any presence event; chip hides offline; durable-id agreement between presence user.id and users.self).
  • Equal-path production build: startup JS 309,747 B (main) → 309,768 B (branch).
  • Structured autoreview (codex/gpt-5.6-sol, xhigh): one real staleness finding (chip vs. local profile edits) — fixed via the shared selector; final run clean, "patch is correct".

@openclaw-barnacle openclaw-barnacle Bot added app: web-ui App: web-ui size: XL maintainer Maintainer-authored PR labels Jul 19, 2026
@steipete
steipete marked this pull request as ready for review July 19, 2026 10:37
@steipete
steipete force-pushed the claude/identity-settings-ui branch from 9f97c21 to 82cfcbc Compare July 19, 2026 10:42

@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: 9f97c2143b

ℹ️ 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 +116 to +118
const selfUser = resolveSelfPresenceUser(entries, client?.instanceId);
if (!sameSelfUser(snapshot.selfUser, selfUser)) {
setSnapshot({ ...snapshot, selfUser });

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 Preserve local profile edits across presence snapshots

After a display-name or avatar save, any later presence broadcast replaces the locally updated selfUser with the connection-time metadata here. The server explicitly snapshots profile metadata only at connect (src/gateway/server/ws-connection/connect-session.ts:143-151), so ordinary presence churn on a shared gateway reverts the footer chip until reconnect; the added test at ui/src/app/gateway-store.test.ts:194-223 even codifies this overwrite. Keep the locally refreshed fields while the presence entry still identifies the same profile.

Useful? React with 👍 / 👎.

Comment on lines +444 to +445
if (avatarUrl) {
this.context.gateway.updateSelfUser?.({ avatarUrl });

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 Feed the saved profile avatar into chat rendering

When an identified user uploads an avatar, this updates only the gateway selfUser projection used by the new footer/profile UI. Chat still initializes userAvatar exclusively from the browser-local identity (ui/src/pages/chat/chat-state.ts:1218-1233), while this commit removes the last profile-page call to saveLocalUserIdentity; consequently the uploaded durable avatar never appears on the user's chat messages, and the previous chat-avatar editor is no longer available. Either bind chat rendering to the durable profile or retain the local editor until that migration is complete.

Useful? React with 👍 / 👎.

Comment on lines +70 to +73
url.pathname = `/api/users/${encodeURIComponent(profileId)}/avatar`;
url.search = `?v=${updatedAt}`;
url.hash = "";
return url.href;

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 Fetch protected profile avatars with gateway authentication

For tokenless Tailscale-authenticated identity connections, this URL is assigned directly to an <img>, which cannot attach the Control UI's Bearer credential. The avatar handler requires scoped HTTP authentication (src/gateway/user-profiles-http.ts:34-45), and HTTP authentication intentionally disables Tailscale forwarded-header auth (src/gateway/auth.ts:605-610), so the new profile preview and own-identity chip receive an unauthorized response instead of the saved image. Load the bytes through an authenticated fetch/blob path or provide an equivalent authenticated image-delivery contract.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. labels Jul 19, 2026
@clawsweeper

clawsweeper Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 19, 2026, 6:45 AM ET / 10:45 UTC.

Summary
Adds an identity settings section and sidebar self-identity chip, backed by existing user-profile RPCs and presence-derived identity state in the Control UI.

PR surface: Source +325, Tests +293. Total +618 across 22 files.

Reproducibility: yes. from source inspection: save an avatar or display name, receive a later same-user presence event, and the gateway store restores the connect-time data; tokenless Tailscale authentication also provides a clear protected-avatar rendering path to verify live.

Review metrics: 1 noteworthy metric.

  • Identity-source transition: 1 browser-local profile editor removed; 1 durable profile editor added. The visible settings transition changes an existing identity source while chat still consumes the browser-local one.

Stored data model
Persistent data-model change detected: persistent cache schema: ui/src/app/user-profile.test.ts. Confirm migration or upgrade compatibility proof before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #111133
Summary: This PR is the proposed settings/UI completion for the canonical shared-gateway identity program; the remaining findings concern its integration boundary rather than a separate root cause.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🦪 silver shellfish
Patch quality: 🧂 unranked krab
Result: blocked until real behavior proof from a real setup is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Show a redacted real Control UI run that saves a display name and avatar, then confirms the footer and chat behavior expected by the chosen migration.
  • Demonstrate protected avatar loading on a tokenless Tailscale-authenticated gateway without exposing private endpoints or credentials.
  • [P1] Add regression coverage for a later same-profile presence event after a local profile save.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR reports focused tests and build output, but needs redacted after-fix real UI proof for profile saving, the footer chip, and authenticated avatar loading before merge. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Risk before merge

  • [P1] Existing browser-local chat-avatar behavior loses its editing path while durable profile edits do not yet drive chat rendering, creating two inconsistent identity sources during upgrade.
  • [P1] Protected profile avatars can fail to render for tokenless Tailscale-authenticated Control UI connections because direct image loads cannot satisfy the endpoint's scoped HTTP-auth contract.
  • [P1] Presence churn can visibly revert a just-saved name or avatar until reconnect, contrary to the immediate-update behavior claimed by the new chip.

Maintainer options:

  1. Repair identity delivery before merge (recommended)
    Preserve local profile edits across same-profile presence events, deliver avatars through authenticated requests, and resolve the chat-identity migration before landing.
  2. Pause pending identity migration decision
    Hold the PR if maintainers do not yet want durable shared profiles to replace the existing browser-local chat identity surface.

Next step before merge

  • [P1] The fixes are concrete, but the chat-identity compatibility boundary requires the product-direction decision recorded above and this PR carries the protected maintainer label.

Maintainer decision needed

  • Question: Should the durable shared-gateway profile now become the canonical identity for chat messages, or should the existing browser-local chat identity/editor remain until that migration is implemented and validated?
  • Rationale: The branch removes the visible editor for an established local chat-identity surface without wiring chat to the new durable profile, so maintainers need to choose the intended compatibility boundary rather than leaving competing sources of truth.
  • Likely owner: steipete — The surrounding merged identity work and this branch are all authored by steipete, who has the strongest demonstrated context for the intended migration boundary.
  • Options:
    • Complete the durable-profile migration (recommended): Wire chat rendering and editing to the durable profile, including upgrade behavior and focused regression coverage.
    • Preserve local chat identity for now: Retain the existing browser-local chat identity editor until a separately approved migration can replace it safely.

Security
Cleared: The diff does not broaden credentials, permissions, or dependency execution; the authenticated-avatar defect is a functional delivery failure rather than a trust-boundary bypass.

Review findings

  • [P2] Retain refreshed profile state across presence updates — ui/src/app/gateway-store.ts:113-119
  • [P1] Load protected avatars through authenticated requests — ui/src/components/viewer-facepile.ts:132-133
  • [P1] Preserve chat-avatar behavior during the profile migration — ui/src/pages/profile/profile-page.ts:17-18
Review details

Best possible solution:

Keep durable gateway profiles as the shared identity source, add an authenticated avatar-delivery path, retain refreshed self-profile state through same-profile presence updates, and either migrate chat rendering to that source or preserve the browser-local chat editor until an explicit migration is approved.

Do we have a high-confidence way to reproduce the issue?

Yes from source inspection: save an avatar or display name, receive a later same-user presence event, and the gateway store restores the connect-time data; tokenless Tailscale authentication also provides a clear protected-avatar rendering path to verify live.

Is this the best way to solve the issue?

No. The durable-profile direction is coherent, but the implementation must preserve or explicitly migrate the existing chat identity surface and use authenticated avatar delivery rather than raw image URLs.

Full review comments:

  • [P2] Retain refreshed profile state across presence updates — ui/src/app/gateway-store.ts:113-119
    Keep a local self-profile overlay (or merge refreshed fields while the profile id matches) instead of assigning the next presence snapshot wholesale. Presence metadata is captured at connection setup, so this code reverts a just-saved name or avatar whenever ordinary presence churn arrives.
    Confidence: 0.97
  • [P1] Load protected avatars through authenticated requests — ui/src/components/viewer-facepile.ts:132-133
    Do not pass the protected avatar endpoint directly to <img>. Tokenless Tailscale-authenticated UI connections cannot attach the Control UI Bearer credential, while the HTTP endpoint requires scoped HTTP authentication; fetch the image through the authenticated client and render a managed blob URL or provide an equivalent authenticated delivery contract.
    Confidence: 0.94
  • [P1] Preserve chat-avatar behavior during the profile migration — ui/src/pages/profile/profile-page.ts:17-18
    This removes the last profile-page save path for browser-local identity, but chat still reads that local identity and never receives the durable profile avatar. Existing users lose the editor while new durable avatar uploads do not affect their chat messages; migrate chat in this PR or keep the old editor until the migration is approved.
    Confidence: 0.91

Overall correctness: patch is incorrect
Overall confidence: 0.93

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against ef91ce57124b.

Label changes

Label changes:

  • add P1: The branch can break visible identity/avatar behavior for active shared-gateway users on supported authenticated connections.
  • add merge-risk: 🚨 compatibility: It removes the existing local identity editing path without yet migrating chat rendering to the new durable profile.
  • add merge-risk: 🚨 auth-provider: The new direct avatar-image path does not satisfy the existing scoped HTTP authentication contract for tokenless Tailscale-authenticated users.
  • add rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🦪 silver shellfish and patch quality is 🧂 unranked krab.
  • add status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR reports focused tests and build output, but needs redacted after-fix real UI proof for profile saving, the footer chip, and authenticated avatar loading before merge. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Label justifications:

  • P1: The branch can break visible identity/avatar behavior for active shared-gateway users on supported authenticated connections.
  • merge-risk: 🚨 compatibility: It removes the existing local identity editing path without yet migrating chat rendering to the new durable profile.
  • merge-risk: 🚨 auth-provider: The new direct avatar-image path does not satisfy the existing scoped HTTP authentication contract for tokenless Tailscale-authenticated users.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🦪 silver shellfish and patch quality is 🧂 unranked krab.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR reports focused tests and build output, but needs redacted after-fix real UI proof for profile saving, the footer chip, and authenticated avatar loading before merge. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +325, Tests +293. Total +618 across 22 files.

View PR surface stats
Area Files Added Removed Net
Source 16 778 453 +325
Tests 6 491 198 +293
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 22 1269 651 +618

What I checked:

  • Presence update overwrites local profile state: The branch assigns the identity resolved from every presence event directly into snapshot.selfUser; a matching later presence snapshot therefore replaces the name/avatar just written through updateSelfUser, despite the sidebar selector preferring that snapshot. (ui/src/app/gateway-store.ts:113, 82cfcbc8f751)
  • Avatar endpoint requires an authenticated HTTP request: The review trace identifies the avatar handler's scoped HTTP-auth requirement and the HTTP-auth path that does not accept Tailscale forwarded-header authentication. The new shared avatar component renders a direct image URL, which cannot attach the Control UI Bearer credential. (src/gateway/user-profiles-http.ts:34, ef91ce57124b)
  • Existing chat identity remains browser-local: The branch removes the profile-page use of saveLocalUserIdentity, while chat rendering still initializes its user avatar from browser-local identity state. A durable profile-avatar save therefore does not reach chat rendering, and the existing local editor is removed. (ui/src/pages/chat/chat-state.ts:1218, ef91ce57124b)
  • Feature-history provenance: The merged identity foundation is carried by the related presence, durable-profile, and connection-enrichment work; this UI branch is the proposed settings/UI completion of that program. (ui/src/pages/profile/profile-page.ts:1, 82cfcbc8f751)
  • Submitted proof is test/build-only: The PR body reports focused Testbox suites and a production build delta, but contains no redacted live UI run, terminal output, recording, screenshot, or runtime log demonstrating the settings save and authenticated-avatar path after the fix. (82cfcbc8f751)

Likely related people:

  • steipete: Authored the current UI branch and the merged identity-foundation PRs that define the profile, presence, and connection contracts this change consumes. (role: feature owner and recent area contributor; confidence: high; commits: c684b1321333, a8b7290f346c, 2e8b042f9fba; files: ui/src/pages/profile/profile-page.ts, ui/src/app/gateway-store.ts, src/gateway/user-profiles-http.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@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: e439871276

ℹ️ 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 +177 to +178
if (nextSelfUser && (clientChanged || selfProfileChanged)) {
void this.loadIdentity();

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 Gate profile bootstrap on operator.write

When an identified Control UI connection is restricted to operator.read—for example, a trusted-proxy connection capped by x-openclaw-scopes—presence still supplies nextSelfUser, so this unconditionally calls users.self. That RPC requires operator.write (src/gateway/methods/core-descriptors.ts:129-133), while read-only identified connections are explicitly supported (src/gateway/server.auth.trusted-proxy-device-autoapprove.test.ts:256-277), leaving the advertised Identity section permanently at “profile could not be loaded” and its controls unusable. Gate the section/bootstrap on write access or provide a read-only profile contract.

Useful? React with 👍 / 👎.

@steipete
steipete merged commit c95a8e3 into main Jul 19, 2026
161 of 163 checks passed
@steipete
steipete deleted the claude/identity-settings-ui branch July 19, 2026 11:14
@steipete

Copy link
Copy Markdown
Contributor Author

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 20, 2026
* feat(ui): identity settings and own-identity chip

* feat(ui): resolve own identity via users.self

* fix(ui): rebase identity settings onto landed identity stack

* fix(ui): footer identity chip prefers locally updated self profile

* fix(ui): settings CI conformance — split profile page, lean exports
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: web-ui App: web-ui maintainer Maintainer-authored PR merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P1 High-priority user-facing bug, regression, or broken workflow. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: XL status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant