Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

sat-tui

sat-tui is the operator dashboard for satd: a curses-style terminal UI that connects over JSON-RPC and shows what the node is doing. It is read-only. The TUI cannot change node state. Bitcoin Core ships no equivalent surface.

This document is the reference for what the TUI shows. It is not a walkthrough. For guidance on what to look at first, see Observability & Metrics.

Running

sat-tui is built as part of the workspace and ships in every release tarball alongside satd and sat-cli.

sat-tui                        # mainnet, default RPC port (8332)
sat-tui --regtest              # regtest, port 18443
sat-tui --testnet              # testnet, port 18332
sat-tui --signet               # signet, port 38332

Authentication

Same precedence as sat-cli:

  1. --rpcuser + --rpcpassword if both provided.
  2. Cookie file at --rpccookiefile if provided.
  3. Auto-detected cookie under --datadir (default ~/.bitcoin) for the active network.

If the cookie rotates while the TUI is running, for example because satd restarted, the RPC client re-reads the cookie file and retries once. If the retry also fails, the TUI falls back to the "Connecting to satd…" splash.

Other CLI flags

FlagDefaultMeaning
--rpcconnect <host>127.0.0.1RPC host.
--rpcport <port>per-network defaultOverride the auto-detected port.
--datadir <path>~/.bitcoinUsed to locate the cookie file.
--rpcuser <user>(none)Userpass auth (with --rpcpassword).
--rpcpassword <pass>(none)Userpass auth (with --rpcuser).
--rpccookiefile <path>autoOverride cookie path.

The TUI exits cleanly on q or Ctrl-C and restores the terminal mode.

Connection states

Before any view is shown, sat-tui is in one of three connection states:

  • "Connecting to satd…" (yellow, centered): RPC is unreachable, or has returned only failures so far. Common during cold start, satd restart, or misconfigured auth.
  • Startup splash: satd is up but still starting (header scan, reindex, address-index backfill). The splash is sourced from satd's getstartupinfo RPC; see Startup splash below.
  • Active view: getblockchaininfo succeeded and one of the four main views is rendered.

A red ✕ stale indicator in the title bar means the last successful poll is more than about 3 seconds old. It means RPC is degraded right now. The TUI recovers on its own when polling resumes.

Views

There are four main views, a fifth while the node has NAP on, plus a startup splash and three modal overlays.

The active view is auto-selected from chain state (is_ibd): the IBD view during initial block download, the Steady view once synced. Press 1 / 2 / 3 / 4 (and 5, see NAP view) to force a view. Press the same key again to return to auto-detect.

Startup splash

Shown while satd is in startup (header scan, reindex, address-index backfill). The splash is one panel:

FieldMeaning
PhaseCurrent startup phase (e.g. reindex_scan, reindex_connect, headers, verify).
StatusFree-form human-readable description from satd.
GaugeProgress through the current phase, 0–100%.
ElapsedWall-clock time since this phase began.
RateItems per second: blocks, headers, or whatever the phase iterates over.
ETAEstimated remaining time for this phase only, not whole startup.

ETAs cover one phase at a time because per-item costs differ sharply between phases. During a reindex, the header scan and the block replay proceed at unrelated rates, so a whole-startup estimate would mislead until the replay dominates.

IBD view (1)

Shown while the node is in initial block download. Five panels stacked vertically:

Title bar

Chain name, satd version, is_ibd indicator.

Progress block

  • Blocks / target: connected blocks vs. the highest block any peer has advertised.
  • blk/s: blocks connected per second, EMA-smoothed.
  • hdr/s: headers received per second.
  • ETA: server-side estimate from satd's getibdprogress RPC. Per-block validation cost varies about 50× across history, from early empty blocks to modern weight-bound blocks. A naive "blocks remaining ÷ blk/s" calculation is wildly wrong, especially in the first few hundred thousand blocks.
  • Peers: connected peer count.

Block map

A bitmap of download state per block group (one cell ≈ many blocks):

GlyphColorMeaning
█greenConnected: validated and in the chain.
░cyanDownloaded: on disk, waiting for sequential connection.
▓yellowIn flight: requested from a peer, not yet received.
·dimPending: queued for download, not yet requested.

A healthy IBD shows a leading wave of █ followed by ░, with a narrow ▓ band at the frontier. Long stretches of ▓ mean a peer is slow or unresponsive. Long stretches of · mean the node is bandwidth-bound or short on peers.

Sync rate + stats

Two sparklines (about 90 seconds of history):

  • blk/s connected (yellow): rate of blocks fully validated.
  • blk/s downloaded (cyan): rate of blocks pulled from peers, all peers summed.

Plus a stats panel: Headers, Connected, Stored, In-Flight, Remaining.

Peers table

ColumnMeaning
AddrPeer IP and port.
AgentSubversion string (/Satoshi:25.1.0/, /satd:0.1.0/, …).
RecvBlocks received from this peer this session.
AssignedBlocks currently assigned to this peer for download.
RatePer-peer blk/s, EMA-smoothed. Shows — below 0.1 blk/s.

Up / Down highlights a row.

Steady view (2)

Default view once is_ibd=false. Six stacked panels.

Title bar

Health dot, uptime.

SymbolMeaning
● ready (green)Polling is fresh, node is at tip.
○ syncing (yellow)Polling is fresh, node is catching up a small lag.
✕ stale (red)Last poll is older than about 3 s. RPC may be degraded.

Chain + Latest block (split)

Chain (left half):

  • Height: current tip height.
  • Difficulty: raw difficulty value.
  • Hash Rate: network hashrate from getmininginfo (H/s).
  • Last Block: seconds since the tip block's timestamp. More than an hour is unusual.

Latest block (right half):

  • Hash: tip hash, truncated.
  • Txs: transaction count.
  • Size / Weight: bytes / weight units.
  • Fees: total miner fees collected (BTC).
  • Avg Rate: average effective fee rate across all txs (sat/vB).

Mempool + Fees (split)

Mempool (left half):

  • Txs: unconfirmed transactions. While a transaction-filtering policy is loaded, the quarantined count follows (12,345 +87 quarantined): the first number, like Size, covers the transactions the node relays and mines, and leaves the quarantined ones out.
  • Size: total bytes.
  • Min Rate: current mempool minimum fee. 0.0 is the default, the min-relay floor of about 1 sat/vB. A non-zero value means the mempool is full and is evicting low-fee txs. New txs need at least this rate to enter.
  • Tx Rate: recent tx-entry rate from getchaintxstats (tx/s).
  • Size distribution: bar chart of transactions per vbyte bucket (0, 100, 250, 500, 1k, 5k, 10k, 50k+), each bar labelled with its lower edge and its count. The bar heights are on a log scale, so a bucket of a few transactions stays visible next to one of tens of thousands; read the counts, not the heights, to compare buckets.

Fees (right half), fee tier estimates from estimatefees (mempool.space convention):

  • High: next-block target (1-block confirmation).
  • Medium: about 30 minutes (3-block target).
  • Low: about 1 hour (6-block target).
  • None: economy / min-relay floor.
  • Mode: the estimator's data source: historical, mempool, or blend.
  • Confidence: high (green) / medium (yellow) / low (red), for the High tier specifically.

UTXO + Network (split)

UTXO (left half):

  • UTXOs: total unspent outputs.
  • Total: sum of UTXO values (BTC).
  • Supply: fraction of the 21M cap. Asymptotic; never reaches 100%.
  • Age distribution: bar chart of UTXOs by age, from gettxoutsetinfo: <1h, 1h–1d, 1d–1w, 1w–1mo, 1mo–6mo, 6mo–1y, 1y–3y, 3y+. Log-scaled heights with the count on each bar, as above. The four youngest buckets are exact counts. The four older ones are estimated from 1000-block chunks, so they can be off by up to a chunk's worth of coins at each edge. After upgrading an existing datadir the node builds its exact counts with a one-time background scan; until it finishes, the heading reads Age Distribution: (est.) and the four youngest bars are yellow.

Network (right half):

  • Peers: inbound and outbound counts.

Peers table

Same controls as IBD's table; columns differ:

ColumnMeaning
AddrPeer IP:port.
AgentSubversion.
HeightPeer's best-known block height.
RecvTotal bytes transferred with this peer.

Services row

A single line summarizing satd's wallet-server surfaces, sourced from getserverstatus and getsatdindexinfo:

addr-idx <state>   [sp-idx <state>]   esplora <state>   electrum <state>

The sp-idx column appears only when the silent-payment index is enabled, so nodes not using it keep the three-column row.

Every column's dot follows one rule: green when done or serving, yellow while work is pending (a sync, backfill, scan or restore, or a pause or wait in one), red when it failed, dim when off or unknown.

addr-idx states:

DisplayMeaning
⬤ synced (green)Address-history index is at tip. Esplora and Electrum are safe to serve history.
⬤ syncing (yellow)Backfill in progress. Address queries may return partial history.
⬤ backfill pass N/2 XX% (C/S) ETA … (yellow)Active backfill with progress. C/S is cursor / snapshot height. The ETA is omitted for the first few seconds after a start, resume, or daemon restart — it is measured over the current working span, so it has nothing to report until that span has run (see Disk footprint).
⬤ backfill paused … (yellow)Backfill paused. Resume with sat-cli resumeindex address.
⬭ backfill FAILED — <err> (red)Backfill errored. Check journalctl or the satd logs.
⬭ off (gray)Address index disabled (-addressindex=0).
⬭ - (dim)Status unknown: older satd, or a transient RPC error.

sp-idx states (BIP 352 tweak index, served via getsilentpaymentblockdata and the streaming tweaks category):

DisplayMeaning
(column absent)Index disabled (-silentpaymentindex=0, the default), or a satd too old to report it. Exception: on an older satd with a backfill mid-flight, the backfill itself is proof the index is on, so the column shows (without a percentage — see below).
⬤ synced (green)Tweak index is at tip. getsilentpaymentblockdata and the streaming tweaks category return data.
⬤ syncing (yellow)Enabled but not caught up: fresh sync, or a backfill is still owed. Tweak-serving surfaces do not return data yet.
⬤ backfill XX% (C/S) ETA … (yellow)Active backfill with progress. C/S is cursor / snapshot height, and the percentage is the daemon-reported ratio over the taproot-era walk — not C/S, which would measure from genesis and overstate progress. Unlike the address index this is a single pass, so there is no pass counter. On a satd too old to report the ratio, the counts show with no percentage.
⬤ backfill paused … (yellow)Backfill paused. Resume with sat-cli resumeindex silentpayment.
⬭ backfill FAILED — <err> (red)Backfill errored. Check journalctl or the satd logs.

The silent-payment backfill is CPU-bound and can run for hours on mainnet; see Disk footprint for measured timings.

op-prune and op-restore states (OP_RETURN data pruning, from getblockchaininfo.opreturn_prune):

DisplayMeaning
(column absent)No block is stripped and pruning is off: an ordinary node, or one whose restore has finished.
⬤ op-prune scanning XX% (E/T), N stripped (yellow)pruneopreturn is on and the worker is still working through the chain: every block below height E examined, of the T past the strip depth. N blocks stripped so far.
⬤ op-prune N blocks stripped (green)The worker has caught up (within a few blocks of T) and keeps up with the tip; N blocks are stripped.
⬤ op-prune N blocks stripped, paused (…) (yellow)The worker waits for IBD, a reindex or an AssumeUTXO background validation to finish.
⬭ op-prune N blocks stripped, off (dim)Pruning is off but N stripped blocks remain; restoreopreturns downloads them again.
⬤ op-restore XX% (D/T) P peers ETA … (yellow)restoreopreturns is downloading: D of the pass's T stripped blocks restored, from P full peers. The percentage and ETA are the daemon's.
⬤ op-restore waiting for … (D/T) (yellow)The restore waits for a full peer (NODE_NETWORK), for disk space, or for the tip to catch up.

esplora and electrum states:

DisplayMeaning
⬭ <bind:port> (green)Bound and serving.
(tls <bind:port>) (cyan)Electrum TLS bind, additional column.
⬭ off (gray)Disabled in config, or auto-disabled (e.g. address index off).
⬭ - (dim)Unknown.

Keybindings hint, plus an unclean-shutdown indicator (⚠ unclean shutdown) if last_shutdown from getsysteminfo is dirty.

Mempool view (3)

Drill-down on unconfirmed transactions. Five panels, and a sixth, the quarantine panel, while a transaction-filtering policy is loaded.

Title bar

Health dot, uptime.

Summary strip

  • Txs: unconfirmed transaction count.
  • Bytes: total mempool bytes.
  • Min / Max fees: feerate range across mempool entries.
  • Δ last Ns: entries added and removed in the last polling window.

Quarantine (only with a filtering policy)

Appears under the summary strip while a ruleset is loaded (see Transaction filtering policy). The rest of the view counts the acting class only, as getmempoolinfo does; this panel is where the transactions the policy holds back show up.

  • Held: quarantined transactions and bytes, against the quarantinemempool budget.
  • Confirmed anyway: quarantined transactions later seen in a block, since satd started.
  • Foregone fees: the fees, above the template floor, of transactions withheld from block templates. Relay-only quarantine is still mined, so it is not counted.
  • fuel exhausted (red, only when non-zero): evaluations that ran out of fuel and were quarantined fail-safe.
  • ⚠ rules withhold Lightning enforcement relay (only when non-zero): see allowdangerousfilters in the policy chapter.
ColumnMeaning
ruleThe rule holding the transactions. (infectious: quarantined ancestor) holds descendants of quarantined transactions that matched no rule themselves.
heldTransactions held under the rule.
bytesTheir serialized size.
sat/vBTheir fee-rate range.
matchedTimes the rule matched since the ruleset loaded; - for a name that is not a rule in the loaded ruleset.

Rows are ordered by bytes held. Every quarantine rule gets a row, a dimmed one when it holds nothing; past five rows, the last line counts the rest.

Feerate histogram

Bars per feerate bucket (1–2, 2–5, 5–10, 10–20, 20–50, 50–100, 100–200, 200–500, 500+ sat/vB) with vbyte counts. Bars are colored by fee tier, matching the Steady view's Fees panel.

Trend + Top-N (split)

Trend (left): sparklines for Bytes, Txs, MinFee over about 40 minutes.

Top-N (right): scrollable table of the top 50 unconfirmed txs by ancestor feerate.

ColumnMeaning
#Rank within top 50.
vsizeVirtual size (vbytes).
anc sat/vBAncestor-adjusted effective feerate. Accounts for CPFP: a low-fee child gets pulled in by a high-fee parent.
A/DAncestor count / descendant count (chain depth in either direction).
ageTime since the tx entered the mempool.

Up / Down scroll.

Keybindings.

Chain view (4)

Long-horizon information that does not change every block. Three rows of two panels each.

Halvings | Retarget

Halvings:

  • Subsidy epoch: index (0 = pre-first-halving).
  • Subsidy: current block reward in BTC. Formula 50 >> halvings, saturates at 0 after halving 64.
  • Halving in: blocks until the next halving.
  • Halving ETA: estimated wall-clock time at the 10-minute target.
  • Progress bar through the current 210,000-block subsidy era.

Retarget:

  • Blocks to retarget: blocks until the next 2,016-block boundary.
  • Retarget ETA: wall-clock estimate at the 10-minute target.
  • Block time (epoch): observed average seconds per block within the current 2,016-block epoch. Empty at the start of an epoch.
  • Δ Est: predicted difficulty adjustment at the next retarget, clamped to ±300% (Bitcoin's hard limit). A positive value means blocks arrive faster than the 10-minute target, so difficulty will rise.
  • Progress bar through the current 2,016-block epoch.

Supply | Chain Security

Supply:

  • Issued: BTC currently in the UTXO set.
  • % issued: fraction of the 21M cap.
  • Remaining: 21M − issued.
  • Inflation: realized / forward: annualized issuance rate at the current subsidy and at the post-next-halving subsidy.

Chain Security:

  • Chain work: cumulative work, in log₂(work) bits. Computed from the RPC's 256-bit chainwork hex string without materializing the full integer.
  • Rewrite at hashrate: wall-clock seconds for the current network hashrate to redo the entire chain's work. Formula 2^(bits − log₂(hps)). This is a back-of-envelope rewrite cost; reorgs of any meaningful depth are economically and physically infeasible.
  • Network hashrate: same number as the Steady view's Chain panel.

Peer clients | Trivia

Peer clients: distribution of peers by user-agent string. Top 5 agents, plus an "other" bucket. Useful for spotting an unusually homogeneous peer set or an unexpected dominant client.

Trivia: subsidy era name, halving date, next halving block height. Light reading.

NAP view (5)

The Node Attestation Protocol as this node runs it. The view is offered only while the node has nap=1, napaggregator=1, or both: on any other node 5 does nothing, and neither the help screen nor the footer lists it. If the node restarts with both off, the TUI goes back to the automatic view. The TUI learns this from the replies to the two RPCs below, which it asks once the node is synced.

It is the only report renderer satd ships. It shows what the node says and checks nothing itself.

NAP panel

  • Publisher: on with how many attestations the node serves and how many of them are valid at the tip, or off.
  • Aggregator: on with the tip the report was computed at, or off.
  • Held / counting / invalid: attestations the aggregator holds, the ones that count at the tip, and those whose signatures failed.
  • UTXO total: the value of every coin at the tip, which coverage is a part of; and the digest of the held set.

Report

From getnapreport. Each key in bold with its participating weight (in BTC), coverage and the weight of coins that withdrew from it; under it, its values, heaviest first, with each value's share of the key, weight, coverage and number of coins. Shares and coverage are the node's parts per million shown as percentages rounded down; weights are the node's satoshis shown as BTC with all eight decimals, so nothing is rounded through floating point.

Breakdown

For the value selected with Up / Down: its weight by coin value tier, anchor age, coin age and script type, each bucket as a share of the value with its coin count, and the share held by its 10 and 100 heaviest coins.

Published

From listnapattestations: the first six attestations the node serves, with outpoint, key, value, anchor height, the coin's value and valid or the rule the attestation fails.

Keys and values are chosen by holders. satd accepts only printable ASCII in them, but the TUI does not count on the node it talks to: it draws anything else as ?, so no escape sequence reaches the terminal.

Modals are drawn over the active view. Esc or the toggle key closes them.

Help (h / ?)

Context-sensitive keybindings for the active view.

Reorg history (r)

Last 7 days of reorg events from getreorghistory. Up to 40 entries.

ColumnMeaning
depthBlocks displaced. Colored: 1 = yellow, 2–3 = light red, 4+ = red.
fork heightHeight at which the old and new chains diverged.
old tip / new tipBlock hashes, truncated.
−N +M blocksDisconnected vs. reconnected counts.
ageTime since the reorg.

A persistent copy lives at $datadir/<network>/reorg.log, in the network-specific datadir subdirectory. On mainnet the file sits directly under $datadir. satd writes this log whether or not the TUI is running; the modal is a viewer of it.

Warnings

A centered overlay (80% × 70%) that appears automatically when getwarnings reports visible warnings. The border is red if any warning has Error severity, otherwise yellow.

FieldMeaning
[ERROR] / [WARN]Severity.
IDWarning identifier (cyan).
first seen Ns ago · ×countAge and recurrence.
messageHuman-readable description.

Press a to acknowledge and dismiss every currently visible warning for this session. Press w to re-show everything previously dismissed.

Dismissal is per-session. If satd clears a warning ID and re-emits it, the modal reappears.

Keybindings

KeyEffect
qQuit. Closes Help / Reorg modal first if open.
Ctrl-CQuit.
h or ?Toggle Help overlay.
rToggle Reorg history.
1IBD view (or back to auto).
2Steady view (or back to auto).
3Mempool view (or back to auto).
4Chain view (or back to auto).
5NAP view (or back to auto). Only while the node has NAP on.
aAcknowledge all visible warnings.
wRe-show dismissed warnings.
EscClose Help or Reorg modal.
Up / DownScroll peers (IBD / Steady / Chain) or top-N (Mempool), or select a value (NAP).

Polling and refresh

The TUI does not push commands to satd; it polls. The render loop runs every 250 ms regardless of polling state, so the UI stays responsive even when RPC is slow.

CadenceRPC calls
1.5 sgetblockchaininfo, getpeerinfo, getmempoolinfo, getconnectioncount, getsysteminfo, getwarnings.
3 sgetibdprogress. During IBD only; the reply is heavy (full bitmap and per-peer breakdown).
~5 sgetsatdindexinfo, getserverstatus, getnetworkinfo, plus the steady-state batch (estimatefees, getmininginfo, getchaintxstats, uptime, getblockstats, getmempoolsummary, gettxoutsetinfo, getreorghistory, getmempoolhistory, getpolicyinfo), and getquarantineinfo while getpolicyinfo reports a loaded ruleset; and listnapattestations and getnapreport, which also tell the TUI whether NAP is on.
per epochgetblockhash + getblockheader to anchor the current 2,016-block epoch's start time. Refreshed only when the epoch floor advances.

If a steady-state RPC has not returned within about 3 s, the title bar shows stale. The view continues to render; the indicator shows that the data on screen is older than the polling cadence implies.

The health dot tracks the connection. A single RPC can fail while the connection is healthy — an unknown method against an older node, or one whose reply the node refuses to build — and that failure has its own indicator: rpc failing: <method> in the title bar, naming each method whose last poll errored. The panel fed by that method keeps its last value rather than blanking, so the indicator is the only thing distinguishing stale data from live data.

Failure modes

What you seeWhat it means
Connecting to satd…RPC unreachable, returning errors, or only getstartupinfo is responding.
rpc failing: <method> in the title barThat method's last poll returned an error while the connection stayed up. The panels it feeds are showing their last good value.
Auth retry, then Connecting…The cookie rotated and the retry also failed, which is common during a satd restart. Recovers on its own.
Stale indicator (✕ stale)Polling is alive but a recent call has not returned. Investigate if persistent.
Empty / dashed fields (—, -)The RPC backing that field has not returned yet, or returned an error.
Warnings modal won't dismissThe warning is still active in satd. Dismissal is per-session; resolve at the source.

The TUI does not panic on RPC errors. It shows them and keeps polling.

See also