Skip to content

feat(mobile-remote): add secure browser session control - #1150

Open
beruro wants to merge 17 commits into
developfrom
junyu/mobile-remote-control
Open

feat(mobile-remote): add secure browser session control#1150
beruro wants to merge 17 commits into
developfrom
junyu/mobile-remote-control

Conversation

@beruro

@beruro beruro commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Problem

ORGII sessions could only be operated reliably from the Desktop ChatPanel. The browser/mobile surface had no authenticated transport into the desktop runtime, no canonical projection for provider history and rounds, and no shared browser-safe presentation path for messages, tools, permissions, loading, file previews, session rows, settings, or device state. This caused stale or misordered rounds, submitted prompts appearing late or in the previous round, empty loading states, overlapping long settings values, inconsistent file-diff surfaces, raw protocol values, inert settings actions, and UI drift from Desktop.

The hosted mobile surface also accepted a device pairing credential without first requiring an ORG2 Cloud identity, so pairing and account authentication were not separate security boundaries.

Solution

Add Mobile Remote as one authenticated end-to-end lifecycle:

  • add a standalone /orgii/mobile browser entry and settings/pairing flow for LAN or outbound relay connections
  • reuse the canonical ORG2 Cloud auth schema, storage key, Supabase project configuration, GitHub provider/scopes, PKCE flow, login layout, SectionLayout rows, and account/logout presentation
  • gate both standalone and main-router mobile entry points; move credential-bearing #pair state into the dedicated opaque intent store before login, persist only the sanitized return path, and prevent BYOK skip from bypassing mobile authentication
  • capture and scrub credential-bearing pairing fragments synchronously before asynchronous startup, then scope stored pairing state by authenticated user ID
  • exchange the browser access token for a same-origin HttpOnly relay session instead of exposing the OAuth token to WebSocket URLs or Durable Object state
  • add an authenticated desktop bridge, payload-opaque relay protocol/server, pairing tiers, device revocation, reconnect/backoff, and bounded fan-out
  • project native and imported provider histories into stable turn identities, materialize headless user sends at the authoritative event boundary, and invalidate Cursor history only after its transcript is readable
  • reuse Desktop chat primitives and tokens for bubbles, composer controls, loading, permissions, scroll following, tool rows, bottom-sheet details, Prism syntax highlighting, and session-list presentation
  • reuse shared SectionLayout rows and containers for Mobile Settings and Devices; equal-width label/value cells guarantee long Relay URLs truncate inside their own column
  • reuse the Desktop EventBlock shell, snippet padding, code typography, and lazy Prism pipeline for mobile file changes
  • resolve file-open requests from authoritative session events, enforce full-tier access, canonicalize the selected path under the session root, and dispatch opening through the Desktop app
  • add regression coverage across authentication, browser navigation, transport, round selection, transcript merging, input behavior, relay lifecycle, permissions, file navigation, settings/device presentation, and shared session-row presentation
  • merge current develop through 68e7aff15; preserve the shared Desktop/Mobile session-row tokens across the Tailwind v4 migration, bind the new pill regression to its canonical shared token, and restore the generated-title repair helpers at their new cache module boundaries

The Cloudflare authentication boundary is reviewed separately in ORGII-cloud-infra#31. The session_turns.turn_intent_id column remains nullable and is added idempotently; the turn index is rebuilt from canonical events rather than destructive history cleanup.

Potential risks

  • This remains a large cross-layer PR. Provider-specific imported-history edge cases beyond the covered Codex/Cursor paths still need broader soak testing.
  • A valid ORG2 account is not yet cryptographically bound to the account active on the paired desktop. A valid account session plus the existing device pairing secret is the effective authorization combination.
  • Normal logout synchronously removes the mobile UI and closes its connection; a socket not closed by the client remains server-valid only until its signed session expiry. Immediate cross-device OAuth revocation is not implemented.
  • The full hosted OAuth redirect was not exercised with a real user account after deployment. Dashboard read-back confirmed production has only the three existing marketplace/cloud redirects and is missing the Worker callback; the currently signed-in Supabase member has read-only controls, so an Owner/Administrator must append the exact callback before OAuth can return to Mobile.
  • Existing databases gain one nullable derived-index column and rebuild the turn index. Older builds ignore the extra column; rollback can disable Mobile Remote/relay settings and return to the prior app and Worker versions without deleting session history.
  • Mobile deliberately reuses only browser-safe Desktop primitives; importing the full Desktop ChatCodeBlock or auth atom would pull editor/Tauri ownership into the public bundle, so shared browser-safe schema and presentation leaves are the reuse boundary.
  • The complete physical-device, accessibility, security-review, and soak matrix is not complete; those gaps remain disclosed for review and may still block merge readiness.

Verification

Latest develop integration (2026-09-02)

  • merged current origin/develop at 68e7aff15; final PR head is 31296cdc0
  • merge conflict resolution preserved SESSION_ROW_PRESENTATION ownership while adopting Tailwind v4 canonical class names and ordering
  • pnpm install --frozen-lockfile — passed
  • pnpm typecheck — passed
  • pnpm lint — passed after fixing 13 Tailwind v4 class-order violations in PR-owned files
  • targeted Mobile Remote/shared row/navigation/permission suite — 34 files, 154 tests passed
  • cargo clippy -p orgtrack_core --all-targets -- -D warnings — passed, covering the prior missing generated-name helper failure path
  • pnpm test -- --reporter=dot — 1,369 files and 10,600 tests passed on the final base
  • an earlier pre-final-base full rerun lost the local esbuild service after 9,837 tests under host contention; after merging develop's bounded Vitest pool, the final full suite passed
  • git diff --check, conflict-marker scan, and staged secret/personal-path scan — passed
  • merge commit hook — lint-staged and the staged-file TypeScript gate passed

Existing feature verification

  • targeted Mobile Remote/auth/router/shared-row suite — 36 files, 168 tests passed
  • mobile auth return-path regression — 5 files, 21 tests passed; verifies the exact same-origin callback, excludes pairing/token state from OAuth URLs, and keeps #pair out of generic router/login storage
  • pnpm check:test-placement — 468 test-bearing directories passed
  • FAST_PROD=true pnpm build:release — production Webpack build passed in 27.438 seconds
  • cargo check -p orgtrack_core — passed
  • generated-cache-title source-boundary regression and Cursor generated-title regression — passed
  • Cloudflare Worker typecheck and 13 unit tests passed; Wrangler dry-run passed
  • production relay version a7f9d501-0fb5-4734-8251-e6a0341ee99a deployed at https://orgii-mobile-relay.superficial-jasper.workers.dev/orgii/mobile
  • deployed /healthz returned protocol version 1; /orgii/mobile and /orgii/mobile/auth/callback?code=probe returned 200 with the mobile shell; invalid auth exchange and unauthenticated WebSocket upgrade both returned 401
  • Supabase URL Configuration read-back confirmed Site URL https://org2-cloud-infra.vercel.app, 3 existing redirects, and no Worker callback; current account exposes disabled edit controls
  • browser visual checks covered signed-out, auth-error, and narrow 393×852 / 320×700 dark-theme layouts without horizontal overflow
  • not run: credentialed OAuth lifecycle with a real SUPABASE_ACCESS_TOKEN, full physical-device matrix, accessibility review, security review, and soak testing

Frontend UI audit

  • Authentication surfaces: 0 fix / 6 keep with reason / 0 abstract
  • Transcript surfaces: 0 fix / 15 keep with reason / 0 abstract
  • Shared session list: 0 fix / 6 keep with reason / 0 abstract
  • Mobile Settings, Devices, file preview, and adjacent shell reuse review: 0 fix / 12 keep with reason / 1 abstract
  • The one abstraction is an opt-in shared inline SectionRow layout; existing SectionRow behavior is unchanged

Architecture audit

  • Covered all 10 layers: compilation, structural duplication, naming, semantic overload, defaults, cross-domain ownership, new-developer clarity, wire compatibility, initialization parity, and resolver symmetry
  • Canonical browser-safe auth state is shared without importing Tauri-owned desktop runtime modules; a boundary test protects that graph
  • Account identity, relay session, desktop token, and device pairing remain separately named and owned
  • Both standalone and main-router startup restore the same auth invariant; the main router moves raw #pair state into the dedicated bounded intent before generic login state is created; malformed callbacks and permanent auth failures fail closed
  • Latest-develop integration preserves the modular cache/app structure and restores generated-title repair behavior in its owning cache modules with a source-level regression test

Performance guard

  • Authentication uses one expiry timeout, clears it while hidden, refreshes once on visibility, and guards async completion by generation
  • The Worker uses one earliest-expiry Durable Object alarm rather than polling or per-socket timers
  • Pairing state is user-keyed; auth in-flight state, timeout state, and broker attachments are bounded
  • No new recurring scan, render loop, or unbounded cache was introduced; lifecycle/unit coverage passed

@Harry19081 Harry19081 added enhancement New feature or request sessions Sessions, history, replay, sidebar, workspace, or worktrees cloud-collaboration Cloud sync, organizations, channels, or collaboration security Security-sensitive behavior, credentials, secrets, or advisories labels Aug 31, 2026
@beruro

beruro commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator Author

@Neonforge98 麻烦帮忙处理这个阻塞项:

生产 Supabase 项目 fpdyejwbiriliuqqcjoyAuthentication → URL Configuration → Redirect URLs 当前只有 3 条,缺少 Mobile Remote 的 PKCE callback。请保留现有 Site URL 和全部 3 条 Redirect URLs,只追加下面这一条精确地址:

https://orgii-mobile-relay.superficial-jasper.workers.dev/orgii/mobile/auth/callback

请不要使用 wildcard,也不要修改 GitHub OAuth App callback。保存后麻烦在这里回读确认:列表中能看到该完整 URL,Total URLs 为 4。

代码侧已验证客户端请求的就是该 callback,Worker 路由也已返回 200;当前故障是 Supabase 未 allowlist 时回退到 Site URL。Infra 配置说明在 https://github.com/org2AI/ORGII-cloud-infra/pull/31 。你确认配置完成后,我会继续跑真实登录回跳 smoke。

@beruro
beruro requested a review from Neonforge98 September 1, 2026 14:38
@sudomaggie
sudomaggie marked this pull request as ready for review September 1, 2026 16:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cloud-collaboration Cloud sync, organizations, channels, or collaboration enhancement New feature or request security Security-sensitive behavior, credentials, secrets, or advisories sessions Sessions, history, replay, sidebar, workspace, or worktrees

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants