Skip to content

fix(tinyplace): wire handle transfer end-to-end (Closes #4929)#4998

Open
M3gA-Mind wants to merge 86 commits into
tinyhumansai:mainfrom
M3gA-Mind:fix/GH-4929-tinyplace-handle-transfer
Open

fix(tinyplace): wire handle transfer end-to-end (Closes #4929)#4998
M3gA-Mind wants to merge 86 commits into
tinyhumansai:mainfrom
M3gA-Mind:fix/GH-4929-tinyplace-handle-transfer

Conversation

@M3gA-Mind

@M3gA-Mind M3gA-Mind commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Wire Tiny Place handle transfer end-to-end ([Tiny Place] Handle transfer is unreachable — SDK registry.transfer has no core RPC handler or UI #4929). The SDK exposed registry.transfer but nothing above it reached the method, so a user could not transfer a @handle from the app at all.
  • New core controller tinyplace_registry_transferinvokeApiClient registry.transfer(name, recipient) → a per-handle Transfer action in the Profiles card that opens a destructive-confirm modal.
  • The transfer is read-back confirmed (not fire-and-forget): the core resolves the recipient @handle, calls registry.transfer, and verifies the returned Identity is now owned by the recipient before reporting success. It fails closed on an unresolved recipient, missing key material, or a read-back mismatch.
  • Seven agentWorld.transferHandle.* useT() keys added to en.ts + all 13 locales (no em dashes); grep-friendly agentworld:identity / registry_transfer logging with no PII.

Problem

The capability existed only in the vendored SDK and was un-bridged at every layer:

  • SDK: vendor/tinyplace/sdk/rust/src/api/registry.rs:254pub async fn transfer(&self, name, IdentityTransferRequest) -> Result<Identity> (POST /registry/names/{name}/transfer).
  • Core: no handle_tinyplace_registry_transfer in manifest.rs, not registered in schemas.rs.
  • Frontend: no registry.transfer in invokeApiClient.ts, no transfer action in the UI.

Result: transfer could not be initiated from the app. (Established while auditing #4920registry.transfer exists, unlike the seller-side marketplace methods, so this is a fixable in-repo wiring gap, not a contract block.)

Solution

  • Core (src/openhuman/tinyplace/manifest.rs): handle_tinyplace_registry_transfer(name, recipient) resolves recipient via registry.get("@…") (the proven availability path), extracts the recipient's cryptoId + publicKey, builds IdentityTransferRequest, and calls client.registry.transfer(&name, req). The SDK attaches the owning wallet's signature, so a caller can only transfer a handle their own wallet owns. Read-back: if the returned identity's cryptoId is not the recipient's, it returns an error instead of a false success. Registered the controller in schemas.rs (schema + import + all-schemas vec + RegisteredController) — no ad-hoc cli.rs/jsonrpc.rs branch.
  • Client (invokeApiClient.ts): registry.transfer(name, recipient) + TransferHandleResult.
  • UI (ProfilesSection.tsx + new components/TransferHandleModal.tsx): non-primary owned handles get a Transfer button (a primary handle is locked from transfer). The modal states the action is permanent, requires an explicit recipient and confirm click, disables close while in-flight, and fails closed — on error it keeps the dialog open with the message and never calls onTransferred. Treated with the care of the Emergency-Stop work in feat(safety): Emergency Stop for desktop automation (#4255) #4600.
  • Logging: never logs the handle, recipient, or cryptoId — only status markers.

Submission Checklist

If a section does not apply to this change, mark the item as N/A with a one-line reason. Do not delete items.

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy — TransferHandleModal.test.tsx (confirm calls registry.transfer('alpha','bravo') + closes on success; fails closed on error), ProfilesSection.test.tsx (Transfer opens the confirm modal), and a Rust transfer_requires_name_and_recipient handler test.
  • Diff coverage ≥ 80% — modal/client/UI covered by the new Vitest cases; the Rust handler's validation branch by the new unit test; schemas.rs registration by the existing all_controller_schemas tests. pnpm test:coverage/test:rust not run locally (machine limit); CI authoritative.
  • Coverage matrix updated — N/A: behaviour-only wiring of an existing SDK capability, no new matrix feature row.
  • All affected feature IDs from the matrix are listed in the PR description under ## Related — N/A: no matrix feature ID for this action.
  • No new external network dependencies introduced — N/A: uses the existing vendored SDK registry.transfer only.
  • Manual smoke checklist updated if this touches release-cut surfaces — N/A: internal Agent World UI, not a release-cut smoke surface.
  • Linked issue closed via Closes #NNN in the ## Related section.

Impact

  • Desktop UI + core RPC. No schema-breaking or dependency changes; the vendored SDK is unchanged (pointer untouched).
  • Destructive/irreversible action: mitigated by an explicit confirm modal, recipient resolution (unknown recipient fails before signing), and a post-transfer read-back that fails closed.
  • On-chain transfer settlement is untestable headless (needs staging + an unlocked signer); the core read-back is the in-app confirmation.

Related


AI Authored PR Metadata (required for Codex/Linear PRs)

Keep this section for AI-authored PRs. For human-only PRs, mark each field N/A.

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: fix/GH-4929-tinyplace-handle-transfer
  • Commit SHA: 143512ca05b915323dd97ea57cd01d19e6a297dd

Validation Run

  • pnpm --filter openhuman-app format:check — N/A: ran targeted prettier --write on touched TS files; Rust formatting matched against sibling handlers (cargo fmt not runnable locally, fmt lane authoritative).
  • pnpm typecheck — clean.
  • Focused tests: added modal + ProfilesSection + Rust handler tests (not executed locally per machine constraints; CI authoritative). pnpm i18n:check and pnpm i18n:english:check run locally → both exit 0.
  • Rust fmt/check (if changed): N/A locally — cannot run cargo (machine limit); formatting hand-matched to sibling registry_* handlers, fmt/clippy lanes authoritative.
  • Tauri fmt/check (if changed): N/A — no Tauri changes.

Validation Blocked

  • command: cargo test / pnpm test:coverage
  • error: not run — machine constraint (local Rust/JS builds fill disk).
  • impact: unit, coverage, fmt, and clippy verified by CI.

Behavior Changes

  • Intended behavior change: users can transfer a non-primary owned @handle to another tiny.place identity from the Profiles card.
  • User-visible effect: a Transfer button + confirm modal; on success the handle leaves the wallet (read-back confirmed).

Parity Contract

  • Legacy behavior preserved: existing registry flows (register / assign-primary / export) and the profile card are unchanged; only additive.
  • Guard/fallback/dispatch parity checks: unresolved recipient / missing key material / read-back mismatch all fail closed; the owning wallet is proven by the signer signature, not params.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): none
  • Canonical PR: this
  • Resolution: N/A

Summary by CodeRabbit

  • New Features
    • Added a localized “Transfer” action for non-primary owned handles, opening an irreversible confirmation dialog.
    • Added recipient handle input with @ normalization and a “type handle to confirm” safeguard.
    • Enabled handle transfers via the core RPC (x402), including server-side recipient resolution.
  • Bug Fixes
    • Success now consistently closes the dialog and refreshes the profile; failures keep the dialog open and show the error.
  • Tests
    • Added coverage for the confirm enable/disable flow, success and “fails closed” failure behavior, and the Profiles section entrypoint.
  • Documentation
    • Added/updated i18n text for the transfer flow across supported languages.

The SDK exposed registry.transfer (POST /registry/names/{name}/transfer)
but nothing above it reached the method: no core controller, no client
method, no UI. Handle transfer was unreachable from the app (tinyhumansai#4929).

Bridge every layer:
- core: handle_tinyplace_registry_transfer resolves the recipient @handle
  to its cryptoId + publicKey via registry.get, calls registry.transfer,
  and read-back-confirms the returned identity is now owned by the
  recipient (fails closed on unresolved recipient / missing key material /
  mismatch). Registered via schemas.rs + the controller registry. No PII
  logged (status-only debug lines).
- client: apiClient.registry.transfer(name, recipient) + TransferHandleResult.
- UI: a per-handle Transfer action on non-primary owned handles in the
  Profiles card opens TransferHandleModal, a destructive-confirm dialog
  that requires a recipient + explicit confirm and fails closed on error.
- i18n: seven agentWorld.transferHandle.* keys across en + all 13 locales
  (no em dashes).
- tests: TransferHandleModal (wiring, recipient-gating, fail-closed),
  ProfilesSection (Transfer opens the modal), and a Rust handler test for
  the name/recipient validation branches.
@M3gA-Mind
M3gA-Mind requested a review from a team July 16, 2026 14:24
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 27 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2a979201-4f87-4c84-af71-91e570b440b3

📥 Commits

Reviewing files that changed from the base of the PR and between 8fc1e71 and 1adfc28.

📒 Files selected for processing (17)
  • app/src/agentworld/pages/ProfilesSection.test.tsx
  • app/src/agentworld/pages/ProfilesSection.tsx
  • app/src/lib/i18n/ar.ts
  • app/src/lib/i18n/bn.ts
  • app/src/lib/i18n/de.ts
  • app/src/lib/i18n/en.ts
  • app/src/lib/i18n/es.ts
  • app/src/lib/i18n/fr.ts
  • app/src/lib/i18n/hi.ts
  • app/src/lib/i18n/id.ts
  • app/src/lib/i18n/it.ts
  • app/src/lib/i18n/ko.ts
  • app/src/lib/i18n/pl.ts
  • app/src/lib/i18n/pt.ts
  • app/src/lib/i18n/ru.ts
  • app/src/lib/i18n/zh-CN.ts
  • src/openhuman/tinyplace/manifest.rs
📝 Walkthrough

Walkthrough

Adds end-to-end handle transfer support: a validated and registered Rust RPC, an SDK client method, a localized profile modal for non-primary handles, success/error handling, and frontend/core tests.

Changes

Handle transfer flow

Layer / File(s) Summary
Core transfer controller and registration
src/openhuman/tinyplace/manifest.rs, src/openhuman/tinyplace/schemas.rs
Validates transfer inputs, resolves the recipient identity, performs and confirms the registry transfer, and registers the RPC endpoint.
Client transfer API bridge
app/src/lib/agentworld/invokeApiClient.ts
Adds TransferHandleResult and exposes registry.transfer(name, recipient) through the core RPC.
Profile transfer interaction
app/src/agentworld/components/TransferHandleModal.tsx, app/src/agentworld/pages/ProfilesSection.tsx, app/src/agentworld/components/TransferHandleModal.test.tsx, app/src/agentworld/pages/ProfilesSection.test.tsx
Adds the non-primary handle transfer action, confirmation modal, recipient normalization, submission state, fail-closed errors, and coverage for the UI flow.
Localized transfer copy
app/src/lib/i18n/*.ts
Adds transfer-handle action, warning, recipient, confirmation, submitting, and validation messages across supported locales.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested labels: feature

Poem

A bunny found a handle bright,
And passed it through the code tonight.
Core checks twice, the UI sings,
Safe buttons guard the transfer wings.
“Hop!” says the rabbit—“success takes flight!”

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: wiring handle transfer end-to-end.
Linked Issues check ✅ Passed The PR implements the requested end-to-end handle transfer path with core RPC, client API, and UI action.
Out of Scope Changes check ✅ Passed The added tests, normalization, fail-closed behavior, and locale strings are all directly tied to the transfer flow.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added bug rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. labels Jul 16, 2026

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

Copy link
Copy Markdown

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: 143512ca05

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread app/src/agentworld/pages/ProfilesSection.tsx

@coderabbitai coderabbitai 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.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/agentworld/components/TransferHandleModal.tsx`:
- Around line 55-59: Update the catch block in the transfer handler around the
debug call to log only a fixed failure-state message, removing String(err) and
any raw error details from logging. Preserve the existing setError(String(err))
UI handling and setSubmitting(false) behavior unchanged.

In `@app/src/agentworld/pages/ProfilesSection.tsx`:
- Around line 379-389: Update the server-side
handleTransfer/handle_tinyplace_registry_transfer handler to identify whether
the requested name is a primary handle and reject the request before calling
registry.transfer. Preserve transfers for non-primary handles, and add a test
covering rejection of a primary-handle transfer through the JSON-RPC path.

In `@app/src/lib/i18n/bn.ts`:
- Around line 467-468: Update the Bengali translation for
agentWorld.transferHandle.warning so the recipient clause uses “প্রাপক একমাত্র
মালিক হয়ে যাবেন।” instead of the grammatically awkward wording, while
preserving the rest of the warning.

In `@app/src/lib/i18n/pl.ts`:
- Around line 476-483: Update the Polish ownership-transfer translations in the
agentWorld.transferHandle entries to use “Przekaż handle” and “Przekazanie
handle’a...” wording consistently for the action, title, warning, and
confirmation labels; leave the recipient, submitting, and required-field
messages unchanged.

In `@src/openhuman/tinyplace/manifest.rs`:
- Around line 5362-5382: The test transfer_requires_name_and_recipient only
covers input validation; extend the transfer test coverage around
handle_tinyplace_registry_transfer to include name "@" normalization, mocked
recipient key resolution, SDK transfer failure, successful confirmation, and
ownership mismatch. Add assertions for each outcome while keeping network and
destructive operations mockable.
- Around line 2513-2578: Move handle_tinyplace_registry_transfer from
manifest.rs into schemas.rs, keeping it as the RPC boundary and changing its
return type to RpcOutcome<T>. Extract recipient lookup, transfer, and read-back
confirmation into a dedicated ops.rs operation, then have the handler validate
RPC parameters, call that operation, and serialize its result without retaining
business logic in the controller.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9b0dd2c0-b461-4153-befd-ceac0b5f512f

📥 Commits

Reviewing files that changed from the base of the PR and between 9fd5fde and 143512c.

📒 Files selected for processing (21)
  • app/src/agentworld/components/TransferHandleModal.test.tsx
  • app/src/agentworld/components/TransferHandleModal.tsx
  • app/src/agentworld/pages/ProfilesSection.test.tsx
  • app/src/agentworld/pages/ProfilesSection.tsx
  • app/src/lib/agentworld/invokeApiClient.ts
  • app/src/lib/i18n/ar.ts
  • app/src/lib/i18n/bn.ts
  • app/src/lib/i18n/de.ts
  • app/src/lib/i18n/en.ts
  • app/src/lib/i18n/es.ts
  • app/src/lib/i18n/fr.ts
  • app/src/lib/i18n/hi.ts
  • app/src/lib/i18n/id.ts
  • app/src/lib/i18n/it.ts
  • app/src/lib/i18n/ko.ts
  • app/src/lib/i18n/pl.ts
  • app/src/lib/i18n/pt.ts
  • app/src/lib/i18n/ru.ts
  • app/src/lib/i18n/zh-CN.ts
  • src/openhuman/tinyplace/manifest.rs
  • src/openhuman/tinyplace/schemas.rs

Comment thread app/src/agentworld/components/TransferHandleModal.tsx
Comment thread app/src/agentworld/pages/ProfilesSection.tsx
Comment thread app/src/lib/i18n/bn.ts Outdated
Comment thread app/src/lib/i18n/pl.ts Outdated
Comment thread src/openhuman/tinyplace/manifest.rs
Comment thread src/openhuman/tinyplace/manifest.rs
- schemas.rs: restore rustfmt wrapping of the registry_transfer 'name'
  input (fixes the Rust Quality + Frontend Checks fmt lanes).
- TransferHandleModal: drop String(err) from the failure debug log so no
  backend/SDK detail is logged (the UI still shows it via setError).
- manifest.rs: reject transferring a PRIMARY handle server-side, not just
  in the UI — a direct JSON-RPC caller could otherwise move the wallet's
  active identity. Also cover the '@' name-normalization case in the test.
- i18n: fix the Bengali warning grammar; use ownership-transfer wording
  (Przekaż/Przekazanie) in Polish.
@M3gA-Mind

Copy link
Copy Markdown
Collaborator Author

Pushed 2c089f2 (rebased onto current main). Summary of changes:

Red lanes — the sole cause of Frontend Checks, Rust Quality (fmt, clippy), and the PR CI Gate rollup was one rustfmt diff: schemas.rs:1480 — rustfmt wants the registry_transfer name input wrapped (its fn_call_width=60 heuristic, not the 100-col limit). Restored the wrapped form. Prettier + tsc + i18n were already green.

CodeRabbit review (all 6 addressed):

  • Do not log raw error — dropped String(err) from the failure debug log (UI still shows it via setError).
  • Enforce primary-handle lock server-sidehandle_tinyplace_registry_transfer now rejects a primary handle before the destructive call, so a direct JSON-RPC caller can't move the wallet's active identity.
  • Bengali grammar / ✅ Polish ownership wording (Przekaż/Przekazanie).
  • ↪︎ Move controller to ops.rs / RpcOutcome — declined with reason: the whole tinyplace domain uses manifest.rs handlers registered via schemas.rs; this mirrors the 4 sibling registry handlers.
  • ↪︎ Execution-path Rust tests — added the @ normalization case; the client-path cases belong in the mock-backend json_rpc_e2e harness (same as sibling handlers), untestable in this env.

Codex — fallback-profile reachability noted as a follow-up (the reverse fallback only carries the primary handle, which isn't transferable).

@coderabbitai review — re-review after fmt fix + server-side primary-handle guard + PII-safe logging.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

@M3gA-Mind Thanks for the detailed update. I’ll re-review the rebased changes, including the server-side primary-handle guard, PII-safe failure logging, formatting fix, and related tests.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@M3gA-Mind

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

Rebased onto current upstream/main (clean merge, only touched tinyflows/caps.rs). All prior CodeRabbit findings are resolved on this head:

  • ✅ PII-safe debug log (String(err) dropped; UI still shows it, still fails closed)
  • ✅ Primary-handle transfer now rejected server-side before the destructive call (JSON-RPC bypass closed)
  • ✅ Bengali grammar + Polish ownership wording (Przekaż/Przekazanie)
  • ✅ '@' name-normalization unit test added; client-path cases deferred to the mock-backend json_rpc_e2e harness (you agreed)
  • ↩︎ ops.rs/RpcOutcome refactor — you withdrew (handler follows the established tinyplace manifest.rs convention)

No code changes since your last pass — this is a main-sync + re-review request to refresh the stale CHANGES_REQUESTED verdict.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

@M3gA-Mind Thanks for the clear summary. I’ll run a fresh review of the rebased head, including the server-side primary-handle guard, PII-safe failure logging, normalization coverage, and the main-sync change.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@graycyrus graycyrus 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.

@M3gA-Mind hey! the code looks good to me — this is a solid, careful piece of work. Wiring registry.transfer through core → client → UI with a resolve-then-verify pattern (recipient resolved via registry.get before signing, read-back confirmation after, fail-closed on every unhappy path) is exactly the right amount of caution for a destructive/irreversible action. The follow-up commit adding the server-side primary-handle guard (so a direct JSON-RPC caller can't orphan the wallet's active identity by bypassing the UI check) was a good catch on your own part. PII-safe logging, i18n across all 13 locales, and the fail-closed modal all check out.

Two very minor things, not blocking:

  • handle_tinyplace_registry_transfer now makes three sequential network round-trips (get-own-identity for the primary check, get-recipient, then transfer). There's a small TOCTOU window between the primary check and the actual transfer call — low real-world impact since only the owning wallet's own signature can execute the transfer, but worth being aware of if this pattern gets reused elsewhere.
  • In TransferHandleModal.tsx, the debug() calls already run under the agentworld:identity namespace, so the [agentworld:identity] prefix baked into each message string is redundant. Cosmetic only.

On the test-coverage gap CodeRabbit flagged (transfer execution / confirmation-failure paths not covered by the current unit test) — I saw you and CodeRabbit already agreed to defer that to mock-backend JSON-RPC E2E coverage tracked under #4776, so I won't re-litigate it here.

CI is still showing Rust Core Coverage and Frontend Checks as pending — I'll come back once those are green. Nice work on this one.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 17, 2026
@M3gA-Mind

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

All review threads are now resolved on the current head (96bb836) — the earlier CHANGES_REQUESTED is fully addressed:

  • PII-safe logging — dropped String(err) from the failure debug log (you confirmed).
  • Primary-handle transfer rejected server-side before the destructive registry.transfer call — closes the JSON-RPC bypass (you confirmed).
  • Transfer success is verified at runtime — the handler read-back-confirms the returned identity is owned by the recipient and fails closed otherwise (manifest.rs:2586).
  • Bengali grammar + Polish ownership wording (Przekaż/Przekazanie) (you confirmed).
  • @ name-normalization test added; client-path E2E cases deferred to tests/json_rpc_e2e.rs under Audit: Tiny Place end-to-end integration — World, Feed, Messages, Ledger, Bounties, Directory, Identities, Profiles #4776 (you agreed).
  • ↩︎ ops.rs/RpcOutcome refactor — you withdrew (handler follows the established tinyplace manifest.rs convention).
  • ↩︎ Codex P2 fallback reachability — resolved as a follow-up (the directory-reverse fallback only carries the primary handle, which isn't transferable; transfer is reachable via the normal GraphQL profile path).

No code changes needed since your last pass; requesting a re-review to refresh the stale verdict.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

@M3gA-Mind I’ll run a fresh review of the current head, with particular attention to the server-side primary-handle guard, fail-closed ownership verification, PII-safe logging, and normalization coverage.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot added agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. feature Net-new user-facing capability or product behavior. and removed bug labels Jul 17, 2026
oxoxDev
oxoxDev previously approved these changes Jul 17, 2026

@oxoxDev oxoxDev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Transfer is correctly authorized end-to-end. Authz rests on the owner's signature over the canonical identity.transfer payload (SDK registry.rs), not on any caller-supplied param — there's no way to transfer a handle the active wallet doesn't own. Recipient is resolved and fail-closed if unregistered or missing key material; the post-transfer read-back rejects on a crypto_id mismatch; the server-side primary-handle lock closes the direct-JSON-RPC bypass of the UI guard. Modal fails closed (keeps the dialog open on error, close disabled in-flight). i18n 14/14.

Non-blocking:

  • Rust coverage only exercises the input-validation branch; the security-relevant branches (primary-lock, recipient resolution, read-back mismatch) are uncovered because the inline module can't mock the client. Deferred to #4776 — reasonable, but worth an E2E on a destructive path.
  • Nit: a "type the handle to confirm" step would harden the irreversible action beyond the free-text recipient + danger button.

LGTM.

Addresses the maintainer reviews on tinyhumansai#4998:
- oxoxDev (confirmation-UX hardening for the irreversible action): add a
  'type the handle to confirm' step to TransferHandleModal — the danger
  button stays disabled until the user re-types the exact handle
  (case- and @-insensitive), and submit() fails closed if it doesn't
  match. Adds agentWorld.transferHandle.confirmLabel / confirmMismatch
  across en + all 13 locales, plus test coverage for the gate.
- graycyrus (cosmetic): the debug() calls already run under the
  'agentworld:identity' namespace, so drop the redundant
  '[agentworld:identity]' prefix baked into each message string.
@M3gA-Mind
M3gA-Mind dismissed stale reviews from oxoxDev and coderabbitai[bot] via 8fc1e71 July 17, 2026 19:32
@M3gA-Mind

Copy link
Copy Markdown
Collaborator Author

Thanks @graycyrus and @oxoxDev — addressed the review feedback in 8fc1e71:

Fixed

  • Type-to-confirm hardening (oxoxDev's nit — a destructive/irreversible action deserves it): TransferHandleModal now requires the user to re-type the exact handle before the danger button enables (case- and @-insensitive), and submit() fails closed if it doesn't match. Added confirmLabel / confirmMismatch across en + all 13 locales, plus a gate test (disabled on wrong text, enabled on exact match) and updated the existing confirm-path tests.
  • Redundant log prefix (graycyrus): dropped the [agentworld:identity] prefix baked into each debug() message — the debug factory is already namespaced agentworld:identity, so it was double-printing.

Acknowledged, no change

  • TOCTOU between the primary-check and transfer (graycyrus): correct that it's a small window, but it isn't exploitable — authz rests entirely on the owner's signature over the canonical identity.transfer payload (as @oxoxDev confirmed), so a stale primary read can't authorize a transfer the wallet doesn't own; worst case the backend re-validates and fails closed. Left as-is to avoid an extra serialized round-trip on the happy path; noted for anyone reusing the pattern.
  • Security-branch Rust coverage (both): the primary-lock / recipient-resolution / read-back-mismatch branches need a mockable client, so they're deferred to mock-backend json_rpc_e2e under Audit: Tiny Place end-to-end integration — World, Feed, Messages, Ledger, Bounties, Directory, Identities, Profiles #4776 — you both already signed off on this split.

@graycyrus — could you take another look when the re-run is green? This should clear your two notes.

@coderabbitai coderabbitai Bot removed the rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. label Jul 17, 2026
M3gA-Mind and others added 25 commits July 18, 2026 06:47
@senamakel senamakel self-assigned this Jul 18, 2026
# Conflicts:
#	app/src/agentworld/pages/ProfilesSection.tsx
#	app/src/lib/i18n/ar.ts
#	app/src/lib/i18n/bn.ts
#	app/src/lib/i18n/de.ts
#	app/src/lib/i18n/en.ts
#	app/src/lib/i18n/es.ts
#	app/src/lib/i18n/fr.ts
#	app/src/lib/i18n/hi.ts
#	app/src/lib/i18n/id.ts
#	app/src/lib/i18n/it.ts
#	app/src/lib/i18n/ko.ts
#	app/src/lib/i18n/pl.ts
#	app/src/lib/i18n/pt.ts
#	app/src/lib/i18n/ru.ts
#	app/src/lib/i18n/zh-CN.ts

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

Copy link
Copy Markdown

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: 1adfc287ea

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

.get(&format!("@{name}"))
.await
.map_err(map_err)?;
if own.identity.as_ref().and_then(|i| i.primary) == Some(true) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject active handles when the primary flag is absent

When registry.get returns an identity with primary omitted/null, this check falls through because it only rejects Some(true). The frontend types make primary optional and ProfilesSection.pickPrimary() already treats the first/only unmarked handle as the active profile handle, so in that scenario a direct RPC caller, and the new Transfer button when GraphQL omits the flag, can transfer the active/only handle even though this handler says active handles must be blocked to avoid orphaning the wallet; fail closed unless the handle is explicitly known to be non-primary or verify against the wallet's full identity list.

Useful? React with 👍 / 👎.

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

Labels

feature Net-new user-facing capability or product behavior.

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

[Tiny Place] Handle transfer is unreachable — SDK registry.transfer has no core RPC handler or UI

7 participants