docs: spec hardening — Wails fallback plans, crash recovery, resource mgmt

- 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) <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell 2026-03-17 05:39:35 -04:00
parent 587b5396fd
commit 850e8e492e

View File

@ -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 |