From 850e8e492edc4149642f48ade90d7e61227befbc Mon Sep 17 00:00:00 2001 From: Vantz Stockwell Date: Tue, 17 Mar 2026 05:39:35 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20spec=20hardening=20=E2=80=94=20Wails=20?= =?UTF-8?q?fallback=20plans,=20crash=20recovery,=20resource=20mgmt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Wails v3: defined Plan A/B/C for multi-window with Phase 1 spike - Crash recovery: workspace snapshot persistence + restore-on-restart - Resource management: session limits, memory budgets, idle handling - DPAPI: designed-for upgrade path in vault (post-MVP) - RDP frame transport spike moved to Phase 1 (don't discover late) Co-Authored-By: Claude Opus 4.6 (1M context) --- .../specs/2026-03-17-wraith-desktop-design.md | 74 +++++++++++++++++-- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/docs/superpowers/specs/2026-03-17-wraith-desktop-design.md b/docs/superpowers/specs/2026-03-17-wraith-desktop-design.md index 05a9e97..6207710 100644 --- a/docs/superpowers/specs/2026-03-17-wraith-desktop-design.md +++ b/docs/superpowers/specs/2026-03-17-wraith-desktop-design.md @@ -113,7 +113,15 @@ A Windows desktop application that replaces MobaXTerm. Multi-tabbed SSH terminal ### Key Architectural Decisions -**Sessions ≠ Windows.** SSH and RDP sessions live as objects in the Go Session Manager. The frontend is a view. Detaching a tab spawns a new Wails window pointing at the same backend session. Re-attaching destroys the window and re-renders the session in the original tab. The session itself never drops. This depends on Wails v3's `application.NewWebviewWindow()` API, which is in alpha — this is an acknowledged risk area. If the multi-window API proves unstable, the fallback is to open sessions in the OS default browser via Wails v3's server mode. +**Sessions ≠ Windows.** SSH and RDP sessions live as objects in the Go Session Manager. The frontend is a view. Detaching a tab spawns a new Wails window pointing at the same backend session. Re-attaching destroys the window and re-renders the session in the original tab. The session itself never drops. + +**Wails v3 multi-window risk mitigation:** This is the project's biggest technical risk. The detach/reattach model depends on Wails v3's alpha `application.NewWebviewWindow()` API. Three fallback plans, validated in priority order during Phase 1: + +- **Plan A (target):** Wails v3 `NewWebviewWindow()` — true native multi-window. Spike this in Phase 1 with a minimal two-window prototype before committing. +- **Plan B:** Single Wails window with internal "floating panel" detach — session renders in a draggable, resizable overlay within the main window. Not true OS windows, but close enough. No external dependency. +- **Plan C:** Wails v3 server mode — detached sessions open in the default browser at `localhost:{port}/session/{id}`. Functional but breaks the native feel. + +If Plan A fails, we fall to Plan B (which is entirely within our control). Plan C is the emergency fallback. **This must be validated in Phase 1, not discovered in Phase 4.** **Single binary + DLL.** No Docker, no sidecar processes. SQLite is embedded (pure Go driver). FreeRDP3 is the only external dependency, loaded dynamically via `purego`. @@ -600,6 +608,16 @@ The `v1:` version prefix enables future key rotation without re-encrypting all s | Key length | 32 bytes (256-bit) | AES-256 key size | | Salt | 32 bytes, random | Unique per installation | +### Future: Windows DPAPI Integration (Post-MVP) + +The current vault is secure and portable (works on any Windows machine, backup the `.db` file and go). Post-MVP, an optional DPAPI layer could wrap the derived AES key with Windows Data Protection API, tying the vault to the current Windows user account. This would enable: + +- Transparent unlock when logged into Windows (no master password prompt) +- Hardware-backed key protection on machines with TPM +- Enterprise trust (DPAPI is a known quantity for IT departments) + +Implementation: the Argon2id-derived key gets wrapped with `CryptProtectData()` and stored. On unlock, DPAPI unwraps the key. Master password remains the fallback for portability (moving the database to another machine). This is designed-for but not built in MVP — the `v1:` encryption prefix enables adding a `v2:` scheme without re-encrypting existing data. + --- ## 9. MobaXTerm Importer @@ -849,6 +867,52 @@ Plugins are compiled into the binary (not runtime-loaded). Community developers **Sensitive data in logs:** Never log passwords, private keys, or decrypted credentials. Log only: connection IDs, hostnames, session IDs, error types. +### Crash Recovery + Workspace Restore + +When the app crashes, the system reboots, or Wails dies, SSH/RDP sessions are gone — there's no way to recover a dropped TCP connection. But the **workspace layout** can be restored. + +**Workspace snapshots:** The Session Manager periodically writes a workspace snapshot to SQLite (every 30 seconds and on clean shutdown): + +```json +{ + "tabs": [ + {"connectionId": 1, "protocol": "ssh", "position": 0, "detached": false}, + {"connectionId": 5, "protocol": "rdp", "position": 1, "detached": false}, + {"connectionId": 3, "protocol": "ssh", "position": 2, "detached": true, "windowBounds": {...}} + ], + "sidebarWidth": 240, + "sidebarMode": "connections", + "activeTab": 0 +} +``` + +**On restart after crash:** +1. Detect unclean shutdown (snapshot exists but no `clean_shutdown` flag) +2. Show: "Wraith closed unexpectedly. Restore previous workspace? [Restore] [Start Fresh]" +3. If Restore: recreate tab layout, attempt to reconnect each session +4. Tabs that fail to reconnect show "Connection lost — [Retry] [Close]" + +Users care about continuity more than perfection. Even if every session dies, restoring the layout and offering one-click reconnect is a massive UX win. + +### Resource Management + +With 20+ SSH sessions and multiple RDP sessions, resource awareness is critical: + +**Memory budget:** Each SSH session costs ~2-5MB (PTY buffer + SFTP client). Each RDP session costs ~8-12MB (pixel buffer at 1080p). Target: stable at 20 SSH + 3 RDP (~100-120MB total backend memory). + +**Session limits:** +- Default max: 32 concurrent sessions (SSH + RDP combined) +- Configurable via settings +- When limit reached: "Maximum sessions reached. Close a session to open a new one." + +**Inactive session handling:** +- Sessions idle for 30+ minutes get a subtle "idle" indicator on the tab (dimmed text) +- SSH keepalive (`ServerAliveInterval` equivalent) prevents server-side timeouts — configurable per connection via `options.keepAliveInterval` (default: 60 seconds) +- No automatic session suspension — users control their sessions explicitly +- SFTP idle connections are closed after 10 minutes of inactivity and silently reopened on next file operation + +**Monitoring:** Expose a "Sessions" panel in Settings showing per-session memory usage, connection duration, and idle time. Simple table, not a dashboard. + --- ## 14. Licensing + Open Source @@ -888,7 +952,7 @@ On first launch: | Phase | Deliverables | |---|---| -| **1: Foundation** | Wails v3 scaffold, SQLite schema + migrations, vault service (master password, Argon2id, AES-256-GCM), connection CRUD, group tree, Vue 3 shell with sidebar + tab container, dark theme, Naive UI integration, plugin interface definitions, README.md, license audit | -| **2: SSH + SFTP** | SSH service (x/crypto/ssh), PTY + shell, xterm.js terminal rendering, multi-tab sessions, SFTP sidebar (pkg/sftp), file tree, upload/download, CWD following (OSC 7), CodeMirror 6 editor in separate window | -| **3: RDP** | FreeRDP3 purego bindings, pixel buffer, canvas rendering, mouse/keyboard input mapping, clipboard sync, connection options (color depth, security, resolution) | -| **4: Polish** | Command palette, tab detach/reattach, terminal theming (built-in + custom), MobaXTerm importer, tab badges, session context awareness, quick connect, host key management UI, settings page, NSIS installer | +| **1: Foundation** | Wails v3 scaffold (including multi-window spike — validate Plan A/B/C), SQLite schema + migrations (WAL mode), vault service (master password, Argon2id, AES-256-GCM), connection CRUD, group tree, Vue 3 shell with sidebar + tab container, dark theme, Naive UI integration, plugin interface definitions, README.md, license audit, **RDP frame transport spike** (benchmark HTTP vs base64 with a test canvas — don't wait until Phase 3) | +| **2: SSH + SFTP** | SSH service (x/crypto/ssh), PTY + shell, xterm.js terminal rendering, multi-tab sessions, SFTP sidebar (pkg/sftp), file tree, upload/download, CWD following (OSC 7), CodeMirror 6 editor in separate window, workspace snapshot persistence | +| **3: RDP** | FreeRDP3 purego bindings, pixel buffer, canvas rendering (using proven transport from Phase 1 spike), mouse/keyboard input mapping (including scancode table + system key pass-through), clipboard sync, connection options | +| **4: Polish** | Command palette, tab detach/reattach, terminal theming (built-in + custom), MobaXTerm importer (with first-run detection), tab badges, session context awareness, quick connect, host key management UI, settings page, crash recovery / workspace restore, resource management panel, NSIS installer |