pusher: optional IPv6 preference for welist reports + log report failures #1

Merged
svatantrya merged 2 commits from SAFE21.io/bal-server:fix/pusher-welist-ipv6-preference into main 2026-07-19 15:09:00 +00:00
Contributor

Problem

welist.bitcoin-after.life publishes both A (IPv4) and AAAA (IPv6) records. On some networks (observed on an Umbrel OS host behind a residential ISP) the IPv4 route to the welist host is broken: the TCP handshake completes, but the connection stalls before any TLS data, while IPv6 works fine. The default connector picks the broken family and the report request hangs until it errors.

To make things worse, the failure is invisible: main_result calls let _ = send_stats_report(...), silently discarding any error, so a failing welist report can go unnoticed indefinitely.

Changes

  1. Optional IPv6 pinning (BAL_PUSHER_PREFER_IPV6, default off): when truthy, the pusher resolves the welist host itself and pins the reqwest client to its first AAAA address. The original hostname is still used for the Host header and TLS SNI. When the variable is unset — the default — or no AAAA record exists, behavior is completely unchanged.
  2. Log report failures: send_stats_report / calculate_stats errors are now logged with warn! instead of being silently discarded, so connectivity problems are diagnosable from the logs.

Backward compatibility

Default behavior is unchanged; IPv6 pinning only activates when explicitly opted in via the new env var.

Testing

  • cargo fmt -- --check: clean
  • cargo test: all tests pass, including 5 new unit tests for the URL host/port parsing
  • cargo clippy: no new warnings introduced (the only warnings present are pre-existing in the current codebase)
  • The same approach has been running in production on an Umbrel OS mainnet node (we.safe21.io): with IPv6 pinning, welist reports succeed every block (Report to welist(...) Sent: "ok")

Possible follow-up

A full happy-eyeballs approach (probe both families concurrently, use whichever connects first) would make this automatic without an env var. It was intentionally left out of scope to keep this change minimal and zero-risk.


Contributed by SAFE21.io (Will-Executor operator, we.safe21.io).

## Problem `welist.bitcoin-after.life` publishes both A (IPv4) and AAAA (IPv6) records. On some networks (observed on an Umbrel OS host behind a residential ISP) the IPv4 route to the welist host is broken: the TCP handshake completes, but the connection stalls before any TLS data, while IPv6 works fine. The default connector picks the broken family and the report request hangs until it errors. To make things worse, the failure is invisible: `main_result` calls `let _ = send_stats_report(...)`, silently discarding any error, so a failing welist report can go unnoticed indefinitely. ## Changes 1. **Optional IPv6 pinning** (`BAL_PUSHER_PREFER_IPV6`, default off): when truthy, the pusher resolves the welist host itself and pins the reqwest client to its first AAAA address. The original hostname is still used for the `Host` header and TLS SNI. When the variable is unset — the default — or no AAAA record exists, behavior is completely unchanged. 2. **Log report failures**: `send_stats_report` / `calculate_stats` errors are now logged with `warn!` instead of being silently discarded, so connectivity problems are diagnosable from the logs. ## Backward compatibility Default behavior is unchanged; IPv6 pinning only activates when explicitly opted in via the new env var. ## Testing - `cargo fmt -- --check`: clean - `cargo test`: all tests pass, including 5 new unit tests for the URL host/port parsing - `cargo clippy`: no new warnings introduced (the only warnings present are pre-existing in the current codebase) - The same approach has been running in production on an Umbrel OS mainnet node (`we.safe21.io`): with IPv6 pinning, welist reports succeed every block (`Report to welist(...) Sent: "ok"`) ## Possible follow-up A full happy-eyeballs approach (probe both families concurrently, use whichever connects first) would make this automatic without an env var. It was intentionally left out of scope to keep this change minimal and zero-risk. --- Contributed by SAFE21.io (Will-Executor operator, `we.safe21.io`).
SAFE21.io added 2 commits 2026-07-19 12:14:03 +00:00
The welist host publishes both A and AAAA records. On networks where the
IPv4 route is broken (connection stalls after the TCP handshake) while
IPv6 works, the default connector may pick the broken family and the
report request hangs.

When BAL_PUSHER_PREFER_IPV6 is truthy, the pusher now resolves the welist
host itself and pins the reqwest client to its first IPv6 address; the
original hostname is still used for the Host header and TLS SNI. When the
variable is unset (default) or no AAAA record exists, behavior is
completely unchanged.

Includes unit tests for the URL host/port parsing and documentation in
docs/07_deployment_and_ops.md.
main_result called send_stats_report/calculate_stats with 'let _ = ...',
silently dropping any failure. A broken welist route (e.g. unreachable
IPv4 path) is then invisible in the logs and can go unnoticed for a long
time. Log failures with warn! so connectivity problems are diagnosable.
svatantrya merged commit 22b60e55c7 into main 2026-07-19 15:09:00 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: bitcoinafterlife/bal-server#1