a0c8483c0e
- New ARCHITECTURE_HTMX.md — comprehensive HTMX architecture doc - ARCHITECTURE.md — updated to point to HTMX version - UI_COMPONENTS.md — replaced React tree with HTMX partials - SPRINT_PLAN.md — all sprint tasks updated for HTMX - BUILD_SYSTEM.md — simplified (no Vite, no npm) - TIMELINE.md — 3.5 week HTMX timeline - PROGRESS.md — updated status + HTMX - AGENTS.md — updated checkpoint with HTMX decision
14 KiB
14 KiB
Hostkeeper V2 — Architecture
Status: Migrasi React → HTMX Last Updated: 2026-07-07 Dokumen arsitektur baru: ARCHITECTURE_HTMX.md V1 (existing): CLI/TUI SSH/SFTP manager — frozen, no changes.
⚠️ Keputusan Arsitektur
Hostkeeper V2 menggunakan GoFiber + HTMX, bukan React/SPA.
Keputusan ini diambil karena:
- Developer lebih nyaman dengan Go daripada JavaScript
- ~90% kode adalah Go + HTML template
- Hanya ~410 baris JS yang diperlukan (xterm.js, clipboard, toggle)
- Build lebih sederhana — tanpa Vite, npm, TypeScript
- Bisa berjalan di browser biasa tanpa Electron
Dokumen arsitektur lengkap ada di ARCHITECTURE_HTMX.md.
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
# 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/...