Entry 59. Mobile Maturity & Cryptographic Hygiene: Resolving Post-Sync Key Decryption & Refining the Android Experience
⏱️ Timestamp: October 1, 2026, 15:00 — 17:30 (~2.5 hours)1. Motivation: Field Testing on Mobile Hardware
I’ll be honest: I don’t spend my days memorizing the syntax of low-level cryptographic byte streams in Dart. My role as an architect is to keep sight of the bigger picture: how data originates on desktop, how it gets packaged into an encrypted payload, how it travels across the wire, and what happens when an engineer taps “Connect” from a smartphone while commuting or on-site.
Desktop vault synchronization worked like a charm: master password derivation, AES-GCM encryption, clean transfer. But once we deployed the build to an actual Android phone and pulled the synced vault database, anomalies surfaced immediately.
Hosts that connected effortlessly on the laptop refused connections on mobile: the terminal dropped before authentication with a generic SSHAuthAbortError. Worse, hosts with strict security policies began temporarily blacklisting the mobile device. And navigating to settings on a 5-inch screen revealed a bloated Gemini AI configuration card — a feature engineered for desktop workflows that had no place on mobile.
It became obvious that the mobile client required a disciplined overhaul and pruning of unnecessary overhead.
2. Technical Findings: Empty Buffers, Aggressive Pings & Host Key Duplicates
Our systematic investigation along the data pipeline uncovered four distinct defects:
-
The Cryptographic Trap in
SyncManager:
When a host is configured with password authentication, itsencryptedPrivateKeyfield in SQLite is empty (Uint8List(0)). During export serialization, the sync loop attempted to pass this empty byte buffer into AES-GCM decryption. The crypto engine threw anArgumentError. Because of an overarchingtry-catchblock, the exception halted the entire iteration: private key handling crashed, and the subsequent export of the server password (encryptedPassphrase) was silently bypassed! The passphrase remained encrypted under the desktop’s local master key instead of the sync key, rendering it undecryptable on Android (password = null). -
Silent Failure in the Connection Controller:
WhenSessionConnectControllerreceivedpassword == null, instead of surfacing an authentication error to the user, it attempted to establish an SSH session with empty credentials. The remote server immediately dropped the socket, leaving the engineer bewildered. -
Socket Ping Aggression vs. Fail2ban:
The backgroundPingMonitorNotifierwas firing raw TCP socket connects to port 22 every 15 seconds for every saved host. On hardened nodes protected by Fail2ban or connection rate limiters, repeatedly opening and resetting TCP connections without completing the SSH banner exchange mimics a port scan. The client was inadvertently triggering its own ban. -
Crash Risk on Duplicate
known_hosts:
InfindKnownHost, query resolution concluded withgetSingleOrNull(). If synchronization ever created duplicate entries for a single host identifier, the query crashed with an unhandledStateError: Too many elements.
3. Engineering Decisions: Systemic Hardening & Touch Ergonomics
We translated our findings into actionable engineering requirements:
- Decoupled Cryptographic Pipeline:
InSyncManager, private key decryption is now strictly conditioned onk.encryptedPrivateKey.isNotEmpty. Passphrase export is fully isolated. Memory buffers (rawPassBytes) are explicitly zeroed out viafillRange(0, length, 0)infinallyblocks. - Safe Host Key Lookup:
Added..limit(1)toknown_host_repository.dartto guarantee fail-safe resolution even in the presence of accidental database duplicates. - Transparent Diagnostic Feedback:
SessionConnectControllernow immediately throws an explanatory exception when decryption fails, directing the user to re-enter credentials or refresh their sync session. - Polite Network Profile:
The aggressive 15-second background ping timer was removed on Android. The client performs a single gentle latency probe on startup, paired with a user-initiated native Pull-to-refresh (RefreshIndicator) gesture in the hosts list. - Pruning Desktop Bloat:
The entire AI and Gemini configuration card was cleanly hidden on mobile form factors (SettingsScreen), preserving interface speed and battery life. - Handheld Ergonomics:
- Added horizontal environment (
PROD,STAGE,DEV) and folder filter chips inMobileHostsView. - Expanded
HostFormDialogto responsive full width on mobile screens for comfortable thumb typing. - Enhanced
MobileAccessoryBarwith vital Unix keys (:,_,$,&,PgUp,PgDn,ENTER) and adjacent navigation arrows. - Streamlined terminal header height in landscape mode down to 36dp to maximize vertical screen real estate for terminal output.
- Added horizontal environment (
4. Summary & Verification
- Resolved and verified issue
BUG-051. - Post-sync SSH connections on Android operate with 100% stability.
- Mobile UI stripped of desktop-only settings and tuned for single-handed touch usage.
- Built and validated release candidate APK
app-release.apk(68.8 MB). - Static analyzer reports 0 warnings, and 100% of monorepo unit tests pass cleanly (63
storage_vault, 150terminal_ui, 61shellit).