Skip to content

Releases: 1mb-dev/natcheck

v0.1.4 — Sparrow

11 May 12:21

Choose a tag to compare

First in the natcheck birds-themed release series. Small, darting — like the hairpin probe.

Added

  • RFC 5780 §4.3 hairpinning detection. New JSON field "hairpinning": true | false | null (additive); null distinguishes "not tested" from "tested false." Probe uses two dedicated unconnected UDP sockets, STUN-probed in parallel against the first --server entry, then a tagged loopback packet from socket A to mapped-B. Listen window default 1s. Wall-clock parallel cost ≤200ms in the common case; runs concurrent with mapping probes.
  • New warning constant hairpin_untested emitted when local socket setup or STUN probe failed.
  • Human-format report adds a Hairpinning: true|false line when the probe produced a value.

Fixed

  • warningText no longer falls through to the raw warning ID for mixed_address_family_probes (#19). Reads "Mapping classification spans IPv4 and IPv6; each family observes its own NAT."
  • WarnInsufficientProbes text refined to "Insufficient probes for one or more address families." Under v0.1.3 combine semantics the warning can fire alongside a confident combined verdict; the old text implied no verdict was reached.

Changed

  • classify.Classify signature gains a third parameter *probe.HairpinningResult. Callers passing nil get the same behavior as v0.1.3 plus the new hairpin_untested warning.
  • docs/design.md v0.2 staged-sequence labels reconciled: hairpinning is v0.1.4, natcheck server is v0.1.5. Hairpinning cost claim updated to reflect actual ≤200ms parallel cost.

Migration note

JSON consumers reading warnings[] will see a new hairpin_untested value when hairpin probe failed setup. Consumers reading hairpinning must handle null as "did not test." Additive only; no removals.

Validation

Captured against a destroy-after coturn droplet on the v0.1.3 residential ISP — see docs/samples/hairpinning.{txt,json} and the validation log entry.

Install: go install github.com/1mb-dev/natcheck/cmd/natcheck@v0.1.4

v0.1.3 — cross-address-family mapping classification fix

26 Apr 11:52
ae2c605

Choose a tag to compare

Closes #14. First 3-segment-semver tag after the v0.1.2.x patch incident — go install ...@v0.1.3 works correctly via the Go module proxy.

Fixed

  • Cross-address-family probe sets no longer produce a wrong ADM/symmetric verdict. Previously, mixing IPv6-resolved hostname servers (e.g., stun.l.google.com) with IPv4-literal servers (e.g., a self-hosted coturn) compared mapped endpoints across address families and reported ADM because the endpoints differed by construction (each family observes its own NAT). The classifier now groups successes by address family, classifies each group independently, and combines under the rule "Unknown is absence of information, not disagreement": matching verdicts win, two confident verdicts that differ produce Unknown, a confident verdict beats Unknown from the other group.

Added

  • New warning value mixed_address_family_probes in warnings[]. Emitted whenever successful probes span both IPv4 and IPv6 address families. Additive to the JSON schema.

Migration

JSON consumers checking nat_type == "ADM" for cross-family probe sets will see "Unknown" on the same input under v0.1.3 — the previous verdict was incorrect. Forecast-checking consumers (webrtc_forecast.direct_p2p) are mostly unaffected: the dominant cross-family disagreement case stays exit 1 (Unknown → 1, was ADM → 1). The verdict-flip case (genuinely agreed EIM across families, previously ADM, now EIM → exit 0) is the bug being fixed.

Install

brew tap 1mb-dev/tap
brew upgrade natcheck      # if already installed
brew install natcheck      # fresh install

or

go install github.com/1mb-dev/natcheck/cmd/natcheck@v0.1.3

v0.1.2.2 — coturn validation works on more provider topologies

26 Apr 10:57
30a0f5e

Choose a tag to compare

Note for go install users: this tag is 4-segment (v0.1.2.2), which Go module proxy treats as invalid semver and silently substitutes a pseudo-version. The Go binary is byte-identical to v0.1.2 — no Go source changed in v0.1.2.1 or v0.1.2.2, the deltas are conf + docs + script that ship via Homebrew or repo clone, not via go install. Use go install github.com/1mb-dev/natcheck/cmd/natcheck@v0.1.2 (same binary) or brew install 1mb-dev/tap/natcheck (binary + corrected assets). v0.1.3 will use 3-segment semver.

Patch release. Closes #15 and #16. No code or JSON schema delta — same binary as v0.1.2 / v0.1.2.1.

Fixed

  • examples/coturn-natcheck.conf now uses the two-listening-ip + two-external-ip-pair form explicitly. v0.1.2.1's external-ip=PUBLIC/PRIVATE only worked on AWS/GCP-style topologies where the two IPs differ naturally. On single-public-IP providers (DigitalOcean basic droplet, Linode Nanode, Hetzner single-IP), eth0's IP IS the public IP — external-ip=A/A doesn't satisfy coturn's "two distinct IPs" requirement and coturn silently logs WARNING: ... only one IP address is provided while natcheck reports filtering: untested.
  • docs/coturn-setup.md adds a per-provider topology table (AWS/GCP / DO basic / bare metal) with a worked DigitalOcean Reserved IP example (ip addr add SECOND_IP/32 dev eth0).

Added

  • scripts/validate-coturn.sh — one-shot SSH-pipe provisioner that installs coturn, writes the conf, opens the firewall, starts coturn in tmux, and verifies the startup log for the two specific warning lines that signal a misconfigured §4.4 path. Exits non-zero with FAIL: ... if either appears, so misconfigured droplets don't silently produce filtering: untested samples. Accepts SECOND_IP=<addr> env var for single-public-IP providers — aliases the IP to the NIC and writes the multi-IP conf.

    # AWS/GCP topology:
    ssh root@<vm-ip> 'bash -s' < scripts/validate-coturn.sh
    
    # Single-public-IP provider, after attaching a second IP:
    ssh root@<vm-ip> "SECOND_IP=<reserved-ip> bash -s" < scripts/validate-coturn.sh

Verified

End-to-end against a real DigitalOcean basic droplet (coturn 4.6, Ubuntu 24.04, primary public IP + Reserved IP aliased to eth0). Canonical filtering verdict reproduces across runs; classification + warnings + exit code stable. tcpdump confirmed coturn responds to RFC 5780 §4.4 Test 2 + Test 3 with routable public source IPs.

Known follow-up

  • #14 — when the default-server hostnames resolve via IPv6 and a custom --server is IPv4 literal, the classifier compares mapped endpoints across address families and produces wrong ADM verdicts. Affects users following docs/coturn-setup.md who pass the natural probe set. Larger surface (Go code change + new schema warning + tests). v0.1.3.

Install

brew tap 1mb-dev/tap
brew upgrade natcheck      # if already installed
brew install natcheck      # fresh install

or

go install github.com/1mb-dev/natcheck/cmd/natcheck@v0.1.2.2

v0.1.2.1 — coturn config fix

26 Apr 09:16
7bed641

Choose a tag to compare

Note for go install users: this tag is 4-segment (v0.1.2.1), which Go module proxy treats as invalid semver and silently substitutes a pseudo-version. The Go binary is byte-identical to v0.1.2 — no Go source changed in v0.1.2.1, the deltas are conf + docs that ship via Homebrew or repo clone, not via go install. Use go install github.com/1mb-dev/natcheck/cmd/natcheck@v0.1.2 (same binary) or brew install 1mb-dev/tap/natcheck (binary + corrected assets). v0.1.3 will use 3-segment semver.

Patch release. Fixes the bundled coturn config so that v0.1.2's filtering classification actually runs against a default-recipe coturn.

Fixed

  • examples/coturn-natcheck.conf now sets rfc5780 (coturn 4.x defaults RFC 5780 NAT behavior discovery to OFF; without this directive, coturn silently omits OTHER-ADDRESS from Binding responses and natcheck reports filtering: untested with WarnFilteringSkippedNoChangeRequest).
  • examples/coturn-natcheck.conf switches external-ip=YOUR_PUBLIC_IPexternal-ip=YOUR_PUBLIC_IP/YOUR_PRIVATE_IP. A bare single-value external-ip triggers STUN CHANGE_REQUEST not supported: only one IP address is provided even on a single-NIC VM.
  • docs/coturn-setup.md documents both requirements and adds a verification step (step 4) to grep coturn's stdout for the two specific warning lines.

Not changed

No code, no JSON schema, no exit-code mapping. go install github.com/1mb-dev/natcheck/cmd/natcheck@v0.1.2 and @v0.1.2.1 produce the same binary. The patch tag exists so the Homebrew formula and changelog can point at the corrected setup story.

Why it shipped broken

v0.1.2's pre-tag review covered the in-process internal/stunserver 4-corner test (which natcheck fully controls) and read the conf for "is the YOUR_PUBLIC_IP gotcha framed right." The framing was right; the conf itself was missing the rfc5780 directive that coturn 4.x requires. Discovered by trying to validate v0.1.2 end-to-end against a real coturn 4.10.0 immediately after release.

Install

brew tap 1mb-dev/tap
brew upgrade natcheck      # if already installed
brew install natcheck      # fresh install

or

go install github.com/1mb-dev/natcheck/cmd/natcheck@v0.1.2.1

v0.1.2 — RFC 5780 §4.4 filtering classification

26 Apr 08:55
cbe8b60

Choose a tag to compare

Added

  • RFC 5780 §4.4 filtering classification when the target STUN server advertises OTHER-ADDRESS. natcheck runs the three-step CHANGE-REQUEST sequence and reports endpoint-independent, address-dependent, address-and-port-dependent, or untested filtering.
  • Top-level "filtering" object in --json output. Always present. tested_against field omitted when behavior is untested.
  • WebRTC forecast value "possible" now emitted for EIM mappings combined with restrictive (address-dependent or address-and-port-dependent) filtering.
  • examples/coturn-natcheck.conf + docs/coturn-setup.md: minimum coturn config for filtering classification and a one-page setup guide.
  • internal/stunserver package (foundation for v0.1.4's natcheck server subcommand).

Changed

  • Default-server users (Google, Cloudflare) see no extra latency: filtering classification is skipped when no probe response advertises OTHER-ADDRESS. coturn / natcheck server users get filtering automatically.
  • --timeout flag help: now notes that filtering classification adds up to 1.5s when applicable.

JSON schema (additive — strict consumers update)

  • New top-level key: "filtering": {"behavior": "...", "tested_against": "..."}. Always present from this release onward; tested_against omitted when behavior == "untested".
  • New warning value in warnings[]: "filtering_skipped_no_change_request" (server response did not include OTHER-ADDRESS, so the §4.4 sequence could not run).
  • The existing "filtering_behavior_not_tested" warning is still emitted when filtering classification was not attempted at all (no server in the probe set advertised OTHER-ADDRESS).

Consumers doing strict equality on the entire JSON blob need to expect the new filtering key. Field-level consumers (e.g., jq '.nat_type') are unaffected.

Known limitations (deferred)

  • Hairpinning detection — planned for v0.1.3.
  • natcheck server subcommand — planned for v0.1.4.
  • WarnFilteringPartial warning — current FilteringResult shape can't distinguish "filter blocked" from "transport error", so the warning is not emitted. Will return when the probe-side gains the necessary granularity.

Install

brew tap 1mb-dev/tap
brew install natcheck

or

go install github.com/1mb-dev/natcheck/cmd/natcheck@v0.1.2

v0.1.1

19 Apr 08:15

Choose a tag to compare

Patch release.

Install: go install github.com/1mb-dev/natcheck/cmd/natcheck@v0.1.1

Fixed

  • natcheck --version now reports the correct tag when installed via go install github.com/1mb-dev/natcheck/cmd/natcheck@vX.Y.Z. Previously fell back to dev because ldflags aren't applied by go install; now resolves via runtime/debug.ReadBuildInfo when ldflags are absent.

Full changelog

v0.1.0...v0.1.1

v0.1.0

19 Apr 07:44

Choose a tag to compare

Initial release.

Install: go install github.com/1mb-dev/natcheck/cmd/natcheck@v0.1.0

See docs/design.md for scope and architecture.

Added

  • natcheck CLI: probes STUN servers and reports NAT mapping classification (EIM / ADM / APDM per RFC 5780) plus a WebRTC direct-P2P forecast.
  • Default STUN servers: stun.l.google.com:19302 and stun.cloudflare.com:3478.
  • Flags: --json, --verbose, --server host:port (repeatable), --timeout, --version, --help.
  • Forecast-first human output; schema-stable JSON via --json.
  • Exit codes: 0 P2P-friendly, 1 P2P-hostile, 2 probe or flag error.
  • CGNAT detection (RFC 6598 100.64.0.0/10) with forecast unknown.
  • IPv4 + IPv6 operation via pion/stun and Go's net package.

Public contracts (stable from v0.1.0)

  • --json schema: additive changes only.
  • Exit-code mapping (0 / 1 / 2).

Dependencies