<p align="center">
  <img src="images/wraith-logo.png" alt="Wraith" width="128" />
</p>

<h1 align="center">Wraith</h1>

<p align="center">
  Native desktop SSH + RDP + SFTP client — a MobaXTerm replacement built with Go and Vue.
</p>

<p align="center">
  <!-- badges -->
  <img alt="Go" src="https://img.shields.io/badge/Go-1.22+-00ADD8?logo=go&logoColor=white" />
  <img alt="Wails v3" src="https://img.shields.io/badge/Wails-v3-red" />
  <img alt="Vue 3" src="https://img.shields.io/badge/Vue-3-4FC08D?logo=vuedotjs&logoColor=white" />
  <img alt="License" src="https://img.shields.io/badge/License-MIT-blue" />
</p>

---

## Features

- **Multi-tabbed SSH terminal** with xterm.js + WebGL rendering
- **SFTP sidebar** on every SSH session (MobaXTerm's killer feature) -- same SSH connection, separate channel
- **RDP** via FreeRDP3 dynamic linking (purego, no CGO)
- **Encrypted vault** -- master password derived with Argon2id, secrets sealed with AES-256-GCM
- **Connection manager** with hierarchical groups, tags, color labels, and full-text search
- **7 built-in terminal themes** -- Dracula, Nord, Monokai, One Dark, Solarized Dark, Gruvbox Dark, MobaXTerm Classic
- **Tab detach / reattach** -- sessions live in the Go backend; tabs can be torn off into separate windows and reattached without dropping the connection
- **MobaXTerm import** -- plugin interface for `.mobaconf` and other formats
- **Command palette** (Ctrl+K) for quick connection search and actions
- **Single binary** -- ships as `wraith.exe` + `freerdp3.dll`, no Docker, no database server

## Tech Stack

### Backend (Go)

| Component | Technology | Purpose |
|-----------|-----------|---------|
| Framework | Wails v3 | Desktop shell, multi-window, type-safe Go-to-JS bindings |
| SSH | `golang.org/x/crypto/ssh` | SSH client, PTY, key/password auth |
| SFTP | `github.com/pkg/sftp` | Remote filesystem over SSH channel |
| RDP | FreeRDP3 via `purego` | RDP protocol, bitmap rendering |
| Database | SQLite via `modernc.org/sqlite` (pure Go) | Connections, credentials, settings, themes |
| Encryption | AES-256-GCM + Argon2id | Vault encryption at rest |

### Frontend (Vue 3 in WebView2)

| Component | Technology | Purpose |
|-----------|-----------|---------|
| Framework | Vue 3 (Composition API) | UI framework |
| Terminal | xterm.js 5.x + WebGL addon | SSH terminal emulator |
| CSS | Tailwind CSS 4 | Utility-first styling |
| Components | Naive UI | Tree, tabs, modals, dialogs |
| State | Pinia | Reactive stores for sessions, connections, app state |
| Build | Vite 6 | Frontend build tooling |

## Prerequisites

| Tool | Version | Install |
|------|---------|---------|
| Go | 1.22+ | [go.dev/dl](https://go.dev/dl/) |
| Node.js | 20+ | [nodejs.org](https://nodejs.org/) |
| Wails CLI | v3 | `go install github.com/wailsapp/wails/v3/cmd/wails3@latest` |

## Quick Start

```bash
# Clone
git clone https://github.com/vstockwell/wraith.git
cd wraith

# Install frontend dependencies
cd frontend && npm install && cd ..

# Run in dev mode (hot-reload frontend + Go backend)
wails3 dev
```

The app opens a 1400x900 window. On first launch you will be prompted to create a master password for the vault.

## Building

```bash
# Production build for Windows
wails3 build

# Output: build/bin/wraith.exe
```

The build embeds the compiled frontend (`frontend/dist`) into the Go binary via `//go:embed`. Ship `wraith.exe` alongside `freerdp3.dll` for RDP support.

## Project Structure

```
wraith/
  main.go                          # Entry point -- Wails app setup, service registration
  go.mod                           # Go module (github.com/vstockwell/wraith)
  internal/
    app/
      app.go                       # WraithApp -- wires all services, vault create/unlock
    db/
      sqlite.go                    # SQLite open with WAL mode, busy timeout, FK enforcement
      migrations.go                # Embedded SQL migration runner
      migrations/
        001_initial.sql            # Schema: groups, connections, credentials, ssh_keys,
                                   #         themes, host_keys, connection_history, settings
    vault/
      service.go                   # Argon2id key derivation + AES-256-GCM encrypt/decrypt
    connections/
      service.go                   # Connection and Group CRUD (hierarchical tree)
      search.go                    # Full-text search + tag filtering via json_each()
    settings/
      service.go                   # Key-value settings store (vault salt, preferences)
    theme/
      builtins.go                  # 7 built-in theme definitions
      service.go                   # Theme CRUD + idempotent seeding
    session/
      session.go                   # SessionInfo struct + state machine (connecting/connected/detached)
      manager.go                   # Concurrent session manager -- create, detach, reattach, 32-session cap
    plugin/
      interfaces.go                # ProtocolHandler + Importer + Session interfaces
      registry.go                  # Plugin registry -- register/lookup protocol handlers and importers
  frontend/
    package.json                   # Vue 3, Pinia, Naive UI, Tailwind CSS, Vite
    vite.config.ts                 # Vite + Vue + Tailwind plugin config
    src/
      main.ts                      # App bootstrap -- createApp, Pinia, mount
      App.vue                      # Root component
      layouts/
        MainLayout.vue             # Sidebar + tab bar + session area + status bar
        UnlockLayout.vue           # Master password entry screen
      components/
        sidebar/
          ConnectionTree.vue       # Hierarchical connection/group tree
          SidebarToggle.vue        # Collapse/expand sidebar
        session/
          TabBar.vue               # Draggable session tabs
          SessionContainer.vue     # Active session viewport
        common/
          StatusBar.vue            # Bottom status bar
      stores/
        app.store.ts               # Global app state (sidebar, vault status)
        connection.store.ts        # Connection + group state
        session.store.ts           # Active sessions state
  images/
    wraith-logo.png                # Application logo
```

## Architecture

```
Go Backend                    Wails v3 Bindings              Vue 3 Frontend
(services + business logic)   (type-safe Go <-> JS)          (WebView2)
                                     |
  WraithApp ─────────────────────────┼──────────────> Pinia Stores
    |-- VaultService                 |                   |-- app.store
    |-- ConnectionService            |                   |-- connection.store
    |-- ThemeService                 |                   |-- session.store
    |-- SettingsService              |                        |
    |-- SessionManager               |                   Vue Components
    |-- PluginRegistry               |                   |-- MainLayout
                                     |                   |-- ConnectionTree
  SQLite (WAL mode)                  |                   |-- TabBar
    wraith.db                        |                   |-- SessionContainer
    %APPDATA%\Wraith\                |                   |-- StatusBar
```

**How it fits together:**

1. `main.go` creates a `WraithApp` and registers Go services as Wails bindings.
2. Wails generates type-safe JavaScript bindings so the Vue frontend can call Go methods directly.
3. The Vue frontend uses Pinia stores to manage reactive state, calling into Go services for all data operations.
4. All secrets (passwords, SSH keys) are encrypted with AES-256-GCM before being written to SQLite. The encryption key is derived from the master password using Argon2id and is never persisted.
5. Sessions are managed by the Go `SessionManager` -- they are decoupled from windows, enabling tab detach/reattach without dropping connections.

**Data storage:** SQLite with WAL mode at `%APPDATA%\Wraith\wraith.db` (Windows) or `~/.local/share/wraith/wraith.db` (Linux/macOS dev). Foreign keys enforced, 5-second busy timeout.

## Plugin Development

Wraith uses a plugin registry with two interfaces: `ProtocolHandler` for new connection protocols and `Importer` for loading connections from external tools.

### Implementing a ProtocolHandler

```go
package myplugin

import "github.com/vstockwell/wraith/internal/plugin"

type MyProtocol struct{}

func (p *MyProtocol) Name() string { return "myproto" }

func (p *MyProtocol) Connect(config map[string]interface{}) (plugin.Session, error) {
    // Establish connection, return a Session
}

func (p *MyProtocol) Disconnect(sessionID string) error {
    // Clean up resources
}
```

### Implementing an Importer

```go
package myplugin

import "github.com/vstockwell/wraith/internal/plugin"

type MyImporter struct{}

func (i *MyImporter) Name() string                  { return "myformat" }
func (i *MyImporter) FileExtensions() []string       { return []string{".myconf"} }
func (i *MyImporter) Parse(data []byte) (*plugin.ImportResult, error) {
    // Parse file bytes into ImportResult (groups, connections, host keys, theme)
}
```

### Registering Plugins

Register handlers and importers with the plugin registry during app initialization:

```go
app.Plugins.RegisterProtocol(&myplugin.MyProtocol{})
app.Plugins.RegisterImporter(&myplugin.MyImporter{})
```

The `ImportResult` struct supports groups, connections, host keys, and an optional theme -- everything needed to migrate from another tool in a single import.

## Contributing

1. Fork the repository
2. Create a feature branch (`git checkout -b feat/my-feature`)
3. Make your changes
4. Run tests: `go test ./...`
5. Run frontend checks: `cd frontend && npm run build`
6. Commit and push your branch
7. Open a Pull Request

## License

[MIT](LICENSE) -- Copyright (c) 2026 Vantz Stockwell
