How-To Guide: Namecoin Integration in Amethyst
- How-To Guide: Namecoin Integration in Amethyst
How-To Guide: Namecoin Integration in Amethyst
What This Is
Amethyst — the Android Nostr client — includes built-in support for resolving Namecoin blockchain names as NIP-05 identities. Instead of relying on a web server to verify alice@example.com, a user can set their NIP-05 address to something like alice@example.bit, and Amethyst will verify it directly against the Namecoin blockchain via ElectrumX servers.
This is censorship-resistant identity: no web server to seize, no DNS to hijack, no TLS certificate to revoke. The name-to-pubkey mapping lives in Namecoin UTXOs.
This guide covers how the system works, how to use it as an end-user, and how to register or update a Namecoin name value so that it resolves to your Nostr pubkey.
Part 1: Using Namecoin Names in Amethyst (End-User)
Searching for Namecoin Users
Open Amethyst’s search bar and type any of these formats:
| Search Input | What It Does |
|---|---|
alice@example.bit |
Looks up the alice entry in Namecoin name d/example |
example.bit |
Looks up the root (_) entry in d/example |
d/example |
Direct Namecoin domain namespace lookup (root entry) |
id/alice |
Direct Namecoin identity namespace lookup |
Amethyst queries the Namecoin blockchain through an ElectrumX server, resolves the name to a Nostr public key, and shows the matching user profile at the top of search results. There is a 400ms debounce on the search input to avoid flooding the server with requests.
NIP-05 Verification via Namecoin
If a Nostr profile has a .bit address in its nip05 metadata field (e.g. m@testls.bit), Amethyst will automatically verify it against the blockchain instead of making an HTTP request. The verification badge works the same as traditional NIP-05 — the only difference is the trust anchor: blockchain consensus rather than a web server.
Configuring Namecoin Settings
Navigate to Settings → Namecoin Settings in Amethyst. From here you can:
- Enable/disable Namecoin resolution entirely
- Add custom ElectrumX servers — useful if you run your own server for privacy
- Remove servers from the custom list
- Test server connectivity — checks TLS, latency, and optionally resolves a test name
- Pin TLS certificates — after testing a server with a self-signed cert, you can pin its certificate via TOFU (Trust On First Use)
- Reset to defaults — reverts to the built-in server list
Server String Format
When adding a custom server, use the format:
host:port → TLS connection (default)
host:port:tcp → Plaintext TCP (for .onion or local servers)
Examples:
electrumx.testls.space:50002
my-local-server.lan:50001:tcp
i665jpw...dsid.onion:50002
Tor Integration
If you have Tor enabled in Amethyst and have set “NIP-05 verifications via Tor” to on, all Namecoin ElectrumX connections are routed through your Tor SOCKS proxy. The server list also switches to prioritize .onion addresses:
| Tor Setting | Primary Server | Fallback |
|---|---|---|
| Off | electrumx.testls.space:50002 |
nmc2.bitcoins.sk:57002 |
| On | .onion:50002 (hidden service) |
electrumx.testls.space:50002 (via Tor) |
Toggling Tor on or off in settings takes effect on the next lookup — no app restart needed.
Default ElectrumX Servers
Amethyst ships with these built-in servers:
| Server | Port | TLS | Notes |
|---|---|---|---|
electrumx.testls.space |
50002 | Yes (self-signed, pinned) | Primary |
nmc2.bitcoins.sk |
57002 | Yes (self-signed, pinned) | Fallback |
46.229.238.187 |
57002 | Yes (self-signed, pinned) | IP fallback |
The self-signed certificates for these servers are pinned directly into the Amethyst binary, so connections succeed even on devices with strict TLS enforcement (Samsung One UI 7, GrapheneOS).
Part 2: Registering & Updating a Namecoin Name Value
Amethyst itself is a read-only consumer of Namecoin data — it resolves names but does not write to the blockchain. To register or update a Namecoin name so that it points to your Nostr pubkey, you need to interact with the Namecoin blockchain directly using Namecoin Core (the full node software) or a compatible wallet.
Prerequisites
- Namecoin Core or Electrum-NMC installed and synced — download from namecoin.org (this guide uses Namecoin Core as an example)
- NMC (Namecoin coins) in your wallet — needed to pay registration and update fees (0.01 NMC per registration operation plus associated fees)
- Your Nostr hex public key — the 64-character hex string (not
npub1...). You can find this in Amethyst under your profile settings or by converting your npub using a tool like https://nostrdeck.com/key-converter.php
Step 1: Choose Your Namespace
Amethyst supports two Namecoin namespaces:
Domain namespace (d/) — maps a domain-like name to one or more Nostr pubkeys. This is the most common choice and mirrors NIP-05’s user@domain format.
Identity namespace (id/) — maps a personal identity name to a single Nostr pubkey.
For most users, the d/ namespace is the right choice because it allows user@name.bit style addresses.
Step 2: Register a New Name
Open a terminal and use namecoin-cli:
# Register a name in the domain namespace
# This creates a "pre-registration" (name_new) first
namecoin-cli name_new "d/yourname"
This returns a transaction ID and a random value. Save both — you need the random value for the next step. Wait for the transaction to get at least 12 confirmations (about 2 hours).
Then finalize the registration:
namecoin-cli name_firstupdate "d/yourname" <random_value> '<json_value>'
Step 3: Set the Name Value for Nostr
The JSON value you store in the Namecoin name is what Amethyst reads to find your Nostr pubkey. There are several supported formats.
Simple Form — Single User, Root Domain
If you just want yourname.bit to resolve to your pubkey:
{
"nostr": "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9"
}
namecoin-cli name_firstupdate "d/yourname" <random> \
'{"nostr":"b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9"}'
This makes yourname.bit and _@yourname.bit resolve to the given pubkey.
Extended Form — Multiple Users with Relay Hints
If you want to host multiple identities under one .bit domain (like a NIP-05 server hosting multiple users):
{
"nostr": {
"names": {
"_": "aaaa000000000000000000000000000000000000000000000000000000000001",
"alice": "bbbb000000000000000000000000000000000000000000000000000000000002",
"bob": "cccc000000000000000000000000000000000000000000000000000000000003"
},
"relays": {
"bbbb000000000000000000000000000000000000000000000000000000000002": [
"wss://relay.example.com",
"wss://nos.lol"
]
}
}
}
With this value stored in d/yourname:
_@yourname.bitoryourname.bit→ resolves to the_pubkeyalice@yourname.bit→ resolves to Alice’s pubkey, with relay hintsbob@yourname.bit→ resolves to Bob’s pubkey
namecoin-cli name_firstupdate "d/yourname" <random> \
'{"nostr":{"names":{"_":"aaaa...0001","alice":"bbbb...0002"},"relays":{"bbbb...0002":["wss://relay.example.com"]}}}'
Identity Namespace Form (id/)
For the id/ namespace, the value format is simpler:
{
"nostr": "cccc000000000000000000000000000000000000000000000000000000000003"
}
Or with relay hints:
{
"nostr": {
"pubkey": "dddd000000000000000000000000000000000000000000000000000000000004",
"relays": ["wss://relay.example.com"]
}
}
namecoin-cli name_firstupdate "id/youridentity" <random> \
'{"nostr":{"pubkey":"dddd...0004","relays":["wss://relay.example.com"]}}'
Step 4: Update an Existing Name
To change the Nostr pubkey (or any other data) associated with an already-registered name, use name_update:
namecoin-cli name_update "d/yourname" \
'{"nostr":{"names":{"_":"<new_hex_pubkey>","alice":"<alice_hex_pubkey>"},"relays":{"<alice_hex_pubkey>":["wss://relay.example.com"]}}}'
This broadcasts a NAME_UPDATE transaction to the Namecoin blockchain. Once confirmed, Amethyst (and any other client that supports Namecoin NIP-05) will resolve the name to the new value.
Important notes on updates:
- Each
name_updatecosts a small NMC fee - Names expire after 36,000 blocks (~36 weeks) if not updated or renewed. Amethyst checks expiry and will treat expired names as non-existent.
- To simply renew without changing the value, you can
name_updatewith the same value - Updates replace the entire value — there is no partial update mechanism
Step 5: Set Your NIP-05 in Your Nostr Profile
Once your Namecoin name is registered and confirmed on the blockchain, update your Nostr profile’s nip05 field in Amethyst:
- Go to your profile → Edit
- Set the NIP-05 field to your Namecoin address, e.g.
alice@yourname.bit - Save
Amethyst (and other Namecoin-aware clients) will verify this address against the blockchain and show the verification badge.
Step 6: Verify It Works
In Amethyst’s search bar, type your Namecoin identifier. You should see your profile appear at the top of results:
| Test Query | Expected Result |
|---|---|
alice@yourname.bit |
Your profile (matched via alice entry) |
yourname.bit |
Your profile (matched via _ root entry) |
d/yourname |
Your profile (direct namespace lookup) |
Part 3: How It Works Under the Hood
Resolution Flow
When Amethyst encounters a .bit address, here is what happens:
1. User types "alice@example.bit" in search (or a profile has it in nip05)
2. NamecoinNameResolver.parseIdentifier() parses it:
→ namecoinName = "d/example", localPart = "alice", namespace = DOMAIN
3. ElectrumXClient builds a canonical "name index script":
OP_NAME_UPDATE(0x53) + push("d/example") + push("") + OP_2DROP + OP_DROP + OP_RETURN
4. Computes the Electrum-style scripthash:
SHA-256(script) → reverse bytes → hex encode
5. Queries the ElectrumX server:
→ blockchain.scripthash.get_history(scripthash)
← [{tx_hash: "abc...", height: 814278}, ...]
6. Fetches the latest transaction (last entry = most recent name update):
→ blockchain.transaction.get(tx_hash, verbose=true)
7. Checks block headers to verify the name hasn't expired:
→ blockchain.headers.subscribe
← current height; if (current - update_height) >= 36000 → expired
8. Parses the NAME_UPDATE script from the transaction output:
Extracts: name string + JSON value
9. NamecoinNameResolver.extractFromDomainValue() reads the JSON:
Finds "nostr" → "names" → "alice" → hex pubkey
10. Returns NamecoinNostrResult(pubkey, relays, namecoinName, localPart)
Caching
Resolved names are cached in an LRU cache (default 500 entries, 1-hour TTL). Both positive and negative results are cached. The cache is keyed by the normalized (lowercased, trimmed) identifier.
Architecture Layers
The implementation is split across two modules:
Quartz (library, no Android dependencies):
NamecoinNameResolver— identifier parsing, value extraction, resolution orchestrationElectrumXClient— TCP/TLS connection to ElectrumX, JSON-RPC, script parsingNamecoinLookupCache— LRU cache with TTLIElectrumXClient— interface for testing/mocking
Amethyst (Android app):
NamecoinNameService— application singleton, wires up caching and proxy-aware clientNamecoinSettings/NamecoinSharedPreferences— persistent configNamecoinSettingsScreen/NamecoinSettingsSection— settings UIProxiedSocketFactory— SOCKS5 proxy routing for TorRoleBasedHttpClientBuilder.socketFactoryForNip05()— Tor-aware socket creation
The integration into NIP-05 is minimal: Nip05Client checks if an identifier matches .bit / d/ / id/ patterns, and routes to NamecoinNameResolver instead of making an HTTP fetch. Non-Namecoin identifiers are completely unaffected.
Part 4: Security Considerations
Tor integration — When enabled, all ElectrumX connections go through the Tor SOCKS proxy. The .onion server is preferred for end-to-end onion routing. Socket factory and server list are evaluated per-request via lambdas, so toggling Tor takes effect immediately.
TLS certificate pinning — The primary ElectrumX servers use self-signed certificates. Their PEM-encoded certs are pinned in the Amethyst binary. Users can pin additional certs via the TOFU flow in settings (test server → view fingerprint → confirm → pin). This is necessary because Samsung One UI 7 and GrapheneOS reject trust-all TrustManagers.
Name expiry — Namecoin names expire after ~36,000 blocks (~250 days) if not renewed. The ElectrumX client checks the current block height against the name’s last update height and treats expired names as non-existent.
Server trust — The client trusts that the ElectrumX server returns accurate transaction data. The Namecoin blockchain itself is the trust anchor — a MITM could return stale data but cannot forge name registrations. For higher assurance, SPV proof verification could be added in the future.
Quick Reference
Identifier Format Summary
| User Input | Namecoin Name | Looked-Up Entry | Namespace |
|---|---|---|---|
alice@example.bit |
d/example |
alice |
Domain |
_@example.bit |
d/example |
_ (root) |
Domain |
example.bit |
d/example |
_ (root) |
Domain |
d/example |
d/example |
_ (root) |
Domain |
id/alice |
id/alice |
_ (root) |
Identity |
Name Value JSON Formats
Domain simple:
{"nostr": "<64-char-hex-pubkey>"}
Domain extended (multi-user + relays):
{"nostr": {"names": {"_": "<hex>", "alice": "<hex>"}, "relays": {"<hex>": ["wss://..."]}}}
Identity simple:
{"nostr": "<64-char-hex-pubkey>"}
Identity with relays:
{"nostr": {"pubkey": "<hex>", "relays": ["wss://..."]}}
Namecoin CLI Commands
# Register a new name
namecoin-cli name_new "d/yourname"
# Returns: [txid, random_value]
# Example:
# [
# "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
# "6e4a2d190f7b"
# ]
# SAVE BOTH — you need the random value (2nd element) for name_firstupdate.
# Wait for at least 12 confirmations (~2 hours) before proceeding.
# Finalize registration with the random value from name_new
namecoin-cli name_firstupdate "d/yourname" <random> '<json>'
# Update an existing name
namecoin-cli name_update "d/yourname" '<new_json>'
# Check a name's current value
namecoin-cli name_show "d/yourname"
# List your names
namecoin-cli name_list
Getting Namecoins via DVM (NIP-90)
Don’t have NMC? You can acquire Namecoin through a Nostr-native exchange service — no centralized exchange account needed.
The NMC Exchange DVM is a NIP-90 Data Vending Machine that accepts Lightning Bitcoin (⚡ sats) or Cashu ecash (🥜) and sends NMC directly to your Namecoin address.
How it works:
- Send a
kind:5950job request to the DVM with your NMC address and desired amount (0.2–0.5 NMC) - The DVM responds with a Lightning invoice or Cashu payment info
- Pay via Lightning zap or Cashu ecash
- Receive NMC at your designated address — the DVM sends the
kind:6950result with the NMC transaction ID
Rate: $10 USD per NMC (max 0.5 NMC per order)
Payment methods: Lightning Bitcoin ⚡ | Cashu ecash 🥜
DVM address (NIP-89):
Referenced article not yet available
naddr1qvzqqq…d5njfjn5
View on NostrHub: NMC Exchange DVM
Example job request:
{
"kind": 5950,
"tags": [
["i", "<your-nmc-address>", "text"],
["param", "amount", "0.5"],
["bid", "10000000"]
],
"content": ""
}
This is an experimental service — no guarantees of uptime, but it’s a quick way to get NMC for name registrations without touching a centralized exchange.
Testing (for Developers)
# Run unit tests
./gradlew :quartz:jvmTest --tests "*NamecoinNameResolverTest*"
# Build and install debug APK
./gradlew assemblePlayDebug
adb install -r amethyst/build/outputs/apk/play/debug/amethyst-play-universal-debug.apk
# Monitor ElectrumX connections
adb root && adb shell tcpdump -i any -nn port 50002 or port 50006
Live Test Data
The name d/testls is registered on the Namecoin blockchain with value:
{"nostr": {"names": {"_": "6cdebccabda1dfa058ab85352a79509b592b2bdfa0370325e28ec1cb4f18667d"}}}
So _@testls.bit (or simply testls.bit) resolves to Vitor Pamplona’s Nostr profile.