Skip to content

feat(gateway): durable user profiles with email aliases and avatars#111224

Merged
steipete merged 10 commits into
mainfrom
claude/identity-profiles-store
Jul 19, 2026
Merged

feat(gateway): durable user profiles with email aliases and avatars#111224
steipete merged 10 commits into
mainfrom
claude/identity-profiles-store

Conversation

@steipete

Copy link
Copy Markdown
Contributor

What Problem This Solves

The identity program (#111133) keys everything on the email that Cloudflare/trusted-proxy auth verifies — but emails are login claims, not durable keys. People change emails; a raw-email identity layer would orphan presence grouping and prompt attribution history. There is also nowhere to store a display name or avatar for a teammate on a shared gateway.

Part of the multiplayer-identity program: #111133 (profiles PR of the train; #111179 and #111207 already landed).

Why This Change Was Made

Adds the durable identity layer as additive tables in the shared state SQLite DB (no schema-version bump; idempotent lazy ensure on first use):

  • user_profiles — stable opaque id, display name, avatar blob + sha256, merged_into tombstone.
  • user_profile_emails — lowercased email aliases → profile id.
  • Resolver: ensureProfileForEmail lazily creates a profile on first login; resolveProfileByEmail follows at most one tombstone hop. Email change: the new email auto-creates a profile; admin users.linkEmail re-points the alias to the person's real profile; if the orphan loses its last alias it is tombstoned (merged_into), and merge path-compression repoints older tombstones so chains never exceed depth 1 — durable references (e.g. prompt attribution) written before the merge still resolve.
  • Gateway RPCs users.list (read scope) / users.linkEmail, users.setDisplayName, users.setAvatar (admin scope), with protocol-complete result payloads.
  • GET /api/users/:profileId/avatar — authenticated avatar serving with content-hash+MIME ETags, HEAD support, RFC 9110 If-None-Match semantics.
  • CLI: openclaw users list, openclaw users link-email <email> --to <profileId>.

Identity remains purely optional: nothing runs (not even DDL) until a profile API is first used; existing gateways are byte-identical.

User Impact

No visible change on its own. Enables the settings identity UI (display name/avatar) and the presence enrichment that switches user.id from email to stable profile id.

Evidence

  • node scripts/run-vitest.mjs src/state/user-profiles.test.ts src/gateway/user-profiles-http.test.ts src/gateway/server-methods/users.test.ts src/cli/users-cli.test.ts src/gateway/method-scopes.test.ts src/gateway/server-methods.authorization.test.ts — all green (store roundtrip/merge/tombstone compression/cycle prevention, ETag + HEAD + If-None-Match forms, RPC scope enforcement incl. dispatcher-level admin test, CLI JSON output).
  • node scripts/generate-kysely-types.mjs --verify — passed.
  • Six structured autoreview rounds (codex/gpt-5.6-sol, xhigh) drove out: tombstone chain compression (P1), avatar blob loading on list (P1), scoped-path routing simplification, content-hash ETags, alias-change timestamps, single-snapshot listing, display-name cap, ensure-cache commit ordering, HEAD + If-None-Match conformance. Final run: clean, "patch is correct".

@steipete
steipete marked this pull request as ready for review July 19, 2026 05:14
@steipete
steipete requested a review from a team as a code owner July 19, 2026 05:14
@openclaw-barnacle openclaw-barnacle Bot added app: web-ui App: web-ui gateway Gateway runtime cli CLI command changes size: XL maintainer Maintainer-authored PR labels Jul 19, 2026
@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. 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, 3:11 AM ET / 07:11 UTC.

Summary
Adds lazy SQLite-backed durable user profiles and email aliases, authenticated avatar serving, four gateway RPCs, Android protocol entries, and openclaw users administration commands.

PR surface: Source +940, Tests +599, Other +4. Total +1543 across 21 files.

Reproducibility: not applicable. This PR proposes a new durable identity capability rather than reporting a current failing behavior; the linked identity program supplies the design rationale.

Review metrics: 2 noteworthy metrics.

  • Persistent storage surface: 2 additive SQLite tables. Durable profile and email-alias records require canonical schema and maintenance ownership before upgrades can safely rely on them.
  • Gateway administration API: 4 added RPC methods. The new read/admin methods create a compatibility-sensitive public operator contract.

Stored data model
Persistent data-model change detected: database schema: src/state/user-profiles-schema.ts, serialized state: src/cli/users-cli.ts, serialized state: src/state/user-profiles.test.ts, serialized state: src/state/user-profiles.ts, unknown-data-model-change: packages/gateway-protocol/src/schema/users.ts, unknown-data-model-change: src/state/user-profiles.test.ts, and 1 more. Confirm migration or upgrade compatibility proof before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #111133
Summary: This PR is a candidate implementation of the durable-profile phase of the broader shared-gateway identity program; the already-merged adjacent PRs cover different phases.

Members:

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

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🦪 silver shellfish
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:

  • Register the tables in the canonical shared-state schema while retaining lazy ensure behavior.
  • [P1] Add a redacted authenticated CLI/RPC/avatar execution transcript or recording showing the after-fix result.
  • Obtain explicit maintainer sponsorship for the durable profile and users.* API contract.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR body provides focused tests and CI claims only; add redacted after-fix evidence from a real authenticated gateway, CLI, or avatar HTTP run before merge, and request @clawsweeper re-review if the body update does not trigger a fresh review. 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] The two durable tables are not registered in the canonical shared-state schema, so generated types and database maintenance/repair paths can omit persistent profile data after merge.
  • [P1] This adds a durable identity model and four public gateway methods; that API and upgrade surface needs explicit maintainer acceptance after the storage contract is corrected.
  • [P1] No contributor-supplied real behavior evidence currently demonstrates an authenticated CLI, RPC, or avatar HTTP path after the change.

Maintainer options:

  1. Register and prove the persistent contract (recommended)
    Move the additive table declarations into the canonical shared-state schema, retain the lazy feature-use ensure, then add redacted authenticated runtime proof before merge.
  2. Pause the durable-profile phase
    Defer this PR if maintainers do not yet want to commit to durable profile identifiers and the public users.* operator API.

Next step before merge

  • [P1] The protected maintainer-labeled feature needs an explicit durable-identity contract decision; the mechanical schema repair and contributor-owned real behavior proof must precede merge review.

Maintainer decision needed

  • Question: Should the shared-gateway identity program adopt durable profiles and the new users.* gateway administration API now, after the canonical-schema repair and real behavior proof are supplied?
  • Rationale: The branch introduces a persistent identity model and public management surface rather than repairing an established behavior; the protected maintainer label requires explicit human ownership of that product contract.
  • Likely owner: steipete — The merged identity-program phases and this proposed profile layer connect this contributor most directly to the required product-contract decision.
  • Options:
    • Sponsor the durable-profile phase (recommended): Approve this phase of the identity program once canonical schema registration, focused compatibility validation, and redacted real behavior proof are added.
    • Defer the profile API: Keep the already-merged identity threading and attribution work while deferring persistent profiles and management RPCs until the long-term operator contract is decided.

Security
Cleared: The diff restricts avatar MIME types and size, serves avatars behind the existing scoped gateway authorization path, and introduces no concrete dependency or privilege-escalation concern.

Review findings

  • [P1] Register profile tables in the canonical state schema — src/state/user-profiles-schema.ts:3
Review details

Best possible solution:

Keep lazy activation, but declare the additive tables in the canonical shared-state schema, preserve the one-time lazy ensure, demonstrate a redacted authenticated end-to-end users/avatar flow, and land only with explicit approval of the durable identity API.

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

Not applicable: this PR proposes a new durable identity capability rather than reporting a current failing behavior; the linked identity program supplies the design rationale.

Is this the best way to solve the issue?

No, not yet: durable profiles are a plausible solution, but persistent tables must be declared through the canonical shared-state schema and the resulting operator flow needs real authenticated proof.

Full review comments:

  • [P1] Register profile tables in the canonical state schema — src/state/user-profiles-schema.ts:3
    Keep the idempotent lazy ensure, but declare these persistent tables in the canonical shared-state schema as well. Leaving them only in feature-local DDL omits them from generated shared-state types and database maintenance/repair coverage, so upgrades can carry durable profile data that the canonical tooling does not know how to validate.
    Confidence: 0.98

Overall correctness: patch is incorrect
Overall confidence: 0.96

AGENTS.md: found and applied where relevant.

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

Label changes

Label changes:

  • add rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🦪 silver shellfish.
  • remove rating: 🦐 gold shrimp: Current PR rating is rating: 🧂 unranked krab, so this older rating label is no longer current.

Label justifications:

  • P2: This is a bounded but compatibility-sensitive gateway feature with no demonstrated urgent user regression.
  • merge-risk: 🚨 compatibility: The PR adds persistent state and four public gateway methods, while its tables are currently outside the canonical schema and maintenance path.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🦪 silver shellfish.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body provides focused tests and CI claims only; add redacted after-fix evidence from a real authenticated gateway, CLI, or avatar HTTP run before merge, and request @clawsweeper re-review if the body update does not trigger a fresh review. 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 +940, Tests +599, Other +4. Total +1543 across 21 files.

View PR surface stats
Area Files Added Removed Net
Source 14 940 0 +940
Tests 6 599 0 +599
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 1 4 0 +4
Total 21 1543 0 +1543

What I checked:

  • Persistent-schema defect: The branch defines user_profiles and user_profile_emails only in feature-local DDL, leaving the canonical shared-state schema, generated database types, and state maintenance/repair path unaware of these durable tables. (src/state/user-profiles-schema.ts:3, 1807de5d266c)
  • Existing review confirmation: The outstanding review discussion identifies the same missing canonical schema registration and explains that lazy ensuring should remain while the canonical schema becomes authoritative. (src/state/user-profiles-schema.ts:3, 1807de5d266c)
  • Identity-program provenance: The linked identity program treats durable profiles as one independently landable phase, while the adjacent identity-threading and prompt-attribution phases were merged earlier today. (src/gateway/server-methods/users.ts:1, c684b1321333)
  • Proof gate: The PR body reports focused unit tests and generated-type verification, but it contains no redacted after-fix authenticated gateway, CLI, or avatar HTTP execution evidence. (src/gateway/user-profiles-http.ts:15, 1807de5d266c)

Likely related people:

  • steipete: Authored the merged identity-threading and prompt-attribution phases that establish the surrounding gateway identity contract, as well as this durable-profile phase. (role: recent identity-program contributor; confidence: high; commits: c684b1321333, 721aee1f19e7; files: src/gateway/server-methods/users.ts, src/state/user-profiles.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.
Review history (1 earlier review cycle)
  • reviewed 2026-07-19T05:19:10.274Z sha 4e9f603 :: needs real behavior proof before merge. :: none

@openclaw-barnacle openclaw-barnacle Bot added the app: android App: android label Jul 19, 2026

@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: 1807de5d26

ℹ️ 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 +1 to +3
// Canonical additive schema for durable user profiles. Kept feature-local so
// ordinary shared-state opens do not create identity tables until they are used.
export const USER_PROFILES_SCHEMA_SQL = `

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.

P1 Badge Register the profile tables in the canonical state schema

These tables exist only in this feature-local DDL, so scripts/generate-kysely-types.mjs omits them from generated shared-state types and src/state/openclaw-state-db-maintenance.ts cannot validate or repair their shape. This also directly violates AGENTS.md:80, which requires additive SQLite tables to be declared in src/state/openclaw-state-schema.sql as well as lazily ensured. Keep the idempotent feature-use ensure, but make the canonical schema the authoritative declaration so future schema generation, verification, and migrations include this persistent user data.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. labels Jul 19, 2026
@steipete
steipete force-pushed the claude/identity-profiles-store branch from 1807de5 to bdfa07c Compare July 19, 2026 07:42
@openclaw-barnacle openclaw-barnacle Bot added the scripts Repository scripts label Jul 19, 2026

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

ℹ️ 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".

export * from "./schema/audit-activity.js";
export * from "./schema/audit.js";
export * from "./schema/board.js";
export * from "./schema/users.js";

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.

P1 Badge Register user schemas in the canonical protocol registry

Exporting users.ts here does not add these definitions to ProtocolSchemas in packages/gateway-protocol/src/schema/protocol-schemas.ts; both scripts/protocol-gen.ts and scripts/protocol-gen-swift.ts iterate only that registry. Consequently, the published protocol.schema.json and generated Swift bindings omit UserProfile and every new Users* request/result type, leaving native and schema-driven clients without the typed contracts for these RPCs. Register all new definitions in ProtocolSchemas and regenerate the protocol outputs.

Useful? React with 👍 / 👎.

@steipete
steipete merged commit a8b7290 into main Jul 19, 2026
132 of 133 checks passed
@steipete
steipete deleted the claude/identity-profiles-store branch July 19, 2026 07:58
@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
…penclaw#111224)

* feat(gateway): durable user profiles with email aliases and avatars

* fix(gateway): compress merge tombstones, content-hash avatar ETags, users CLI json output

* fix(gateway): lean profile listing, scoped avatar routing, typed email validation

* fix(gateway): protocol-complete profile payloads and consistent store reads

* fix(gateway): mark profile schema ensured only after commit

* fix(gateway): avatar endpoint HEAD support and RFC If-None-Match

* fix(gateway): profiles CI conformance — bindings, lint, knip, sql boundary

* feat(gateway): self-service profile edits for authenticated users

* fix(gateway): users.self bootstrap, tombstone-aware ownership, escaped CLI output

* fix(gateway): raw-DDL allowlist entry and lean profile exports
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: android App: android app: web-ui App: web-ui cli CLI command changes gateway Gateway runtime maintainer Maintainer-authored PR merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. scripts Repository scripts 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