fix(mobile): enforce local access at final transport dispatch - #5658
Closed
iscekic wants to merge 2 commits into
Closed
fix(mobile): enforce local access at final transport dispatch#5658iscekic wants to merge 2 commits into
iscekic wants to merge 2 commits into
Conversation
This was referenced Aug 28, 2026
Contributor
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (33 files)
Reviewed by grok-4.6 · Input: 377.2K · Output: 26.6K · Cached: 795K Review guidance: REVIEW.md from base branch |
This was referenced Aug 28, 2026
Contributor
Author
|
Retired. The owner cancelled this section and restarted the work with a tighter brief. The biometric gate scope grew into a full native-action admission audit; the replacement covers initial app unlock only. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The software development kit (SDK) adds optional
captureActionAdmissiontoUserWebConnectionConfigandUserWebConnection, bindingUserWebActionAdmissiontoUserWebActionTargetbefore waits.Both
SendCommandOptionsandSendCommandToConnectionInputaccept this local proof; senders validate it immediately before sending without changing the wire format.Existing connections without hooks keep unrestricted commands, while guarded connections reject unclassified commands and require retries to preserve the original proof.
Files
packages/cloud-agent-sdk/src/user-web-connection.ts— Source, M (modified), 162 changed lines; adds frozen admission handles and a registry specific to each connection. Matches the command, session, and connection before waiting, then asserts the captured authority immediately before sending. Requires admission forsend_message,send_command,interrupt,question_reply,question_reject,permission_respond,suggestion_accept,suggestion_dismiss,create_session, andexit_cli. Leaveslist_models,list_commands,list_directories, and subscriptions outside action admission. Captures omitted proof at sender entry, but never puts proof in the wire frame ormutationId. Handles serialization, admission, and socket-send failures by rejecting the command, clearing its timer and pending entry, and releasing its retain.packages/cloud-agent-sdk/src/index.ts— Source, M (modified), 3 changed lines; makesSendCommandOptions,UserWebActionAdmission, andUserWebActionTargetavailable from the public SDK entry point.packages/cloud-agent-sdk/src/user-web-connection.test.ts— Test, M (modified), 340 changed lines; expands the connection admission tests.MobileActionAdmissiongives new mutations a foreground lease;AcceptedWorkReceiptpermits dispatchedquick-chat-turnandapp-store-purchasecompletion under the original account and organization.quickChat.appendMessagesnow requireslocalAccessReceipt;kiloPass.completeAppStorePurchasestill accepts older StoreKit callers without it and retains backend verification.Queries and
activeSessions.createWebTicketneed ownership;user.registerPushTokendoes too, whileuser.unregisterPushTokenanduser.revokeCurrentDeviceSessioncan also run during sign-out cleanup.Files
apps/mobile/src/lib/local-access-transport.ts— Source, A (added), 316 changed lines; introduces shared checks for credential ownership, foreground admission, and accepted completion. Validates authentication epochs, credential generations, and known user identities while allowing bootstrap reads before account restoration finishes. Uses registries to reject copied admissions and receipts, and issues receipts only after synchronous dispatch returns. Completion checks the original owner, organization, work kind, and applicable work identifier without requiring the current foreground lease or unlock grant. Quick Chat completion must match the first message'sclientId; supplied mutation admission must match the request's account and organization. Captures admission when a mutation caller omits it. Adds a mandatory mobile connection wrapper that checks ownership around token waits and event delivery, records session scopes, and clears them on destruction. Imports the narrow SDK connection entry point and recovers typed denials fromError.cause.apps/mobile/src/lib/local-access-transport.test.ts— Test, A (added), 373 changed lines; introduces tests for transport admission and completion rules.TransportOperationcaptureslocalAccessOwnerandlocalAccessAdmissionbefore authentication waits;trpcClientnow sends mutations individually instead of batching them.Query batches require one authentication epoch and credential generation;
skipBatchstill requests a single operation.deadlineFetchkeeps its deadline and abort contract; transport assertions run after artificial latency, and expired ownership producesLocalAccessDeniedErrorbefore dispatch or result delivery.Files
apps/mobile/src/lib/trpc.ts— Source, M (modified), 235 changed lines; replaces shared delegates with a delegate for each mutation and query batches for each owner. Rechecks ownership around token acquisition, stored expiry reads, refreshes, and observable delivery. Accepts token rotation within the captured credential generation but prevents requests from using a replacement account. Places the final dispatch assertion after test latency and the cancellation check, while preserving deadline extension, caller abort reasons, and client metadata. Keeps the library observable's pipe and teardown behavior. Tracks cancelled batch members in a weak registry until release and removes empty batches for stale owners.apps/mobile/src/lib/trpc.test.ts— Test, M (modified), 660 changed lines; revises request tests for owner checks and separate mutation requests.MobileConnectionConfigrequirescaptureActionAdmission, anduseUserWebConnectionreturnsMobileUserWebConnectionwithownerandsetSessionScope.commandScopeSchemagives explicitorgIdprecedence over the session scope and ready organization selection; unresolved targets fail.UserWebConnectionProviderdestroys stale sockets and remounts consumers when ownership changes, but organization changes and StrictMode cleanup retain a reusable connection.Files
apps/mobile/src/components/agents/user-web-connection-provider.tsx— Source, M (modified), 109 changed lines; subscribes to published owners and creates a connection for each owner. Sends ticket requests with the captured owner and keeps the latest organization selection in a ref for action capture. Rejects commands without a resolved target organization. Invalidates the old connection synchronously during owner publication rather than waiting for React's replacement commit. Uses the account, authentication epoch, and credential generation as the provider key. Ordinary cleanup unsubscribes and releases the retain so StrictMode can replay the effect.apps/mobile/src/components/agents/user-web-connection-provider.mounted.test.tsx— Test, M (modified), 320 changed lines; revises mounted tests for the provider's owner-bound connection.CreateMobileAgentSessionManagerOptionsnow requiresMobileUserWebConnection, so session reads, tickets, attachment caches, and failures retain their captured account owner.Personal and organization calls to
sendMessage,interruptSession,answerQuestion,rejectQuestion, andanswerPermissioncarry admission.prepareSessionsaves admission forinitiateFromPreparedSessionin resolution order; missing or expired admission prevents initiation, and owner replacement destroys the manager.Files
apps/mobile/src/components/agents/mobile-session-manager.ts— Source, M (modified), 162 changed lines; takes ownership from the connection instead of a later global account. Checks ownership around session resolution, snapshot reads, history pages, authentication waits, and stream-ticket responses. Keeps session-load retries with that owner and records each loaded or resolved session's organization throughsetSessionScope. Prevents stale attachment callbacks from writing caches. Adds admission to personal and organization mutations, and queues preparation admissions by completion order when requests share a session identifier. Initiation consumes the corresponding queued admission rather than capturing another lease. Destruction clears the queue and owner subscription; stale failures stop silently, while local-access denials propagate without a generic toast.apps/mobile/src/components/agents/mobile-session-manager.local-access.test.ts— Test, A (added), 334 changed lines; introduces session-manager admission tests.apps/mobile/src/components/agents/mobile-session-manager.test.ts— Test, M (modified), 37 changed lines; revises existing session-manager tests for captured ownership.createCliLiveTransportandcreateRemoteSessionOnConnectioncapturecreate_sessionauthority once and pass it to both the extended attempt and compatibility retry.A retry therefore cannot acquire permission from a later context, and connection objects without capture keep their existing arguments.
Only the delivered
invalid create_session commanderror permits one bare retry; clone requests never use that fallback.Files
packages/cloud-agent-sdk/src/cli-live-transport.ts— Source, M (modified), 29 changed lines; carries local command options through the session sender. Requires a connected command-line interface (CLI) owner before admission capture and forwards the same admission to the bare retry. Preserves separate mutation identities and omits extra arguments when callers provide no local options.packages/cloud-agent-sdk/src/create-session.ts— Source, M (modified), 11 changed lines; extends the connection input with optional admission capture. Captures proof before the extended request and includes it again on the bare request, preserving distinct:extand:baremutation identities.packages/cloud-agent-sdk/src/cli-live-transport.local-access.test.ts— Test, A (added), 379 changed lines; introduces local-admission tests for the live transport.packages/cloud-agent-sdk/src/create-session.test.ts— Test, M (modified), 18 changed lines; adds checks for admission reuse during session creation.QuickChatCompletionInputmakesadmission,turnId, andonDispatchmandatory;useQuickChat.onSendresolvesPromise<AcceptedWorkReceipt>when the fetch call returns, before its response.quickChat.getOrCreateThreadruns on send rather than mount, andquickChat.appendMessagesuses the dispatched turn's proof instead of requesting new foreground permission.Dispatched turns can finish saving to their original organization after backgrounding or context changes; account replacement blocks both saving and visible updates.
Files
apps/mobile/src/components/quick-chat/quick-chat-gateway.ts— Source, M (modified), 44 changed lines; validates the organization and cancellation signal before calling fetch. Issues the dispatch receipt only after that call returns, then checks ownership after the response, before each content delta, and at completion. Keeps the existing streaming request free of tools.apps/mobile/src/components/quick-chat/use-quick-chat.ts— Source, M (modified), 385 changed lines; keys history and local state by user, authentication epoch, credential generation, and organization. Waits for a ready authenticated context before showing history, and prevents stale pages, errors, or loading callbacks from updating the visible scope. Moves thread creation into the send flow and revalidates admission after thread creation and token acquisition. Separates the dispatch promise from streaming completion, removes turns without receipts, and completes each dispatched turn at most once for its original organization. Stops streams on explicit stop, scope replacement, and unmount; restricts refetches, notices, and visible updates to the current scope. Propagates local-access denials without a generic toast and uses the Promise constructor for Hermes support. Adds a line-count exception so paging and turn completion keep one ownership boundary.apps/mobile/src/components/quick-chat/quick-chat-gateway.local-access.test.ts— Test, A (added), 184 changed lines; introduces gateway admission tests.apps/mobile/src/components/quick-chat/quick-chat-gateway.test.ts— Test, M (modified), 49 changed lines; supplies the new completion inputs in existing gateway tests.apps/mobile/src/components/quick-chat/use-quick-chat.local-access.test.ts— Test, A (added), 339 changed lines; introduces tests for Quick Chat admission and completion.ActiveSessionsLiveSyncrequiresCreateLiveSyncOptions.ownerand checks that owner around every query and queued cache update.Ownership loss detaches subscriptions, cancels pending queries, and releases the retained connection before stale work can update the cache.
Callers keep context-specific query keys, serialized refreshes, enrichment, reconnect behavior, and manual refresh.
Files
apps/mobile/src/lib/active-sessions-live-sync.ts— Source, M (modified), 43 changed lines; wraps queries and queued writes with ownership checks before and after waits. Detaches on owner replacement, clears pending refresh reasons, and treats repeated detach calls as no-ops. Adds a line-count exception to keep the serialized refresh state machine together.apps/mobile/src/lib/active-sessions-live-sync-mount.tsx— Source, M (modified), 8 changed lines; supplies the connection's owner when creating synchronization. Preserves the standing retain that keeps the socket open across organization changes.apps/mobile/src/lib/active-sessions-live-sync.attention.test.ts— Test, M (modified), 8 changed lines; supplies captured ownership to the attention tests.apps/mobile/src/lib/active-sessions-live-sync.departure.test.ts— Test, M (modified), 5 changed lines; supplies captured ownership to the departure tests.apps/mobile/src/lib/active-sessions-live-sync.enrichment.test.ts— Test, M (modified), 36 changed lines; revises enrichment tests for owner-bound synchronization.apps/mobile/src/lib/active-sessions-live-sync.manual-refresh.test.ts— Test, M (modified), 9 changed lines; passes the owner in manual-refresh tests.apps/mobile/src/lib/active-sessions-live-sync.ownership.test.ts— Test, A (added), 117 changed lines; introduces tests for synchronization after ownership changes.apps/mobile/src/lib/active-sessions-live-sync.pending.test.ts— Test, M (modified), 40 changed lines; revises pending-refresh tests for captured ownership.apps/mobile/src/lib/active-sessions-live-sync.race.test.ts— Test, M (modified), 7 changed lines; passes the owner in synchronization race tests.apps/mobile/src/lib/active-sessions-live-sync.reconnect.test.ts— Test, M (modified), 29 changed lines; revises reconnect tests for owner-bound synchronization.apps/mobile/src/lib/active-sessions-live-sync.test-helpers.ts— Test, M (modified), 25 changed lines; supplies ownership through the shared synchronization helpers.apps/mobile/src/lib/active-sessions-live-sync.test.ts— Test, M (modified), 31 changed lines; updates core synchronization tests to pass the owner.Tests: 6 added files and 15 modified files (21 total). Added (6):
mobile-session-manager.local-access.test.ts,quick-chat-gateway.local-access.test.ts,use-quick-chat.local-access.test.ts,active-sessions-live-sync.ownership.test.ts,local-access-transport.test.ts,cli-live-transport.local-access.test.ts. Modified (15):mobile-session-manager.test.ts,user-web-connection-provider.mounted.test.tsx,quick-chat-gateway.test.ts,active-sessions-live-sync.attention.test.ts,active-sessions-live-sync.departure.test.ts,active-sessions-live-sync.enrichment.test.ts,active-sessions-live-sync.manual-refresh.test.ts,active-sessions-live-sync.pending.test.ts,active-sessions-live-sync.race.test.ts,active-sessions-live-sync.reconnect.test.ts,active-sessions-live-sync.test-helpers.ts,active-sessions-live-sync.test.ts,trpc.test.ts,create-session.test.ts,user-web-connection.test.ts.Generated: 0 generated files.
Verification
Runtime verification remains bot-e2e, not human delegation.
Visual Changes
Visual Changes: N/A
Reviewer Notes
Human steps
No human steps are required before merge or after merge.
Automated evidence
Scope
Kilo-Org/cloud./Users/igor/Projects/.worktrees/mobile-context-lock-758a.mobile-context-lock-758a-s2...mobile-context-lock-758a-s3.Notes
This level adds transport admission and ownership boundaries without exposing biometric controls. Full iOS and Android verification runs on the final stack level.
Device verification remains bot-e2e, not human delegation.
Stacked PRs — merge bottom to top. Each level shows only its own diff.
Runtime verification (E2E, user advocacy, simplify) runs on the tip PR over every level.
Every level keeps its own checks, its own bot review, and its own threads; each one is answered on its own PR.
Each level is its own deliverable: it builds and passes its own checks alone.
A finding on a level is repaired on that level, then carried upward with stack.sh forward.
mobile-context-lock-758a— feat(mobile): add account-owned local access state #5642mobile-context-lock-758a-s2— fix(mobile): bind restored context and drafts to account scope #5651mobile-context-lock-758a-s3— fix(mobile): enforce local access at final transport dispatch #5658 ← this PRmobile-context-lock-758a-s4— fix(mobile): bind Kilo Chat effects to local access #5664mobile-context-lock-758a-s5— feat(mobile): protect native privacy and accessibility boundaries #5683 (tip)