feat(mobile): add account-owned local access state - #5642
Open
iscekic wants to merge 2 commits into
Open
Conversation
Contributor
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (12 files)
Reviewed by grok-4.6 · Input: 210.8K · Output: 22.8K · Cached: 439.8K Review guidance: REVIEW.md from base branch |
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.
No new behavior — the account protection stays inactive, with no new controls in the app.
Summary
LocalAccessSnapshot,LocalAccessDependencies,LocalAccessAction, andLocalAccessActionResultgive one owner atomic control over authenticated preference changes and access grants.LocalAccessScopeandLocalAccessLeasecaptureauthEpochandunlockGeneration; rejected admission raisesLocalAccessDeniedError(LOCAL_ACCESS_DENIED) rather than accepting stale work.On return, five background minutes or invalid elapsed time expire protected grants and active attempts before foreground publication; native effects require independent foreground checks.
Files
apps/mobile/src/lib/local-access.ts— Source; added (A); 395 changed lines. Adds frozen snapshots, subscriptions, one process owner, and cleanup that rejects pending completions. Owner changes and preference reloads revoke grants and leases; callers supply a validated identity and its authentication epoch. New work requires a captured lease, current ownership, foreground readiness, a ready context, and unlocked access. Owner-only checks permit passive completion of accepted work; lease scopes carryorganizationIdwithout checking organization membership. Unlock requests share the pending attempt; competing preference actions returnbusy, and cancelled authentication requires explicit retry. Enable, disable, and malformed-record repair authenticate before a guarded write; only a committed write publishes the preference with its grant. Missing or disabled preferences allow foreground access, while failed reads and malformed records deny access. Authentication completed while inactive waits to grant access until the foreground; expiry checks cover pending grants and active enable or repair writes. Locking revokes grants without deleting the preference, and owner or attempt changes discard stale results. The coordinator has no app-provider or screen integration at this level.apps/mobile/src/lib/local-access.test.ts— Test; added (A); 953 changed lines. Introduces coordinator and grant-expiry tests.LocalAccessStorage,LocalAccessReadResult, andLocalAccessWriteResultserialize account preferences throughSecurityStore, keeping them separate from credentials and retaining them after sign-out.recordSchemaaccepts onlyversion: 1and Booleanenabled; absent records disable protection, but malformed records or failed reads deny access.local-access-v1-keys useWHEN_UNLOCKED_THIS_DEVICE_ONLY;isCurrentprevents old native writes from granting access, even when they finish saving the original account's preference.Files
apps/mobile/src/lib/local-access-storage.ts— Source; added (A); 73 changed lines. Encodes account identifiers into keys and parses stored JSON with a strict schema. Serializes reads behind existing writes for the same account. Checks attempt ownership before each native write and after its completion, returning committed, failed, or stale. Leaves credential keys and storage options unchanged.apps/mobile/src/lib/local-access-storage.test.ts— Test; added (A); 167 changed lines. Introduces tests for durable protection preferences.authenticateLocalAccessrechecks device capability throughNativeAuthentication;LocalAuthenticationOutcomeandLocalAuthenticationFailurereport authentication or recovery classified byRecoveryStatus.disableDeviceFallback: falsekeeps passcodes and Expo's default Android Class 2-or-better authentication, including Android 9/10, without turning protection off after failures.expo-local-authentication~57.0.2andfaceIDPermissionadd native support and a Face ID explanation; developers must rebuild the development apps.Files
apps/mobile/src/lib/local-authentication.ts— Source; added (A); 70 changed lines. Imports the native module only when called and supports an injected adapter. Accepts passcode-only enrollment and reports missing enrollment as unavailable. Classifies cancellation, lockout, timeout, processing failures, and authentication failures as retryable. Invalid context, missing usage descriptions, and storage exhaustion require terminal recovery; missing device security requires unavailable recovery. Knownunknownerrors remain retryable; unrecognized error codes produce terminalunexpected_error. Exceptions return retryablerejected, and recovery never clears protection.apps/mobile/app.config.ts— Source; modified (M); 6 changed lines. Adds the Expo authentication plugin and its permission message for Face ID account unlocking on this device.apps/mobile/package.json— Source; modified (M); 1 changed line. Addsexpo-local-authenticationat~57.0.2to runtime dependencies.apps/mobile/src/lib/local-authentication.test.ts— Test; added (A); 114 changed lines. Introduces tests for native authentication outcomes.pnpm-lock.yaml— Generated; modified (M); 13 changed lines. Records the dependency lockfile update.Continuous integration (CI) now accepts
pull_requestevents for every target branch inCIandkilo-app CI, allowing stacked pull requests to receive checks.pushremains restricted tomain;kilo-app CIretainsworkflow_calland broadenspathsto include the root workflow and admission tests.Both workflows run admission regressions and preserve their existing job structure, permissions, and cancellation behavior.
Files
.github/workflows/ci.yml— Source; modified (M); 4 changed lines. Removes the pull-request target restriction and runs stacked-admission tests before change detection..github/workflows/kilo-app-ci.yml— Source; modified (M); 8 changed lines. Removes the pull-request target restriction and adds the root workflow and admission tests to both event filters. Runs admission tests before the mobile tests.scripts/stacked-ci.test.mjs— Test; added (A); 254 changed lines. Introduces stacked-workflow admission tests.Tests: 4 new files, totaling 1,488 changed lines:
local-access.test.ts,local-access-storage.test.ts,local-authentication.test.ts, andstacked-ci.test.mjs.Generated: 1 updated lockfile,
pnpm-lock.yaml(13 changed lines).Verification
bot-e2eworkflow; the orchestrator performs it before completion, not a human tester.bot-e2e, nothuman-e2e.Visual Changes
Visual Changes: N/A
Reviewer Notes
Human steps
Supplied verification evidence
Scope
Kilo-Org/cloud./Users/igor/Projects/.worktrees/mobile-context-lock-758a.mobile-context-lock-758a; base:origin/main.7503e72a5andcf2150203; level 1 only.Notes
This level does not expose biometric controls. Full iOS and Android verification runs on the final stack level.
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 #5642 ← this PRmobile-context-lock-758a-s2— fix(mobile): bind restored context and drafts to account scope #5651 (tip)