Skip to content

feat: Namecoin NIP-05 identity resolution (.bit domains) — proof of concept#662

Open
mstrofnone wants to merge 1 commit intocoracle-social:masterfrom
mstrofnone:feat/namecoin-nip05
Open

feat: Namecoin NIP-05 identity resolution (.bit domains) — proof of concept#662
mstrofnone wants to merge 1 commit intocoracle-social:masterfrom
mstrofnone:feat/namecoin-nip05

Conversation

@mstrofnone
Copy link
Copy Markdown

@mstrofnone mstrofnone commented Mar 15, 2026

Proof of Concept: Decentralised NIP-05 via Namecoin

This PR is a proof of concept demonstrating what becomes possible when Nostr clients can verify identities directly against the Namecoin blockchain — no centralised HTTP servers, no single points of failure.

The goal is to show developers what decentralised NIP-05 verification looks like in practice and to highlight the browser-level constraints that currently require workarounds.

What this enables

When a user's NIP-05 identifier ends in .bit (e.g. alice@example.bit), Coracle resolves it directly from the Namecoin blockchain via ElectrumX servers. The identity mapping lives on-chain — censorship-resistant, self-sovereign, and persistent without any web server.

Namecoin is the oldest blockchain fork (2011), purpose-built for decentralised naming. It solves Zooko's triangle — names that are simultaneously human-meaningful, decentralised, and secure.

Ported from Amethyst's production Namecoin implementation (Kotlin → TypeScript).

The browser constraint

This is where it gets interesting — and where the proof of concept hits real-world friction.

Amethyst (Android) connects directly to ElectrumX servers over raw TCP/TLS sockets. It works beautifully. But browsers cannot make raw TCP connections. This is the same constraint that blocked Namecoin integration in Tor Browser — a project that aimed to resolve .bit domains to .onion addresses directly in the browser.

The ElectrumX protocol is a simple JSON-RPC protocol over TCP. Browsers have WebSocket, fetch(), and WebRTC — but no raw TCP. The Raw Sockets API was proposed but never standardised. The Direct Sockets API exists in Chrome's Isolated Web Apps but isn't available to regular web pages.

This means any browser-based Namecoin client currently needs a proxy layer:

Browser  →  HTTP/fetch()  →  Proxy  →  TCP  →  ElectrumX server

If browsers gained the ability to make raw TCP connections (even in a sandboxed, permission-gated way like navigator.tcpSocket()), this entire proxy layer disappears and the browser talks directly to ElectrumX — truly peer-to-peer, no intermediary.

What's in this PR

Despite the proxy requirement, this implementation works end-to-end:

Zero-setup development — the ElectrumX proxy is embedded as a Vite plugin. npm run dev resolves .bit names with no additional setup:

npm run dev
# Search for testls.bit, seed@testls.bit, m@testls.bit — they resolve to Nostr profiles

Architecture:

Browser (namecoin.ts)          Vite dev middleware / hosted proxy        ElectrumX
  │                               │                                        │
  ├─ GET /api/namecoin/name/d/testls ──►  TCP JSON-RPC ──────────────────► │
  │                               │  ◄── scripthash.get_history ──────── │
  │                               │  ──► transaction.get ─────────────► │
  │                               │  ◄── NAME_UPDATE script ──────────── │
  ◄─ {name, value, height, ...} ──┤                                        │
  │                               │                                        │
  ├─ Parse nostr.names.seed       │                                        │
  ├─ Return pubkey + relays       │                                        │

New files:

File Description
src/util/namecoin.ts Browser-side resolver — identifier parsing, value extraction, LRU cache
src/app/views/NamecoinSettings.svelte Settings UI — enable/disable, proxy URL, test tool
tests/namecoin.test.ts 25 unit tests (mirroring Amethyst's suite)
proxy/electrumx-client.mjs Server-side ElectrumX TCP client
proxy/electrumx-proxy.mjs Standalone HTTP→ElectrumX proxy
proxy/vite-plugin-namecoin.mjs Vite dev middleware (zero-setup)

Modified files:

File Change
vite.config.js Register proxy plugin
.env.template Add VITE_NAMECOIN_PROXY_URL
src/util/nostr.ts Route .bit identifiers to resolver
src/engine/state.ts Settings + env var
src/app/App.svelte /settings/namecoin route
src/app/MenuDesktop.svelte Settings menu link
src/app/MenuMobile.svelte Settings menu link
src/app/shared/PersonHandle.svelte .bit verification badge
src/app/shared/SearchResults.svelte .bit search support
src/app/views/PersonInfo.svelte Blockchain verification status
src/app/views/UserProfile.svelte .bit info in NIP-05 field

Supported formats

Input Namecoin Name Lookup
alice@example.bit d/example nostr.names.alice
_@example.bit / example.bit d/example Root entry
d/example d/example Direct domain lookup
id/alice id/alice Identity namespace

Testing

# 25 unit tests, all pass
npx vitest run tests/namecoin.test.ts

# Dev server with live resolution
npm run dev

The bigger picture

Standard NIP-05 relies on HTTP servers — a centralisation pressure point. If the server goes down or is censored, verification breaks. Namecoin stores these mappings on a proof-of-work blockchain that has been running continuously since 2011.

The technologies that would make this work natively in browsers:

  • Raw/Direct Sockets API — TCP connections from web pages (proposed, not standardised)
  • WebSocket support in ElectrumX — server-side fix, some forks support it
  • WebTransport — could work if ElectrumX servers adopted it

Until then, the proxy layer is a practical workaround that keeps the architecture clean and the user experience seamless.

Related work

  • Amethyst — production Namecoin NIP-05 on Android (original implementation)
  • Tor Browser #30558 — same browser TCP constraint for .bit.onion resolution
  • Namecoin — decentralised naming blockchain (2011)
  • NIP-05 — Nostr identity verification
  • Direct Sockets API — Chrome Isolated Web Apps only

Add decentralised NIP-05 identity verification via the Namecoin blockchain.
When a user's NIP-05 identifier ends in .bit (e.g. alice@example.bit),
Coracle resolves it directly from the Namecoin blockchain via ElectrumX
servers — no centralised HTTP server needed for identity verification.

Ported from Amethyst's Kotlin implementation with browser-specific
adaptations.

## Architecture

Browsers cannot make raw TCP/TLS connections to ElectrumX servers
(see https://gitlab.torproject.org/tpo/applications/tor-browser/-/issues/30558
for prior art on this constraint in Tor Browser's Namecoin integration).

This implementation uses a lightweight HTTP proxy that bridges browser
fetch() calls to ElectrumX JSON-RPC over TCP:

  Browser  →  HTTP GET /api/namecoin/name/d/testls  →  Proxy  →  ElectrumX TCP

The proxy handles all protocol details: scripthash computation,
transaction fetching, NAME_UPDATE script parsing, and expiry checks.

### Zero-setup development

The proxy is embedded as a Vite plugin (proxy/vite-plugin-namecoin.mjs)
that serves /api/namecoin/* during development. Running `npm run dev`
works with no additional setup — the browser resolves .bit names
through the same-origin dev server.

### Production deployment

For production, set VITE_NAMECOIN_PROXY_URL to a hosted proxy instance.
The proxy is a standalone Node.js server (proxy/electrumx-proxy.mjs)
that can be deployed anywhere (VPS, Cloudflare Worker, Vercel, etc.).
When no proxy URL is configured, the browser falls back to the
same-origin /api/namecoin/* path.

## New files

- src/util/namecoin.ts — Browser-side resolver (HTTP client, identifier
  parsing, value extraction, LRU cache, settings helpers)
- src/app/views/NamecoinSettings.svelte — Settings UI (enable/disable,
  custom proxy URL, test resolution)
- tests/namecoin.test.ts — 25 unit tests mirroring Amethyst's test suite
- proxy/electrumx-client.mjs — Server-side ElectrumX TCP client
  (scripthash computation, transaction parsing, NAME_UPDATE extraction)
- proxy/electrumx-proxy.mjs — Standalone HTTP proxy server
- proxy/vite-plugin-namecoin.mjs — Vite dev middleware plugin

## Modified files

- vite.config.js — Register namecoin proxy plugin
- .env.template — Add VITE_NAMECOIN_PROXY_URL
- src/util/nostr.ts — Route .bit identifiers to Namecoin resolver
- src/engine/state.ts — User settings + env var + settings accessor
- src/app/App.svelte — Register /settings/namecoin route
- src/app/MenuDesktop.svelte — Namecoin link in desktop settings
- src/app/MenuMobile.svelte — Namecoin link in mobile settings
- src/app/shared/PersonHandle.svelte — .bit badge on verified handles
- src/app/shared/SearchResults.svelte — Trigger entity parsing for .bit
- src/app/views/PersonInfo.svelte — Blockchain verification status
- src/app/views/UserProfile.svelte — Mention .bit support in NIP-05 info

## Supported identifier formats

- alice@example.bit → d/example, looks up 'alice' in nostr.names
- _@example.bit / example.bit → root entry
- d/example → direct Namecoin domain name lookup
- id/alice → Namecoin identity namespace lookup
@mstrofnone mstrofnone force-pushed the feat/namecoin-nip05 branch from e896781 to 0d43742 Compare March 15, 2026 23:54
@mstrofnone mstrofnone changed the title feat: Namecoin NIP-05 identity resolution (.bit domains) feat: Namecoin NIP-05 identity resolution (.bit domains) — proof of concept Mar 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant