About

fbd is a Fistbump full node written in Swift, built by eskimo.

Read the whitepaper, browse the block explorer, or view the source code.

Download

Download pre-built releases for the node, wallet, and other software from fistbump.org.

Build from source

$git clone https://github.com/fistbump-org/fbd.git $cd fbd

macOS

$./install/macos.sh

Linux

$./install/linux.sh

Windows

$powershell -ExecutionPolicy Bypass -File install\windows.ps1

macOS, Linux, Windows. Each script installs dependencies, builds, and installs fbd and fbdctl.

Configuration

$fbd --network main --host 0.0.0.0 --log-level debug --index-tx
FlagDescriptionDefault
--network, -nmain, testnet, regtest, simnetmain
--datadir, -dData directory~/.fbd (macOS/Linux), %LOCALAPPDATA%\fbd (Windows)
--hostP2P listen address127.0.0.1
--port, -pP2P portnetwork default
--max-outboundMax outbound peers8
--max-inboundMax inbound peers64
--seedsAdditional seed peers
--nodesConnect only to these peers
--agentUser agent suffix
--rpc-hostRPC listen address127.0.0.1
--rpc-portRPC portnetwork default
--api-keyRPC API keyauto-generated
--no-authDisable RPC authfalse
--ns-hostDNS listen address127.0.0.1
--ns-portDNS portnetwork default
--miner-addressCoinbase payout address
--miner-threadsCPU miner threads (0 = auto, max = cores - 1)0
--log-leveltrace, debug, info, warning, errorinfo
--index-txTransaction index for historical lookupsfalse
--index-addressAddress index for lookups by addressfalse
--index-auctionsAuction index for bid historyfalse

Config file

<datadir>/fbd.conf — CLI flags override config values.

network = main host = 0.0.0.0 nodes = 1.2.3.4:32867, 5.6.7.8:32867 log-level = debug index-tx = true

Networks

NetworkP2PBrontideRPCDNSHRP
main32867328683286932870fb
testnet42867428684286942870ft
regtest52867528685286952870fr
simnet62867628686286962870fs

RPC

fbdctl

$fbdctl getblockcount
{ "result": 318614, "error": null }

curl

$curl -s http://127.0.0.1:32869/ \ -X POST \ -u "x:YOUR_API_KEY" \ -d '{"method":"getblockcount","params":[],"id":1}'
{ "result": 318614, "error": null, "id": 1 }
curl returns standard JSON-RPC with an extra id field. Examples below show the result value only.

Auth

A random key is generated on startup at <datadir>/.cookie. fbdctl reads it automatically.

HTTP Basic auth: username x, password is the key.

Wallets

Wallet commands require --wallet <name>. Auto-selected if only one wallet exists.

$fbdctl --wallet main getbalance

Methods tagged wallet required need this flag.

Subdomains

Fistbump supports on-chain subdomains — names like example.fistbump that are registered and resolved entirely on the blockchain. Subdomains go through the same auction process as top-level names.

Enabling subdomains

A name owner must explicitly opt in by setting the auctionSubdomains flag via sendupdate:

$fbdctl sendupdate fistbump '{"records":[]}' --flags 1

Once enabled, this flag is permanent — it cannot be revoked and persists through name expiration and re-registration.

Delegation records (DS, NS, GLUE4, GLUE6, SYNTH4, SYNTH6) and SUB records cannot coexist with auctionSubdomains. You must remove all delegation and SUB records before enabling subdomain auctions, and you cannot add them after enabling.

Registering a subdomain

Once the parent has auctionSubdomains enabled, anyone can open an auction for a subdomain:

$fbdctl sendopen example.fistbump

The node automatically detects the parent name, verifies that subdomains are allowed, and includes the parentHash (SHA3-256 of the parent name) in the covenant. Bidding and registration follow the same auction flow as top-level names.

Payment splitting

When a subdomain is registered, the winning bid is split:

Name rules

Inline subdomain records (SUB)

As an alternative to subdomain auctions, name owners can define subdomains directly in their resource data using SUB records. A SUB record contains a single label and a set of nested records:

$fbdctl sendupdate fistbump '{"records":[{"type":"SUB","name":"www","records":[{"type":"A","address":"1.2.3.4"}]}]}'

This creates a www.fistbump subdomain that resolves to 1.2.3.4, without any auction. The owner retains full control and can update or remove SUB records at any time. SUB records and auctionSubdomains are mutually exclusive — you cannot use inline subdomains and subdomain auctions on the same name.

DNS resolution

The built-in DNS server resolves subdomains using progressive lookup: it first tries the full name (example.fistbump), then strips the leftmost label to check the parent. If the parent has a matching SUB record, those nested records are returned. If the subdomain exists as its own on-chain name with resource records, those are returned directly. If neither exists, the parent's NS records are returned as a referral.

DANE / TLSA

TLSA records are stored on-chain with port and protocol fields. The DNS server synthesizes standard DANE owner names:

$dig @127.0.0.1 -p 32870 TLSA _443._tcp.fistbump

This works for both top-level names and subdomains.

Querying subdomain info

$fbdctl getnameinfo example.fistbump
{ "name": "example.fistbump", "parentHash": "b9c3...", "flags": 0, "auctionSubdomains": false, ... }

Records

Resource data passed to sendupdate, sendregister, and sendmany is a JSON object with a records array. Each record has a type field and type-specific fields.

$fbdctl --wallet main sendupdate myname '{"records":[...]}'

A

IPv4 address.

{"type": "A", "address": "1.2.3.4"}

AAAA

IPv6 address.

{"type": "AAAA", "address": "2001:db8::1"}

CNAME

Canonical name (alias).

{"type": "CNAME", "target": "alias.example.com."}

NS

Nameserver delegation (without glue).

{"type": "NS", "ns": "ns1.example.com."}

GLUE4

Nameserver with an IPv4 glue address.

{"type": "GLUE4", "ns": "ns1.example.com.", "address": "1.2.3.4"}

GLUE6

Nameserver with an IPv6 glue address.

{"type": "GLUE6", "ns": "ns1.example.com.", "address": "2001:db8::1"}

SYNTH4

Synthetic nameserver from an IPv4 address. The NS name is generated automatically at serve time.

{"type": "SYNTH4", "address": "1.2.3.4"}

SYNTH6

Synthetic nameserver from an IPv6 address. The NS name is generated automatically at serve time.

{"type": "SYNTH6", "address": "2001:db8::1"}

MX

Mail exchange. preference is the priority (lower = higher priority, 0–65535).

{"type": "MX", "preference": 10, "exchange": "mail.example.com."}

TXT

Text record. txt is an array of strings (each max 255 bytes).

{"type": "TXT", "txt": ["v=spf1 include:_spf.example.com -all"]}

DS

DNSSEC delegation signer. algorithm: 8 (RSA/SHA-256), 13 (ECDSA P-256), 14 (ECDSA P-384), 15 (Ed25519), 16 (Ed448). digestType: 1 (SHA-1), 2 (SHA-256), 4 (SHA-384). digest is hex-encoded.

{"type": "DS", "keyTag": 12345, "algorithm": 13, "digestType": 2, "digest": "a1b2c3..."}

TLSA

DANE certificate association. protocol: 6 (TCP), 17 (UDP), 132 (SCTP). usage: 0 (PKIX-TA), 1 (PKIX-EE), 2 (DANE-TA), 3 (DANE-EE). selector: 0 (full cert), 1 (SubjectPublicKeyInfo). matchingType: 0 (exact), 1 (SHA-256), 2 (SHA-512). certificate is hex-encoded.

{"type": "TLSA", "port": 443, "protocol": 6, "usage": 3, "selector": 1, "matchingType": 1, "certificate": "30820122..."}

The DNS server synthesizes the standard DANE owner name (_443._tcp.myname) at serve time from the port and protocol fields.

CAA

Certification authority authorization. flags: 0 or 128. tag: issue, issuewild, or iodef.

{"type": "CAA", "flags": 0, "tag": "issue", "value": "letsencrypt.org"}

WALLET

Fistbump payment address. Associates a receive address with the name for on-chain payments.

{"type": "WALLET", "address": "fb1q..."}

SUB

Inline subdomain records. name is a single label (1–63 bytes). records is a nested array of records. See Subdomains for details.

{"type": "SUB", "name": "www", "records": [{"type": "A", "address": "1.2.3.4"}]}

Example

A complete sendupdate setting an A record, mail exchange, TXT for SPF, and a TLSA record:

$fbdctl --wallet main sendupdate myname '{ "records": [ {"type": "A", "address": "1.2.3.4"}, {"type": "MX", "preference": 10, "exchange": "mail.example.com."}, {"type": "TXT", "txt": ["v=spf1 include:_spf.example.com -all"]}, {"type": "TLSA", "port": 443, "protocol": 6, "usage": 3, "selector": 1, "matchingType": 1, "certificate": "30820122..."} ] }'

The same records can be set in a batch with sendmany:

$fbdctl --wallet main sendmany update myname '{"records":[{"type":"A","address":"1.2.3.4"}]}'
Hostnames (ns, target, exchange) use DNS format: labels 1–63 bytes, alphanumeric plus hyphens, no leading or trailing hyphens. Trailing dot is optional. Names with auctionSubdomains enabled cannot use delegation records (NS, DS, GLUE4, GLUE6, SYNTH4, SYNTH6).

Browser Extension

The Fistbump browser extension exposes a window.fistbump API to web pages, letting dApps connect to the user's wallet, send transactions, and sign messages. It communicates with the Fistbump desktop wallet via Chrome native messaging — no HTTP, no localhost ports.

Available for Chrome, Chromium, Brave, Edge, Arc, Vivaldi, and Firefox. Requires the Fistbump desktop wallet to be installed.

Install

Load the extension in developer mode:

Chrome / Chromium-based browsers

  1. Open chrome://extensions
  2. Toggle Developer mode on (top-right)
  3. Click Load unpacked and select the extension directory

Firefox

  1. Open about:debugging#/runtime/this-firefox
  2. Click Load Temporary Add-on
  3. Select manifest.json from the extension directory

The Fistbump icon appears in the toolbar. Clicking it opens a popup showing the extension version and a live indicator for whether the wallet is running.

API

All methods are available on window.fistbump and return promises. You can detect the extension with window.fistbump.isFistbump.

connect

Request wallet access and get the user's address. The first call from a new origin shows a confirmation modal in the wallet. Subsequent calls from the same origin skip the prompt.

// connect to the wallet const result = await window.fistbump.connect(); // → { address: "fb1...", origin: "https://example.com" }

isConnected

Check whether the current origin is already approved, without prompting the user.

const connected = await window.fistbump.isConnected(); // → true or false

sendTx

Build, sign, and broadcast a transaction. The to field accepts a raw fb1… address or a Fistbump name — the wallet resolves names automatically. A review modal shows the recipient, fee, and total before signing.

// send by name const tx = await window.fistbump.sendTx({ to: 'eskimo', amount: 1.5 }); // → { txid: "a1b2c3..." } // send by address const tx = await window.fistbump.sendTx({ to: 'fb1q...', amount: 0.25 }); // → { txid: "d4e5f6..." }

signMessage

Sign an arbitrary message. Without a name option, signs with the wallet's receive address. With name, signs with the key that owns that Fistbump name — the wallet verifies ownership before showing the review modal.

// sign with wallet address const sig = await window.fistbump.signMessage('hello'); // → { signature: "...", address: "fb1..." } // sign as a Fistbump name const sig = await window.fistbump.signMessage('hello', { name: 'eskimo' }); // → { signature: "...", name: "eskimo" }

How it works

The extension uses a four-layer message flow to reach the desktop wallet:

LayerComponentTransport
1dApp calls window.fistbumppostMessage
2Content script relays to service workerchrome.runtime.sendMessage
3Service worker connects to native hostchrome.runtime.connectNative
4Bridge forwards to wallet over local socketUnix socket / named pipe

If the wallet isn't running, the bridge launches it automatically and waits for the socket to come up before forwarding the request.

Security