Files
stealth/api

Stealth API

stealth-api is the Rust HTTP transport layer for Stealth. It connects to a running bitcoind via JSON-RPC, imports descriptors into temporary wallets, builds a transaction graph, and runs privacy detectors from stealth-engine.

Running

# Stop any old API process, then start the current source build
pkill -f 'target/debug/stealth-api' 2>/dev/null || true

# Auto-detects local bitcoind RPC port (prefers 18443, then 8332/18332/38332)
# and uses credentials from bitcoin.conf or local cookie files.
cargo run --bin stealth-api

Set auth explicitly with username/password:

STEALTH_RPC_URL=http://127.0.0.1:8332 \
STEALTH_RPC_USER=user \
STEALTH_RPC_PASS=pass \
  cargo run --bin stealth-api

Or use a cookie file:

STEALTH_RPC_URL=http://127.0.0.1:8332 \
STEALTH_RPC_COOKIE=~/.bitcoin/.cookie \
  cargo run --bin stealth-api

Configure the listen address with STEALTH_API_BIND (default 127.0.0.1:20899).

If you see Connection refused (os error 111), either:

  1. an old stealth-api process is still running, or
  2. bitcoind RPC is not reachable on the detected/configured URL.

API

POST /api/wallet/scan

Accepts one mutually-exclusive source:

Field Type Description
descriptor string Single output descriptor
descriptors string[] Multiple descriptors
utxos UtxoInput[] Raw UTXO set

Descriptor scan flow: creates a blank watch-only wallet, imports the descriptor(s) with a full blockchain rescan, builds a TxGraph, runs all 17 detectors, then cleans up the temporary wallet.

UTXO scan flow: resolves each UTXO's address from the node, builds a partial transaction graph, and runs applicable detectors.

Example (real descriptor from Bitcoin Core)

RPC="bitcoin-cli -regtest -rpcport=18443 -rpcuser=localuser -rpcpassword=localpass"
WALLET="scanwallet_$(date +%s)"

$RPC createwallet "$WALLET" >/dev/null
ADDR="$($RPC -rpcwallet="$WALLET" getnewaddress)"
DESC="$($RPC -rpcwallet="$WALLET" getaddressinfo "$ADDR" | jq -r '.desc')"

curl 'http://localhost:20899/api/wallet/scan' \
  -H 'content-type: application/json' \
  -d "{\"descriptor\":\"$DESC\"}" | jq

Responses

Status Meaning
200 Scan completed — body is a Report
400 Invalid input (bad descriptor shape, empty UTXOs, …)
502 bitcoind RPC unavailable/auth failed/connection failed

Environment variables

Variable Description
STEALTH_API_BIND Listen address (default 127.0.0.1:20899)
STEALTH_RPC_URL bitcoind RPC endpoint (overrides auto-detection)
STEALTH_RPC_USER RPC username (otherwise read from bitcoin.conf when available)
STEALTH_RPC_PASS RPC password (otherwise read from bitcoin.conf when available)
STEALTH_RPC_COOKIE Path to .cookie file (otherwise API auto-detects common local cookie locations)

E2E test (regtest)

The API includes an end-to-end regtest integration test that:

  1. creates wallets,
  2. gets a real descriptor from bitcoind,
  3. scans once with no history (summary.clean = true),
  4. creates/mine transactions,
  5. scans again and asserts findings (summary.clean = false).

Run it with:

cargo test -p stealth-api scan_descriptor_clean_then_findings_after_regtest_activity -- --nocapture