feat(mobile): add Personal pairing and Android transport foundation#1538
Conversation
80b43f9 to
0e385bc
Compare
Signed-off-by: liying <422107224@qq.com>
Signed-off-by: liying <422107224@qq.com>
0e385bc to
f0b8244
Compare
Signed-off-by: liying <422107224@qq.com>
Signed-off-by: liying <422107224@qq.com>
Signed-off-by: liying <422107224@qq.com>
Signed-off-by: liying <422107224@qq.com>
…personal-transport Signed-off-by: liying <422107224@qq.com> # Conflicts: # services/tuttid/go.mod # services/tuttid/wiring_daemon_api.go
Signed-off-by: liying <422107224@qq.com>
Signed-off-by: liying <422107224@qq.com>
Signed-off-by: liying <422107224@qq.com>
Signed-off-by: liying <422107224@qq.com>
Signed-off-by: liying <422107224@qq.com>
Signed-off-by: liying <422107224@qq.com>
Signed-off-by: liying <422107224@qq.com>
Signed-off-by: liying <422107224@qq.com>
Signed-off-by: liying <422107224@qq.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
| if (cors) { | ||
| writer.write("Access-Control-Allow-Origin: *\r\n") | ||
| writer.write("Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n") | ||
| writer.write("Access-Control-Allow-Headers: Content-Type\r\n") | ||
| writer.write("Access-Control-Allow-Private-Network: true\r\n") | ||
| } |
There was a problem hiding this comment.
🟨 Loopback auth bridge returns wildcard CORS with private-network access enabled
The loopback GitHub-login bridge writes Access-Control-Allow-Origin: * together with Access-Control-Allow-Private-Network: true and Access-Control-Allow-Methods: GET, POST, OPTIONS on every JSON/CORS response (apps/mobile/android/app/src/main/java/dev/tutti/mobile/MobileBrowserAuthBridge.kt:610-615). While the bridge only listens on 127.0.0.1 and the sensitive /oauth/health response is gated behind the secret attempt_id+token pair, the wildcard origin plus private-network allowance lets any web origin loaded in the system browser issue cross-origin requests to the loopback server during the short login window. The OPTIONS handler restricts origins to the auth origin, but the actual JSON responses do not echo that restriction, so the CORS grant is broader than the preflight check implies.
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
packages/device-linkICE/QUIC transport and Android gomobile boundarytuttidto the deployed same-account device registration, QR pairing, and paired-device rendezvous control planeScope
This PR validates the Tutti Personal direct P2P lane first. It intentionally does not upgrade TSH, add Relay fallback, push notifications, long-running background operation, or the full Desktop side-panel surface.
Desktop account login remains visible independently. The phone pairing and remote-access block in Account settings is hidden by default and appears only when Developer settings enables the durable
mobile.remoteAccessSettingsfeature flag. The flag controls presentation only; it does not create another login state, device entity, or protocol path.The Mobile UI follows one consistent hierarchy:
The mobile client consumes the generated
@tutti-os/client-tuttid-tscontracts. Session, Turn, Message, Interaction, and submit identity remain Host-owned facts; the mobile app does not introduce a second Agent entity model.Security and lifecycle
agentSessionIdandclientSubmitIdfor safe retryAndroid physical-device fixes
Physical-device testing exposed two independent blockers after the initial emulator validation:
CAMERA, and surfaces denied permission separately.PrivateKeyEntry. Mobile now initializes the system Ed25519 curve explicitly, reads the private key and certificate public key separately, validates the exact Ed25519 SPKI prefix, and signs through the Android Keystore operation provider.Android now reuses the existing desktop-bridge transfer-code protocol for GitHub login. The system browser owns provider credentials; a short-lived loopback bridge returns only the one-time transfer code, and the redeemed session is installed into React Native Android’s native CookieManager before account requests. Pairing errors now remain visible above the footer actions instead of being rendered off-screen inside the device-list scroller. Sanitized native/JS diagnostics identify identity, refresh, and pairing failures without logging credentials, pairing payloads, keys, or Agent content.
The same run also found that changed-aware lane fingerprinting inherited Node's 1 MiB synchronous subprocess output limit. A bounded 64 MiB Git buffer and a 2 MiB regression fixture allow this PR's 1.43 MiB diff to reach its actual validation lanes.
Review
Three focused Codex reviewers covered:
All reported blockers were resolved. The final targeted reviews reported no remaining blocker for the Personal direct-lane MVP.
Validation
pnpm check:changed: 30/30 selected lanes passedpnpm check:changed -- --push-ready: 33/33 selected lanes passedapp:assembleDebugCaptureActivityuser_devicerow containsed25519, a 32-byte public key, identity revision 1, registered and not revokedgo test ./...andgo test -race ./authenticated ./mobileinpackages/device-linkgo test ./service/mobileremoteandgo test -race ./service/mobileremoteinservices/tuttidAcceptance remaining
The Android app is installed on the physical device with identity registration, permission, scanner launch, and manual-entry error behavior verified. The remaining Personal MVP acceptance requires fresh external pairing state:
Relay fallback, event streaming, automatic foreground reconnect, and TSH cutover remain follow-up milestones rather than blockers for this direct-lane PR.
Documentation impact
Updated the AgentGUI ownership/data-flow architecture, DeviceLink responsibility, full mobile design, beginner Android development/validation guide, and changed-aware static-analysis convention.