1
Ringkasan Proyek Termimus
Termimus adalah SSH client sekaligus server management suite yang berjalan local-first: database-nya hidup di mesin pengguna, dan sinkronisasi multi-device bersifat opsional menuju relay yang di-host sendiri. README menyebutnya sebagai alternatif Termius yang self-hosted untuk Linux, macOS, dan Windows pada versi v0.7.0 (README.md:8-91). Karena kredensial produksi tidak boleh disimpan di cloud pihak ketiga, basis datanya berjalan 100% offline lewat SQLite tertanam, dan private key maupun password tidak pernah meninggalkan mesin dalam bentuk terdekripsi (README.md:29-342).
Desktop client dibangun dengan Tauri v2 + Rust backend + React frontend (AGENTS.md:73). Versi 0.7.0 konsisten di tiga berkas manifes: package.json, src-tauri/Cargo.toml, dan src-tauri/tauri.conf.json (package.json:1-114, src-tauri/Cargo.toml:1-65, src-tauri/tauri.conf.json:1-116).
Tiga Komponen dan Batas Kepercayaan
Sistem terdiri atas tiga bagian yang batas kepercayaannya berbeda tajam: desktop client, SQLite lokal, dan relay Go. Hanya dua komponen pertama yang pernah melihat plaintext.
graph TD
subgraph Client["Desktop client (Tauri v2 + Rust + React 19)"]
UI["UI React<br/>src/components"]
Core["IPC commands<br/>commands/mod.rs"]
Vault["VaultManager<br/>AES-256-GCM + Argon2id"]
end
DB["SQLite lokal<br/>termimus.db"]
Relay["Go sync relay<br/>termimus-sync"]
UI --> Core
Core --> Vault
Vault --> DB
Core --> DB
Core -->|"POST ciphertext bundle"| Relay
Relay -->|"SYNC_UPDATED"| Core
style DB fill:#336791,color:#fff
style Relay fill:#336791,color:#fff
style Vault fill:#f59e0b,color:#000
style Core fill:#8b5cf6,color:#fff
%% fig: Tiga komponen Termimus dan batas kepercayaannya
%% desc: Client memegang kunci dan plaintext, SQLite lokal menyimpan data terenkripsi, sedangkan relay Go hanya menerima ciphertext dan mengirim notifikasi SYNC_UPDATED.Pembagian itu juga terlihat pada diagram arsitektur di README, yang menggambarkan dua mesin pengguna yang sama-sama memegang kunci enkripsi dan sebuah relay di antaranya yang hanya menerima ciphertext bundle (README.md:99-1187).
Local-First: Data Dimulai dari Disk
Saat aplikasi dijalankan, Database::new membuka SQLite di config_dir()/termimus/termimus.db (src-tauri/src/db/mod.rs:1250-12558). Objek itu dibungkus Arc bersama VaultManager, SessionManager, SftpManager, TunnelManager, dan PtyManager, lalu dimasukkan ke satu AppState yang dipakai seluruh handler Tauri (src-tauri/src/lib.rs:22-309).
VaultManager hanya menyimpan derived_key: RwLock<Option<[u8; 32]>> di memori; lock() menulis nol ke seluruh byte kunci lewat zeroize sebelum melepasnya, dan is_unlocked() menjadi gerbang sebelum secret apa pun disentuh (src-tauri/src/vault/mod.rs:10-3410).
Relay Buta Isi
Server sinkronisasi adalah relay Go yang tidak bisa membaca payload. Dokumen server menyatakan bahwa semua host, folder, snippet, tunnel, dan credential dienkripsi di sisi client dengan AES-256-GCM memakai kunci turunan Argon2id, dan server hanya menyimpan serta meneruskan EncryptedBackupEnvelope (server/README.md:3-1011). Client mengirim envelope dengan format_version: 2, dan import menolak versi lain lewat pesan Unsupported encrypted backup version: {} (src-tauri/src/commands/mod.rs:1460-146112).
Route yang tersedia dibagi antara satu health check publik dan lima endpoint sinkronisasi yang dilindungi (server/cmd/server/main.go:48-6713):
| Method | Path | Handler | Kegunaan |
|---|---|---|---|
GET | /health | h.Health | health check publik, tanpa auth |
GET | /api/v1/sync/status | h.SyncStatus | versi terakhir dan metadata device |
GET | /api/v1/sync/bundle | h.GetBundle | unduh ciphertext bundle terbaru |
POST | /api/v1/sync/bundle | h.PostBundle | unggah ciphertext bundle baru |
GET | /api/v1/sync/devices | h.GetDevices | daftar device yang pernah sync |
GET | /api/v1/sync/ws | h.WebSocket | stream notifikasi WebSocket |
Autentikasi memakai AuthMiddleware: Authorization: Bearer <token> atau query ?token= untuk WebSocket, dibandingkan dengan subtle.ConstantTimeCompare, dan request yang gagal menerima 401 beserta body Unauthorized: invalid or missing authentication token (server/internal/middleware/auth.go:18-4914).
⚠️ Bila
TERMIMUS_AUTH_TOKENkosong, middleware meneruskan semua request tanpa pemeriksaan. Server juga mencetak[Security Warning] TERMIMUS_AUTH_TOKEN is not set! Running in OPEN mode.saat start. Jangan pernah menjalankan relay tanpa token di host yang dapat dijangkau publik (server/internal/middleware/auth.go:18-4914).
Di sisi client, useSyncStore memodelkan sinkronisasi sebagai empat keadaan: idle, syncing, connected, dan error (src/stores/useSyncStore.ts:18-2815). Ketika autoSync aktif, useLiveSync membuka WebSocket ke /api/v1/sync/ws, dan setiap pesan SYNC_UPDATED dari device lain langsung memicu pull(); pesan dari device sendiri diabaikan (src/hooks/useLiveSync.ts:46-5916).
Tumpukan Teknologi
Dua manifes menentukan seluruh dependensi runtime. Sisi Rust memakai russh untuk SSH, russh-sftp untuk SFTP, rusqlite (feature bundled) untuk SQLite, aes-gcm + argon2 + zeroize untuk vault, keyring untuk OS keychain, dan portable-pty untuk shell lokal (src-tauri/Cargo.toml:20-4317). Sisi frontend memakai React 19 dan @xterm/xterm 6 dengan Zustand sebagai store (package.json:12-3318).
| Lapisan | Pustaka | Versi |
|---|---|---|
| SSH engine | russh | 0.50 |
| SFTP | russh-sftp | 3.0.0 |
| Database lokal | rusqlite (bundled) | 0.33 |
| Enkripsi | aes-gcm | 0.10 |
| Key derivation | argon2 | 0.5 |
| Zeroization | zeroize | 1 |
| Terminal | @xterm/xterm | ^6.0.0 |
| State management | zustand | ^5.0.15 |
| UI | react | ^19.1.0 |
Dokumen kontribusi merangkum tumpukan yang sama: React 19 + TypeScript + Vite + Tailwind CSS v4 di frontend, Tauri v2 + Rust dengan russh, russh-sftp, rusqlite, AES-256-GCM, dan Argon2id di backend (CONTRIBUTING.md:23-2519).
Aturan yang Mengikat Seluruh Kode
Tiga aturan berlaku lintas domain dan menjadi alasan banyak keputusan desain di halaman lain:
- Setiap koneksi keluarga SSH — terminal, SFTP, port forwarding — wajib melewati
SshClientHandlerdengandb: Arc<Database>, agar verifikasi host key TOFU berlaku seragam (CONTRIBUTING.md:31-3920). - Password, private key, dan passphrase tidak pernah disimpan atau dikirim sebagai plaintext; enkripsi dan dekripsi hanya lewat
VaultManagersetelahis_unlocked()terpenuhi (CONTRIBUTING.md:31-3920). - Command Tauri baru harus didaftarkan di tiga tempat: handler di
commands/mod.rs, entrigenerate_handler![...]dilib.rs, dan wrapper bertipe disrc/lib/api.ts(AGENTS.md:11-2221).
AGENTS.md juga menetapkan pemetaan direktori backend per domain — ssh/, sftp/, tunnel/, vault/, sshkey/, db/, commands/ — dan satu Zustand store per domain di src/stores/ (AGENTS.md:11-2221). Struktur tingkat atas repository mengikuti pembagian itu: src/ untuk frontend, src-tauri/ untuk backend Rust, dan server/ untuk relay Go (README.md:226-26422).
Rilis dan Distribusi
Penomoran versi mengikuti SemVer vMAJOR.MINOR.PATCH, dan setiap tag wajib berawalan huruf v kecil; breaking change pada schema database atau format enkripsi menuntut bump MAJOR (RELEASE.md:7-1723). Desktop client dan sync server dirilis dengan nomor yang sama, saat ini v0.7.0 untuk keduanya (README.md:120-12524).
Mendorong tag memicu dua workflow GitHub Actions secara paralel: release.yml membangun tiga runner (ubuntu-22.04 untuk .deb dan .AppImage, macos-latest dengan universal-apple-darwin untuk .dmg, windows-latest untuk .msi dan .exe), sedangkan docker.yml membangun image multi-arch linux/amd64 + linux/arm64 ke GHCR dengan tag :vX.Y.Z, :X.Y, :latest, dan :edge (RELEASE.md:96-12225).
Peta Dokumentasi
Halaman-halaman berikut membahas tiap bagian sistem secara mendalam.
| Halaman | Cakupan |
|---|---|
| Arsitektur Sistem dan Batas Modul | pembagian modul Rust, store Zustand, dan arah dependensi |
| Memulai Pengembangan dan Build | prasyarat, bun run tauri dev, dan bun run tauri build |
| Vault dan Model Kriptografi | Argon2id, AES-256-GCM, keyring, dan auto-lock |
| Skema Database Lokal | tabel di init_tables() dan model baris |
| Siklus Hidup Koneksi SSH | SessionManager dan streaming PTY |
| Verifikasi Host Key TOFU | SshClientHandler dan penolakan saat fingerprint berubah |
| Manajemen Host dan Keychain | folder, host, credential, dan derivasi public key |
| Terminal, Split Pane, dan Workspace | terminalPool, split layout, dan preset workspace |
| SFTP Dual-Pane dan Editor Remote | SftpManager dan editor di atas SFTP |
| Port Forwarding dan Tunnel | TunnelManager dan channel_open_direct_tcpip |
| Backup, Sync Relay, dan API Server | BackupBundle, endpoint relay, dan EncryptedBackupEnvelope |