9.5 KiB
9.5 KiB
Signer egui GUI & signer:// QR Connection Scheme Plan
1. Overview
This plan adds:
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.egui/eframeSingle-Window GUI (src/gui.rs): Modeled directly on~/lt/otp_rs/src/gui.rsand~/lt/aesthetics/WEB.md+TUI.md(Four-Token Monochrome +#FF0000Red Accent, Iosevka monospace typography, Sacred Shell layout with Header, overlay SideNav, Central Dashboard, and Footer).- TUI QR Code Overlay (
src/tui.rs): Renders thesigner://URI and a Unicode half-block (▀/▄/█) QR code directly in the terminal UI. - Client
--connect <URI>Support (src/client/): Allowssigner-client --connect "signer://..." <verb>to automatically configure transport (includingunix,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
ConnectTransportenum:Unix,Qrexec,Tcp,Fips,Http,SerialConnectUristruct:transport: ConnectTransporttarget: Stringrole: 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) -> StringConnectUri::parse(s: &str) -> Result<Self, String>ConnectUri::qr_matrix(&self) -> Result<(usize, Vec<bool>), String>(usingqrcode::QrCodewithEcLevel::LorEcLevel::Mfor compact scannability)ConnectUri::qr_unicode_lines(&self) -> Result<Vec<String>, String>(renders 2 vertical modules per row using█,▀,▄, andwith a 2-module quiet zone for TUI display)
3. signer-client Updates (src/client/)
src/client/cli.rs:- Add
--connect <URI>(-c) global flag toCli.
- Add
src/client/transport.rs:- Add
ClientTransport::Http { host: String, port: u16, timeout_ms: u64 }variant sosigner-clientcan also speak directly tosigner's HTTP listener (POST / HTTP/1.1using::signer::http) when givensigner://http/host:port...or--http host:port. - Add
--http <host:port>flag toClifor completeness alongside--tcp,--qrexec,--serial, and-n.
- Add
src/client/main.rs:- If
--connect <URI>is provided, parseConnectUriand 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.
- If
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
- Light mode:
- Typography: Load Iosevka (
~/.fonts/iosevka/Iosevka-Regular.ttf//usr/share/fonts/truetype/iosevka/...) for bothProportionalandMonospacefamilies 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
- Startup Seed Unlock / Generate Screen (or Modal):
- Shown before the server starts (just like
Screen::SeedEntry/Screen::SeedDisplayinsrc/tui.rs). - Password/seed input with live per-word BIP-39 validation badges (
✓wordin primary,✗wordin#FF0000accent) 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 ].
- Shown before the server starts (just like
- Header (
60px, 3px bottom border):- Hamburger button (
☰) toggling the overlaySideNav(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.
- Hamburger button (
- Main Dashboard (
CentralPanel):- Top Row (Two Columns):
- Left: Information & Endpoints Card:
- Displays
Signer Name,Unix Address,Qube Address,FIPS Address(withfipsctlauto-lookup),HTTP Address, andAuth Mode. - If an OTP pad is bound: renders the monochrome visual
partition_bar([Used | Owned | Queued | Peer]) identical tootp_rs, plus[ Split Pad ]. - Primary
[ Connect / Show QR Code ]button.
- Displays
- 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 ].
- Checkboxes/toggles for
- Left: Information & Endpoints Card:
- 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 ].
- Monospaced grid of roles:
- Bottom: Activity Log Card:
- Scrollable monospaced log (newest-first, matching
ActivityLog) with[ Copy Log ]and[ Clear ]buttons. - Polls
ServerContext::poll_request/finish_requestevery frame (ctx.request_repaint_after(Duration::from_millis(50))), showingSTARTactivity before blocking mining requests.
- Scrollable monospaced log (newest-first, matching
- Top Row (Two Columns):
- 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 embedssecret=<hex>. - Crisp custom-painted
eguiQR code canvas (ui.painter().rect_filled(...)per module). - Selectable/copyable
signer://...URI string and ready-to-runsigner-client --connect "signer://..." get-infocommand snippet with[ Copy URI ]and[ Copy Command ]buttons.
- Dropdowns to choose which Transport (
- 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
FileBrowserinotp_rs).
- Help & About Modals.
- Connection QR Modal (
5. Launch & CLI Behavior (src/main.rs)
- Add
--gui/-wand--tui/-tflags toCliinsrc/main.rs. - Add
has_display()helper checkingDISPLAYandWAYLAND_DISPLAY. - In
server_main:- Non-interactive (
--mnemonic-stdin/--mnemonic-fd) or explicit headless--listen(stdio,qrexec,tcp,httpwithout--guior--tui): runsrun_headless. - If
--guiis passed, or (interactive default without--tuiandhas_display()is true): launchessigner::gui::run_gui(...). - If
--tuiis passed, orhas_display()is false: launchessigner::tui::App.
- Non-interactive (
- Note on musl static builds (
Cargo.toml):- Make
guia Cargo feature enabled by default (default = ["gui"]), or configureeframe/eguiso thatcargo buildbuilds the GUI by default whileDockerfile.muslcan build cleanly on Alpine musl.
- Make