Replace the inline cookie split with stealth_bitcoincore::read_cookie_file, which already implements the same parsing with proper error handling. Removes duplication and keeps the cookie format in one place.
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:
- an old
stealth-apiprocess is still running, or bitcoindRPC 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:
- creates wallets,
- gets a real descriptor from
bitcoind, - scans once with no history (
summary.clean = true), - creates/mine transactions,
- 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