Skip to content

fix(android): expose exact gateway recovery actions#99414

Merged
steipete merged 8 commits into
mainfrom
codex/android-auth-recovery-98046
Jul 3, 2026
Merged

fix(android): expose exact gateway recovery actions#99414
steipete merged 8 commits into
mainfrom
codex/android-auth-recovery-98046

Conversation

@steipete

@steipete steipete commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Closes #98046
Closes #98045

AI-assisted: yes. Sanitized transcript logs were not included.

What Problem This Solves

Android Gateway Recovery collapsed stale credentials, expired setup codes, and pending node capability approval into generic status and retry actions. Even when the Gateway knew the pending node request ID, a read-scoped Android client could not display the exact safe approval command.

Why This Change Was Made

The Gateway now includes a pending node request ID only when a signed read-scoped client is reading its own device-backed node. Pending capability declarations and every other node's request ID remain redacted.

Android carries the current phone's capability approval state and optional request ID as one closed value. Onboarding and Nodes & Devices share one command mapper, so a safe request ID produces an exact openclaw nodes approve <id> command and malformed or unavailable IDs fall back to openclaw nodes status.

Exact approval IDs are gateway-local and short-lived. Android clears them when the target changes, ages cached commands to the status fallback, and refreshes node.list while approval remains pending.

Structured connection problems now select the recovery action that can resolve them: an expired bootstrap token scans a fresh setup code, stale client credentials edit the connection, Gateway-host auth configuration stays on an explicit retry path, and transient failures remain retryable. Terminal structured auth advice pauses automatic reconnect so the recovery UI is not immediately overwritten.

User Impact

Android users can distinguish the three common recovery paths and act directly. Pending approval shows a selectable exact host command, expired setup codes lead to a fresh scan, and stale stored authentication leads to connection editing instead of another ineffective retry.

Evidence

  • Gateway authorization test: unrelated read-only clients cannot see pending request IDs; the same signed device identity can see only its own request ID in node.list and node.describe.
  • Focused Gateway proof: 20 tests passed in src/gateway/server.node-pairing-authz.test.ts.
  • Focused Android proof: node approval, onboarding recovery, and Settings tests passed; ktlint and Play debug APK assembly passed.
  • Repository proof: production build, native i18n inventory, oxlint, and whitespace checks passed.
  • Real source Gateway + Android emulator matrix:
    • pending node approval exposed the exact safe command for this phone;
    • expired bootstrap rendered Authentication needed with Scan fresh setup code;
    • stale token rendered Authentication needed with Edit connection;
    • the recovery scanner launched Google Code Scanner from the exact Play APK.

Screenshots are attached in the PR comments, not committed to the repository. No physical Android device was available.

@steipete
steipete requested a review from a team as a code owner July 3, 2026 07:16
@openclaw-barnacle openclaw-barnacle Bot added app: android App: android gateway Gateway runtime size: XL maintainer Maintainer-authored PR labels Jul 3, 2026
@steipete

steipete commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Real Gateway + Android emulator evidence

Exact head: bca890b473597abae52de1803108ded346b72aec

The matrix used a source Gateway and the exact Play debug APK. No credentials or approval identifiers are visible in these captures.

Pending node capability approval

Before: generic status command. After: this phone's exact safe approval command.

Before: generic pending node status After: exact pending node approval command

Expired bootstrap code

Before: generic authentication state. After: structured recovery offers Scan fresh setup code.

Before: generic expired bootstrap state After: scan fresh setup code action

Stale stored token

Before: generic authentication state. After: structured recovery offers Edit connection.

Before: generic stale token state After: edit connection action

Recovery scanner

The action launched Google Code Scanner from ai.openclaw.app; dumpsys activity identified com.google.android.gms/.mlkit.barcode.ui.BarcodeScanningActivityProxy.

Google Code Scanner launched by the Android recovery action

Physical-device coverage remains the only visual proof gap.

@steipete

steipete commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Land-ready verification

Exact head: bca890b473597abae52de1803108ded346b72aec

Best-fix review: the Gateway projects a pending approval request ID only to the signed device reading its own node. Android owns one closed approval state, validates IDs before rendering a shell command, expires gateway-local IDs, and routes structured authentication failures to the recovery action that can resolve them. This avoids broad operator-pairing privilege and avoids parallel nullable approval fields.

Fresh autoreview: clean, no accepted/actionable findings, confidence 0.90.

Local exact-head proof:

  • node scripts/run-vitest.mjs src/gateway/server.node-pairing-authz.test.ts test/scripts/native-app-i18n.test.ts — 24 tests passed across both shards (20 Gateway + 4 inventory tests).
  • ./gradlew :app:testPlayDebugUnitTest --tests 'ai.openclaw.app.GatewayNodeApprovalStateTest' --tests 'ai.openclaw.app.gateway.GatewaySessionReconnectTest' --tests 'ai.openclaw.app.ui.GatewayDiagnosticsTest' --tests 'ai.openclaw.app.ui.OnboardingFlowLogicTest' --tests 'ai.openclaw.app.ui.SettingsScreensTest' :app:ktlintCheck :app:assemblePlayDebug — passed.
  • node --import tsx scripts/native-app-i18n.ts check — 2,402 entries, no drift.
  • git diff --check — passed.

Broad repository proof:

Live proof:

Known proof gap: no physical Android device was available. No other known blockers remain.

@steipete
steipete merged commit 1c7229d into main Jul 3, 2026
126 of 129 checks passed
@steipete
steipete deleted the codex/android-auth-recovery-98046 branch July 3, 2026 07:30
@steipete

steipete commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 4, 2026
* fix(android): expose exact gateway recovery actions

* fix(android): keep auth recovery actionable

* fix(android): cover terminal gateway auth states

* fix(android): keep recovery commands current

* fix(android): reset gateway-scoped recovery state

* fix(android): keep auth rate limits terminal

* fix(android): expire approval ids before refresh

* chore(i18n): refresh Android recovery inventory
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: android App: android gateway Gateway runtime maintainer Maintainer-authored PR size: XL

Projects

None yet

1 participant