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.
Fix HttpClient timeouts due to broken IPv6 connectivity
Details
Root cause: privstack.io has an AAAA (IPv6) record (2a02:4780:4:99db::1)
alongside its A (IPv4) record. .NET's SocketsHttpHandler uses Happy
Eyeballs (RFC 8305) which tries IPv6 first. On this Mac, IPv6
connectivity to privstack.io is completely broken (curl -6 hangs for 5s+)
while IPv4 works in <200ms. The HttpClient was hanging on the IPv6
attempt for the full Timeout duration (30s) before falling back.
Fix: Use SocketsHttpHandler.ConnectCallback to force IPv4 (AF_INET)
connections on all HttpClient instances that connect to privstack.io.
This bypasses the broken IPv6 path entirely.
Verified: curl -4 returns in 157ms, curl -6 times out at 5s.
Affected services:
- PrivStackApiClient (update checks, plugin registry, auth, OAuth)
- PluginInstallService (online check, plugin downloads)
- RegistryUpdateService (update downloads)
Filter Graph/Backlink data by active plugins and add memory diagnostics
Details
BacklinkService and GraphDataService legacy fallbacks loaded ALL entity
types from hardcoded lists regardless of plugin activation state. When
plugins are disabled, their IGraphDataProvider isn't available, so every
entity type fell into the legacy path — loading all data into memory and
rendering it in the Graph. Now both services build a set of entity types
from ActivePlugins and filter legacy loading against it.
Added detailed memory diagnostics:
- SystemMetricsService.GetDetailedMemoryDiagnostic() returns GC generation
sizes, native memory estimate, loaded assemblies, thread count, and
active plugin counts
- Dashboard memory card now shows "X GC heap · Y native" with full
breakdown on tooltip hover
- Startup logs a [MemoryDiag] line after deferred services complete,
showing the full breakdown for post-mortem analysis
Get notified about new releases