descry

module
v0.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 14, 2026 License: Apache-2.0

README

descry

ci codecov pkg.go.dev

descry (verb, literary) — to catch sight of something distant or hard to make out.

That is the whole job: watch remote endpoints from a distance and notice when something is wrong.

A sink-agnostic, event-sourced HTTP uptime engine. A Check runs against a Target, produces an Observation, mapped to a CloudEvents 1.0 event, handed to an EventSink (stdout / file in v1); tenancy and routing ride along as an opaque Labels map the engine never interprets, so it owes no particular consumer anything. It runs in production in my homelab, watching real endpoints.

The part that is more than table stakes is the SSRF guard: two layers, the second of which checks the already-resolved socket address inside net.Dialer.ControlContext, so a rebound DNS answer is blocked before connect(2) rather than after. Threat model and residual risks are in Security.

Install

As a binary:

go install github.com/fairbearlab/descry/cmd/descry@latest

As a library:

go get github.com/fairbearlab/descry

Design

docs/ARCHITECTURE.md is the producer/consumer seam contract: what the engine owns and never delegates, what a consumer owns, and the litmus test for whether a proposed change leaks consumer specifics into the engine's exported types. Read it before adding a typed field to Observation.

60-second demo (stdout)

git clone https://github.com/fairbearlab/descry
cd descry
go run ./cmd/descry --config example.yaml
# prints CloudEvents 1.0 observations to stdout on the configured interval

File-sink replay demo

Edit example.yaml to switch the sink:

sink: file
file_path: events.jsonl

Then run:

go run ./cmd/descry --config example.yaml &
sleep 15
cat events.jsonl   # one valid CloudEvent per line

Version

descry --version prints version, commit, and build date. Release builds stamp them via -ldflags -X main.version=… (see .goreleaser.yaml); a plain go build reports the dev defaults.

HTTP check options

httpcheck.New takes a timeout plus optional functional options:

import httpcheck "github.com/fairbearlab/descry/checks/http"

// Default: net/http's stdlib User-Agent ("Go-http-client/1.1"), no impersonation.
c := httpcheck.New(10 * time.Second)

// Override the User-Agent (e.g. to satisfy a WAF that blocks the default UA):
c := httpcheck.New(10*time.Second, httpcheck.WithUserAgent("MyMonitor/1.0 (+https://example.com)"))

WithUserAgent("") (or omitting the option) leaves the stdlib default in place — the engine never impersonates a browser unless the caller explicitly opts in.

Security

The SSRF guard is best-effort, not a security boundary. Network egress isolation is the real control and the deployer's responsibility.

The guard runs two layers:

  1. Parse-time (Layer 1): A string-only allowlist/blocklist applied before any DNS lookup. Blocks private RFC 1918 ranges, loopback, link-local, CGNAT, TEST-NET ranges, reserved blocks, and non-standard ports. Literal IPv6 addresses are decoded for known embedding classes (::ffff:x.x.x.x).

  2. Dial-time (Layer 2): A net.Dialer.ControlContext hook runs on the already-resolved socket address before connect(2) — no TOCTOU window. Applies to every dial attempt including Happy Eyeballs and each redirect hop.

Residual risks the guard does not cover:

  • Kernel/network routing manipulation that maps a public-looking address to an internal host after the Layer 2 check — e.g. policy routing, NAT/NAT64, or a VPN/overlay that redirects an allowed destination IP to an internal endpoint. (Classic DNS rebinding is not a residual risk here: Layer 2 re-checks the resolved socket address on every dial, so a later private DNS answer is blocked locally before connect rather than bypassing the guard.)
  • IPv6 embedding classes not explicitly decoded (unknown future embedding schemes).
  • Non-HTTP protocols tunneled over allowed ports 80/443.

For production use, run descry inside a network namespace or egress firewall that enforces the isolation policy independently of this guard.

License

Apache-2.0. See LICENSE.

Directories

Path Synopsis
Package check defines the engine's core abstractions: the Check interface probes run against, the Target it runs against, and the Observation it produces.
Package check defines the engine's core abstractions: the Check interface probes run against, the Target it runs against, and the Observation it produces.
checks
http
Package http implements an HTTP uptime check.Check with a two-layer SSRF guard (parse-time scheme/port/literal-IP checks, plus a dial-time control hook on the resolved socket address — see ssrf.go for the threat model).
Package http implements an HTTP uptime check.Check with a two-layer SSRF guard (parse-time scheme/port/literal-IP checks, plus a dial-time control hook on the resolved socket address — see ssrf.go for the threat model).
cmd
descry command
Command descry is a lightweight uptime/observability probe.
Command descry is a lightweight uptime/observability probe.
Package config loads and validates descry's YAML configuration file.
Package config loads and validates descry's YAML configuration file.
Package event maps a check.Observation onto a validated CloudEvents 1.0 envelope, ready to hand to a sink.EventSink.
Package event maps a check.Observation onto a validated CloudEvents 1.0 envelope, ready to hand to a sink.EventSink.
Package runner schedules a check.Check against a set of targets on a ticker, bounds concurrency, and publishes the resulting CloudEvents to a sink.EventSink with bounded retry.
Package runner schedules a check.Check against a set of targets on a ticker, bounds concurrency, and publishes the resulting CloudEvents to a sink.EventSink with bounded retry.
Package sink defines EventSink and its implementations: StdoutSink (writes JSONL to an io.Writer) and FileSink (writes JSONL to a file, append-only, concurrency-safe).
Package sink defines EventSink and its implementations: StdoutSink (writes JSONL to an io.Writer) and FileSink (writes JSONL to a file, append-only, concurrency-safe).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL