← All Projects
Hidden Photos

Hidden Photos

Privacy-focused mobile vault for securely storing photos and videos on-device with biometric authentication and intruder detection

react-native
expo
typescript
redux
sqlite
security
biometrics
camera
file-system
animation
mobile
theme-system
app-hero

Problem

Mobile users wanted a private vault for sensitive photos and videos that required no cloud account, no subscription, and no trust in a third-party server. Existing options either synced to the cloud by default or had weak PIN-only auth with no deterrent against repeated guessing.

Stack Decisions

No cloud sync by design

All media lives in a structured on-device directory tree with UUID-named originals and thumbnails. Removing the sync layer entirely eliminated the attack surface and the privacy risk — there is no server to breach.

Salted SHA-256 PIN + Expo SecureStore

The PIN is hashed with 1,000 iterations of SHA-256 and a random salt stored in the device Secure Enclave. Biometric (Face ID / Fingerprint) is layered on top as a convenience fallback, not as the primary credential.

SQLite with WAL mode

Write-Ahead Logging allows concurrent reads during thumbnail-heavy gallery scrolling without locking the database. Foreign keys with cascading deletes keep albums, images, and on-disk files in sync atomically.

Challenges

01

Silent intruder selfie capture

Capturing a photo after 3 failed PIN attempts required a camera view rendered at 1×1px — invisible to the user but still active enough for the OS to take a shot. Making this work reliably across Android camera permission models took several iterations.

02

Orphaned files on cascading delete

Deleting an album in SQLite cascades to its image rows, but the on-disk files are a separate concern. A two-phase delete — collect file paths first, run the DB delete, then unlink files — ensured disk and database never diverged even if the app was killed mid-operation.

03

Thumbnail generation without UI blocking

Generating 300px compressed previews via expo-image-manipulator on import is CPU-bound. Batching imports and yielding between frames with requestAnimationFrame kept the import progress UI responsive during large photo set imports.

Outcome

Ships a genuine security feature set — intruder log with selfie capture, biometric auth, batch album operations — with zero network access. The local-only architecture meant no GDPR surface area and no backend to maintain, keeping ongoing costs at zero while delivering stronger privacy guarantees than cloud-based alternatives.