c215d68c81
- git mv docs/v2/*.md docs/ (11 planning documents) - Remove empty docs/v2/ directory - Update all references: AGENTS.md, CHANGELOG.md, README.md, ARCHITECTURE.md - Fix outdated repo tree in ARCHITECTURE.md to match actual structure
463 lines
16 KiB
Markdown
463 lines
16 KiB
Markdown
# Hostkeeper V2 — Architecture
|
|
|
|
> **Status**: V2 Planning Complete
|
|
> **Last Updated**: 2026-06-29
|
|
> **V1 (existing)**: CLI/TUI SSH/SFTP manager — frozen, no changes.
|
|
|
|
---
|
|
|
|
## 1. System Overview
|
|
|
|
Hostkeeper V2 is a Termius-like cross-platform SSH/SFTP management GUI. The architecture follows a **Go backend + Web UI + Electron/WebView** pattern — the same approach used by Termius, VS Code, Discord, Slack, and Figma.
|
|
|
|
```
|
|
┌──────────────────────────────────────────────────────┐
|
|
│ ELECTRON / WEBVIEW │
|
|
│ Native window, menu bar, tray icon, auto-updater │
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────┐ │
|
|
│ │ WEB UI (React + TypeScript) │ │
|
|
│ │ Host List │ Terminal │ SFTP │ Keys │ Settings │ │
|
|
│ │ ┌───────────────────────────────────────────┐ │ │
|
|
│ │ │ xterm.js (terminal) │ │ │
|
|
│ │ └───────────────────────────────────────────┘ │ │
|
|
│ └─────────────────────────────────────────────────┘ │
|
|
│ │ HTTP + WebSocket │
|
|
│ ┌─────────────────────────────────────────────────┐ │
|
|
│ │ GO BACKEND (GoFiber v2) │ │
|
|
│ │ REST API │ WebSocket │ SSH │ SFTP │ │
|
|
│ │ ┌───────────────────────────────────────────┐ │ │
|
|
│ │ │ pkg/ssh │ pkg/sftp │ pkg/crypto │ ... │ │ │
|
|
│ │ └───────────────────────────────────────────┘ │ │
|
|
│ └─────────────────────────────────────────────────┘ │
|
|
└──────────────────────────────────────────────────────┘
|
|
│ Distributed as:
|
|
├── .dmg (macOS)
|
|
├── .exe (Windows)
|
|
├── .AppImage (Linux)
|
|
└── .aab / .ipa (Android / iOS)
|
|
```
|
|
|
|
### Communication Flow
|
|
|
|
```
|
|
User interaction (keyboard, mouse)
|
|
│
|
|
▼
|
|
React component (event handler)
|
|
│
|
|
▼
|
|
API client (fetch / WebSocket)
|
|
│
|
|
▼
|
|
GoFiber HTTP server (localhost:randomPort)
|
|
│
|
|
├── REST handlers → pkg/storage → JSON files
|
|
└── WebSocket handlers → pkg/ssh → remote server
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Technology Stack
|
|
|
|
### Backend (Go)
|
|
|
|
| Component | Library | Purpose |
|
|
|-----------|---------|---------|
|
|
| HTTP server | `gofiber/fiber/v2` | REST API, static file serving |
|
|
| WebSocket | `gofiber/contrib/websocket` | Terminal streaming, real-time updates |
|
|
| WebSocket engine | `fasthttp/websocket` | Underlying WS protocol (fork of gorilla) |
|
|
| SSH client | `golang.org/x/crypto/ssh` | Remote SSH connections (reuse V1 pkg/ssh) |
|
|
| SFTP client | `github.com/pkg/sftp` | File transfer (reuse V1 pkg/sftp) |
|
|
| Crypto | `crypto/aes`, `crypto/cipher` | AES-256-GCM encryption (reuse V1 pkg/crypto) |
|
|
| Storage | Standard `os` + `encoding/json` | JSON file persistence (reuse V1 pkg/storage) |
|
|
| Config | `github.com/spf13/viper` | App configuration (reuse V1 pkg/config) |
|
|
| UUID | `github.com/google/uuid` | Unique IDs for entities |
|
|
| Routers | `github.com/gofiber/fiber/v2` | HTTP routing |
|
|
| Middleware | GoFiber built-in | CORS, compress, recover, logger |
|
|
|
|
### Frontend (React/TypeScript)
|
|
|
|
| Component | Library | Purpose |
|
|
|-----------|---------|---------|
|
|
| Framework | React 19 + TypeScript | UI components |
|
|
| Build tool | Vite | Fast development + production build |
|
|
| Terminal | xterm.js + addons | Terminal emulation (fit, web-links, search) |
|
|
| State | Zustand | Lightweight state management |
|
|
| Routing | React Router v7 | Screen navigation |
|
|
| Styling | TailwindCSS + CSS variables | Theming (dark/light/contrast) |
|
|
| Icons | Lucide React | Consistent icon set |
|
|
| Notifications | Sonner | Toast notifications |
|
|
|
|
### Desktop (Electron)
|
|
|
|
| Component | Library | Purpose |
|
|
|-----------|---------|---------|
|
|
| Shell | Electron 33 | Native desktop wrapper |
|
|
| Packaging | electron-builder | Build .dmg, .exe, .AppImage |
|
|
| Auto-update | electron-updater | In-app updates |
|
|
| Keychain | Node.js safeStorage | OS keychain integration |
|
|
| IPC | Electron IPC | Go backend <-> Electron communication |
|
|
|
|
### Mobile
|
|
|
|
| Component | Library | Purpose |
|
|
|-----------|---------|---------|
|
|
| Go library | gomobile bind | Compile Go to .aar/.xcframework |
|
|
| UI shell | React Native WebView | Display web UI on mobile |
|
|
| Native code | Java (Android) / Swift (iOS) | Minimal WebView wrapper |
|
|
|
|
---
|
|
|
|
## 3. Directory Structure
|
|
|
|
```
|
|
hostkeeper/
|
|
├── cmd/ # V1 CLI (FROZEN)
|
|
│ └── hostkeeper/
|
|
├── pkg/ # V1 shared packages (FROZEN)
|
|
│ ├── ssh/
|
|
│ ├── sftp/
|
|
│ ├── crypto/
|
|
│ ├── storage/
|
|
│ ├── config/
|
|
│ ├── knownhosts/
|
|
│ └── tui/
|
|
├── internal/ # V1 internal packages (FROZEN)
|
|
│ ├── models/
|
|
│ └── errors/
|
|
├── test/ # V1 tests (FROZEN)
|
|
│
|
|
├── app/ # V2 GUI (NEW)
|
|
│ ├── backend/ # Go HTTP server
|
|
│ │ ├── main.go # Entry point, fiber server setup
|
|
│ │ ├── go.mod # Go module for backend
|
|
│ │ ├── bridge/ # REST API handlers
|
|
│ │ │ ├── hosts.go
|
|
│ │ │ ├── keys.go
|
|
│ │ │ ├── snippets.go
|
|
│ │ │ ├── groups.go
|
|
│ │ │ ├── portforward.go
|
|
│ │ │ ├── sftp.go
|
|
│ │ │ ├── config.go
|
|
│ │ │ ├── vault.go
|
|
│ │ │ └── export.go
|
|
│ │ ├── ws/ # WebSocket handlers
|
|
│ │ │ ├── terminal.go # SSH terminal streaming
|
|
│ │ │ └── session.go # Session management
|
|
│ │ └── middleware/ # Custom middleware
|
|
│ │ └── middleware.go # (optional overrides)
|
|
│ │
|
|
│ ├── frontend/ # React web UI
|
|
│ │ ├── src/
|
|
│ │ │ ├── components/ # Reusable UI components
|
|
│ │ │ ├── screens/ # Page-level components
|
|
│ │ │ ├── hooks/ # Custom React hooks
|
|
│ │ │ ├── stores/ # Zustand state stores
|
|
│ │ │ ├── api/ # API client functions
|
|
│ │ │ └── styles/ # CSS, themes, variables
|
|
│ │ ├── index.html
|
|
│ │ ├── vite.config.ts
|
|
│ │ ├── tailwind.config.js
|
|
│ │ ├── tsconfig.json
|
|
│ │ └── package.json
|
|
│ │
|
|
│ └── electron/ # Electron shell
|
|
│ ├── main.ts # Main process
|
|
│ ├── preload.ts # Preload script (IPC bridge)
|
|
│ ├── build.ts # Build configuration
|
|
│ ├── package.json
|
|
│ └── electron-builder.yml
|
|
│
|
|
├── mobile/ # V2 Mobile (NEW)
|
|
│ ├── android/ # Android WebView app
|
|
│ │ ├── app/
|
|
│ │ ├── build.gradle
|
|
│ │ └── ...
|
|
│ ├── ios/ # iOS WKWebView app
|
|
│ │ ├── Hostkeeper/
|
|
│ │ ├── Hostkeeper.xcodeproj
|
|
│ │ └── ...
|
|
│ └── gomobile/ # Go mobile compilation
|
|
│ ├── build.sh
|
|
│ └── ...
|
|
│
|
|
├── scripts/ # Build scripts
|
|
│ ├── build.sh # Full build (Go + FE + Electron)
|
|
│ ├── build-go.sh # Go cross-compile
|
|
│ ├── build-fe.sh # Frontend build
|
|
│ ├── build-electron.sh # Electron package
|
|
│ └── build-mobile.sh # Mobile build
|
|
│
|
|
├── docs/ # Documentation (V2 planning)
|
|
│ ├── PROGRESS.md # Status tracker (read this first)
|
|
│ ├── ARCHITECTURE.md # This file
|
|
│ ├── API.md # REST API + WebSocket spec
|
|
│ ├── DATA_MODELS.md # Extended data models
|
|
│ ├── UI_COMPONENTS.md # React component tree
|
|
│ ├── UI_TERMIUS_REFERENCE.md # Termius visual reference
|
|
│ ├── SPRINT_PLAN.md # Sprint-by-sprint task breakdown
|
|
│ ├── BUILD_SYSTEM.md # Build pipeline
|
|
│ ├── PERFORMANCE.md # Performance & stability
|
|
│ └── TIMELINE.md # Project timeline
|
|
│
|
|
├── CHANGELOG.md
|
|
├── README.md
|
|
├── AGENTS.md # AI session checkpoint
|
|
├── v1/ # V1 CLI/TUI (FROZEN)
|
|
│ ├── cmd/hostkeeper/
|
|
│ ├── internal/
|
|
│ ├── pkg/
|
|
│ ├── test/
|
|
│ ├── docs/ # V1 docs
|
|
│ ├── go.mod
|
|
│ └── Makefile
|
|
├── template/ # React UI template (Lumina System)
|
|
└── app/ # V2 app (scaffolding not started)
|
|
├── backend/
|
|
├── frontend/
|
|
└── electron/
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Process Model
|
|
|
|
### Startup Sequence
|
|
|
|
```
|
|
1. User double-clicks Hostkeeper.app (Electron)
|
|
2. Electron main process starts
|
|
3. Electron spawns Go binary as child process
|
|
4. Go binary starts GoFiber server on localhost:randomPort
|
|
5. Go serves frontend dist (embedded in Electron)
|
|
6. Electron loads web UI from localhost:PORT
|
|
7. Web UI renders vault unlock screen
|
|
8. User enters password -> Go unlocks vault (decrypts data)
|
|
9. User sees host list screen -> ready to use
|
|
```
|
|
|
|
### Shutdown Sequence
|
|
|
|
```
|
|
1. User closes window / Cmd+Q
|
|
2. Electron sends SIGTERM to Go child process
|
|
3. Go receives signal -> starts graceful shutdown
|
|
4. Go disconnects all active SSH sessions
|
|
5. Go saves any pending state
|
|
6. Go closes fiber server
|
|
7. Go exits
|
|
8. Electron exits
|
|
```
|
|
|
|
### Development Mode
|
|
|
|
```
|
|
1. Terminal: cd app/backend && go run .
|
|
2. Terminal: cd app/frontend && npm run dev
|
|
3. Go server at localhost:8080
|
|
4. Frontend dev server at localhost:5173 (Vite HMR)
|
|
5. Frontend proxies API requests to localhost:8080
|
|
6. Open browser at localhost:5173
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Data Flow
|
|
|
|
### REST API Request Flow
|
|
|
|
```
|
|
React component
|
|
→ useQuery/useMutation (React Query or manual fetch)
|
|
→ API client (fetch wrapper)
|
|
→ HTTP request to GoFiber (localhost:PORT/api/...)
|
|
→ GoFiber router -> handler function
|
|
→ Handler calls pkg/storage (read/write JSON)
|
|
→ Response sent back through the chain
|
|
```
|
|
|
|
### Terminal WebSocket Flow
|
|
|
|
```
|
|
User opens terminal tab
|
|
→ React: XTermWrapper mounts
|
|
→ XTermWrapper opens WebSocket to ws://localhost:PORT/ws/terminal?host_id=xxx&cols=80&rows=24
|
|
→ GoFiber WebSocket handler accepts connection
|
|
→ Go: SSH client connects to remote host (reuse pkg/ssh)
|
|
→ SSH session created, PTY requested
|
|
→ SSH stdout goroutine -> reads SSH output -> c.WriteMessage(BinaryMessage, data)
|
|
-> xterm.js receives data -> renders in terminal
|
|
-> xterm.js onKey -> c.WriteMessage(BinaryMessage, input)
|
|
-> Go receives input -> session.Stdin.Write(input) -> SSH remote
|
|
```
|
|
|
|
### SFTP File Transfer Flow
|
|
|
|
```
|
|
User drags file to remote pane
|
|
→ React: FileList onDrop handler
|
|
→ API client: POST /api/sftp/upload (multipart/form-data)
|
|
→ Go: bridge/sftp.go receives upload
|
|
→ Go: pkg/sftp client writes to remote
|
|
→ Response: { "success": true }
|
|
→ React: refresh remote file list
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Security Architecture
|
|
|
|
### Encryption Model
|
|
|
|
| Layer | Method | Implementation |
|
|
|-------|--------|----------------|
|
|
| Data at rest | AES-256-GCM | `pkg/crypto/crypto.go` (reuse V1) |
|
|
| Key derivation | PBKDF2 (100k iterations) | `pkg/crypto/crypto.go` (reuse V1) |
|
|
| Password storage | SHA-256 hash | `pkg/config/config.go` (reuse V1) |
|
|
| Known hosts | TOFU verification | `pkg/knownhosts/knownhosts.go` (reuse V1) |
|
|
| Transport | localhost only | No TLS needed (same machine) |
|
|
| Export | AES-256-GCM with password | `pkg/storage/json_storage.go` (reuse V1) |
|
|
|
|
### Vault Flow
|
|
|
|
```
|
|
App start -> vault locked -> prompt password
|
|
-> PBKDF2 derive key from password
|
|
-> Try decrypt hosts.json
|
|
-> Success -> vault unlocked, data accessible
|
|
-> Fail -> wrong password, show error
|
|
-> Vault locked -> no data accessible
|
|
-> Auto-lock after inactivity timer
|
|
```
|
|
|
|
### OS Keychain Integration
|
|
|
|
| Platform | Library | Purpose |
|
|
|----------|---------|---------|
|
|
| macOS | `security` CLI or `go-keychain` | Store vault master key |
|
|
| Windows | `go-credential` | Store vault master key |
|
|
| Linux | `dbus` Secret Service | Store vault master key |
|
|
| Electron | `safeStorage` API | Store vault master key |
|
|
|
|
---
|
|
|
|
## 7. WebSocket Protocol
|
|
|
|
### Terminal WebSocket
|
|
|
|
**Connection**: `ws://localhost:PORT/ws/terminal?host_id=xxx&cols=80&rows=24`
|
|
|
|
**Message types** (binary frames):
|
|
|
|
| Direction | Type | Content |
|
|
|-----------|------|---------|
|
|
| Client → Server | Input | Raw keystroke bytes |
|
|
| Server → Client | Output | Terminal output bytes |
|
|
| Client → Server | Resize | JSON: `{ "type": "resize", "cols": 120, "rows": 40 }` |
|
|
| Server → Client | Status | JSON: `{ "type": "status", "state": "connected" }` |
|
|
| Server → Client | Error | JSON: `{ "type": "error", "message": "..." }` |
|
|
|
|
**Keepalive**: Client sends ping every 30s, server responds with pong.
|
|
|
|
---
|
|
|
|
## 8. Build System
|
|
|
|
### Development
|
|
|
|
```bash
|
|
# Terminal 1: Go backend
|
|
cd app/backend
|
|
go run .
|
|
|
|
# Terminal 2: Frontend with hot reload
|
|
cd app/frontend
|
|
npm run dev
|
|
|
|
# Browser opens at http://localhost:5173
|
|
```
|
|
|
|
### Production Build
|
|
|
|
```bash
|
|
# Full build
|
|
./scripts/build.sh
|
|
|
|
# Output:
|
|
# dist/hostkeeper-mac-arm64.dmg
|
|
# dist/hostkeeper-win-x64.exe
|
|
# dist/hostkeeper-linux-x64.AppImage
|
|
```
|
|
|
|
### Cross-Compilation Matrix
|
|
|
|
| Platform | GOOS | GOARCH | Output |
|
|
|----------|------|--------|--------|
|
|
| macOS ARM64 | darwin | arm64 | hostkeeper-mac-arm64 |
|
|
| macOS x64 | darwin | amd64 | hostkeeper-mac-x64 |
|
|
| Windows x64 | windows | amd64 | hostkeeper-win-x64.exe |
|
|
| Linux x64 | linux | amd64 | hostkeeper-linux-x64 |
|
|
| Linux ARM64 | linux | arm64 | hostkeeper-linux-arm64 |
|
|
| Android ARM64 | android | arm64 | hostkeeper-android.aar |
|
|
| iOS ARM64 | ios | arm64 | hostkeeper-ios.xcframework |
|
|
|
|
---
|
|
|
|
## 9. Error Handling
|
|
|
|
### Go Backend Errors
|
|
|
|
```go
|
|
// All API errors follow this format:
|
|
{
|
|
"error": "Human-readable error message",
|
|
"code": "MACHINE_READABLE_CODE",
|
|
"details": "Optional technical details"
|
|
}
|
|
|
|
// HTTP status codes:
|
|
// 200 - Success
|
|
// 201 - Created
|
|
// 400 - Bad Request (validation error)
|
|
// 401 - Unauthorized (vault locked)
|
|
// 404 - Not Found
|
|
// 409 - Conflict (duplicate)
|
|
// 500 - Internal Server Error
|
|
```
|
|
|
|
### Frontend Error Handling
|
|
|
|
- Network errors: automatic retry with exponential backoff (3 retries)
|
|
- API errors: toast notification with error message
|
|
- WebSocket errors: reconnect button in terminal, auto-reconnect 3 times
|
|
- Validation errors: inline field-level error messages
|
|
|
|
---
|
|
|
|
## 10. Testing Strategy
|
|
|
|
| Layer | Tool | Coverage Target |
|
|
|-------|------|----------------|
|
|
| Go unit tests | `go test` | 80%+ |
|
|
| Go integration tests | `go test -tags=integration` | Key flows |
|
|
| React unit tests | Vitest + React Testing Library | 70%+ |
|
|
| React E2E tests | Playwright | Critical flows |
|
|
| Manual testing | All platforms | Every sprint |
|
|
|
|
### Test Commands
|
|
|
|
```bash
|
|
# Go backend tests
|
|
cd app/backend && go test ./...
|
|
|
|
# Frontend tests
|
|
cd app/frontend && npm test
|
|
|
|
# Full test suite
|
|
make test
|
|
|
|
# Race condition check
|
|
go test -race ./app/backend/...
|
|
```
|