Sign inSign up

nrafinia/geopulse-probe

By nrafinia

Updated 27 days ago

Outbound-only network probe: measures the same targets from many countries at once

Image
Networking
0

247

nrafinia/geopulse-probe repository overview

GeoPulse Probe

A lightweight measurement agent for distributed network observation. Run it on cheap VPS boxes in as many countries as you like; each one measures the same list of targets and pushes results to the GeoPulse hub.

The point is the question single-vantage-point monitors cannot answer: not "is it up?" but "up from where?" github.com resolving fine from Frankfurt and timing out from Muscat at the same minute is the finding — and you only see it if something is watching from both.


What it does

Every 60 seconds the probe runs its checks and pushes the results outbound to the hub.

CheckWhat it measures
HttpRequest timing and status class against a URL
DnsA / AAAA lookups, queried twice — once against a chosen public resolver, once against the box's own /etc/resolv.conf resolver
TcpHandshake latency to an open port
TlsHandshake, certificate issuer and expiry

There is no ICMP check. TCP connect timing replaces it, which means no CAP_NET_RAW, no root, no privileged container.

At the top of each hour the probe folds its own buffer into one row per check — counts, min/max/sum, and a 14-bucket log-spaced latency histogram — and pushes that too. Aggregation happens on the probe's CPU, so adding nodes doesn't add hub load.


Networking model

All traffic is outbound. The hub never dials the probe. No inbound port, no public IP, no per-node certificate, and NAT is irrelevant.

The container exposes exactly one HTTP endpoint, GET /health, for your orchestrator's liveness check. It returns liveness only — never measurement data. Nothing else listens.

Every payload to the hub is HMAC-SHA256 signed and AES-GCM encrypted, on every route, always. Two keys derived from one shared secret via HKDF; the secret itself never crosses the wire. There is no plaintext mode and no flag to disable it.

The check list comes from the hub over that same authenticated channel, so a probe needs no target configuration of its own. Nothing else can hand it a host to measure — no unauthenticated path accepts a target, ever. A probe that checks attacker-supplied targets is a free scanning proxy and gets the underlying VPS suspended, so that door doesn't exist.


Quick start

docker run -d \
  --name geopulse-probe \
  --restart unless-stopped \
  -e Probe__NodeId=helsinki-01 \
  -e Probe__HubUrl=https://geopulse.rafinia.info/ \
  -e Probe__SharedSecret=... \
  nrafinia/geopulse-probe:latest

Or with compose:

services:
  probe:
    image: nrafinia/geopulse-probe:latest
    restart: unless-stopped
    environment:
      Probe__NodeId: helsinki-01
      Probe__HubUrl: https://geopulse.rafinia.info/
      Probe__SharedSecret: ${GEOPULSE_SECRET}

Double underscore is the .NET section separator, so Probe__HubUrl maps to Probe:HubUrl.

Configuration
KeyDefaultNotes
Probe__NodeIdRequired. Stable identifier for this box.
Probe__HubUrlRequired.
Probe__SharedSecretRequired. Must match the hub.
Probe__BufferMinutes180In-memory resend window.

No location configuration is needed. On startup the probe asks a free IP-info API for its own public IP, country, city and ASN, and reports them in the heartbeat. Deploy the same image anywhere with no per-node setup. It rechecks every six hours in case the IP moves.


Behaviour worth knowing

Hub downtime does not lose data. Results sit in a per-check ring buffer — three hours by default — and the probe only advances its pointer once the hub acknowledges a watermark. Hourly rollups are held up to 48 hours. Bring the hub back and the gap fills itself.

A NOERROR with no answers is a success, not a failure. Plenty of domains legitimately have no AAAA record. The probe records it as success with a "no record" detail. NXDOMAIN on a target you explicitly configured is a failure — the thing you asked it to watch stopped existing. SERVFAIL and timeout are both failures, and stay distinguishable from each other, because a resolver failing while another succeeds is the entire product.

State survives restarts. The buffer snapshots to a single file once a minute. Losing up to 60 seconds on restart is expected and fine.


Footprint

Built with NativeAOT into a single self-contained binary — no runtime to install, no reflection, fast start.

Designed for 256–512 MB shared-vCPU boxes, which is what free and near-free tiers actually give you. Outbound traffic runs around 4 KB per minute of results plus a heartbeat, deliberately below the threshold where free-tier providers start asking questions. Checks are spread deterministically across the minute (stableHash(checkId) % 60000), so load is flat rather than spiky and the pattern reproduces across restarts.


Tags

TagMeaning
latestNewest build
<sha>Exact commit — pin this in production

Also published to ghcr.io/nrafinia/geopulse-probe.


Hub

This image is only half the system. It needs a GeoPulse hub to push to — that's where results are stored, rolled up, and drawn as a cross-country comparison: https://geopulse.rafinia.info/

Tag summary

Content type

Image

Digest

sha256:22c2ce3c9

Size

11.7 MB

Last updated

27 days ago

docker pull nrafinia/geopulse-probe