Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
f54f05d
feat(vault): add password manager README with zero-cloud architecture
npiesco Dec 1, 2025
2bb7ac3
feat(vault): mobile scaffold with encrypted VaultDatabase
npiesco Dec 1, 2025
982f1a0
docs(vault): add planning and progress tree with 9-phase roadmap
npiesco Dec 1, 2025
ea5c2b6
feat(vault): add credential CRUD with password generation
npiesco Dec 2, 2025
b296915
feat(vault): verify encrypted SQLite persists across app terminate/re…
npiesco Dec 2, 2025
4ae3858
feat(vault): add CredentialDetailScreen with password reveal and copy…
npiesco Dec 2, 2025
8367d07
feat(vault): add SettingsScreen with vault stats, lock, and export
npiesco Dec 2, 2025
002dc36
feat(vault): add configurable password length slider (8-128 chars)
npiesco Dec 2, 2025
a31b5b2
chore(vault): add e2e tsconfig with Jest and Detox types
npiesco Dec 2, 2025
3b887bb
feat(vault): add passphrase mode with word-based generation
npiesco Dec 2, 2025
6777938
feat(vault): add copy-to-clipboard for generated passwords
npiesco Dec 2, 2025
a325b19
feat(vault): add TOTP secret storage for 2FA credentials
npiesco Dec 2, 2025
f0e168f
feat(vault): add custom fields for credentials
npiesco Dec 2, 2025
47f4e33
feat(vault): add favorites toggle for credentials
npiesco Dec 2, 2025
c35af83
feat(vault): add tags for credential categorization
npiesco Dec 2, 2025
4778112
wip(vault): credential sorting E2E test - TDD RED phase ready
npiesco Dec 2, 2025
6ddfc95
fix: iOS build setup and dependency fixes
npiesco Dec 6, 2025
c3f68c8
docs: update Android build instructions and fix ubrn:android script
npiesco Dec 6, 2025
8a9cf05
feat(vault): implement credential sorting options
npiesco Dec 6, 2025
76ed69b
feat(mobile): implement folder management with E2E tests
npiesco Dec 7, 2025
4eb8e79
feat(folders): implement nested folder hierarchy
npiesco Dec 7, 2025
f996493
docs: update progress tree with nested folders completion
npiesco Dec 7, 2025
14c0107
feat: replace emojis with react-native-vector-icons, add folder icons…
npiesco Dec 8, 2025
fbfc495
feat(vault): implement recent items feature with lastAccessedAt tracking
npiesco Dec 8, 2025
94184f1
feat(mobile): add move-to-folder modal for credential organization
npiesco Dec 9, 2025
eba5a3c
feat: implement export vault to file with share sheet integration
npiesco Dec 9, 2025
1075c69
feat: Add typed Row/ColumnValue UniFFI types and fix mobile tests
npiesco Dec 10, 2025
19596cc
fix: Use ATTACH DATABASE for encrypted import
npiesco Dec 10, 2025
f2a2f90
feat: Add file picker integration for vault import
npiesco Dec 10, 2025
f02dea6
feat(biometric): implement biometric unlock with Face ID/Touch ID
npiesco Dec 11, 2025
972930b
docs: update Planning_and_Progress_Tree with biometric completion
npiesco Dec 11, 2025
e167745
feat(vault): implement auto-lock and clipboard auto-clear (Phase 4.2)
npiesco Dec 12, 2025
39a253f
feat(vault): implement sync conflict detection and merge (Phase 3.2)
npiesco Dec 12, 2025
446eb40
feat: add Security Audit feature and fix E2E test regressions
npiesco Dec 14, 2025
88a622e
feat(vault): implement master password change with strength meter and…
npiesco Dec 15, 2025
daba185
feat(totp): implement TOTP code generation, display, countdown, and copy
npiesco Dec 16, 2025
d02d1ed
feat(mobile): implement QR Scanner and TOTP Quick View
npiesco Dec 16, 2025
ff95542
feat(mobile): implement dark/light theme toggle
npiesco Dec 16, 2025
9abfc5d
feat(mobile): add haptic feedback with settings toggle
npiesco Dec 17, 2025
3f7a7fd
test(mobile): add loading states and error handling
npiesco Dec 17, 2025
6b4552a
test(mobile): add empty states
npiesco Dec 17, 2025
118dd4d
feat(mobile): add accessibility labels for screen readers
npiesco Dec 17, 2025
7d1fb82
feat(mobile): add dynamic font sizing setting
npiesco Dec 17, 2025
eea1c9c
feat(mobile): add high contrast mode accessibility setting
npiesco Dec 17, 2025
09188d2
docs: remove manual QA items from Phase 6.2
npiesco Dec 17, 2025
85b31e2
perf(mobile): add performance optimizations
npiesco Dec 17, 2025
d339782
feat(mobile): add performance E2E tests and disable iOS autofill
npiesco Dec 17, 2025
56af7c1
fix(mobile): fix Android database path resolution and improve error h…
npiesco Jan 1, 2026
fd116c9
chore(mobile): update Android config and tests [skip ci]
npiesco Jan 1, 2026
760f946
docs: update Planning_and_Progress_Tree.md - mark Android testing com…
npiesco Jan 1, 2026
39371c1
chore: add Android .gitignore for build artifacts [skip ci]
npiesco Jan 1, 2026
086052f
fix: IndexedDB persistence corruption - remove checksum from keys
npiesco Jan 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,51 @@ await db.close();

**Setup:** See [**absurder-sql-mobile/README.md**](absurder-sql-mobile/README.md) for build instructions.

#### Mobile Development Environment Setup

Building mobile apps requires native toolchains and Rust cross-compilation targets. Follow these steps to set up your development environment:

**Prerequisites:**
- **Rust 1.85.0+** with iOS/Android targets
- **Xcode** (iOS) with Command Line Tools
- **Android Studio** (Android) with NDK

**1. Install Rust iOS targets:**
```bash
rustup target add aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios
```

**2. Build iOS bindings with UniFFI:**
```bash
cd absurder-sql-mobile
npx uniffi-bindgen-react-native build ios --and-generate
```

**3. Install CocoaPods dependencies:**
```bash
cd your-react-native-app/ios && pod install && cd ..
```

**4. Run on iOS Simulator:**
```bash
npx react-native run-ios --simulator="iPhone 16"
```

**Android Setup:**
```bash
# Install Rust Android targets
rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android i686-linux-android

# Build Android bindings
cd absurder-sql-mobile
npx uniffi-bindgen-react-native build android --and-generate

# Run on Android Emulator
cd your-react-native-app && npx react-native run-android
```

> **Important:** The `uniffi-bindgen-react-native build` step compiles the Rust native library for the target platform and generates the TypeScript/Swift/Kotlin bindings. This must be run before `pod install` (iOS) or Gradle build (Android).

## Performance Features

AbsurderSQL includes several performance optimizations for high-throughput applications:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<key>BinaryPath</key>
<string>libabsurder_sql_mobile.a</string>
<key>LibraryIdentifier</key>
<string>ios-arm64</string>
<string>ios-arm64-simulator</string>
<key>LibraryPath</key>
<string>libabsurder_sql_mobile.a</string>
<key>SupportedArchitectures</key>
Expand All @@ -17,12 +17,14 @@
</array>
<key>SupportedPlatform</key>
<string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict>
<dict>
<key>BinaryPath</key>
<string>libabsurder_sql_mobile.a</string>
<key>LibraryIdentifier</key>
<string>ios-arm64-simulator</string>
<string>ios-arm64</string>
<key>LibraryPath</key>
<string>libabsurder_sql_mobile.a</string>
<key>SupportedArchitectures</key>
Expand All @@ -31,8 +33,6 @@
</array>
<key>SupportedPlatform</key>
<string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict>
</array>
<key>CFBundlePackageType</key>
Expand Down
89 changes: 74 additions & 15 deletions absurder-sql-mobile/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ android/src/main/jni/sqlcipher-libs/
1. **OpenSSL 1.1.1w** (for each ABI):
```bash
# Example for arm64-v8a
export ANDROID_NDK_HOME=$HOME/Library/Android/sdk/ndk/27.1.12297006
export ANDROID_NDK_HOME=$HOME/Library/Android/sdk/ndk/29.0.14206865
cd /tmp && tar xzf openssl-1.1.1w.tar.gz && cd openssl-1.1.1w

./Configure android-arm64 \
Expand Down Expand Up @@ -272,11 +272,20 @@ encryption-ios = ["encryption-commoncrypto"] # Alias for convenience
- iOS Simulator or device

**For Android:**
- Android Studio
- Android NDK 27.1.12297006 (or compatible)
- Android Studio (with bundled JDK 21)
- Android NDK (installed via SDK Manager)
- Android SDK with API 23+
- cargo-ndk v3.5.4 (`cargo install cargo-ndk --version 3.5.4`)
- Emulator or device

**Environment Variables (add to `~/.zshrc`):**
```bash
export ANDROID_HOME=$HOME/Library/Android/sdk
export ANDROID_NDK_HOME=$HOME/Library/Android/sdk/ndk/29.0.14206865
export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"
export PATH=$JAVA_HOME/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator:$PATH
```

### Rust Targets

Install all required targets:
Expand Down Expand Up @@ -319,30 +328,26 @@ The `npm run ubrn:ios` script:

#### Android Build

**Prerequisites:** SQLCipher static libraries must be built first. See "Building SQLCipher for Android" below.

```bash
cd absurder-sql-mobile

# Build Rust + generate UniFFI bindings + fix RN 0.82 compatibility
# Build Rust + generate UniFFI bindings
npm run ubrn:android

# Bundle React Native app and build APK
cd react-native
npx react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res
cd android && ./gradlew assembleDebug

# Install to emulator/device
adb install -r app/build/outputs/apk/debug/app-debug.apk
adb shell am start -n com.absurdersqltestapp/.MainActivity
# Run on emulator (from vault app directory)
cd ../vault/mobile
npx react-native run-android
```

The `npm run ubrn:android` script:
- Builds Rust for all 4 Android ABIs (arm64-v8a, armeabi-v7a, x86, x86_64)
- Builds Rust for configured Android ABIs (currently arm64-v8a)
- Generates Kotlin bindings via UniFFI
- Copies `.a` libraries to `jniLibs/`
- Generates TypeScript bindings
- **Runs `scripts/fix_cpp_adapter.py`** to fix React Native 0.82 compatibility issues

**Critical:** The `fix_cpp_adapter.py` script replaces UniFFI's generated `cpp-adapter.cpp` with a React Native 0.82-compatible version. This step is required because UniFFI generates code that's incompatible with RN 0.82's `CallInvokerHolder` API.
**Note:** The `ubrn.config.yaml` controls which ABIs are built. Currently only `arm64-v8a` is enabled since SQLCipher libs are only built for that ABI.

### Important: Clean Build Environments

Expand All @@ -355,6 +360,60 @@ printenv | grep -E "CC|ANDROID|NDK|CLANG|AR_|RANLIB"
# Should return nothing - if polluted, start new terminal
```

### Building SQLCipher for Android

SQLCipher static libraries must be built once per ABI. Currently only `arm64-v8a` is built.

**1. Build OpenSSL 1.1.1w:**
```bash
export ANDROID_NDK_HOME=$HOME/Library/Android/sdk/ndk/29.0.14206865
export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin:$PATH

cd /tmp
curl -LO https://www.openssl.org/source/openssl-1.1.1w.tar.gz
tar xzf openssl-1.1.1w.tar.gz && cd openssl-1.1.1w

./Configure android-arm64 -D__ANDROID_API__=23 no-shared no-asm -fPIC --prefix=/tmp/openssl-arm64-v8a
make -j8 && make install_sw
```

**2. Build SQLCipher 4.6.0:**
```bash
export TOOLCHAIN=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64
export CC="$TOOLCHAIN/bin/clang --target=aarch64-linux-android23"
export AR=$TOOLCHAIN/bin/llvm-ar
export RANLIB=$TOOLCHAIN/bin/llvm-ranlib

cd /tmp
curl -LO https://github.com/sqlcipher/sqlcipher/archive/refs/tags/v4.6.0.tar.gz
tar xzf v4.6.0.tar.gz && cd sqlcipher-4.6.0

./configure --host=aarch64-linux-android --with-crypto-lib=openssl --enable-tempstore=yes --disable-tcl \
CFLAGS="-D__ANDROID_API__=23 -DSQLITE_HAS_CODEC -fPIC -I/tmp/openssl-arm64-v8a/include" \
CPPFLAGS="-I/tmp/openssl-arm64-v8a/include" \
LDFLAGS="-L/tmp/openssl-arm64-v8a/lib" \
LIBS="-lcrypto -lssl"

make -j8
$TOOLCHAIN/bin/llvm-ar rcs .libs/libsqlcipher.a .libs/sqlite3.o
```

**3. Copy libs to project:**
```bash
mkdir -p android/src/main/jni/sqlcipher-libs/arm64-v8a
cp /tmp/sqlcipher-4.6.0/.libs/libsqlcipher.a android/src/main/jni/sqlcipher-libs/arm64-v8a/
cp /tmp/openssl-arm64-v8a/lib/libcrypto.a android/src/main/jni/sqlcipher-libs/arm64-v8a/
cp /tmp/openssl-arm64-v8a/lib/libssl.a android/src/main/jni/sqlcipher-libs/arm64-v8a/
```

### cargo-ndk Version

**Critical:** Must use cargo-ndk v3.5.4. Version 4.x has breaking changes with `--no-strip` flag that uniffi-bindgen-react-native uses.

```bash
cargo install cargo-ndk --version 3.5.4
```

---

## API Reference
Expand Down
6 changes: 5 additions & 1 deletion absurder-sql-mobile/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ android {
}
}
ndk {
abiFilters "arm64-v8a", "armeabi-v7a", "x86", "x86_64"
abiFilters "arm64-v8a"
}
}

Expand All @@ -103,6 +103,10 @@ android {
disable "GradleCompatible"
}

packagingOptions {
pickFirst '**/*.so'
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.facebook.react.bridge.*

/**
* Custom initialization module for AbsurderSQL on Android.
*
*
* This module is NOT generated and will not be overwritten.
* It provides platform-specific setup that must happen before
* any database operations.
Expand All @@ -14,33 +14,26 @@ class AbsurderSqlInitializer(reactContext: ReactApplicationContext)

override fun getName() = "AbsurderSqlInitializer"

// External JNI function defined in cpp-adapter.cpp
external fun nativeSetDataDirectory(path: String): Boolean

companion object {
init {
// Load the same native library as the main module
System.loadLibrary("absurder-sql")
}
}

/**
* Initialize Android-specific paths for Rust database code.
* Must be called before any database operations.
* Get the app's files directory path for database storage.
* Returns the absolute path to the app's internal files directory.
*/
@ReactMethod
fun initialize(promise: Promise) {
fun getDataDirectory(promise: Promise) {
try {
val filesDir = reactApplicationContext.filesDir.absolutePath
val success = nativeSetDataDirectory(filesDir)

if (success) {
promise.resolve(null)
} else {
promise.reject("INIT_ERROR", "Failed to set Android data directory")
}
promise.resolve(filesDir)
} catch (e: Exception) {
promise.reject("INIT_ERROR", "Error during initialization: ${e.message}", e)
promise.reject("DIR_ERROR", "Failed to get data directory: ${e.message}", e)
}
}

/**
* Initialize is now a no-op since we handle path resolution in TypeScript.
* Kept for backwards compatibility.
*/
@ReactMethod
fun initialize(promise: Promise) {
promise.resolve(null)
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Generated by uniffi-bindgen-react-native
// Custom modifications - DO NOT REGENERATE (added to noOverwrite in ubrn.config.yaml)
package com.absurdersqlmobile

import com.facebook.react.TurboReactPackage
Expand All @@ -9,11 +9,15 @@ import com.facebook.react.module.model.ReactModuleInfoProvider
import java.util.HashMap

class AbsurderSqlPackage : TurboReactPackage() {
companion object {
const val INITIALIZER_NAME = "AbsurderSqlInitializer"
}

override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
return if (name == AbsurderSqlModule.NAME) {
AbsurderSqlModule(reactContext)
} else {
null
return when (name) {
AbsurderSqlModule.NAME -> AbsurderSqlModule(reactContext)
INITIALIZER_NAME -> AbsurderSqlInitializer(reactContext)
else -> null
}
}

Expand All @@ -28,6 +32,15 @@ class AbsurderSqlPackage : TurboReactPackage() {
false, // isCxxModule
true // isTurboModule
)
// AbsurderSqlInitializer is a bridge module (not turbo module)
moduleInfos[INITIALIZER_NAME] = ReactModuleInfo(
INITIALIZER_NAME,
INITIALIZER_NAME,
false, // canOverrideExistingModule
false, // needsEagerInit
false, // isCxxModule
false // isTurboModule - this is a bridge module
)
moduleInfos
}
}
Expand Down
8 changes: 5 additions & 3 deletions absurder-sql-mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"test": "jest",
"prepare": "tsc",
"ubrn:ios": "IPHONEOS_DEPLOYMENT_TARGET=13.0 ubrn build ios --and-generate",
"ubrn:android": "ubrn build android --and-generate && python3 scripts/fix_cpp_adapter.py",
"ubrn:android": "ubrn build android --and-generate",
"ubrn:bindings": "ubrn generate --all-platforms",
"ubrn:clean": "rm -rfv cpp/ android/generated/ ios/generated/ src/generated/",
"fix:cpp-adapter": "python3 scripts/fix_cpp_adapter.py"
Expand Down Expand Up @@ -45,6 +45,9 @@
"react": ">=17.0.0",
"react-native": ">=0.64.0"
},
"dependencies": {
"uniffi-bindgen-react-native": "^0.29.3-1"
},
"devDependencies": {
"@types/jest": "^29.5.0",
"@types/react": "19.2.2",
Expand All @@ -53,8 +56,7 @@
"react": "^19.2.0",
"react-native": "^0.82.1",
"ts-jest": "^29.1.0",
"typescript": "^5.0.0",
"uniffi-bindgen-react-native": "^0.29.3-1"
"typescript": "^5.0.0"
},
"files": [
"lib/",
Expand Down
6 changes: 3 additions & 3 deletions absurder-sql-mobile/react-native/.detoxrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ module.exports = {
'ios.debug': {
type: 'ios.app',
binaryPath: 'ios/build/Build/Products/Debug-iphonesimulator/AbsurderSQLTestApp.app',
build: 'xcodebuild -project ios/AbsurderSQLTestApp.xcodeproj -scheme AbsurderSQLTestApp -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build'
build: 'xcodebuild -workspace ios/AbsurderSQLTestApp.xcworkspace -scheme AbsurderSQLTestApp -configuration Debug -sdk iphonesimulator -arch arm64 -derivedDataPath ios/build'
},
'ios.release': {
type: 'ios.app',
binaryPath: 'ios/build/Build/Products/Release-iphonesimulator/AbsurderSQLTestApp.app',
build: 'xcodebuild -project ios/AbsurderSQLTestApp.xcodeproj -scheme AbsurderSQLTestApp -configuration Release -sdk iphonesimulator -derivedDataPath ios/build'
build: 'xcodebuild -workspace ios/AbsurderSQLTestApp.xcworkspace -scheme AbsurderSQLTestApp -configuration Release -sdk iphonesimulator -arch arm64 -derivedDataPath ios/build'
},
'android.debug': {
type: 'android.apk',
Expand All @@ -38,7 +38,7 @@ module.exports = {
simulator: {
type: 'ios.simulator',
device: {
type: 'iPhone 15'
type: 'iPhone 17 Pro'
}
},
attached: {
Expand Down
Loading
Loading