226 lines
9.9 KiB
Markdown
226 lines
9.9 KiB
Markdown
# Login Dialog Comparison: Original (C) vs Rust Implementation
|
||
|
||
**Date:** 2026-08-17
|
||
**Scope:** The Nostr login dialog shown when the browser starts.
|
||
**Reference files:**
|
||
- Original (C): [`sovereign_browser/src/login_dialog.c`](../sovereign_browser/src/login_dialog.c)
|
||
- Rust: [`src/login_dialog.rs`](../src/login_dialog.rs)
|
||
|
||
---
|
||
|
||
## 1. Overview
|
||
|
||
Both implementations present a modal GTK dialog on startup that lets the user
|
||
authenticate with a Nostr identity before the browser becomes usable. The
|
||
original C implementation is a mature, feature-complete dialog with **five
|
||
method tabs** plus a **"No Login"** option. The Rust implementation is a
|
||
minimal port that currently supports only **two** of the five methods and
|
||
lacks most of the original's UI affordances.
|
||
|
||
The Rust dialog is a functional but **visually and functionally reduced**
|
||
version of the original. This document details, page by page, what each
|
||
login screen shows and the differences between the two implementations.
|
||
|
||
---
|
||
|
||
## 2. High-Level Structural Differences
|
||
|
||
| Aspect | Original (C) | Rust |
|
||
|---|---|---|
|
||
| Window title | `sovereign browser <version>` (e.g. `sovereign browser v0.0.70`) | `Nostr Login` |
|
||
| Default size | 560 × 380 | 500 × 400 |
|
||
| Method selection | **GtkNotebook tabs** (Local Key, Seed Phrase, Read-only, NIP-46, n_signer) | **GtkComboBoxText dropdown** (5 entries) |
|
||
| "No Login" button | ✅ Present (far left) | ❌ Absent |
|
||
| Buttons | `No Login` \| `Cancel` \| `Sign In` | `Cancel` \| `Login` |
|
||
| Title header | Bold markup: **"Sign in with your Nostr key"** | ❌ None |
|
||
| Status/error label | ✅ Present (shows validation errors inline) | ❌ None (silently returns `None` on error) |
|
||
| Monospace font styling | ✅ Applied via CSS provider | ❌ None |
|
||
| Agent auto-close (200ms poll) | ✅ Present | ❌ None |
|
||
| Methods actually implemented | All 5 (local, seed, readonly, nip46, nsigner) | Only 2 (local, readonly) |
|
||
| Error messaging | Detailed inline messages | Silent failure (returns `None`) |
|
||
|
||
---
|
||
|
||
## 3. Page-by-Page Comparison
|
||
|
||
### 3.1 Local Key
|
||
|
||
**Original (C)** — tab label **"Local Key"**
|
||
- Instruction label: *"Enter your Nostr private key (nsec) or generate a new one:"*
|
||
- Text entry with placeholder `nsec1...`, width 60 chars
|
||
- **"Generate New Key"** button — generates a fresh keypair and fills the entry with the `nsec`
|
||
- Accepts `nsec1...` (bech32) **or** 64-char hex private key
|
||
- Validates input; shows inline errors: *"Invalid nsec."*, *"Invalid hex private key."*, *"Enter an nsec1... or 64-char hex key."*, *"Enter an nsec or click Generate."*
|
||
|
||
**Rust** — dropdown entry **"Local Key (nsec)"**
|
||
- Label: *"Private Key (hex):"*
|
||
- Text entry with **password masking** (`set_visibility(false)`)
|
||
- ❌ No "Generate New Key" button
|
||
- ❌ Only accepts 64-char **hex** private key (no `nsec1...` bech32 support)
|
||
- ❌ No inline validation/error messages — invalid input silently returns `None`
|
||
|
||
**Key differences:**
|
||
1. Rust only accepts hex, not `nsec1...` bech32.
|
||
2. Rust has no key generation button.
|
||
3. Rust masks the input as a password; the original shows it in plain text.
|
||
4. Rust has no error feedback.
|
||
|
||
---
|
||
|
||
### 3.2 Seed Phrase
|
||
|
||
**Original (C)** — tab label **"Seed Phrase"**
|
||
- Instruction label: *"Enter your 12-word BIP-39 seed phrase:"*
|
||
- **12 numbered word entry boxes in a 4×3 grid** (1. through 12.)
|
||
- Each box has **dropdown completion** (BIP-39 wordlist) + **inline auto-complete** on typing
|
||
- **Tab/Enter** moves focus to the next word box
|
||
- **"Generate New Mnemonic"** button — fills all 12 boxes with a fresh mnemonic
|
||
- **Account #** spin button (0–1000) with live derivation hint: *"Key derivation: m/44'/1237'/<N>'/0/0"*
|
||
- Validates exactly 12 words; errors: *"Expected 12 words, got N."*, *"Invalid seed phrase. Check the words and try again."*
|
||
|
||
**Rust** — dropdown entry **"Seed Phrase (BIP-39)"**
|
||
- Label: *"Seed Phrase (BIP-39):"*
|
||
- Single text entry with **password masking**
|
||
- ❌ No 12-box grid, no word completion, no auto-complete, no Tab navigation
|
||
- ❌ No "Generate New Mnemonic" button
|
||
- ❌ No Account # selector or derivation path hint
|
||
- ❌ **Not implemented** — selecting this method and clicking Login returns `None` (the `Seed` arm is not handled in the match)
|
||
|
||
**Key differences:**
|
||
1. Rust does not implement seed-phrase login at all (falls through to `None`).
|
||
2. The original's rich 12-word grid with completion is entirely absent.
|
||
|
||
---
|
||
|
||
### 3.3 Read-only (npub)
|
||
|
||
**Original (C)** — tab label **"Read-only"**
|
||
- Instruction label: *"Enter a Nostr public key (npub) for read-only mode:"*
|
||
- Text entry with placeholder `npub1...`, width 60 chars
|
||
- Hint (dimmed): *"Read-only mode: you can view content but cannot sign events."*
|
||
- Accepts `npub1...` (bech32) **or** 64-char hex pubkey
|
||
- Validates; errors: *"Invalid npub."*, *"Invalid hex pubkey."*, *"Enter an npub1... or 64-char hex pubkey."*
|
||
|
||
**Rust** — dropdown entry **"Read-only (npub)"**
|
||
- Label: *"Public Key (npub or hex):"*
|
||
- Text entry (visible, not masked)
|
||
- ✅ Accepts `npub1...` (via `nostr_url_normalize`) or hex
|
||
- ❌ No hint text
|
||
- ❌ No inline validation/error messages
|
||
|
||
**Key differences:**
|
||
1. Rust supports this method (the only one besides local that works).
|
||
2. Rust lacks the explanatory hint and error feedback.
|
||
|
||
---
|
||
|
||
### 3.4 NIP-46 Remote Signer
|
||
|
||
**Original (C)** — tab label **"NIP-46"**
|
||
- Instruction label: *"Connect to a NIP-46 remote signer (bunker:// URL):"*
|
||
- Text entry with placeholder `bunker://<pubkey>?relay=wss://...&secret=...`, width 60 chars
|
||
- Wrapped hint (dimmed): *"The remote signer holds your private key. Signing requests are sent over Nostr relays."*
|
||
- Parses and validates the bunker URL; errors: *"Invalid bunker:// URL. Format: ..."*, *"Enter a bunker:// URL."*
|
||
- Generates a client keypair, stores the remote signer's pubkey as identity
|
||
|
||
**Rust** — dropdown entry **"NIP-46 Remote Signer"**
|
||
- Label: *"Bunker URL:"*
|
||
- Text entry (visible)
|
||
- ❌ **Not implemented** — the `Nip46` arm is not handled in the match, returns `None`
|
||
- ❌ No hint text, no URL validation
|
||
|
||
**Key differences:**
|
||
1. Rust does not implement NIP-46 login at all.
|
||
|
||
---
|
||
|
||
### 3.5 n_signer Hardware
|
||
|
||
**Original (C)** — tab label **"n_signer"**
|
||
- Instruction label: *"Connect to n_signer hardware signer:"*
|
||
- **Transport** combo: `USB Serial` / `UNIX Socket` / `TCP` / `Other Qube` (default: Other Qube)
|
||
- **Device** entry — label/placeholder adapts to transport:
|
||
- Serial: *"Device Path:"* / `/dev/ttyACM0` + **"Detect Serial Devices"** button
|
||
- UNIX: *"Socket Name:"* / `nsigner`
|
||
- TCP: *"Host:Port:"* / `127.0.0.1:7777`
|
||
- Qube: *"Target Qube:"* / `nostr_signer` (default value)
|
||
- **Service** entry (qrexec only): `qubes.NsignerRpc`
|
||
- **Role** entry: `nostr_range`
|
||
- **Key Index** spin (0–1000) with live BIP-32 path label: *"Key Index (m/44'/1237'/<N>'/0/0):"*
|
||
- Wrapped hint (dimmed): *"n_signer is a foreground, RAM-only hardware signer. Your private key never leaves the device."*
|
||
- Detailed error mapping (auth denied, policy denied, index not whitelisted, etc.)
|
||
|
||
**Rust** — dropdown entry **"n_signer Hardware"**
|
||
- Label: *"Device Path:"*
|
||
- Text entry (visible)
|
||
- ❌ **Not implemented** — the `Nsigner` arm is not handled in the match, returns `None`
|
||
- ❌ No transport selector, no service/role/index fields, no device detection
|
||
|
||
**Key differences:**
|
||
1. Rust does not implement n_signer login at all.
|
||
2. The original's transport-adaptive UI is entirely absent.
|
||
|
||
---
|
||
|
||
## 4. Feature Matrix
|
||
|
||
| Feature | Original (C) | Rust |
|
||
|---|---|---|
|
||
| Local key (nsec/hex) | ✅ | ⚠️ hex only |
|
||
| Local key generation | ✅ | ❌ |
|
||
| Seed phrase (BIP-39) | ✅ | ❌ |
|
||
| Seed word grid + completion | ✅ | ❌ |
|
||
| Read-only (npub) | ✅ | ✅ |
|
||
| NIP-46 remote signer | ✅ | ❌ |
|
||
| n_signer hardware | ✅ | ❌ |
|
||
| "No Login" (browse w/o identity) | ✅ | ❌ |
|
||
| Inline error/status messages | ✅ | ❌ |
|
||
| Method tabs (notebook) | ✅ | ❌ (dropdown) |
|
||
| Title header | ✅ | ❌ |
|
||
| Monospace styling | ✅ | ❌ |
|
||
| Agent auto-close | ✅ | ❌ |
|
||
| Account # / key index selectors | ✅ | ❌ |
|
||
|
||
---
|
||
|
||
## 5. Summary of Gaps
|
||
|
||
The Rust login dialog is a **minimal skeleton** compared to the original.
|
||
The most significant gaps, in priority order:
|
||
|
||
1. **Only 2 of 5 methods work** — `Seed`, `Nip46`, and `Nsigner` are listed in
|
||
the dropdown but return `None` when selected. The original implements all five.
|
||
2. **No "No Login" option** — the original lets users browse without a Nostr
|
||
identity; the Rust version has no equivalent.
|
||
3. **No error feedback** — the original shows inline validation/error messages;
|
||
the Rust version silently returns `None` on any failure, giving the user no
|
||
indication of what went wrong.
|
||
4. **Reduced local-key support** — Rust only accepts hex private keys (no
|
||
`nsec1...` bech32) and has no "Generate New Key" button.
|
||
5. **UI structure differs** — the original uses notebook tabs with rich per-method
|
||
screens (word grids, transport selectors, derivation hints); Rust uses a single
|
||
dropdown that swaps a single label/entry.
|
||
6. **Missing polish** — no title header, no monospace styling, no agent
|
||
auto-close, no version in the window title.
|
||
|
||
---
|
||
|
||
## 6. Recommended Path to Parity
|
||
|
||
To bring the Rust dialog in line with the original:
|
||
|
||
1. **Implement the missing methods** (`Seed`, `Nip46`, `Nsigner`) in
|
||
[`src/login_dialog.rs`](../src/login_dialog.rs) and
|
||
[`src/key_store.rs`](../src/key_store.rs) (`key_store_create_signer` already
|
||
returns `None` for these — see [`src/key_store.rs`](../src/key_store.rs:108)).
|
||
2. **Add a "No Login" button** that returns a `None`-method result so the browser
|
||
can run without an identity.
|
||
3. **Add a status/error label** and surface validation errors instead of
|
||
returning `None` silently.
|
||
4. **Add `nsec1...` bech32 support** to the local-key path and a
|
||
"Generate New Key" button.
|
||
5. **Switch from a dropdown to notebook tabs** to match the original's layout,
|
||
and add the per-method helper widgets (word grid, transport selector, etc.).
|
||
6. **Restore polish**: title header, monospace CSS, version in the window title,
|
||
and the agent auto-close timeout.
|