Skip to content

feat(devices): opt-in encrypted LAN phone pairing (#2599) - #2600

Merged
DoodleBears merged 4 commits into
mainfrom
codex/2599-lan-pairing
Oct 22, 2026
Merged

DoodleBears merged 4 commits into
mainfrom
codex/2599-lan-pairing

Conversation

@DoodleBears

@DoodleBears DoodleBears commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Refs #2599 · Parent #2598 · Agent claim

Task: codex/local/01a1639d-a211-7a30-a976-b6f6416ca9b1

Why the change

Let an installed phone pair with a computer over encrypted local Wi-Fi without a Connect account, Tailscale, certificates or a typed IP address.

Special things to note

  • Merge risk: two-way door — disable LAN and revert the code, preserving the existing pairing store; large blast radius — shared device authentication, mobile connection selection and native network policy can affect phone access or confidentiality. Review the restricted listener, Host pin, exact phone-key admission, route refusal boundaries and native receive limits.
  • Draft: native and Human acceptance are pending. Android is submitted to the shared QA queue; iOS is independently untested. New native builds are required. Real offline Host Feed, real-model DM, live CLI qualification and before/after screenshots are not yet proved. Focused regression: 83 passed; typecheck, lint, format and build passed. Linux CI at 013bf12ab passed all three full test shards and isolated Host smoke; current-head static checks, mobile verification, docs build and isolated DSH Host smoke passed; Android native module tests and full shards are still running (Doctor 20/20 locally; 14 CLI fingerprint follow-up regressions passed). The Windows full suite was not green; nine Marketplace cleanup failures also reproduce on the base revision.
  • This is T1 only: default-off RFC 1918 IPv4 entry, encrypted enrollment, existing-route fallback and revocation. The mobile React DOM type package is aligned to its existing React 19.3 / Expo 58 runtime. Proactive switching, changed-address discovery and legacy trust upgrades remain in Spec: encrypted LAN phone access with automatic route recovery and pairing upgrades #2598. Get feedback on this working slice before extending them.

Change outline

Computer Devices / device-lan enable --confirm
  → LAN lifecycle owner → persisted opt-in port
  → restricted /botharness-device/channel listener
      ordinary HTTP → 404; operator DSH stays on loopback

Phone “Use the same Wi-Fi” → trusted computer QR → pinned Host key
  → scoped native socket → Noise XK (LAN prologue)
  → canonical Device Pairing → exact phone-key approval
  → existing encrypted gateway → existing authorized device API

Revoke → canonical owner closes device channels
Disable LAN → closes LAN sockets and removes opt-in
Unreachable LAN → may try saved remote routes
Identity/admission refusal → stops selection
 Devices settings
+ Local network access: explicit enable notice / disable
 Phone pairing chooser
+ Use the same Wi-Fi → existing QR scanner
 CLI
+ device-lan enable --confirm | disable
+ device-pair --link-file <private-invite> --out <new-private-file> --confirm
+ device-probe --device-file <private-pairing>

The pairing CLI emits its locally derived public fingerprint once on stderr before approval; devices.pendingKeys[].fingerprint enables an independent full comparison. Final stdout remains one JSON result. The integration test uses the real pairing database, restricted listener, encrypted gateway and mobile carrier to prove key approval, ciphertext transport and revocation; its upstream response is a fixture, not a real Host Feed or model reply. Android validates frame lengths before OkHttp allocation, including fragmented-message totals. Lifecycle logs carry initiator, phase, duration and coded refusal.

UI evidence blocker and runnable review path: the shared device owner has not cut this native build into a batch; this task has not claimed a phone or QA Host. The queued runner builds this branch, starts the dedicated QA Host using dev-instance.mjs, opens computer Devices, enables Local network access, scans from the installed phone’s Wi-Fi option, approves the exact phone key, verifies offline Host Feed, then separately verifies DM/reply, revocation and disablement. Capture matched base/head Devices and pairing chooser/scanner states in light/dark and the required text sizes; redact QR contents and private addresses. Feature journey · LAN contract

Issue: #2599

Agent-Task: codex/local/01a1639d-a211-7a30-a976-b6f6416ca9b1
Issue: #2599

Agent-Task: codex/local/01a1639d-a211-7a30-a976-b6f6416ca9b1
@coderabbitai

coderabbitai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 8c36a23b-309a-4706-af08-24df896cf462
📥 Commits

Reviewing files that changed from the base of the PR and between 914bfa5 and bac5bf9.

⛔ Files ignored due to path filters: 1
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing: 62
  • .agents/skills/verify-deepseekbot/features/phone-pairing.md
  • .github/workflows/verify.yml
  • apps/mobile/AGENTS.md
  • apps/mobile/app.config.js
  • apps/mobile/app.json
  • apps/mobile/app/pair/index.tsx
  • apps/mobile/app/pair/tailscale.tsx
  • apps/mobile/locales/zh-Hans.json
  • apps/mobile/modules/lan-channel/README.md
  • apps/mobile/modules/lan-channel/android/build.gradle
  • apps/mobile/modules/lan-channel/android/src/main/AndroidManifest.xml
  • apps/mobile/modules/lan-channel/android/src/main/java/expo/modules/lanchannel/BoundedChannelInput.kt
  • apps/mobile/modules/lan-channel/android/src/main/java/expo/modules/lanchannel/LanChannelModule.kt
  • apps/mobile/modules/lan-channel/android/src/test/java/expo/modules/lanchannel/BoundedChannelInputTest.kt
  • apps/mobile/modules/lan-channel/android/src/test/java/expo/modules/lanchannel/LanChannelTest.kt
  • apps/mobile/modules/lan-channel/expo-module.config.json
  • apps/mobile/modules/lan-channel/ios/LanChannel.podspec
  • apps/mobile/modules/lan-channel/ios/LanChannelModule.swift
  • apps/mobile/package.json
  • apps/mobile/src/host/client.ts
  • apps/mobile/src/host/encrypted-carrier.ts
  • apps/mobile/src/host/native-carrier.ts
  • apps/mobile/src/host/native-lan-socket.ts
  • apps/mobile/src/host/pairing.ts
  • apps/mobile/src/host/route.ts
  • apps/mobile/src/telemetry/schema.ts
  • apps/mobile/src/ui/strings.ts
  • apps/mobile/test/encrypted-carrier.test.ts
  • apps/mobile/test/host-client.test.ts
  • apps/mobile/test/localization.test.ts
  • apps/mobile/test/native-lan-socket.test.ts
  • apps/mobile/test/pair-choice.test.tsx
  • apps/mobile/with-lan-channel.js
  • changes/2599-encrypted-lan.md
  • docs/adr/0244-connect-encrypts-device-traffic-against-pinned-host-keys.md
  • docs/agents/development-scripts.md
  • docs/architecture/botharness-architecture.md
  • docs/architecture/lan-phone-v1.md
  • docs/bot-cli.md
  • docs/bot-cli.zh.md
  • packages/client/src/client/locale.ts
  • packages/client/src/client/paired-devices.tsx
  • packages/client/src/client/phone-route-steps.tsx
  • packages/core/src/bots/cli-device-client.ts
  • packages/core/src/bots/cli-devices.ts
  • packages/core/src/bots/cli-live.ts
  • packages/core/src/devices/encrypted-gateway.ts
  • packages/core/src/devices/http.ts
  • packages/core/src/devices/lan-address.ts
  • packages/core/src/devices/lan.ts
  • packages/core/src/devices/mount.ts
  • packages/core/src/devices/node-carrier.ts
  • packages/core/src/devices/noise/channel.ts
  • packages/core/src/devices/routes.ts
  • packages/core/test/cli-device-client.test.ts
  • packages/core/test/cli-devices.test.ts
  • packages/core/test/connect-encrypted-gateway.test.ts
  • packages/core/test/connect-noise.test.ts
  • packages/core/test/device-lan.test.ts
  • scripts/ci-plan.mjs
  • scripts/cli-ui-inventory.json
  • tsconfig.json
+-----------------------------------------+
| Because, faster merges = more features. |
+-----------------------------------------+
  \
   \     /)/)
        ( '')
        /  |
      o(___)
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Use an explicit OkHttp Dns implementation and match React DOM types to the installed Expo 58 / React 19.3 runtime.

Issue: #2599

Agent-Task: codex/local/01a1639d-a211-7a30-a976-b6f6416ca9b1
@DoodleBears

Copy link
Copy Markdown
Contributor Author

Validation update for 2a2ee209160b3d31b0595f3f553d23a1c7ca13de:

  • At 013bf12ab, Linux CI passed all three full-test shards, static checks and the isolated DSH Host smoke test. This does not prove LAN phone operation. Run.
  • That run exposed a Kotlin Dns interface construction error and an existing Expo/React DOM type mismatch. The new head fixes both. Typecheck and Expo Doctor (20/20) pass locally; current-head Android compilation/tests and remaining CI are pending.
  • Focused LAN/pairing/CLI/mobile regression: 83 passed. Windows full suite remains non-green. Marketplace's nine cleanup EPERM failures reproduce on the exact base; the other failures are not all attributed. A second Windows run was interrupted by local dependency cleanup and is excluded from qualification; dependencies were restored and focused tests/typecheck passed again.
  • Native camera pairing, real offline Host Feed, real-model DM, live device CLI, matched UI evidence and Human acceptance remain pending in the shared QA workflow. Android and iOS require separate results. The PR stays draft.
  • A head-bound HTML explanation was generated and checked locally; no reviewer-accessible HTML attachment has been published. The linked LAN contract and PR outline remain the shared review documents.

Issue: #2599

Agent-Task: codex/local/01a1639d-a211-7a30-a976-b6f6416ca9b1
@DoodleBears

Copy link
Copy Markdown
Contributor Author

CLI qualification follow-up at bac5bf9d7bc461f4d5bea33aff020dc6236f331e:

The pairing process previously waited for approval without exposing its own public-key fingerprint. It now writes one public fingerprint status to stderr, while devices.pendingKeys[].fingerprint provides the independently derived Host-side value. Compare every digit before approving the exact receipt/public key; names and list order are insufficient. Final stdout remains one JSON document, and no offer, private key or credential is included in the pending status.

Red/green coverage at the CLI seam, 14 focused regressions, typecheck, lint and build passed. Both source reviewers found no remaining issue in the follow-up. Current-head CI is pending; native phone and Human acceptance remain separate queued gates. The queue and local explainer were updated to this head. The HTML remains local only.

@DoodleBears
DoodleBears marked this pull request as ready for review October 22, 2026 18:50
@DoodleBears
DoodleBears merged commit 9cd926c into main Oct 22, 2026
13 checks passed
@DoodleBears

Copy link
Copy Markdown
Contributor Author

Android build qualification PASS — 2026-10-22: exact main 843d84b1c304a6dfac4c405c1da70ebfe2512ed1, including #2597/#2600/#2605. Fresh prebuild emits four includeSubdomains="false" attributes; :app:lintVitalRelease passes. Release build succeeded in 17m41s and Debug in 4m47s, serial Gradle max-workers 2, actual Node 24.21.0 / pnpm 12.4.2.

Artifact Bytes SHA256
Release, arm64-v8a 52,749,174 9018D62C4CD5B3EDC79C8BE9DF0EFB49BF873F9B0C5AE85B0D7FC628EC928E44
Debug, arm64-v8a 116,489,638 EF7BE31A0E7E8DB05D72AB001491E83CDCF35EB40561EAFB418014E906336ED8

Both use ai.botharness.deepseekbot.dev and the existing local debug signer; neither is a store distribution build. Actual APK package/ABI/signature, LAN DEX class and compiled network-policy binding passed inspection. Release class/resource renaming was resolved through R8 mapping and the APK resource table. Release contains the JS bundle and is non-debuggable.

Final strict post-CNG Android fingerprint, including the generated native directory: 1da81501f7520b1b26002d234fae90eb3041b389 (234 sources). This was recomputed serially after stable builds; an earlier computation overlapping prebuild was discarded. Do not equate this mode directly with a pre-CNG fingerprint.

UNTESTED: installation/cold launch of these APKs, encrypted LAN pairing/offline Feed/DM/restart/revoke/disable, performance/FPS, screenshots and iOS. No app install, Host, Metro or phone UI/settings/pairing/reverse changes occurred. Only designated POCO Android 16/API36 identity/ABI/OS was read. Own processes/listeners were cleared and lease released at 20:04:15.7163242Z; global ADB was untouched.

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

Labels

ci:full Run the complete verification test plan for this pull request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant