Files
signer/plans/egui_gui_and_qr_plan.md

147 lines
9.5 KiB
Markdown

# Signer `egui` GUI & `signer://` QR Connection Scheme Plan
## 1. Overview
This plan adds:
1. **`signer://` Connection URI & QR Code Scheme (`src/connect_uri.rs`)**: A unified URI format and QR code generator so any client (`signer-client`, web app, mobile/desktop Nostr client, or another VM) can scan or paste a single string to know how to reach the signer, which role/path/algorithm to select, and what auth credentials to use.
2. **`egui` / `eframe` Single-Window GUI (`src/gui.rs`)**: Modeled directly on `~/lt/otp_rs/src/gui.rs` and `~/lt/aesthetics/WEB.md` + `TUI.md` (Four-Token Monochrome + `#FF0000` Red Accent, Iosevka monospace typography, Sacred Shell layout with Header, overlay SideNav, Central Dashboard, and Footer).
3. **TUI QR Code Overlay (`src/tui.rs`)**: Renders the `signer://` URI and a Unicode half-block (`▀`/`▄`/`█`) QR code directly in the terminal UI.
4. **Client `--connect <URI>` Support (`src/client/`)**: Allows `signer-client --connect "signer://..." <verb>` to automatically configure transport (including `unix`, `qrexec`, `tcp`, `fips`, `http`, `serial`), role, derivation path, algorithm, index, and auth envelope private key/label.
---
## 2. `signer://` Connection URI Specification (`src/connect_uri.rs`)
### 2.1 URI Syntax
```text
signer://<transport>/<target>[?role=<name>&path=<bip44>&alg=<alg>&index=<n>&auth=<mode>&secret=<hex64>&label=<str>]
```
| Component | Description | Examples |
|---|---|---|
| `<transport>` | Active transport protocol | `unix`, `qrexec`, `tcp`, `fips`, `http`, `serial` |
| `<target>` | Endpoint address for the transport | - `unix`: `<socket_name>` (e.g. `signer01`)<br>- `qrexec`: `<qube>:<service>` (e.g. `sys-signer:qubes.SignerRpc`)<br>- `tcp`: `<host>:<port>` (e.g. `127.0.0.1:11111`)<br>- `fips`: `<npub>.fips:<port>` or `<host>:<port>`<br>- `http`: `<host>:<port>` (e.g. `127.0.0.1:11111`)<br>- `serial`: `/dev/ttyACM0` (percent-encoded or path) |
| `role` | Role name (serves as capability password) | `role=main`, `role=ssh`, `role=otp` |
| `path` | Concrete BIP-44 derivation path (percent-encoded `'` as `%27` or unescaped `'` both accepted by parser) | `path=m/44'/1237'/0'/0/0` |
| `alg` | Algorithm name for algorithm verbs / OTP | `alg=secp256k1`, `alg=ed25519`, `alg=ml-dsa-65`, `alg=otp` |
| `index` | Derivation index for algorithm verbs | `index=0` |
| `auth` | Server auth mode hint | `auth=off`, `auth=optional`, `auth=required` |
| `secret` | Optional 32-byte hex `auth_privkey` for NIP-42 request signing | `secret=0123...abcdef` |
| `label` | Optional client `auth_label` | `label=laptop` |
### 2.2 Architecture Flow
```mermaid
flowchart LR
subgraph Signer Daemon
STATE[App / GuiApp State]
URI[connect_uri::ConnectUri]
QR[QR Matrix Generator]
GUI_QR[egui Painter QR Widget]
TUI_QR[ratatui Unicode Half-Block QR]
end
subgraph Client
CLI[signer-client --connect URI]
PARSE[ConnectUri::parse]
TRANS[ClientTransport: Unix / TCP / HTTP / Qrexec / Serial]
end
STATE --> URI
URI --> QR
QR --> GUI_QR
QR --> TUI_QR
URI -. Scan QR or Copy URI .-> CLI
CLI --> PARSE
PARSE --> TRANS
```
### 2.3 `src/connect_uri.rs` API
- `ConnectTransport` enum: `Unix`, `Qrexec`, `Tcp`, `Fips`, `Http`, `Serial`
- `ConnectUri` struct:
- `transport: ConnectTransport`
- `target: String`
- `role: Option<String>`
- `path: Option<String>`
- `alg: Option<String>`
- `index: Option<i64>`
- `auth: Option<String>`
- `secret: Option<String>`
- `label: Option<String>`
- `ConnectUri::to_uri_string(&self) -> String`
- `ConnectUri::parse(s: &str) -> Result<Self, String>`
- `ConnectUri::qr_matrix(&self) -> Result<(usize, Vec<bool>), String>` (using `qrcode::QrCode` with `EcLevel::L` or `EcLevel::M` for compact scannability)
- `ConnectUri::qr_unicode_lines(&self) -> Result<Vec<String>, String>` (renders 2 vertical modules per row using `█`, `▀`, `▄`, and ` ` with a 2-module quiet zone for TUI display)
---
## 3. `signer-client` Updates (`src/client/`)
1. **`src/client/cli.rs`**:
- Add `--connect <URI>` (`-c`) global flag to `Cli`.
2. **`src/client/transport.rs`**:
- Add `ClientTransport::Http { host: String, port: u16, timeout_ms: u64 }` variant so `signer-client` can also speak directly to `signer`'s HTTP listener (`POST / HTTP/1.1` using `::signer::http`) when given `signer://http/host:port...` or `--http host:port`.
- Add `--http <host:port>` flag to `Cli` for completeness alongside `--tcp`, `--qrexec`, `--serial`, and `-n`.
3. **`src/client/main.rs`**:
- If `--connect <URI>` is provided, parse `ConnectUri` and populate any unset CLI fields (`socket_name` / `tcp` / `http` / `qrexec` / `serial`, `role`, `path`, `algorithm`, `index`, `auth_privkey`, `auth_label`). Explicit CLI flags override values from `--connect`.
---
## 4. `egui` GUI Design (`src/gui.rs`)
### 4.1 Aesthetic System (Matching `~/lt/otp_rs/src/gui.rs`)
- **Four-Token Monochrome + Red Accent (`#FF0000`)**:
- Light mode: `primary=#000000`, `secondary=#FFFFFF`, `accent=#FF0000`, `muted=#DDDDDD`, `muted_text=#777777`
- Dark mode: `primary=#FFFFFF`, `secondary=#000000`, `accent=#FF0000`, `muted=#777777`, `muted_text=#999999`
- **Typography**: Load Iosevka (`~/.fonts/iosevka/Iosevka-Regular.ttf` / `/usr/share/fonts/truetype/iosevka/...`) for both `Proportional` and `Monospace` families with graceful fallback.
- **Spacing Ladder**: `SP_XS=5.0`, `SP_S=10.0`, `SP_M=15.0`, `SP_L=20.0`, `HEADER_H=60.0`, `FOOTER_H=24.0`, `BORDER=3.0`.
### 4.2 Sacred Shell & Views
1. **Startup Seed Unlock / Generate Screen (or Modal)**:
- Shown before the server starts (just like `Screen::SeedEntry` / `Screen::SeedDisplay` in `src/tui.rs`).
- Password/seed input with live per-word BIP-39 validation badges (`✓word` in primary, `✗word` in `#FF0000` accent) and word count indicator (`12/15/18/21/24`).
- `[ Unlock Seed (Enter) ]` and `[ Generate 12-Word Mnemonic ]` buttons.
- When generating, shows the numbered 12 words with a `[ Copy ]` button and `[ I Have Written This Down — Start Signer ]`.
2. **Header (`60px`, 3px bottom border)**:
- Hamburger button (`☰`) toggling the overlay `SideNav` (with Navigation links, Quick Actions, and pinned bottom version bar + Dark/Light toggle).
- Menu bar: `File` (Lock/Reset Seed, Exit), `Transports` (Toggle Unix, Qube, FIPS, HTTP), `Roles` (Add Role, Bind OTP Pad, Split Bound OTP Pad), `Connect` (Show Connection QR...), `Help` (Guide, About).
- Right side: `SIGNER v0.0.37` + active socket name badge.
3. **Main Dashboard (`CentralPanel`)**:
- **Top Row (Two Columns)**:
- **Left: Information & Endpoints Card**:
- Displays `Signer Name`, `Unix Address`, `Qube Address`, `FIPS Address` (with `fipsctl` auto-lookup), `HTTP Address`, and `Auth Mode`.
- If an OTP pad is bound: renders the monochrome visual `partition_bar` (`[Used | Owned | Queued | Peer]`) identical to `otp_rs`, plus `[ Split Pad ]`.
- Primary `[ Connect / Show QR Code ]` button.
- **Right: Transports Configuration Card**:
- Checkboxes/toggles for `[x] Unix Socket`, `[ ] Qube bridge`, `[ ] FIPS (TCP)`, `[ ] HTTP`.
- Editable bind address fields for TCP (`127.0.0.1:11111`) and HTTP (`127.0.0.1:11111`) with `[ Apply ]`.
- **Middle: Roles Card**:
- Monospaced grid of roles: `Role`, `Purpose`, `Curve`, `Key Path`, and per-row `[ QR ]`, `[ Edit ]`, `[ Delete ]` buttons.
- Action bar: `[ + Add Role... ]`, `[ Split OTP Pad ]`.
- **Bottom: Activity Log Card**:
- Scrollable monospaced log (newest-first, matching `ActivityLog`) with `[ Copy Log ]` and `[ Clear ]` buttons.
- Polls `ServerContext::poll_request` / `finish_request` every frame (`ctx.request_repaint_after(Duration::from_millis(50))`), showing `START` activity before blocking mining requests.
4. **Modals**:
- **Connection QR Modal (`show_qr`)**:
- Dropdowns to choose which **Transport** (`unix`, `qrexec`, `tcp`, `fips`, `http`) and which **Role** (or algorithm) to encode.
- Optional checkbox: `[ ] Include ephemeral auth keypair (for --auth)` which generates a fresh 32-byte hex secret and embeds `secret=<hex>`.
- Crisp custom-painted `egui` QR code canvas (`ui.painter().rect_filled(...)` per module).
- Selectable/copyable `signer://...` URI string and ready-to-run `signer-client --connect "signer://..." get-info` command snippet with `[ Copy URI ]` and `[ Copy Command ]` buttons.
- **Add / Edit Role Modal**:
- Preset selector (Standard Nostr, Nostr Range, Nostr Agent, SSH, Age, ML-DSA-65, SLH-DSA-128s, ML-KEM-768, OTP Pad, Custom).
- Role Name, Curve, and Path Template inputs, or built-in OTP Pad Directory Browser (matching `FileBrowser` in `otp_rs`).
- **Help & About Modals**.
---
## 5. Launch & CLI Behavior (`src/main.rs`)
- Add `--gui` / `-w` and `--tui` / `-t` flags to `Cli` in `src/main.rs`.
- Add `has_display()` helper checking `DISPLAY` and `WAYLAND_DISPLAY`.
- In `server_main`:
- Non-interactive (`--mnemonic-stdin` / `--mnemonic-fd`) or explicit headless `--listen` (`stdio`, `qrexec`, `tcp`, `http` without `--gui` or `--tui`): runs `run_headless`.
- If `--gui` is passed, or (interactive default without `--tui` and `has_display()` is true): launches `signer::gui::run_gui(...)`.
- If `--tui` is passed, or `has_display()` is false: launches `signer::tui::App`.
- Note on musl static builds (`Cargo.toml`):
- Make `gui` a Cargo feature enabled by default (`default = ["gui"]`), or configure `eframe`/`egui` so that `cargo build` builds the GUI by default while `Dockerfile.musl` can build cleanly on Alpine musl.