BareProxy Adds a Drift Watcher for DNS Records and TLS Certificates
BareProxy 0.16.0 adds a drift watcher. You give it a list of domains, a few or a few hundred, and it tells you when something about them changes or is about to break.
Certificates are the classic case. Renewal fails quietly, and the first sign is a browser warning on the live site. DNS is the other one. A record edited by mistake, a registrar that moved your nameservers, a missing MX after a migration. None of it shows up until mail stops or a page goes dark.
What It Watches
For each domain it looks up the A, AAAA, CNAME, MX, NS, TXT and CAA records. It connects over TLS, reads the certificate, and asks for the home page to read the HSTS header. Then it compares all of it with the last state it knew was good, kept on disk. It raises an alert for:
- a certificate that ends within 21 days, or has already ended
- a certificate that changed, and says so when the issuer changed too
- a certificate that doesn’t verify for the name
- a changed record, with what it was before and what it is now
- nameservers that moved
- no HSTS header, or no CAA records on the name or any name above it
Every change also goes into a history file, with the time, so you can look back and see what changed when.
A Short Example
The watch goes in the same config file as the rest of BareProxy:
drift sites
domains example.com www.example.com shop.example.com
resolver 1.1.1.1
every 6h
webhook https://alerts.example.com/bareproxy
bareproxy drift runs a check now. It needs no running server, so it works from a CI job too:
$ bareproxy drift --config sites.conf
Drift watch sites, 3 domains, 2026-10-31 09:00:00 UTC, took 0.4 s
ALERT example.com A changed
before: 192.0.2.1
after: 192.0.2.7
ALERT shop.example.com certificate ends in 9 days (2026-11-09 12:00 UTC), inside the 21-day window
UNCHECKED www.example.com MX: lookup failed twice (timeout); last known 10 mx.example.com kept
Result: 2 alerts, 1 unchecked
The exit code is 4 when there are alerts, 3 when something couldn’t be checked, and 0 when all is well, so a pipeline can fail on it. --json gives the same report as data. --history prints every change ever recorded.
With every set, a running BareProxy checks on its own schedule. The results show up in bareproxy events, and the webhook gets the report whenever there is something in it.
Honest About What It Couldn’t See
A watcher that cries wolf gets ignored. One that stays quiet when it couldn’t look is worse. Two rules handle both.
When a lookup times out or the resolver fails, the watcher asks once more. If that fails too, the report says unchecked, and the exit code shows it. It never reports that part as unchanged, and it keeps the last good value for next time, so a real change hiding behind an outage still shows up later.
When a lookup shows a change, the watcher also asks once more. If the second answer is the old value, it was a flap: a resolver with stale data, or one bad node behind a load balancer. That goes in as a note, with no alert. The same second look applies to certificates.
A change alerts once and then becomes the new known state. A problem that stays, like a certificate near its end or a missing HSTS header, alerts on every check until it’s fixed. When the TLS connection fails, the certificate seen last time still counts, so an expiring certificate keeps alerting even while the server is unreachable.
How It Was Tested
All tests run against a stub DNS server and local TLS servers with test certificates, so they never touch the internet and give the same result every time.
A changed record alerted with the right before and after for six record types, and stayed quiet on the next check. A certificate inside its window alerted on three checks in a row, and again with the server down. A changed certificate and a changed issuer each alerted. Timeouts, SERVFAIL and REFUSED from the resolver each came out as unchecked, with exactly one retry and the old value kept. A record that showed a wrong value once on each of ten checks raised no alert at all. Answers too long for UDP were read again over TCP.
For size, one test watches 500 domains, each with all seven record types plus a TLS connection and the HSTS check, with 16 at a time. The first check took 1.8 seconds and the second 2.7. Five planted changes were all found and three planted timeouts were all reported as unchecked. The log is in results/drift-test.log.
Why It Lives in the Core
The watcher needs to send DNS queries and open TLS connections of its own. A BareProxy plugin runs in a sandbox whose only way out is an HTTP request, and it only runs inside a server, while the watcher has to work from a command line in CI. So it is part of BareProxy itself, next to the automatic certificates the kernel already has. The uptime checks plugin, planned next, will watch whether your sites answer. This one watches what they are made of.
Limits
The resolver’s cache sits between the watcher and the real DNS servers, so a change shows up once the old record’s time to live runs out there. A renewed certificate counts as a changed certificate and alerts once, with the old and new end dates. It is new, and has been tested against local servers, not yet against hundreds of real domains. The drift manual has every setting.
A case study follows the watcher through a DNS move that went wrong, and the use examples post shows it in CI and inside a running server.
The same release adds a webhook inbox. The binaries are on the releases page.