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:
parent
587b5396fd
commit
850e8e492e
@ -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 |
|
||||
|
||||
Loading…
Reference in New Issue
Block a user