Add privstack-db crate and migrate BlobStore from DuckDB to SQLCipher
Details
Phase 1 (Foundation):
- Add rusqlite 0.31 with bundled-sqlcipher to workspace dependencies
- Create privstack-db crate: unified SQLCipher connection management with
open_db(), rekey(), checkpoint(), compact(), and helper utilities
- Implement cosine_similarity() as a registered Rust scalar function
(replaces DuckDB's list_cosine_similarity for RAG vector search)
- Add derive_sqlcipher_key() to privstack-crypto for raw hex key formatting
- Align privstack-sync to use workspace rusqlite (resolves libsqlite3-sys
link conflict between bundled and bundled-sqlcipher features)
- 24 tests for privstack-db (encryption, rekey, FTS5, cosine similarity)
Phase 2 (BlobStore Migration):
- Replace duckdb dependency with privstack-db in privstack-blobstore
- Remove all per-entity encryption from BlobStore: encryptor field,
open_with_encryptor(), encrypt/decrypt in store/read, re_encrypt_all(),
migrate_unencrypted(), blob_entity_id() -- at-rest encryption now handled
by SQLCipher at the database file level
- Remove BlobStoreError::Encryption variant (no longer needed)
- Remove privstack-crypto dependency from blobstore
- Translate schema: VARCHAR→TEXT, BIGINT→INTEGER
- Translate SQL: positional params now use ?N syntax
- BlobStore accepts shared Arc<Mutex<Connection>> via open_with_conn()
- 36 tests pass (removed 15 encryption-specific tests, all CRUD/error tests
preserved and passing)
Note: Build may fail at this commit due to privstack-ffi still referencing
removed BlobStore APIs (open_with_encryptor, re_encrypt_all,
migrate_unencrypted) -- will be resolved when FFI is migrated.
Fix UI freeze on Setup Wizard password step due to blocking async call
Details
CompleteSetup() called _biometricService.IsAvailableAsync().GetAwaiter().GetResult()
synchronously on the UI thread, blocking it while the Task.Run-wrapped ObjC
interop checked Touch ID availability. This caused the macOS beachball spinner
after the recovery mnemonic was generated.
Fix: Convert CompleteSetup() to async (CompleteSetupAsync), properly await the
biometric availability check, and update the GoNext() call site to await it.
Fix cloud workspace checkmark visibility and add delete capability in Setup Wizard
Details
The Setup Wizard Step 5 (Cloud Workspaces) had two issues:
1. Every workspace item showed a checkmark regardless of selection state — the
checkmark TextBlock had no IsVisible binding. Fixed by adding a MultiBinding
with EqualityConverter that compares the current item to SelectedCloudWorkspace.
2. Users could not delete unwanted cloud workspaces. Added a delete button (trash
icon) to each workspace item that calls the existing server-side
DELETE /api/cloud/workspaces/:workspaceId endpoint via a new
DeleteCloudWorkspaceAsync method on PrivStackApiClient. On success, the
workspace is removed from the list and selection resets to "Create New" if the
deleted workspace was selected.
Fix OAuth token exchange crash for perpetual license plans
Details
The server returns expires_in: 3153600000 (36500 days * 86400 sec/day)
for perpetual plans, which overflows Int32.MaxValue (2,147,483,647).
System.Text.Json throws FormatException when deserializing the value
into the int ExpiresIn property on OAuthTokenResponse.
Changed ExpiresIn from int to long on the C# side.
Get notified about new releases