Files
sovereign_browser_rust/plans/login-dialog-comparison.md
T

9.9 KiB
Raw Blame History

Login Dialog Comparison: Original (C) vs Rust Implementation

Date: 2026-08-17 Scope: The Nostr login dialog shown when the browser starts. Reference files:


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'/'/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'/'/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.

To bring the Rust dialog in line with the original:

  1. Implement the missing methods (Seed, Nip46, Nsigner) in src/login_dialog.rs and src/key_store.rs (key_store_create_signer already returns None for these — see src/key_store.rs).
  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.