# Hostkeeper V2 — Architecture (HTMX)
> **Status**: Approved — Menggantikan arsitektur React sebelumnya
> **Last Updated**: 2026-07-07
> **Keputusan**: React → GoFiber + HTMX + Alpine.js + minimal JS
---
## 1. Arsitektur Baru
Hostkeeper V2 menggunakan arsitektur **server-rendered HTML** dengan interaktivitas via HTMX, bukan SPA React.
```
┌────────────────────────────────────────────────────┐
│ Browser / Electron WebView │
│ ┌──────────────────────────────────────────────┐ │
│ │ HTMX (14kb) + Alpine.js (15kb) + xterm.js │ │
│ │ │ │
│ │ ● HTMX: semua interaksi (search, filter, │ │
│ │ modal, form, navigasi) │ │
│ │ ● Alpine.js: client state (toast, toggle, │ │
│ │ passphrase reveal, tab) │ │
│ │ ● xterm.js: terminal emulator (hanya │ │
│ │ halaman terminal) │ │
│ │ ● Vanilla JS: clipboard, toggle switch, │ │
│ │ strength calc │ │
│ └────────────────┬─────────────────────────────┘ │
│ │ hx-get/hx-post/hx-ws │
│ ┌────────────────▼─────────────────────────────┐ │
│ │ GoFiber v2 Backend (localhost:1947) │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────────────┐ │ │
│ │ │ HTML handlers │ │ WebSocket /xterm.js │ │ │
│ │ │ (Fiber + html│ │ (SSH I/O via WS) │ │ │
│ │ │ template) │ └──────────────────────┘ │ │
│ │ └──────┬───────┘ │ │
│ │ │ go.mod replace │ │
│ │ ┌────▼────┐ │ │
│ │ │ v1/pkg/ │ ssh, crypto, storage, config │ │
│ │ └─────────┘ │ │
│ └───────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
```
### Alur Request
```
1. User klik tombol "Filter Active"
│ hx-get="/hosts?filter=active"
│ hx-target="#host-list"
▼
2. GoFiber handler:
func (h *Handler) ListHosts(c fiber.Ctx) error {
hosts := h.store.Filter("active")
return c.Render("partials/host_list", fiber.Map{
"Hosts": hosts,
})
}
▼
3. Server return HTML partial
{{range .Hosts}}
...
{{end}}
▼
4. HTMX swap HTML ke DOM (tanpa reload)
```
---
## 2. Tech Stack
| Layer | Teknologi | Versi | Fungsi |
|-------|-----------|-------|--------|
| Backend | GoFiber v2 | v2.52+ | HTTP server, routing, middleware |
| Template | html/template | stdlib | Server-side HTML rendering |
| Frontend | HTMX | v2.0+ | HTML-over-HTTP interaktivitas |
| Client state | Alpine.js | v3.14+ | Modal, toast, toggle state |
| Terminal | xterm.js | v5.5+ | SSH terminal emulator |
| Styling | TailwindCSS v4 | v4.0+ | Utility-first CSS |
| CSS custom | Custom CSS | — | dot-grid, glassmorphism, keyframes |
| Icons | Lucide (inline SVG) | — | SVG icon system |
| Desktop | Electron (opsional) | v35+ | Browser wrapper |
| Mobile | WebView | — | Android/iOS shell |
---
## 3. Struktur Folder
```
app/backend/
├── main.go ← Entry point Fiber app
├── go.mod ← module git.tukangketik.id/swanadiva/hostkeeper/v2
├── go.sum
│
├── internal/
│ ├── handler/ ← HTTP handlers (render HTML + HTMX partials)
│ │ ├── dashboard.go GET/POST /hosts, /hosts/{id}
│ │ ├── terminal.go GET /terminal, WS /terminal/ws
│ │ ├── sftp.go GET /sftp/ls, POST /sftp/{action}
│ │ ├── snippets.go GET/POST /snippets
│ │ ├── keychain.go GET/POST /keys
│ │ ├── settings.go GET/POST /settings
│ │ ├── brief.go GET /brief (overlay)
│ │ └── health.go GET /api/health
│ │
│ ├── model/ ← Go structs (data models)
│ │ ├── host.go Host, HostFilter
│ │ ├── file.go FileItem
│ │ ├── snippet.go Snippet
│ │ ├── keychain.go KeychainItem
│ │ ├── device.go Device
│ │ ├── transfer.go BackgroundTransfer
│ │ └── config.go Config, Toggle
│ │
│ ├── template/ ← Template helpers/funcs
│ │ └── funcs.go activeClass, formatBytes, timeAgo, strengthBadge
│ │
│ └── middleware/
│ └── viewdata.go Inject .View, .ActiveNav, .User ke semua template
│
├── static/ ← Static assets (served via Fiber)
│ ├── css/
│ │ ├── output.css ← TailwindCSS v4 build output
│ │ └── custom.css ← dot-grid, glassmorphism, keyframes, scrollbar
│ ├── js/
│ │ ├── htmx.min.js ← HTMX (14kb)
│ │ ├── alpine.min.js ← Alpine.js (15kb)
│ │ ├── terminal.js ← xterm.js + WebSocket init
│ │ ├── clipboard.js ← navigator.clipboard.writeText()
│ │ ├── toggles.js ← Custom toggle switch click handler
│ │ └── utils.js ← passphrase reveal, strength calc, toast
│ ├── xterm/ ← xterm.js build files
│ └── img/ ← Images/icons
│
└── views/ ← Go HTML templates
├── layout.html ← Base layout (sidebar, header, scripts)
├── index.html ← Entry page (extends layout + content block)
│
└── partials/ ← HTMX-swappable partials
├── nav.html ← Sidebar navigation
├── dashboard.html ← Host page container
├── host_list.html ← Host card grid/list (HTMX swap target)
├── host_card.html ← Single host card
├── host_modal.html ← Add host modal form
├── terminal.html ← Terminal page (xterm.js container)
├── sftp.html ← Dual-pane file browser
├── sftp_pane.html ← Single file pane (for HTMX swap)
├── snippets.html ← Snippet page
├── snippet_grid.html ← Snippet cards (HTMX swap target)
├── snippet_modal.html ← Add snippet form
├── keychain.html ← Key page
├── key_grid.html ← Key cards (HTMX swap target)
├── key_modal.html ← Add key form
├── settings.html ← Settings form + toggles
├── brief.html ← Brief overlay panel
└── toast.html ← Toast notification
```
---
## 4. Komponen UI & State Management
### 4.1 Navigasi (HTMX)
Sidebar navigation menggunakan HTMX untuk swap halaman:
```html
{{svg "server"}}
Hosts
```
### 4.2 State Client (Alpine.js)
State yang butuh client-side reactivity:
```html
{{template "host_list" .}}
```
### 4.3 JS Murni (Vanilla)
```javascript
// clipboard.js — copy to clipboard
function copyToClipboard(text) {
navigator.clipboard.writeText(text);
// trigger toast via Alpine
document.querySelector('[x-data]').__x.$data.toast = 'Copied!';
document.querySelector('[x-data]').__x.$data.show = true;
}
// toggles.js — custom toggle switch
function toggleSwitch(el) {
const checked = el.getAttribute('aria-checked') === 'true';
el.setAttribute('aria-checked', !checked);
fetch(`/settings/toggle?key=${el.dataset.key}&val=${!checked}`, { method: 'POST' });
}
```
---
## 5. Data Models (Go Structs)
Semua data model di `internal/model/`, mapping dari template React `types.ts`:
```go
// model/host.go
type Host struct {
ID string `json:"id"`
Name string `json:"name"`
IP string `json:"ip"`
OS string `json:"os"`
Provider string `json:"provider"`
Status string `json:"status"` // "active" | "offline"
LastSeen string `json:"lastSeen"`
Type string `json:"type"` // "api" | "db" | "edge" | "web" | "desktop" | "server"
}
// model/snippet.go
type Snippet struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Code string `json:"code"`
Tags []string `json:"tags"`
Collection string `json:"collection"` // "favorites" | "recent" | "all"
UpdatedAt string `json:"updatedAt"`
Icon string `json:"icon"`
}
```
Lihat `docs/DATA_MODELS.md` untuk definisi lengkap.
---
## 6. HTMX Patterns yang Digunakan
| Pattern | Contoh | Keterangan |
|---------|--------|------------|
| Search | `hx-get="/hosts?q=..." hx-trigger="keyup delay:200ms" hx-target="#host-list"` | Debounced search |
| Filter | `hx-get="/hosts?filter=active" hx-target="#host-list"` | Tab filter |
| Modal | Alpine `x-show` + `hx-post` | Form dalam modal |
| Infinite scroll | `hx-get="/hosts?page=2" hx-trigger="revealed" hx-target="#host-list" hx-swap="beforeend"` | Load more |
| Delete | `hx-delete="/hosts/123" hx-target="#host-card-123" hx-swap="delete" hx-confirm="Yakin?"` | Confirm + delete |
| Toast | Alpine state + `hx-trigger="htmx:afterRequest"` dari response header `HX-Trigger: showToast` | Notifikasi |
| Tab | Alpine `x-data` + `x-show` | Panel tab |
---
## 7. Keuntungan HTMX Dibanding React
| Aspek | React (sebelumnya) | HTMX (sekarang) |
|-------|-------------------|-----------------|
| Dependency JS | 10+ library (React, Zustand, dll) | 3 library (HTMX, Alpine, xterm) |
| Baris JS | ~2.878 baris TS/JSX | ~410 baris vanilla JS |
| Build step | Vite + TypeScript + npm | CSS build + copy static |
| Bahasa dominan | 60% TS + 40% Go | 90% Go + 10% HTML/JS |
| State management | Zustand store | Server-side (HTMX) + Alpine |
| API layer | REST JSON + manual fetch | HTML partials langsung |
| Electron | Wajib (SPA) | Opsional (bisa browser) |
| Debugging | React DevTools | Browser DevTools (HTML langsung) |
| Time to implement | ~6 minggu | ~4 minggu (estimasi) |
---
## 8. Cara Kerja: React → HTMX Mapping per Halaman
### Dashboard (Host List)
| React | HTMX |
|-------|------|
| `useState` searchQuery | `hx-get="/hosts?q={search}"` |
| `useState` statusFilter | `hx-get="/hosts?filter=active"` |
| `useState` viewMode | Alpine `x-data="{ view: 'grid' }"` |
| `useState` showAddModal | Alpine `x-show="open"` |
| `onChange` filter | HTML `