- Offline-first Android password manager built with Expo SDK 57, React Native, TypeScript.
- Security model: KS1 (AES-256-CBC + HMAC-SHA256) with Argon2/PBKDF2 key derivation.
- Android-first, iOS paused.
- Required toolchain: Bun 1.3.14 and Node.js 22.13 or newer
- Install:
bun install; clean validation/CI:bun install --frozen-lockfile - Dev server for Expo Go:
bun run start - Android with Expo Go:
bun run android - Expo Go tunnel:
bun run start:tunnel - Web:
bun run web - Preview cloud build:
bun run build:android:preview - Production cloud build:
bun run build:android:production - Production submit:
bun run submit:android:production(Google Play submission is currently performed manually) - Build from GitHub: EAS Workflow
.eas/workflows/build-android-production.yml(Android production), triggered on a version tag push (v*) or manual dispatch only - Android bundle export check:
bunx expo export --platform android --output-dir C:\tmp\keysoft-android-export - Lint:
bun run lint - Typecheck:
bun run typecheck - Tests:
bun run test - Coverage CI tests:
bun run test:ci - Health check:
bunx expo-doctor - Full local checks:
bun run verify - Dependency audit:
bun run deps:audit
- Use TypeScript with strict mode and interfaces over types.
- Use functional components and hooks; avoid classes.
- Use the
functionkeyword for pure functions. - Prefer named exports.
- Use descriptive boolean names like
isLoading,hasError. - Keep files structured as: exported component, subcomponents, helpers, static content, types.
- Use lowercase-with-dashes for directory names.
- UI in
src/screensandsrc/components. - Business logic in
src/services. - Shared state in
src/contexts. - Complex screen logic in
src/hooks.
- Use
src/utils/cryptoRandom.tsfor any randomness. Do not useMath.randomfor security-sensitive operations. - Always derive keys via
CryptoService.deriveKeyand verify withCryptoService.verifyDerivedKey. - Derived keys must be 64-char hex strings.
- Keep the active vault key in memory by default. The only allowed persistence exception is SecureStore-backed biometric unlock via
StorageService.saveBiometricKey/getBiometricKey/deleteBiometricKey, with device authentication required and no logging. - Update or delete the biometric SecureStore key when biometrics are disabled or the PIN changes.
- Do not log secrets. Use
Loggerwith sanitized messages. - Copy secrets with
ClipboardService.copyToClipboard, which schedules the auto-clear. UseClipboardService.copyPlainTextonly for non-secret text such as a contact address; never route a password through it. - Local secrets live in
.secrets/and must never be committed. - The project requires no local environment variables. Keep
.env.examplenon-secret, never put credentials inEXPO_PUBLIC_*, and use EAS/GitHub secret stores for automation credentials. - Treat imported files and KDF metadata as untrusted. Preserve backup/file-size and KDF-cost bounds when changing validation or crypto code.
- Persist encrypted storage mutations before updating decrypted caches. Reset only Keysoft-owned keys; do not use
AsyncStorage.clear(). - Clear backup passwords/ciphertext from UI state and remove temporary export files after sharing.
- All user-visible strings must use
t('key'). - No fallbacks like
t('key') || 'Fallback'. - Add keys in both Italian and English dictionaries.
- Day-to-day Android development uses Expo Go.
- Build artifacts are produced on expo.dev through EAS, not local Gradle.
- EAS build commands upload the project to expo.dev and require explicit approval before running.
- Keep
app.config.js, EAS profiles, and generated native configuration in sync when permissions or updates change. INTERNETis allowed only for Expo/EAS update delivery; do not add vault sync or remote secret transport.- Camera feature requires
CAMERA. READ_MEDIA_*permissions are blocked.POST_NOTIFICATIONSis required on Android 13+ for local notifications.expo-updatesis enabled; update manifest and config together if changing.- Release builds must keep minification and resource shrinking enabled through
expo-build-properties. plugins/withAndroidReleaseOptimization.jsmust keepproguard-android-optimize.txtandandroid.r8.optimizedResourceShrinking=truein generated Android projects.
- Update Jest mocks when AuthService, CryptoService, or StorageService surfaces change.
- Prefer unit tests in
src/__tests__/services. - Use
src/__tests__/contextsfor provider lifecycle tests andsrc/__tests__/hooksfor hook workflow tests. - Expo Go uses the PBKDF2 fallback because custom native modules are not available there; EAS/native builds may use Argon2.
- Use EAS/native builds for release-grade Argon2 validation; Expo Go vaults are development data.
- When R8 or native build configuration changes, verify optimized shrinking and the Argon2 keep rules in a temporary generated Android project before starting EAS.
- Dependency removals or native-package changes require
bun run verifyand a local Android export.
- Keep
bun.lockcommitted and frozen in normal CI runs. - Pin third-party GitHub Actions to full commit SHAs and keep workflow permissions minimal.
- Do not weaken the required
Validatebranch-protection check or bypass human review for dependency updates. - Do not modify design, icons, or visual assets unless the task explicitly requests it. Lossless size optimization must preserve format, proportions, transparency, and visual quality.
- Never run EAS build, submit, deployment, tag, push, or release commands without explicit approval; these create external changes or consume cloud resources.
README.mdis the public project overview and setup guide.CHANGELOG.mdtracks notable release and unreleased changes.docs/architecture.mddocuments system structure and data flow.docs/security.mddocuments the cryptographic and storage model.docs/development.mddocuments local workflow, coding standards, and verification.docs/release.mddocuments release readiness, Android permissions, and security checks.- Update
README.md,CHANGELOG.md, and the relevantdocs/file for significant changes. - Do not recreate
memory-bank; it has been retired in favor of thedocs/directory.