# Vault native preview

**0.3.3 / build 6 security hardening:** extension filling now requires a matching HTTPS page and HTTPS form destination. Wi-Fi hosting authenticates uploads before reading their bodies, caps control requests and concurrent transfers, and checks locks/revocation again after asynchronous reads. The desktop client rejects non-success HTTP responses. TLS 1.3 certificate pinning and explicit main-device approval remain required. Older downloads do not include these fixes.

**0.3.2 / build 5:** restores the original empty dark-green outline hexagon supplied as the brand reference. The app, extension, favicon, installer and launch icons share this mark. This branding update preserves the vault format and Android signing identity. Earlier archives retain their original artwork.

The 0.3 web app, developer extensions, native source, macOS DMGs, Windows installer and Android APK include private workspace pages and project boards. The Android App Bundle (AAB), version 0.3.3 / build 6, is prepared for Play Console upload. It is not a public store release. [Workspace storage and security](https://vault.maneuler.com/ARCHITECTURE.md) documents the new data and locking behavior.

Vault stores passwords, usernames, cards and notes in a local KDBX4 file. New vaults use AES-256 and Argon2id (64 MiB, three iterations). The master passphrase is never sent through the native sync bridge or stored in device pairing settings. There is no hosted vault database, telemetry, recovery service or public relay.

## Pair and sync

1. Install the desktop app on the main Mac or Windows computer. Create or import a vault and unlock it.
2. Open **Trusted devices → Start Wi-Fi sync → Create 5-minute invitation**. Keep the main app open, unlocked and in the foreground.
3. On a second desktop or Android app, open **Pair a device**. Paste the complete invitation and name the device. A QR contains the same invitation text; this version does not include an in-app scanner.
4. Compare the six-digit code on both devices. On the main device, choose **Codes match · Approve**. Check approval on the second device.
5. Choose **Fetch main copy**, enter its master passphrase locally, review changes, and accept the complete copy.
6. After editing on the second device, choose **Send my copy for review**. The main user decrypts and reviews that update before accepting it. Fetch the accepted main copy again on the second device.

This is deliberate, user-driven snapshot synchronization. It is not continuous background sync or automatic field merging. The review lists added, changed and removed visible items. It does not show individual secret values, attachment differences, recycle-bin contents or all KDBX metadata. Accepting replaces the complete encrypted database. A stale base revision is rejected. If copies diverge, export both first, choose which complete copy to use, and reconcile desired items manually. No silent last-writer-wins promise is made.

Before a sync/import replacement, native apps preserve the previous encrypted file. **Trusted devices → View local backups** opens the last ten recovery copies for review and restoration, including while the vault is locked. Each backup retains its original passphrase. Regular edits are not a substitute for an exported backup; browser and extension storage do not have these native recovery copies.

## Network and approval boundary

The main app binds HTTPS port 49261 only to an active private IPv4 Wi-Fi interface. It checks its current address and netmask and admits only the same private subnet (/16–/30); localhost, public addresses, hostnames, broadcasts, browser Origin requests and redirects are refused. The invitation pins the exact SHA-256 TLS certificate. Node clients authenticate that pin and certificate validity before releasing HTTP data. Android uses its active Wi-Fi Network and a pinned certificate trust manager. iOS source uses Network.framework with a required Wi-Fi interface and pinned TLS. TLS 1.3 protects the transfer in addition to KDBX encryption.

The invitation is random, single-use and expires in five minutes. Approval creates a revocable per-device bearer credential; only its hash is persisted on the main device. Device names and codes are visible to the main user. Updates expire after ten minutes. HTTP bodies, vault size, peers and pending updates are bounded. Native file writes compare the current ciphertext revision, use atomic replacement, and preserve the old encrypted file for sync/import operations.

The checks establish a local interface/subnet policy, not physical proximity or proof of a particular SSID. An approved modified client, network bridge, compromised router, VPN or OS can undermine claims about where a peer physically is. Guest Wi-Fi client isolation may block otherwise valid devices. Changing the main device's IP can require pairing again. Locking, leaving the desktop window, OS lock and suspend stop its listener and pending invitations; trusted approvals persist until revoked. No router port forwarding or public tunnel should be configured for this service.

## Native storage and release status

- macOS/Windows: encrypted KDBX file under the application's user data directory; device identity/credentials protected through Electron safeStorage. OS-protected encryption must be available. The renderer has no Node integration, denied permissions, a network-blocking request filter and a restrictive CSP. Clipboard values clear after 30 seconds if unchanged.
- Android 10+: enable a device PIN/password/pattern before pairing. On Android 12–14, removing the screen lock invalidates these pairing keys; re-pair after restoring a secure lock. App-private no-backup KDBX files; pairing settings encrypted with an Android Keystore AES-GCM key, usable only while the device is unlocked. Cloud backup is disabled, FLAG_SECURE blocks normal screenshots/recents capture, and clipboard contents are marked sensitive. The release APK is signed with WebView debugging disabled, not a debug APK. Android app signing identifies build continuity; it is not a Play Store review or security audit.
- iOS 15+: source prepared for device-only Keychain pairing settings, complete file protection, backup exclusion, a privacy cover on backgrounding, and local-only expiring clipboard. Not compiled or run yet: this machine's Xcode license has not been accepted and no Apple signing identity is installed. There is no downloadable IPA or live TestFlight/App Store release.
- macOS DMGs and Windows installer are unsigned preview builds. Apple notarization, Windows code signing, Windows runtime testing and Intel Mac runtime testing remain pending.
- Browser, CLI and extensions still use explicit encrypted-file import/export. They do not host or join the native Wi-Fi service.

No independent security audit has been performed. A compromised OS, malicious extension, vulnerable renderer, weak master passphrase, clipboard monitor, unlocked session or stolen backup can still expose data. JavaScript cannot guarantee memory erasure. This is not a claim of security unless only the OS is compromised. Use sample data while evaluating the preview.

## Download publication

The live download page is the source of truth for available releases. As of September 18, 2026, the web app, CLI, developer extensions and native source are published. The macOS DMGs, Windows installer and signed Android APK are built locally, but their public downloads are pending. Firebase rejected both the complete release and a DMG-only native release on this project's Spark plan. See [Firebase's executable-file hosting restriction](https://firebase.google.com/docs/hosting/faq-and-troubleshooting).

`npm run deploy:vault` checks billing on the existing `maneuler-com` project. On Spark it removes executable installers from deployment output, labels their publication pending, and regenerates checksums for available archives. On Blaze it includes the four current native installers after checking file formats. An authentication or billing-check error stops preparation rather than guessing the plan. Original installers remain in `apps/vault/public/downloads` and the release/build directories. No installer is renamed or wrapped to bypass Firebase's restriction.

After the owner enables Blaze billing, run `npm run collect:vault:installers` followed by `npm run deploy:vault` to publish the actual DMG, EXE and APK files. Local packaging can be checked without authentication with `npm run build:vault && npm run prepare:vault:hosting -- --blaze`; this does not upgrade billing or publish anything. GitHub Releases is another distribution option, requiring a signed-in publishing account. No billing plan has been changed by these scripts.

## Build

From the repository root (Node 22.16+):

```sh
npm ci
npm run build:vault
node scripts/build-vault-platforms.mjs
node scripts/package-vault-desktop.mjs
node scripts/build-vault-mobile.mjs
node scripts/package-vault-android.mjs
node scripts/collect-vault-installers.mjs
```

Android needs Java 21 and SDK 36. The Android packaging script uses `.tooling/jdk21/Contents/Home` and the standard macOS SDK path (or ANDROID_HOME). It creates a local preview signing key on first use under `.local/vault-signing` with owner-only permissions. Preserve that key and password securely to sign compatible updates. Never publish or commit them. Versioned installers and the AAB are also copied to `apps/vault/releases`. The collector writes release hashes and copies the four end-user installers into `public/downloads`; the AAB stays outside public downloads. A partial signing-key/password pair causes a failure, so an update identity cannot be silently replaced. The AAB is not directly installable.

iOS requires Xcode 26+ and acceptance of Apple's license by the account owner. Open `apps/vault/mobile/ios/App/App.xcodeproj`, choose your signing team, then build a simulator or archive for a registered device/TestFlight. `node scripts/build-vault-mobile.mjs` bundles the existing offline UI and updates Capacitor's native dependencies. Run `node scripts/package-vault-ios.mjs --simulator` for a simulator build. For a signed archive, configure the enrolled team in Xcode and set `VAULT_APPLE_TEAM_ID`, then use `--archive` and `--export`. The scripts do not accept licenses, enroll accounts, change billing or submit compliance declarations. Store submission and privacy disclosures still require review by the publisher. See `docs/vault-store-release.md` and `apps/vault/store` for the prepared listing, review instructions and remaining release gates.

Useful upstream references: [Capacitor native iOS code](https://capacitorjs.com/docs/ios/custom-code), [Electron safeStorage](https://www.electronjs.org/docs/latest/api/safe-storage), [Android security guidance](https://developer.android.com/privacy-and-security/security-tips), [Apple device-only Keychain accessibility](https://developer.apple.com/documentation/security/ksecattraccessiblewhenunlockedthisdeviceonly).
