Files
signer/plans/egui_gui_and_qr_plan.md

9.5 KiB

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

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)
- qrexec: <qube>:<service> (e.g. sys-signer:qubes.SignerRpc)
- tcp: <host>:<port> (e.g. 127.0.0.1:11111)
- fips: <npub>.fips:<port> or <host>:<port>
- http: <host>:<port> (e.g. 127.0.0.1:11111)
- 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

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.