Name bindings and the name log
A bWalletX name points to one identity key. The owner’s wallet signs that binding, and every change is written to a public, hash-chained log. Background: bwalletx.com/name-bindings. Proposal: bsv-blockchain/BRCs#329.
Look up a name
Section titled “Look up a name”curl https://pay.bwallet.space/api/paymail/id/npg@bwalletx.comThe answer has pubkey and, once the owner’s wallet has signed, a binding with alias, seq, timestamp and signature.
Verify the signature
Section titled “Verify the signature”The signed message is the fields sorted by name, as key=value, after bwallet-paymail|v1|bind:
bwallet-paymail|v1|bind|alias=<alias>|identityKey=<key>|seq=<n>|timestamp=<ms>It is signed with BRC-43 protocol [2, "bwallet paymail"], key ID "1", counterparty anyone. A complete script (node verify-binding.mjs npg@bwalletx.com, needs @bsv/sdk):
// Verify a bWalletX signed name binding from the public pki endpoint.import { ProtoWallet, Utils } from '@bsv/sdk';
const handle = process.argv[2] ?? 'npg@bwalletx.com';const r = await fetch(`https://pay.bwallet.space/api/paymail/id/${handle}`).then((x) => x.json());const b = r.binding;if (!b) throw new Error('This name has no signed binding yet');
// The signed text: bwallet-paymail|v1|bind|<fields sorted by name as key=value>const fields = { alias: b.alias, identityKey: b.identityKey, seq: String(b.seq), timestamp: b.timestamp };const message = ['bwallet-paymail', 'v1', 'bind', ...Object.keys(fields).sort().map((k) => `${k}=${fields[k]}`)].join('|');
const { valid } = await new ProtoWallet('anyone').verifySignature({ data: Utils.toArray(message, 'utf8'), signature: Utils.toArray(b.signature, 'hex'), protocolID: [2, 'bwallet paymail'], keyID: '1', counterparty: b.identityKey,});console.log(handle, '->', b.identityKey.slice(0, 16) + '…', 'seq', b.seq);console.log('pki pubkey matches binding:', r.pubkey === b.identityKey);console.log('owner signature valid:', valid);Output when we ran it (17 Oct 2026):
npg@bwalletx.com -> 026607f69e27c84e… seq 1pki pubkey matches binding: trueowner signature valid: trueIf binding is missing, the name has not been signed by its owner yet: treat it as unverified and say so to your user.
Read the log
Section titled “Read the log”curl https://pay.bwallet.space/api/paymail/name-logcurl "https://pay.bwallet.space/api/paymail/name-log?alias=npg"Each entry has alias, identity_key, prev_identity_key, action, actor (owner, server or admin), seq, signature, note, created_at and its hash:
entry_hash = sha256(prev_hash|alias|identity_key|prev_identity_key|action|actor|seq|signature|note|created_at)Empty strings for missing fields; created_at in UTC with microseconds (YYYY-MM-DDTHH:MM:SS.ffffffZ). The first entry’s prev_hash is 64 zeros. Add ?alias=npg for one name’s history.
This script re-computes every hash and checks each entry points at the one before:
// Re-compute the public name log's hash chain (each entry commits to the one before it).import { createHash } from 'node:crypto';
const { log } = await fetch('https://pay.bwallet.space/api/paymail/name-log').then((x) => x.json());const entries = [...log].sort((a, b) => a.id - b.id);const ts = (s) => new Date(s).toISOString().replace(/\.(\d{3})Z$/, (_m, ms) => `.${ms}${s.match(/\.\d{3}(\d{3})/)?.[1] ?? '000'}Z`);let ok = 0;for (const e of entries) { const parts = [e.prev_hash, e.alias, e.identity_key ?? '', e.prev_identity_key ?? '', e.action, e.actor, e.seq ?? '', e.signature ?? '', e.note ?? '', ts(e.created_at)]; const h = createHash('sha256').update(parts.map(String).join('|'), 'utf8').digest('hex'); if (h === e.entry_hash) ok++; else console.log('mismatch at entry', e.id);}const linked = entries.every((e, i) => i === 0 || e.prev_hash === entries[i - 1].entry_hash);console.log(`${entries.length} entries, ${ok} hashes recomputed OK, chain linked: ${linked}`);10 entries, 10 hashes recomputed OK, chain linked: trueHandovers
Section titled “Handovers”A name moves only with two signatures: a release by the current owner and an accept by the new one. There is no server-side recovery for a lost key; the name stays bound to it.
Status
Section titled “Status”- ✓ Owner-signed bindings in the paymail
pkianswer - ✓ Public hash-chained log, including admin edits
- ✓ Two-party handovers
- ◻ Daily on-chain anchor of the log head