Skip to content

About

Prometheus exporter for SV Node and Teranode RPC: node health, peers, mempool and chain-tip forks

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

bsv-node-exporter

A Prometheus exporter for BSV nodes: SV Node and Teranode. On every scrape it makes up to four JSON-RPC calls from a fixed allowlist, and turns them into about 30 node-health, fork and mempool series per node. It is scrape-only and stateless.

Configuration

Configuration is read from environment variables only.

Variable Default Meaning
BSV_RPC_URL required e.g. http://rpc:9292. Must not contain credentials
BSV_RPC_USER / BSV_RPC_PASSWORD empty RPC credentials
BSV_RPC_PASSWORD_FILE empty Read the password from this file instead; takes precedence over BSV_RPC_PASSWORD
BSV_RPC_TIMEOUT 5s Timeout for each RPC call, at most 5m. Keep it below the scraper's scrape_timeout (default 10s)
BSV_MEMPOOL_SOURCE mempoolinfo mempoolinfo or miningcandidate. Teranode needs miningcandidate: its getmempoolinfo is unimplemented
BSV_COLLECTORS blockchain,peers,mempool,chaintips Comma-separated collectors to enable
LISTEN_ADDR :9480 HTTP listen address

The exporter serves GET /metrics and GET /healthz. /healthz never calls the node. At most two /metrics scrapes run at once; further concurrent scrapes get HTTP 503, so the exporter cannot multiply load on the node. Node RPC never goes through HTTP_PROXY / HTTPS_PROXY.

Metrics

Series Type Source
bsv_rpc_up{method} gauge 0/1 1 if the call succeeded during this scrape
bsv_rpc_duration_seconds{method} gauge wall time of the call during this scrape
bsv_blocks gauge getblockchaininfo.blocks
bsv_headers gauge getblockchaininfo.headers
bsv_difficulty gauge getblockchaininfo.difficulty
bsv_peers{kind} gauge getpeerinfo, counted as inbound / outbound / p2p
bsv_mempool_txs gauge getmempoolinfo.size; with BSV_MEMPOOL_SOURCE=mempoolinfo
bsv_mining_candidate_txs gauge getminingcandidate.num_tx, coinbase included; with BSV_MEMPOOL_SOURCE=miningcandidate
bsv_mempool_bytes gauge getmempoolinfo.bytes; with BSV_MEMPOOL_SOURCE=mempoolinfo
bsv_chaintips{status} gauge getchaintips, counted by status
bsv_chaintip_forks{window,length} gauge see below
bsv_exporter_build_info{version,goversion} gauge always 1

A failed call sets bsv_rpc_up{method} to 0 and omits that call's series. A scrape takes at most about BSV_RPC_TIMEOUT, so as long as that is below the scraper's scrape_timeout, one hung call costs only its own series. The rest of the scrape is still served, with HTTP 200. Alert on bsv_rpc_up == 0 for "node unreachable", and on the scraper's own up for "exporter down".

Peer kinds. A peer with a non-empty peerid is a Teranode libp2p peer and counts as p2p. Teranode sets inbound on those peers to mean "connected", not direction, so direction is ignored for them. Every other peer counts as inbound when inbound is true, and as outbound when it is false or missing. Teranode omits inbound: false from legacy peers. All three kinds are always emitted.

Statuses. The five statuses SV Node and Teranode return (active, valid-fork, valid-headers, headers-only, invalid) are always emitted, at 0 when absent. Any other value counts as other, so a node cannot create arbitrary label values.

Fork buckets.

  • The active height is the highest height among tips with status active.
  • A fork is a non-active tip with 0 <= active height − height <= window, for window 144 and 10000. Tips above the active height are headers ahead of validation, not forks, and are not counted (they still count in bsv_chaintips{status}).
  • length="single" counts tips with branchlen == 1.
  • length="long" counts tips with branchlen > 1.

All four buckets are always emitted, at 0 when empty. With no active tip, all four are 0.

The exporter adds no deployment labels such as network, host or node type. Attach them in the scrape configuration, so a single build works everywhere.

Teranode notes

Recommended Teranode settings:

BSV_MEMPOOL_SOURCE=miningcandidate
BSV_COLLECTORS=blockchain,peers,mempool
  • miningcandidate is required, because Teranode does not implement getmempoolinfo.
  • Leave the chaintips collector off for Teranode. On long chains its getchaintips may not return at all, so every scrape would start another expensive call that only ends at BSV_RPC_TIMEOUT.
  • getminingcandidate is not strictly read-only: the node builds and caches a candidate. At normal scrape intervals that is harmless.

Running

Docker

The container runs as UID 65532, so the password file must be readable by that UID:

sudo install -d -m 0755 /etc/bsv-node-exporter
sudo install -m 0400 -o 65532 -g 65532 ./rpc-password /etc/bsv-node-exporter/rpc-password

docker run --read-only -p 127.0.0.1:9480:9480 \
  -e BSV_RPC_URL=http://node:8332 \
  -e BSV_RPC_USER=exporter \
  -e BSV_RPC_PASSWORD_FILE=/run/secrets/rpc-password \
  -v /etc/bsv-node-exporter/rpc-password:/run/secrets/rpc-password:ro \
  ghcr.io/bsv-blockchain/bsv-node-exporter:<version>

The image is distroless and needs no writable filesystem. Inside the container the exporter listens on all interfaces (:9480) so it can be reached at all; publish the port only where the scraper runs, as 127.0.0.1 does above. /metrics has no authentication.

systemd

/etc/bsv-node-exporter.env holds the non-secret settings:

BSV_RPC_URL=http://127.0.0.1:8332
BSV_RPC_USER=exporter
LISTEN_ADDR=127.0.0.1:9480

/etc/systemd/system/bsv-node-exporter.service:

[Unit]
Description=BSV node Prometheus exporter
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/local/bin/bsv-node-exporter
EnvironmentFile=/etc/bsv-node-exporter.env
LoadCredential=rpc-password:/etc/bsv-node-exporter/rpc-password
Environment=BSV_RPC_PASSWORD_FILE=%d/rpc-password
DynamicUser=yes
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
RestrictAddressFamilies=AF_INET AF_INET6
Restart=on-failure

[Install]
WantedBy=multi-user.target

Scraping

With Prometheus, attach deployment labels on the target:

scrape_configs:
  - job_name: bsv-node
    scrape_interval: 60s
    static_configs:
      - targets: ["localhost:9480"]
        labels:
          network: mainnet

With the OpenTelemetry Collector (contrib), add a prometheus receiver to a metrics pipeline. A resource processor in the same pipeline can attach deployment attributes:

receivers:
  prometheus/bsv-node:
    config:
      scrape_configs:
        - job_name: bsv-node
          scrape_interval: 60s
          static_configs:
            - targets: ["localhost:9480"]

Security

  • Credentials never appear in logs, metric labels or error messages, and a BSV_RPC_URL containing credentials is rejected.
  • Only getblockchaininfo, getpeerinfo, getmempoolinfo, getminingcandidate and getchaintips can be called. There is no RPC passthrough.
  • RPC calls never follow redirects. Each method has a response budget: 1 MiB for getblockchaininfo, getmempoolinfo and getminingcandidate, 8 MiB for getpeerinfo, 16 MiB for getchaintips. Response headers are capped at 64 KiB. Peer and chain-tip arrays are decoded one element at a time and rejected beyond 10,000 peers or 100,000 tips.
  • Logged errors carry no node-supplied data. A JSON-RPC error is logged as its code, plus a fixed description for well-known codes; the node's message is never logged. Transport and decode failures map to fixed categories. Every logged error is capped at 300 bytes. The RPC endpoint is logged as scheme and host only.
  • The HTTP server sets read, write and header timeouts, and serves only /metrics and /healthz.
  • The only direct dependencies are the Go standard library and prometheus/client_golang.
  • CI runs govulncheck and golangci-lint, and release images are scanned with trivy.

Report vulnerabilities privately; see SECURITY.md.

License

Apache-2.0. See LICENSE.

About

Prometheus exporter for SV Node and Teranode RPC: node health, peers, mempool and chain-tip forks

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages