NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
Go modules · #2050 by repository stars
Last release today
07 Oct 2026
Ships fairly regularly
a new release about every 3 weeks
Some releases are documented
notes for 13 of 40 stable releases
Nothing withdrawn
no release was ever pulled
4 years old
78 releases · first in 2022
One column per quarter.
This contains new features, security hardening and bug fixes.
This contains new features, security hardening and bug fixes.
upstream.custom insteadresolver.split_dns and routes each domain and its subdomains to the OS resolver, or to the configured resolvers when some are given. Explicit resolvers are exclusive: when all of them fail, the query returns SERVFAIL instead of falling back to the OS resolverctrld-journal.log sits next to ctrld.log and keeps all warnings, errors, and the network state of the host with each change of that state (interfaces, routes, resolvers, recoveries). The journal survives a service start and a self-upgrade, so support can read one file to see what happened around an incident. Each log file and each log send upload starts with a header line, and uploads are limited to about 16 MBctrld diag for provisioning support. It collects the client version, MDM-managed preferences (macOS), the last provisioning result, the service state and API reachability in one copy-paste-safe report, with --json for a machine-readable copy. It never prints the provisioning token--cd-org, --custom-hostname, --intercept-mode and conflicting flags now fail before any network call with input-stage codes (exit 20–29). Rejected provisioning codes report TOKEN_INVALID, TOKEN_EXPIRED, TOKEN_LIMIT_REACHED or TOKEN_DISABLED. Other terminal failures that used to crash with nothing to read now record UNCLASSIFIED. The macOS package reports its own pre-flight failures (for example a missing ProvisionToken) in the installer logdnsechotest.zscaler.com health-check name through the OS resolver and does not cache it, so Client Connector can validate its DNS path and enable Private Access. Every other name stays filtered by Control D, and --intercept-mode hard opts outnextdns.io, such as the ultralow and anycast variants, so these upstreams send client info too. Based on the change proposed by @mike406 (#335)indeterminate and no longer reloads pfnetworksetup service name. An unknown or unreadable native state still leaves DNS unchanged~. catch-all is no longer treated as a split-DNS routescutil, and stopped binding scoped resolvers to the default interface's source addresslog send uploads to the API's direct IP with the full body, and moved the log to the API-configured log_path without losing earlier linesmongo executable is not available on the routerThis release contains an improvement and a bug fix.
This release contains an improvement and a bug fix.
off intercept mode now explicitly disables interception and clears any persisted intercept_mode configuration.recoveryBypass/recoveryRunning) caused by operation cancellation during recovery in intercept mode.This contains new features, security hardening and bug fixes.
This contains new features, security hardening and bug fixes.
ctrld status. The command now confirms that ctrld is actually serving rather than only registered as running, and reports a distinct exit code for a service that never finished starting--intercept-mode off actually turn interception off, instead of leaving a previously persisted mode in the config for the next service start to pick upupstream.os can reach a VPN-only resolver under a full tunnel instead of failing with no route to hostinsomniacslk/dhcp to c76316d to fix an nclient4 panic (insomniacslk/dhcp#583)This contains security hardening and bug fixes.
This contains security hardening and bug fixes.
golang.org/x/text to v0.40.0 to address GO-2026-5970attacker.example in response to a query for victim.example), which would be cached under the legitimate request key and served to later queries. The answer is now validated against the request's question (case-insensitive name plus Qtype/Qclass, per RFC 1035 §4.1.2) before it is served or cached; a mismatch is logged at debug level and the upstream is skipped, failing safe to the next upstream or SERVFAILcache_enable = true, one cache entry was shared by every client asking the same name against the same upstream, so a response tailored for subnet A could be served to subnet B (returning the wrong CDN/policy answer, per RFC 7871 §7.3). Both cache paths are now keyed by a canonical ECS tuple so different subnets never share an entry, while same-subnet queries still share oneSetReply on the already-unpacked answer, which forced the RCODE to NOERROR and overwrote the Question section. This masked upstream failures from the proxy's failover logic (a SERVFAIL looked like a successful empty response) and corrupted the Question served to clients. Only the downstream transaction ID is now restored, preserving the upstream RCODE, Question, and answers - matching the DoH and DoT resolversallow_wan_clients was unset (the default), breaking DNS resolution on the affected connection. The 192.0.0.0/29 range is now recognized as local*:53, ctrld falls back to 127.0.0.1:5354, but an hourly CD config reload reverted the in-memory port to 53 while the listener stayed on 5354, causing an endless watchdog "force reload" loop and dig timeouts. The actual bound listener IP/port is now restored into the in-memory config after reload--silent mode. In cd mode, --silent still created and grew a log file and reset the global log level back to debug, overriding the level --silent had set. ctrld now neither creates the internal log file nor writes debug logs when silent is setNothing published for this version
Fix macOS pf watchdog exec storms.
This contains some bug fixes.
pf watchdog exec storms.Nothing published for this version
Upgraded quic-go to v0.59.1 to address CVE-2026-40898
This contains new features, significant performance improvements, and bug fixes.
Upgraded quic-go to v0.59.1 to address CVE-2026-40898
Rejected oversized upstream DNS responses on the DoH, DoH3, and DoQ paths — these previously used io.ReadAll on attacker-controlled responses before enforcing any protocol-level limit, allowing a malicious or compromised upstream to force unbounded buffering. Bodies are now capped at dns.MaxMsgSize (and non-200 DoH error bodies are bounded as well)
Validated DNS-over-QUIC response framing (RFC 9250) — the resolver previously assumed at least two bytes were present and could panic on truncated or malicious replies; the length prefix is now validated and framing failures retire the connection from the pool
Rate-limited PIN attempts on the control socket to provide defense-in-depth against brute-force if an attacker gains socket access
Switched temp file creation to os.CreateTemp for symlink-safe writes, preventing symlink attacks on systems without fs.protected_symlinks (e.g. embedded routers)
Switched internal/router/dnsmasq to text/template instead of html/template, since the generated config is plain text
Shared a single QUIC transport and UDP socket across DoQ dials so parallel dial and reconnect churn no longer allocate a socket per attempt or leak sockets; the query stream's send side is now closed before reading the response per RFC 9250 §4.2
Updated the Docker base image to bookworm
Refreshed macOS VPN DNS after pf stabilization
Allowed intercept fallback for the default listener
Flushed pf states after a forced DNS intercept reload
Nothing published for this version
This release contains a bug fix for Windows platform.
This release contains a bug fix for Windows platform.
This contains bug fixes and a new diagnostic command.
This contains bug fixes and a new diagnostic command.
ctrld log tail command for live log streaming — streams runtime debug logs to the terminal in real-time, similar to tail -f. Supports --lines/-n flag to control initial context linesKeepAlivePeriod: 15s) on idle pooled connections, keeping them alive across NAT rebinding and proactively detecting dead paths before the next query hits a stale connectionFixed handle leak in hasLocalDnsServerRunning() on Windows — the process snapshot handle from CreateToolhelp32Snapshot was not being closed, leaking a handle on every call
Fixed dnsFromResolvConf not filtering loopback IPs — the continue statement only broke out of the inner loop, allowing loopback addresses (e.g. 127.0.0.1) through. This caused ctrld to use itself as bootstrap DNS when already installed as the system resolver, creating a self-referential loop
Fixed IPv6 VPN DNS server addresses not formatted correctly on macOS — upstreamConfigFor() passed bare IPv6 addresses to net.Dial without brackets or port, causing too many colons in address errors and immediate failure for all IPv6 VPN DNS queries
Fixed DNS responses failing with sendmsg: invalid argument for IPv6-sourced clients on macOS — the pf nat rule on lo0 inet6 did not match packets arriving via the rdr chain as inet4, so the client's global IPv6 source address was preserved, and the kernel rejected responses from [::1]:53 to non-loopback destinations
Fixed VPN DNS queries routed over wrong source interface on macOS — when a VPN client (e.g. FortiClient) was active, DNS queries to LAN servers used the VPN tunnel IP as source, making responses unroutable. Combined with the IPv6 bugs, this cascaded into complete VPN DNS failure and VPN disconnection
Fixed DoQ queries failing with idle timeout errors — pooled QUIC connections that timed out server-side now trigger a transparent retry with a fresh connection instead of propagating the error
Fixed DoQ queries failing with too many open streams — replaced non-blocking OpenStream with OpenStreamSync, which waits for the server's MAX_STREAMS credit replenishment instead of racing against it. Added StreamLimitReachedError as a retry condition for defense-in-depth when the stream credit wait times outetS
Fixed a crash in SetSelfIP triggered by network transitions before clientinfo initialization.
Fixed a recovery race condition, reduce worst-case recovery from ~30s to <3s.
This contains new features, significant performance improvements, and bug fixes.
This contains new features, significant performance improvements, and bug fixes.
Added DNS intercept mode (--intercept-mode=dns|hard|off) — a major new feature that intercepts all DNS traffic on the system and routes it through ctrld
route-to lo0 + rdr rules, _ctrld group exemption, watchdog auto-heal for Parallels VM pf corruption, and IPv6 DNS blockingAdded robust platform-specific username detection for Control D metadata (macOS: directory services/console user, Linux: loginctl/utmp/passwd, Windows: WTS/registry/token)
Added hostname hints in provisioning metadata for API-side fallback, allowing the server to repair generic hostnames
Implemented connection pooling for DoQ (QUIC) — eliminates per-query handshake overhead by reusing connections, matching DoH3 performance
Implemented connection pooling for DoT (TLS) — eliminates per-query TLS handshake overhead by reusing connections
Improved DNS server discovery for domain-joined Windows machines — non-physical adapters with matching DNS suffix are now considered valid for remote VPN scenarios
Consolidated network change monitoring into a single goroutine for simpler, more reliable handling
Fixed macOS hostname detection — uses scutil ComputerName instead of os.Hostname(), which returns generic names like "Mac.lan" when Private Wi-Fi Address is enabled
Fixed DoT connection validation — connections are now checked before reuse to prevent io.EOF errors from server-side idle timeouts
Fixed handling of empty and invalid IP addresses to prevent panics when processing client info
Fixed a data race in transport rebootstrap using a three-state atomic instead of a boolean flag
Nothing published for this version
Nothing published for this version
This contains new features, improvements and bug fixes.
This contains new features, improvements and bug fixes.
This contains improvements and bug fixes.
This contains improvements and bug fixes.
Nothing published for this version
This contains new features and improvements.
This contains new features and improvements.
This contains improvements and bug fixes.
This contains improvements and bug fixes.
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →