Skip to content

Repository files navigation

Namehold — a non-custodial Handshake (HNS) wallet

Coverage

Namehold is a local desktop wallet for Handshake (HNS): hold HNS, manage the names you own, run the full name-auction lifecycle, and edit on-chain DNS — all non-custodially, with your keys encrypted on your own machine. It also includes a guided Move from Namebase helper for migrating names and funds off the custodial service.

Built with Tauri v2, React + TypeScript, Rust, and SQLite.

Namehold wallet

⚠️ Beta software. Namehold is under active development and can make mistakes. Transactions and Namebase transfers are irreversible — always test with a single name or a small amount and confirm it arrives before sending or transferring everything.

Features

Wallet (the core)

  • Create or import a wallet from a BIP39 mnemonic, or add a watch-only wallet from an account xpub.
  • Hardware wallet support — connect a Ledger Nano S / S Plus / X and let the device sign every transaction. Private keys never leave the device; you confirm each send, bid, or covenant action on-screen. Requires the official ledger-app-hns firmware.
  • Multiple wallets — switch between them and delete ones you no longer need.
  • Receive — your address with a QR code and one-click copy. Expand "View all addresses" to see the full list of derived receive addresses with derivation index, used/fresh badge, first-seen date, copy/QR/explorer buttons, and a "Generate new address" button to allocate the next unused index.
  • Per-wallet balances — each wallet shows its own balance; values persist and refresh on demand (no bleed between wallets).

Send HNS

  • A build → sign → broadcast draft flow: preview fee/change before any key is touched, sign in the secure window, then broadcast.
  • Fee-rate control — set a global default fee rate in Settings > Advanced, or override per-transaction with a collapsible "Advanced" widget in every send/bid/ batch flow. Minimum 1000 doos/kvB (1 sat/byte).
  • Send Max to sweep a wallet.
  • Status tracking — sent transactions move Pending → Confirmed (with block height), or are flagged "Not confirmed" if they never make it on-chain.

Names & auctions

  • The complete Vickrey-auction lifecycle: open, bid, reveal, redeem, register, update, transfer, finalize, cancel, renew, revoke.
  • Phase badges + countdowns for each owned name, a reveal-required alert, and a "Locked in Auctions" balance for in-flight bids.
  • A typed DNS-record editor (TXT/A/AAAA/NS/CNAME…) for register/update, with a raw-JSON fallback.
  • Batch operations — bid, renew, reveal, redeem, or finalize multiple names in a single transaction. Multi-select checkboxes on the Owned Names table with a batch action bar and a confirmation modal showing estimated fee + name list.
  • Name watchlist — track names you don't own for monitoring. Watchlist page in the sidebar with add/remove, tags, CSV import/export, and "Add to Watchlist" buttons in name modals.
  • Paid name swaps — atomic buyer-seller name transfer with payment. The buyer finalizes a TRANSFER and pays the seller in a single transaction (finalizeWithPayment), so neither party can renege after the lockup expires.
  • Recover lost bids — if you lose your local bid data (reinstall, seed- restore, import from another wallet), Namehold can brute-force the bid value from your seed and reveal it before the window closes. See docs/RECOVER_LOST_BIDS.md.

Node-free reads

  • Reads are node-free by default via the HNSFans explorer — no node required just to view your wallet. When your local hsd is synced, the app automatically switches to node-authoritative reads (owned names, balances, bid history) for faster, more reliable data. A local node is needed only to send or perform name actions.
  • When you click a transaction, name, or address link, it opens on Shakeshift (https://shakeshift.com) for viewing on-chain details.

Move from Namebase (one feature, not the core)

  • Connect with your Namebase session cookie to list custodial domains, see which are expiring soon, transfer names out to your wallet, withdraw HNS, and compare your inventory against what Namebase still holds.

Auto-update (since v0.2.0)

  • Namehold checks for updates automatically ~30 seconds after launch. When a new version is available, a banner appears at the top of the window offering a one-click install; you can also check manually in Settings > Updates. Update bundles are Ed25519-signed at release and verified against the embedded public key before install; unsigned or tampered bundles are rejected.

Background sync

  • Namehold can keep your wallet data fresh even when the app is closed. A lightweight background daemon (namehold-syncd) wakes every 60 seconds and syncs all wallet profiles (UTXOs, name states, transactions) from the local hsd node into the shared SQLite database.
  • Controlled by a Settings checkbox "Sync in background" (default ON).
  • When enabled, hsd stays running after you close the app so the daemon can query it; the next app launch adopts the running node (no duplicate spawned).
  • Crash recovery: if the daemon dies, the app respawns it on startup.
  • The daemon is read-only — it never signs transactions or broadcasts.

SPV mode

  • An opt-in lightweight alternative to the full node mode. SPV downloads only block headers (~tens of MB vs ~15GB), enabling fast first launch and minimal disk usage.
  • Balance and name data come from the explorer — sending is blocked in SPV mode (read-only).
  • Controlled via a "Node mode" dropdown in Settings → Connections (default: Full node). When using SPV, hsd runs with --spv instead of --index-address --index-tx.
  • Explorer failover — all explorer HTTP requests support automatic failover to a configurable fallback URL.
  • SPV indicator in the StatusStrip — shows "Explorer (SPV)" when in SPV mode.

Keyboard-first navigation

  • Command palette (⌘K / Ctrl+K) — fuzzy-searchable list of navigation targets and view actions on the current page. Type to filter, ↑/↓ to move, Enter to run.
  • Cheatsheet (Shift+?) — overlay documenting all bindings for the current page.
  • Per-page shortcuts — common flows without the mouse:
    • Wallet (/): s send, r refresh, u unlock, q toggle QR, / filter names, j/k + Enter to walk owned names
    • Auctions (/auctions): / lookup, b batch bid
    • Activity (/activity): / search
    • Watchlist (/watchlist): a add name, e export CSV

How it works

  • Reads are node-free. Balances and names come from the explorer and are cached locally per wallet. Links to transactions, names, and addresses open on Shakeshift.
  • Sending needs a node. Broadcasting and coin/owner discovery use a local hsd node over RPC. The app can start/stop hsd for you (Settings → Connections).
  • Secrets stay in a secure window. Your mnemonic/passphrase is only ever typed into — and your backup phrase only ever shown in — a small Rust-owned window; it never passes through the web UI. At rest it's an encrypted vault (Argon2id + AES-256-GCM) inside the local database.
  • Write-capability gating. Spend/name actions are enabled only when the signer is unlocked and the node is reachable, synced, and address-indexed — with a precise reason shown when any condition isn't met.
  • Background sync daemon. When "Sync in background" is enabled (Settings → Connections, default ON), a separate Rust binary syncs all profiles every 60s. A cross-process DB lock table (sync_locks) coordinates the app's manual Sync and the daemon via heartbeats (10s) and stale-lock takeover (30s) so they never write the same profile concurrently. The daemon writes its PID to ~/.namehold/syncd.pid for lifecycle tracking.

Security

For a detailed threat model, attack surfaces, and mitigations, see SECURITY.md.

  • Non-custodial — your keys live on your device, encrypted; nothing is custodied.
  • Secrets never reach the web layer — entry/display happen in the secure window, and signing happens in Rust.
  • Local-first — keys and secrets stay on your device (encrypted at rest). By default, balance and name lookups query the public HNSFans explorer, which sees your wallet addresses and tracked names. Run your own hsd node (Settings) to keep all lookups fully local. No cloud, no telemetry.
  • Auto-lock — the unlocked signer times out after a configurable idle period.
  • Namebase migration — optional in-app helper for transferring domains from Namebase. The session cookie is encrypted at rest and never exposed to the web layer. See SECURITY.md for the full threat model and a lower-risk alternative.

Prerequisites

  • Node.js 22+
  • pnpm 11+ (CI pins 11.17.0 via the packageManager field; run corepack enable to match it locally)
  • Rust (stable, edition 2021)
  • hsdonly needed for sending / name actions (reads work without it). Run it with --index-address.

Quick start

git clone <repo-url>
cd namehold-wallet
pnpm install
pnpm tauri dev

Running a node (only to send)

Reads need no node. To send HNS or perform name actions, run an address-indexed hsd node:

hsd --index-address --index-tx --api-key=<your-key>
# mainnet node RPC: http://127.0.0.1:12037
  • --index-address is required (the wallet finds your coins by address); --index-tx backs transaction history + confirmation tracking.
  • The app can manage hsd for you — set the data directory and (if needed) the hsd binary path in Settings → Connections, then click Start hsd.
  • hsd can't add an index to an already-synced chain. If your existing chain was synced without these indexes, the app detects it and offers a one-click re-sync (it moves the old chain to a backup and re-syncs with the right indexes).
Network Node RPC port
Mainnet 12037
Testnet 13037
Regtest 14037

See docs/NODE_SETUP.md and docs/REGTEST_TESTING.md for details.

Move from Namebase

A guided migration helper (not the wallet's core function). In Move from Namebase, paste your Namebase session cookie to connect, then:

Namebase migration

  • review your custodial domains and which are expiring soon,
  • transfer names out to your own wallet address,
  • withdraw HNS to an address,
  • compare your imported inventory against Namebase's current list.

On-chain finalization of transfers uses the same node-backed write path as the rest of the wallet.

Build for production

pnpm tauri build

Output in src-tauri/target/release/bundle/ — macOS .app/.dmg, Windows .msi, Linux .AppImage/.deb. CI (PR tests), a dependency-audit gate (cargo audit + pnpm audit), and the cross-platform release pipeline live in .github/workflows.

macOS

The macOS build is not code-signed. On first launch macOS may show:

"Namehold" can't be opened because Apple cannot check it for malicious software.

To remove the quarantine flag, run:

xattr -cr /Applications/Namehold.app

Then open the app normally.

Data location

All app data lives in one SQLite file in your home folder (pairs with hsd's ~/.hsd), on every platform:

  • ~/.namehold/portfolio.db
  • ~/.namehold/syncd.pid — background sync daemon's process ID (present only while the daemon is running)

It holds your wallet profiles, the encrypted vault, the local chain cache, and (if used) the local transaction history.

Tech stack

  • Tauri v2 — desktop shell
  • React 19 + TypeScript — frontend
  • Vite — build tool
  • TanStack Query — async state (TanStack Table + Virtual for data tables and virtualized lists)
  • Zustand — client state
  • Zod — validation
  • SQLite (rusqlite) — local database
  • reqwest — HTTP client (explorer + hsd RPC)
  • secp256k1 · bip39 · argon2 · aes-gcm · zeroize — keys, mnemonics, vault crypto
  • hidapi — Ledger hardware wallet HID transport
  • notify-rust — cross-platform OS notifications (watchlist alerts)
  • Tailwind CSS — styling

About

A non-custodial Handshake (HNS) wallet and TLD manager

Topics

Resources

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages