Files
HostKeeper/docs/v2/ARCHITECTURE.md
T
2026-07-07 11:53:16 +07:00

16 KiB

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/                       # V2 planning docs
│   │   ├── 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
│   ├── v1/                       # V1 docs (FROZEN, reference only)
│   │   ├── ARCHITECTURE.md
│   │   ├── TEST_PLAN.md
│   │   ├── INSTALLATION.md
│   │   └── USAGE.md
│   └── plans/                    # V1 design docs (FROZEN)
│
├── go.mod                        # V1 Go module
├── go.sum
├── Makefile
├── build.sh
├── CHANGELOG.md
├── PROJECT_STATE.md              # V1 project state
└── README.md

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

# 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

# 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

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

# 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/...