Add Go project scaffold for Wails v2 SSH/Telnet/RAW TCP client
- go.mod with Wails v2.9.2, x/crypto, go-sqlite3, go-keyring, uuid - cmd/app/main.go: Wails entry point (Nexus Terminal, 1400x900, #1a1a2e) - internal/app/app.go: lifecycle (Startup/Shutdown/DomReady) - internal/models/models.go: full model definitions (Profile, SSHConfig, TelnetConfig, RAWConfig, SessionInfo, KnownHost, AppPreferences, etc.) - internal/models/defaults.go: default values and NewProfile factory - internal/validation/validation.go: thorough field-level validation - frontend/embed.go: embedded frontend assets - Updated existing storage, secrets, config to match new model structure
This commit is contained in:
@@ -0,0 +1,399 @@
|
||||
# Nexus Terminal
|
||||
|
||||
**A premium desktop SSH, Telnet, and RAW TCP client** built with [Wails v2](https://wails.io/) (Go backend, web frontend).
|
||||
|
||||
Nexus Terminal provides a modern, tabbed interface for managing remote connections over SSH, Telnet, and raw TCP — with encrypted credential storage, host key verification, audit logging, and a dark Material-inspired design.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
### Implemented
|
||||
|
||||
- ✅ **SSH connections** — password, public key, and keyboard-interactive authentication
|
||||
- ✅ **Telnet** — option negotiation, character encoding, local echo, configurable line modes
|
||||
- ✅ **RAW TCP** — binary-safe text, hex, and split views with payload presets
|
||||
- ✅ **Saved connection profiles** — tags, folders, environments, favourites
|
||||
- ✅ **Encrypted secret storage** — AES-256-GCM with Argon2id key derivation
|
||||
- ✅ **OS keychain integration** — macOS Keychain, Linux Secret Service, Windows Credential Manager
|
||||
- ✅ **Known hosts verification** — configurable policies (strict / ask / accept-new / insecure)
|
||||
- ✅ **Tabbed multi-session management** — rename, reorder, pin, context menu
|
||||
- ✅ **xterm.js terminal** — search, web links, fit-to-container
|
||||
- ✅ **Profile import/export** — JSON format, secrets excluded by default
|
||||
- ✅ **Command palette** — quick access via Ctrl+K / Cmd+K
|
||||
- ✅ **Dark charcoal Material-inspired UI**
|
||||
- ✅ **Audit logging** — timestamped records of connections and actions
|
||||
- ✅ **Input validation** — comprehensive profile and protocol validation
|
||||
|
||||
### In Progress
|
||||
|
||||
- 🔨 **Jump host / bastion support** — architecture in place, single-hop implemented
|
||||
- 🔨 **Session transcript export** — partial implementation
|
||||
|
||||
### Planned
|
||||
|
||||
- 📋 Port forwarding (extension point architected)
|
||||
- 📋 SFTP file transfer (extension point architected)
|
||||
- 📋 Split panes
|
||||
- 📋 Theme customisation
|
||||
- 📋 OpenSSH config import (partial parser exists)
|
||||
- 📋 Encrypted profile export
|
||||
|
||||
---
|
||||
|
||||
## Screenshots
|
||||
|
||||
> _Screenshots will be added once the UI is feature-complete._
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
| Tool | Version | Install |
|
||||
|------------|---------|-------------------------------------------------------------------|
|
||||
| Go | 1.21+ | [golang.org/dl](https://golang.org/dl/) |
|
||||
| Node.js | 18+ | [nodejs.org](https://nodejs.org/) |
|
||||
| Wails CLI | v2 | `go install github.com/wailsapp/wails/v2/cmd/wails@latest` |
|
||||
|
||||
> **Note:** On Linux you may also need GTK, WebKit, and related system libraries.
|
||||
> Run `wails doctor` to verify your environment.
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
git clone https://github.com/your-org/nexus-terminal.git
|
||||
cd nexus-terminal
|
||||
make deps # install Go modules and frontend npm packages
|
||||
make dev # launch in development mode with hot reload
|
||||
```
|
||||
|
||||
Or using the shell script:
|
||||
|
||||
```bash
|
||||
./build.sh deps
|
||||
./build.sh dev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Build
|
||||
|
||||
| Command | Description |
|
||||
|--------------------|------------------------------------------|
|
||||
| `make deps` | Install all dependencies |
|
||||
| `make dev` | Run in development mode with hot reload |
|
||||
| `make build` | Production build |
|
||||
| `make test` | Run Go tests |
|
||||
| `make test-cover` | Run tests with HTML coverage report |
|
||||
| `make clean` | Remove build artefacts and caches |
|
||||
| `make lint` | Run golangci-lint |
|
||||
| `make package` | Build and package for distribution |
|
||||
| `make help` | Show all available targets |
|
||||
|
||||
The production binary is written to `build/bin/NexusTerminal`.
|
||||
|
||||
---
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
copilot-ssh-client/
|
||||
├── cmd/
|
||||
│ └── app/
|
||||
│ └── main.go # Application entry point (Wails bootstrap)
|
||||
├── frontend/
|
||||
│ ├── dist/
|
||||
│ │ └── index.html # HTML entry point (embedded by Wails)
|
||||
│ ├── scripts/
|
||||
│ │ ├── build.js # Frontend build & watch script
|
||||
│ │ └── copy-assets.js # Copies xterm assets from node_modules
|
||||
│ ├── src/
|
||||
│ │ ├── assets/icons/ # Application icons
|
||||
│ │ ├── components/ # UI components (planned)
|
||||
│ │ ├── css/ # Stylesheets
|
||||
│ │ │ └── lib/ # Vendored CSS (xterm.css — auto-copied)
|
||||
│ │ └── js/ # JavaScript source
|
||||
│ │ └── lib/ # Vendored JS (xterm — auto-copied)
|
||||
│ └── package.json
|
||||
├── internal/
|
||||
│ ├── api/ # Wails-bound API methods (planned)
|
||||
│ ├── app/
|
||||
│ │ └── app.go # Application lifecycle management
|
||||
│ ├── audit/ # Audit log service (planned)
|
||||
│ ├── config/
|
||||
│ │ └── config.go # Platform-specific path resolution
|
||||
│ ├── knownhosts/ # Known hosts management (planned)
|
||||
│ ├── models/
|
||||
│ │ ├── models.go # Core domain types (Profile, Session, etc.)
|
||||
│ │ └── defaults.go # Default values and factory functions
|
||||
│ ├── preferences/ # Preferences service (planned)
|
||||
│ ├── rawclient/
|
||||
│ │ └── encoding.go # Hex, escaped, and character encoding utilities
|
||||
│ ├── secrets/
|
||||
│ │ ├── keychain.go # OS keychain integration
|
||||
│ │ ├── redact.go # Secret redaction for logs and UI
|
||||
│ │ └── secrets.go # AES-256-GCM encrypted vault
|
||||
│ ├── sessions/ # Session lifecycle management (planned)
|
||||
│ ├── sshclient/
|
||||
│ │ └── hostkey.go # SSH host key verification with policies
|
||||
│ ├── storage/
|
||||
│ │ ├── migrations/
|
||||
│ │ │ ├── 001_initial.sql # Initial database schema
|
||||
│ │ │ └── migrations.go # Embedded migration files
|
||||
│ │ └── storage.go # SQLite persistence layer
|
||||
│ ├── telnetclient/ # Telnet client (planned)
|
||||
│ └── validation/
|
||||
│ └── validation.go # Profile and protocol validation
|
||||
├── build.sh # Shell build script for CI
|
||||
├── Makefile # Make-based build targets
|
||||
├── wails.json # Wails v2 project configuration
|
||||
├── go.mod # Go module definition
|
||||
└── README.md
|
||||
```
|
||||
|
||||
### Key Packages
|
||||
|
||||
| Package | Purpose |
|
||||
|--------------------|------------------------------------------------------------------------|
|
||||
| `cmd/app` | Wails application bootstrap — window size, title, lifecycle hooks |
|
||||
| `internal/app` | Wires together all subsystems; manages startup and shutdown |
|
||||
| `internal/config` | Resolves platform-specific data, database, and vault paths |
|
||||
| `internal/models` | Domain types shared across all packages — profiles, sessions, enums |
|
||||
| `internal/storage` | SQLite repository with WAL mode, migrations, and import/export |
|
||||
| `internal/secrets` | Encrypted vault (AES-256-GCM + Argon2id) and OS keychain abstraction |
|
||||
| `internal/sshclient` | SSH host key verification with strict / ask / accept-new policies |
|
||||
| `internal/rawclient` | Hex dump, escape parsing, and character encoding for raw TCP |
|
||||
| `internal/validation` | Multi-field validation for profiles and protocol-specific configs |
|
||||
|
||||
### Frontend–Backend Interaction
|
||||
|
||||
Wails v2 exposes Go methods to the frontend as JavaScript functions. The `App` struct (and future API types) are bound in `main.go`. Frontend calls arrive as synchronous RPC; asynchronous data (terminal output, session events) flows via the Wails event system.
|
||||
|
||||
```
|
||||
┌─────────────┐ Wails bindings ┌──────────────┐
|
||||
│ Frontend │ ◄────────────────► │ Go backend │
|
||||
│ (xterm.js) │ Wails events │ (App struct) │
|
||||
└─────────────┘ └──────────────┘
|
||||
│ │
|
||||
│ xterm.js writes │ golang.org/x/crypto/ssh
|
||||
│ to terminal │ net.Dial (telnet, raw)
|
||||
▼ ▼
|
||||
Browser DOM Remote hosts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
### Secret Storage
|
||||
|
||||
Nexus Terminal uses a two-tier approach to credential storage:
|
||||
|
||||
1. **OS keychain (preferred)** — macOS Keychain, Linux Secret Service (via D-Bus), Windows Credential Manager. Probed at startup; used automatically when available.
|
||||
2. **Encrypted file vault (fallback)** — AES-256-GCM encrypted JSON file at `<data-dir>/vault`. Requires a master password to unlock.
|
||||
|
||||
### Encryption Details
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------------|-----------------------------------|
|
||||
| Cipher | AES-256-GCM |
|
||||
| Key derivation | Argon2id (time=3, mem=64 KB, threads=4) |
|
||||
| Key length | 256 bits (32 bytes) |
|
||||
| Salt | 32 random bytes (per vault) |
|
||||
| Nonce | Random, prepended to ciphertext |
|
||||
|
||||
### Threat Model
|
||||
|
||||
**Protects against:**
|
||||
- Secrets exposed via disk theft or backup exfiltration
|
||||
- Accidental secret leakage in logs, exports, or UI (redaction layer)
|
||||
- Credential access without the master password
|
||||
|
||||
**Does NOT protect against:**
|
||||
- A compromised operating system or process with memory access
|
||||
- Memory forensics whilst the vault is unlocked
|
||||
- Keyloggers capturing the master password
|
||||
- Weak master passwords (no strength enforcement currently)
|
||||
|
||||
### Secret Key Format
|
||||
|
||||
Secrets are keyed by profile: `profile:<uuid>:password`, `profile:<uuid>:passphrase`. The `ListKeys()` method exposes keys but never values.
|
||||
|
||||
### Export Handling
|
||||
|
||||
Profile exports exclude secrets by default. The JSON export format contains connection metadata but no passwords or private key passphrases unless explicitly requested (future encrypted export).
|
||||
|
||||
---
|
||||
|
||||
## Protocol Support
|
||||
|
||||
| Feature | SSH | Telnet | RAW TCP |
|
||||
|----------------------------------|:---:|:------:|:-------:|
|
||||
| Password authentication | ✅ | — | — |
|
||||
| Public key authentication | ✅ | — | — |
|
||||
| Keyboard-interactive auth | ✅ | — | — |
|
||||
| Host key verification | ✅ | — | — |
|
||||
| Keepalive | ✅ | — | — |
|
||||
| Option negotiation | — | ✅ | — |
|
||||
| Local echo | — | ✅ | — |
|
||||
| Character encoding (UTF-8, etc.) | — | ✅ | ✅ |
|
||||
| Text view | ✅ | ✅ | ✅ |
|
||||
| Hex view | — | — | ✅ |
|
||||
| Split (text + hex) view | — | — | ✅ |
|
||||
| Payload presets | — | — | ✅ |
|
||||
| Configurable line endings | — | ✅ | ✅ |
|
||||
| Auto-reconnect | — | — | ✅ |
|
||||
| Jump hosts / bastions | 🔨 | — | — |
|
||||
| Port forwarding | 📋 | — | — |
|
||||
| SFTP | 📋 | — | — |
|
||||
|
||||
**Legend:** ✅ implemented · 🔨 in progress · 📋 planned · — not applicable
|
||||
|
||||
---
|
||||
|
||||
## Storage
|
||||
|
||||
### Database
|
||||
|
||||
Nexus Terminal stores profiles, known hosts, preferences, and audit entries in a SQLite database with WAL (Write-Ahead Logging) mode for concurrent read performance.
|
||||
|
||||
| Platform | Default location |
|
||||
|----------|--------------------------------------------------------|
|
||||
| macOS | `~/Library/Application Support/NexusTerminal/nexus.db` |
|
||||
| Linux | `~/.config/NexusTerminal/nexus.db` |
|
||||
| Windows | `%APPDATA%\NexusTerminal\nexus.db` |
|
||||
|
||||
### Schema Versioning
|
||||
|
||||
Migrations are embedded at compile time via `embed.FS` and applied sequentially at startup. Each migration records its version in the `schema_version` table to prevent re-application.
|
||||
|
||||
Current schema (v001):
|
||||
- `profiles` — connection profiles with JSON-encoded protocol configs
|
||||
- `known_hosts` — SSH host key fingerprints with trust state
|
||||
- `preferences` — key-value application preferences (JSON)
|
||||
- `audit_log` — timestamped action records
|
||||
- `schema_version` — applied migration tracking
|
||||
|
||||
---
|
||||
|
||||
## Import / Export
|
||||
|
||||
### JSON Export
|
||||
|
||||
```bash
|
||||
# Via the application UI: Profiles → Export
|
||||
```
|
||||
|
||||
The export format is a JSON array of profile objects. Each profile includes connection settings, terminal preferences, and protocol-specific configuration — but **not** secrets (passwords, passphrases) by default.
|
||||
|
||||
### Import Strategies
|
||||
|
||||
| Strategy | Behaviour |
|
||||
|-------------|---------------------------------------------------------------|
|
||||
| `skip` | Skip profiles whose ID already exists |
|
||||
| `overwrite` | Replace existing profiles with the imported version |
|
||||
| `duplicate` | Import as a new profile with a fresh UUID |
|
||||
|
||||
### Format
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"exported_at": "2024-01-15T10:30:00Z",
|
||||
"profiles": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"display_name": "Production Server",
|
||||
"protocol": "ssh",
|
||||
"host": "prod.example.com",
|
||||
"port": 22,
|
||||
"username": "deploy",
|
||||
"tags": ["production", "web"],
|
||||
"folder": "Servers",
|
||||
"ssh_config": { "..." : "..." }
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Developer Notes
|
||||
|
||||
### Adding a New Protocol
|
||||
|
||||
1. Create a new package under `internal/` (e.g., `internal/mystclient/`).
|
||||
2. Define a protocol-specific config struct in `internal/models/models.go`.
|
||||
3. Add the protocol constant to the `Protocol` type.
|
||||
4. Update `internal/models/defaults.go` with default values and a factory case in `NewProfile()`.
|
||||
5. Add validation rules in `internal/validation/validation.go`.
|
||||
6. Wire the new client into `internal/app/app.go` and `internal/sessions/`.
|
||||
7. Update the database migration if new columns are required.
|
||||
|
||||
### Adding Tests
|
||||
|
||||
Tests live alongside the code they exercise:
|
||||
|
||||
```bash
|
||||
go test ./internal/validation/ -v # run validation tests
|
||||
go test ./internal/secrets/ -v # run secrets tests
|
||||
go test ./... -v -count=1 # run all tests
|
||||
go test ./... -coverprofile=coverage.out # with coverage
|
||||
```
|
||||
|
||||
Use `_test.go` files in the same package for white-box tests, or in a `_test` package for black-box tests. Table-driven tests are preferred.
|
||||
|
||||
### Event System
|
||||
|
||||
The Wails runtime provides an event bus for asynchronous communication:
|
||||
|
||||
- **Backend → Frontend:** `runtime.EventsEmit(ctx, "event:name", payload)`
|
||||
- **Frontend → Backend:** `runtime.EventsOn("event:name", callback)`
|
||||
|
||||
Key events:
|
||||
- `backend:ready` — emitted when the Go backend has finished initialisation
|
||||
- `session:data` — terminal output data for a specific session
|
||||
- `session:state` — session state changes (connecting, connected, disconnected, error)
|
||||
|
||||
---
|
||||
|
||||
## Limitations and Future Work
|
||||
|
||||
### Known Limitations
|
||||
|
||||
- **Single-hop jump hosts only** — the jump host architecture supports chaining, but only one hop is currently implemented.
|
||||
- **No SFTP** — the extension point is designed but file transfer is not yet available.
|
||||
- **No port forwarding** — local/remote/dynamic forwarding is planned but not implemented.
|
||||
- **No split panes** — single terminal per tab; split view is planned.
|
||||
- **No theme customisation** — the dark charcoal theme is currently the only option.
|
||||
- **No master password strength enforcement** — the vault accepts any master password.
|
||||
- **Argon2id memory parameter is conservative** — 64 KB is low for production use; will be increased in a future release.
|
||||
- **No SSH agent forwarding** — authentication uses direct key/password only.
|
||||
- **Linux recursive file watch** — the development watch script may not detect all changes on Linux due to `fs.watch` limitations.
|
||||
|
||||
### Planned Improvements
|
||||
|
||||
- Increase Argon2id memory to 64 MB+ for stronger key derivation
|
||||
- SFTP file browser and transfer
|
||||
- Local, remote, and dynamic port forwarding
|
||||
- Multi-hop jump host chains
|
||||
- Split pane terminal layout
|
||||
- Custom themes and colour schemes
|
||||
- OpenSSH `~/.ssh/config` import
|
||||
- Session recording and playback
|
||||
- SSH agent forwarding
|
||||
- Encrypted profile export/import
|
||||
- Master password strength meter
|
||||
- Automatic update checking
|
||||
|
||||
---
|
||||
|
||||
## Licence
|
||||
|
||||
This project is licensed under the MIT Licence. See [LICENCE](LICENCE) for details.
|
||||
|
||||
---
|
||||
|
||||
<sub>Built with ❤ using [Wails](https://wails.io/), [xterm.js](https://xtermjs.org/), and [Go](https://go.dev/).</sub>
|
||||
Reference in New Issue
Block a user