- SSH rules with check action are now supported, prompting users to authenticate via OIDC or CLI approval before access is granted
- New headscale auth CLI command group with approve, reject, and register subcommands
- Policy tests block in policy files now evaluated to assert reachability between named sources and destinations
- SSH policy tests block (beta) now evaluated with accept, deny, and check assertions for SSH rule testing
- Support for Tailscale grants alongside ACLs to control application-level features like Taildrive file sharing and peer relay
- New autogroup:danger-all that resolves to all IP addresses including those outside the tailnet
- Node attributes (nodeAttrs) block in ACL policies to assign Tailscale node capabilities to nodes matching specified targets
- randomizeClientPort as a top-level policy field that toggles the default for every node
- New auto_update.enabled config option to control tailnet-wide default for client auto-update
- Minimum supported Tailscale client version is now v1.80.0
- Tailscale ACL compatibility improved through extensive test cases generated using Tailscale clients and official SaaS
- SSH rule parsing now trims whitespace on action, users, src, and dst fields and validates against empty or wildcard user entries
Minimum supported Tailscale client version: v1.80.0
Tailscale ACL compatibility improvements
Extensive test cases were systematically generated using Tailscale clients and the official SaaS to understand how the packet filter should be generated. We discovered a few differences, but overall our implementation was very close. #3036
SSH check action
SSH rules with "action": "check" are now supported. When a client initiates a SSH connection to a node
with a check action policy, the user is prompted to authenticate via OIDC or CLI approval before access
is granted. OIDC approval requires the authenticated user to own the source node; tagged source nodes
cannot use SSH check-mode.
A new headscale auth CLI command group supports the approval flow:
headscale auth approve --auth-id <id>approves a pending authentication request (SSH check or web auth)headscale auth reject --auth-id <id>rejects a pending authentication requestheadscale auth register --auth-id <id> --user <user>registers a node (replaces deprecatedheadscale nodes register)
Policy tests (beta)
Headscale now evaluates the tests block in a policy file. Tests assert reachability between
named sources and destinations and cover the whole policy — both acls and grants rules
contribute. They run on user-initiated writes via headscale policy set, on SIGHUP reload
(systemctl reload headscale / kill -HUP $(pidof headscale)), and on headscale policy check.
A failing test rejects the write before it is applied, with the same error message Tailscale SaaS
would return for the same policy.
At boot a stored policy whose tests no longer pass — for example because a referenced user was deleted while the server was offline — logs a warning and the server keeps running. Fix the policy and reload.
This feature is beta while behavioural coverage against Tailscale SaaS broadens.
SSH policy tests (beta)
Headscale now evaluates the sshTests block in a policy file. Each entry names a source, one or
more destination hosts, and three optional user lists: accept asserts the listed login users
reach every destination via an accept- or check-action SSH rule, deny asserts none of them
reach any destination, and check requires reachability specifically through a check-action
rule. Tests run on headscale policy set, on SIGHUP reload (systemctl reload headscale /
kill -HUP $(pidof headscale)), and on headscale policy check. A failing test rejects the
write before it is applied, with the same error message Tailscale SaaS would return for the same
policy.
At boot a stored policy whose sshTests no longer pass — for example because a referenced user was deleted while the server was offline — logs a warning and the server keeps running. Fix the policy and reload.
This feature is beta while behavioural coverage against Tailscale SaaS broadens.
SSH rule validation
SSH rule parsing now trims surrounding whitespace on action, users, src, and dst,
rejects empty or wildcard entries in users, rejects empty acceptEnv, and rejects negative
checkPeriod. hosts: aliases are rejected as SSH destinations, non-ASCII tag names are
rejected at parse time, and the wording for group-nesting cycles matches Tailscale SaaS.
#3263
Grants
We now support Tailscale grants
alongside ACLs. Grants extend what you can express in a policy beyond packet filtering: the app
field controls application-level features like Taildrive file sharing and peer relay, and the via
field steers traffic through specific tagged subnet routers or exit nodes. The ip field works like
an ACL rule. Grants can be mixed with ACLs in the same policy file.
#2180
As part of this, we added autogroup:danger-all. It resolves to 0.0.0.0/0 and ::/0, all IP
addresses, including those outside the tailnet. This replaces the old behaviour where * matched
all IPs (see BREAKING below). The name is intentional: accepting traffic from the entire
internet is a security-sensitive choice. autogroup:danger-all can only be used as a source.
Node attributes (nodeAttrs)
ACL policies now accept a nodeAttrs block. Each entry hands a list of
Tailscale node capabilities to every node matching target. The accepted
target forms are the same as acls.src and grants.src: users, groups,
tags, hosts, prefixes, autogroup:member, autogroup:tagged, and *.
{
"randomizeClientPort": true,
"nodeAttrs": [
{ "target": ["autogroup:tagged"], "attr": ["disable-captive-portal-detection"] },
{ "target": ["alice@example.com"], "attr": ["nextdns:abc123"] },
],
}
Frequently requested capabilities this unlocks include magicdns-aaaa,
disable-relay-server, disable-captive-portal-detection,
nextdns:<profile> / nextdns:no-device-info, randomize-client-port,
and the Taildrive drive:share / drive:access pair. The set is not
limited to these, any string-only cap an operator places in policy
reaches clients unchanged.
randomizeClientPort also lands as a top-level policy field that toggles
the default for every node, replacing the old server-config knob.
A new auto_update.enabled config option controls the tailnet-wide
default for client auto-update. When true, every node's CapMap carries
default-auto-update: [true] so fresh clients pick up the default
unless they make a local opt-in / opt-out choice.
Policies that use the funnel cap, ipPool blocks, or
autogroup:admin / autogroup:owner targets are rejected at load —
those features depend on machinery headscale does not yet ship.
Taildrive
Taildrive (file-sync between
nodes) is now
configurable through policy. Grant drive:share to the node that
hosts files and drive:access to nodes that read or write them; pair
with a tailscale.com/cap/drive grant to set the per-share access
mode:
{
"nodeAttrs": [
{ "target": ["tag:fileserver"], "attr": ["drive:share"] },
{ "target": ["autogroup:member"], "attr": ["drive:access"] },
],
"grants": [
{
"src": ["autogroup:member"],
"dst": ["tag:fileserver"],
"app": {
"tailscale.com/cap/drive": [{ "shares": ["*"], "access": "rw" }],
},
},
],
}
A wildcard nodeAttrs ("target": ["*"]) hands the caps to every
node when fine-grained control is not needed.
Hostname sanitisation
Hostnames are now santised using Tailscales magicdns sanitisation rules, matching Tailscale SaaS behavior. This means that hostnames with non-ASCII characters, special characters, or reserved DNS label characters are now transformed into valid DNS labels for MagicDNS. This improves our previously too strict sanitisation that rejected hostnames based on our guesswork and not based on the Tailscale upstream behaviour.
Examples that previously regressed and now work:
| Input | Raw (Hostname) | DNS label (GivenName) |
|---|---|---|
Joe's Mac mini | Joe's Mac mini | joes-mac-mini |
Yuri's MacBook Pro | Yuri's MacBook Pro | yuris-macbook-pro |
Test@Host | Test@Host | test-host |
mail.server | mail.server | mail-server |
My-PC! | My-PC! | my-pc |
我的电脑 | 我的电脑 | node |
HA subnet router health probing
Headscale now actively probes HA subnet routers to detect nodes that are connected but not
forwarding traffic. The control plane periodically pings HA subnet routers via the Noise
control channel and fails over to a healthy standby if the primary stops responding. This is
enabled by default (node.routes.ha.probe_interval: 10s, probe_timeout: 5s) and only
active when HA routes exist (2+ nodes advertising the same prefix). Set probe_interval to
0 to disable. This complements the existing disconnect-based failover, catching "zombie
connected" routers that maintain their control session but cannot route packets.
#3194
BREAKING
Hostname handling
- The
GivenNamecollision policy changed from an 8-char random hash suffix (laptop-abc12xyz) to a monotonic numeric suffix (laptop,laptop-1,laptop-2, …), matching Tailscale SaaS. Empty / all-non-ASCII hostnames now fall back to the literalnodeinstead ofinvalid-<rand>. MagicDNS names change on upgrade for any node whose previous label was a random-suffix form; the rawHostnamecolumn is unchanged. #3202
ACL Policy
- Wildcard (
*) in ACL sources and destinations now resolves to Tailscale's CGNAT range (100.64.0.0/10) and ULA range (fd7a:115c:a1e0::/48) instead of all IPs (0.0.0.0/0and::/0) #3036- This better matches Tailscale's security model where
*means "any node in the tailnet" rather than "any IP address" - Policies that need to match all IP addresses including non-Tailscale IPs should use
autogroup:danger-allas a source, or explicit CIDR ranges as destinations #2180 autogroup:danger-allcan only be used as a source; it cannot be used as a destination- Note: Users with non-standard IP ranges configured in
prefixes.ipv4orprefixes.ipv6(which is unsupported and produces a warning) will need to explicitly specify their CIDR ranges in ACL rules instead of using*
- This better matches Tailscale's security model where
- Validate
autogroup:selfsource restrictions matching Tailscale behavior - tags, hosts, and IPs are rejected as sources forautogroup:selfdestinations #3036- Policies using tags, hosts, or IP addresses as sources for
autogroup:selfdestinations will now fail validation
- Policies using tags, hosts, or IP addresses as sources for
- The
proto:icmpprotocol name now only includes ICMPv4 (protocol 1), matching Tailscale behavior #3036- Previously,
proto:icmpincluded both ICMPv4 and ICMPv6 - Use
proto:ipv6-icmpor protocol number58explicitly for ICMPv6
- Previously,
Upgrade Path
- Headscale now enforces a strict version upgrade path #3083
- Skipping minor versions (e.g. 0.27 → 0.29) is blocked; upgrade one minor version at a time
- Downgrading to a previous minor version is blocked
- Patch version changes within the same minor are always allowed
Configuration
-
The
randomize_client_portserver-config key was removed; the toggle now lives in the policy file as a top-levelrandomizeClientPortfield, matching the Tailscale-hosted schema. #3251 Headscale refuses to start when the old key is set. Move it to the policy file referenced bypolicy.path:{ "randomizeClientPort": true, }If you do not have a policy file yet, create one with that minimal content and point
policy.pathat it. The default carries over — empty / absent policy meansrandomizeClientPort: false, matching the previous behaviour for operators who never set the key. Per-node opt-in vianodeAttrsis also supported and stacks on top of the global default.
CLI
headscale nodes registeris deprecated in favour ofheadscale auth register --auth-id <id> --user <user>#1850- The old command continues to work but will be removed in a future release
Changes
ACL Policy
- Fix subnet-to-subnet peer visibility — subnet routers now correctly become peers when ACL rules reference only subnet CIDRs as sources, without requiring node IP rules #3175
- Fix filter rule reduction to use only approved subnet routes instead of all advertised routes, matching Tailscale SaaS behavior #3175
- Add ICMP and IPv6-ICMP protocols to default filter rules when no protocol is specified #3036
- Fix autogroup:self handling for tagged nodes - tagged nodes no longer incorrectly receive autogroup:self filter rules #3036
- Use CIDR format for autogroup:self destination IPs matching Tailscale behavior #3036
- Merge filter rules with identical SrcIPs and IPProto matching Tailscale behavior - multiple ACL rules with the same source now produce a single FilterRule with combined DstPorts #3036
- Fix exit nodes incorrectly receiving filter rules for destinations that only overlap via exit routes #3169 #3175
- Fix address-based aliases (hosts, raw IPs) incorrectly expanding to include the matching node's other address family #2180
- Fix identity-based aliases (tags, users, groups) resolving to IPv4 only; they now include both IPv4 and IPv6 matching Tailscale behavior #2180
- Fix wildcard (
*) source in ACLs now using actually-approved subnet routes instead of autoApprover policy prefixes #2180 - Fix non-wildcard source IPs being dropped when combined with wildcard
*in the same ACL rule #2180 - Fix exit node approval not triggering filter rule recalculation for peers #2180
- Policy validation error messages now include field context (e.g.,
src=,dst=) and are more descriptive #2180 - Reject policies whose
user@tokens match multiple DB users; rename the duplicate viaheadscale users renameto load #3160 - Evaluate the policy
testsblock on user-initiated writes across bothaclsandgrants; reject policies whose tests fail (beta) #1803
Grants
- Add support for policy grants with
ip,app, andviafields #2180 - Add
autogroup:danger-allas a source-only autogroup resolving to all IP addresses #2180 - Add capability grants for Taildrive (
cap/drive) and peer relay (cap/relay) with automatic companion capabilities #2180 - Add per-viewer via route steering — grants with
viatags control which subnet router or exit node handles traffic for each group of viewers #2180 - Enable Taildrive node attributes on all nodes; actual access is controlled by
cap/drivegrants #2180
SSH Policy
- Add support for
localpart:*@<domain>in SSH ruleusersfield, mapping each matching user's email local-part as their OS username #3091 - Add SSH
checkaction support with OIDC and CLI-based approval flows #1850
CLI
- Add
headscale auth register,headscale auth approve, andheadscale auth rejectCLI commands #1850 - Deprecate
headscale nodes register --keyin favour ofheadscale auth register --auth-id#1850 headscale policy check --bypass-grpc-and-access-database-directlyvalidatesuser@tokens against the live user database #3160- Remove deprecated
--namespaceflag fromnodes list,nodes register, anddebug create-nodecommands (use--userinstead) #3093 - Remove deprecated
namespace/nscommand aliases forusersandmachine/machinesaliases fornodes#3093 - Fix
DestroyUserdeleting all pre-auth keys in the database instead of only the target user's keys #3155 headscale policy checkevaluates thetestsblock when invoked with--bypass-grpc-and-access-database-directly; without the flag it warns instead of running the tests against empty data #1803
API
- Add
authrelated routes. Theauth/registerendpoint now expects data as JSON #1850 - Remove gRPC reflection from the remote (TCP) server #3180
OIDC
- Add a confirmation page before completing node registration, showing the device hostname and machine key fingerprint #3180
- Generalise auth templates into reusable
AuthSuccessandAuthWebcomponents #1850 - Unify auth pipeline with
AuthVerdicttype, supporting registration, reauthentication, and SSH checks #1850
Configuration
- Add
node.expiryconfiguration option to set a default node key expiry for nodes registered via auth key #3122- Tagged nodes (registered with tagged pre-auth keys) are exempt from default expiry
oidc.expiryhas been removed; usenode.expiryinstead (applies to all registration methods including OIDC)ephemeral_node_inactivity_timeoutis deprecated in favour ofnode.ephemeral.inactivity_timeout
- Add
trusted_proxiesto gateTrue-Client-IP/X-Real-IP/X-Forwarded-For(previously honoured from any client) #3268
Debug
- Add node connectivity ping page for verifying control-plane reachability #3183
- Omit secret fields (
Pass,ClientSecret,APIKey) from/debug/configJSON output #3180 - Route
statsvizthroughtsweb.Protected#3180
Other
- Remove old migrations for the debian package #3185
- Install
config-example.yamlas example for the debian package #3186 - Fix user-owned re-registration with zero client expiry and no default storing
0001-01-01 00:00:00in the database instead ofNULL#3199- Pre-existing rows with
0001-01-01 00:00:00are not backfilled; they clear themselves the next time the node re-registers
- Pre-existing rows with
- Fix
tailscaledrestart on a node with no expiry resettingNULLto0001-01-01 00:00:00in the database, affecting both tagged and untagged nodes #3197
Upgrade
Please follow the steps outlined in the upgrade guide to update your existing Headscale installation.
Changelog
- 7f02210863692ac1a5692d019125a02c82860b9a .golangci: ignore tests for goconst, raise occurrence threshold
- 157e3a30fcbe6354ef42b627b071a82966840fe8 AGENTS.md: trim to behavioural guidance, drop deprecated sub-agent
- cfb308b4a7599a22f3fc209c83dea406702422fd Add FAQ entry to migrate back to default IP prefixes
- e597f4c8a04cccbed03186389d64e7d1bce65745 Add Headscale UI to web UI documentation
- df339cd29003d74adf52fd7ae9917979efb56fa9 Add a link to Authentik's integration guide
- f3f84a5a63ba69e31caee6bb5a4467dbbb47a63b Add docs for policy-wide options and node attributes
- 890a044ef677314a649973c974c8b18f436af508 Add more UIs
- 4eb589915436c88468ac55d346b542bda72e9586 Add taildrive, tests, sshTests as supported features
- 542091e82b7541ab40d64f7e7d8d22f689594444 Add unit test
- 20dff82f95e33316d52f0d0148f2b2ac00a8a464 CHANGELOG: add minimum Tailscale version for 0.29.0
- 58a85b68b3d9571bd77d5ecc5ab4bcd12b69e156 CHANGELOG: bump 0.29.0 minimum tailscale client to v1.80.0
- f693cc085165395b4e2d2256c8bc973b53f4653a CHANGELOG: document grants support for 0.29.0
- 4e1d83ecefeb4b7c3dea7d153dfb46dde09e59e5 CHANGELOG: document hostname cleanroom rewrite
- b52f8cb52fcb8c5d4c58c6583a7cdf7b9bbed5e6 CHANGELOG: document node.expiry and oidc.expiry deprecation
- 408f4022e4dbadf2c0472affcee887bea721e8b6 CHANGELOG: document nodeAttrs feature and migrations
- f03d41ea9a4bd5277ff70f10140bf3afa43a1ddc CHANGELOG: document policy tests (beta)
- e78a24b8925f3fa51deb8f2d4885560fe75fef70 CHANGELOG: document sshTests evaluation (beta)
- 30d18575befea6043e42bde004415f0c64a29a4a CHANGELOG: document strict version upgrade path
- ec48f34e1cfd8781b33ddbf3f9921b03da81f240 CHANGELOG: document subnet-to-subnet ACL fixes
- fd1074160e541a47935c11ca6ec31a087ec67d1e CHANGELOG: document user-facing changes from #3180
- bcfaf6ad68516787065457d7a1adc5aa08ecc3e2 CHANGELOG: note nil expiry preservation fix
- e3323b65e5395e4a7f02d93f9901562ef3495dd8 Describe how to set username instead of SPN for Kanidm
- dc733767c40d6b97435ea1fe9f51214486fc7b93 Dockerfile.tailscale-HEAD,Dockerfile.derper: bump golang to 1.26.3
- 78570c754f2bd2a155429de05fbe1985ae60675f Dockerfile: bump base images
- e40dbe3b28ffb019e0e49b753efe7d634c69d1f9 Dockerfile: bump tailscale DERPer builder to Go 1.26.2
- 6390fcee79aa594f61349754aac0d6d14ff7a8ac Dockerfile: bump tailscale HEAD builder to Go 1.26.2
- 7e6c7924ad813e721b7ff2728febfbf50be4228c Document availability of autgroup:internet
- faf55f5e8f955e610960346e7358302e5b1e9564 Document how to use the provider identifier in the policy
- 1a64d950fd23a635a08d6188bc746606d15e3ed3 Document supported autogroups once
- 0f12e414a67ad1afc846c189336890723e88a7a6 Explain one approach to update OIDC provider info
- efd83da14e71a5cffee54d41edb86694ab14642d Explicitly mention that a headscale username should not end with @
- d556df1c3649f5e3ac8d53e625385c36c8e059c1 Extend upgrade guide with backup instructions
- 44600550c6ccd477ba70048c18c821a05ea083af Fix invisible selected menu item
- a0d6802d5b3859586217d44b2fc2ce5a8fa0f08c Fix minor formatting issue in FAQ
- c7f221dd0a99777957103e4c6c3303eede1c0552 Fix typo and wording
- 3672a2df3a860bcb97f932d3dc7785406329b49e Fix typo in API key creation help text
- 414d3bbbd830dc4d73b9200b09930cc1d9caa9d7 Fix typo in comment about fsnotify behavior
- c907b0d32328ed822cca23ad0f238e7e6651fba4 Fix version in mkdocs
- 32e1d77663e50ec607e512640d281857ce5fb3c8 Install config-example.yaml as example for the debian package
- 9e50071df9ab70730a2921f3e6b3693bc0a60118 Link Fosdem 2026 talk
- 84c7f0d450fe627676c498f3ec7c015667312f32 Link to development builds
- 8028fa5483fed557e63d902647232e5f262f003c No longer consider autogroup:self experimental
- e07b39108f4bad3045ee062aa3cca79a5884666a Quote autogroup:self in the CHANGELOG
- acddd73183bfb53143c5cd3ce9808dcb9d5efe4c Reformat docs with mdformat
- 109bfc404c3cf13116958c4dcb0eb8c7393a0f73 Refresh docs for Grants
- c4ab267c36b91ee3e2da599f06abd9cb1a205292 Refresh features page
- 8f60b819ec89b12775ba44e08587f55113b62b04 Refresh update path
- c29bcd2eaff7b03213022ff19f372b2fb10a2d75 Release planning happens in milestones
- 9ea09ea4b6f7513488f21e9cfec2cbf83ecaefff Remove changelog section for 0.28.1
- 14ce7e9106c898b259ee22cbf5ab740b50ee09ba Remove link to Arch AUR headscale-git
- 484462898b55cbc0e8e2f92e750b3b9b9f2f41a3 Remove link to sqlite
- 892ffffc4a9541d3f0029e8e7336b62fd4a20dc7 Remove misleading comment
- 61c9ae81e440e15492dac2b970387080525f6355 Remove old migrations for the debian package
- e13f0458bb2f53360aa9d005cddf0c9848252df4 Remove redundant prefix
- 4bb02412579aacb9cf11f8555aeb1593ea1d57db Require to update from one version to the next
- edb7ad0f811d4cd63139135e355f9774f0501f48 Rewrite ACL docs as policy
- 45b698dbac1f68a1be36e17507fd728326d1d95b Shorten container introduction
- 513544cc11cb5abee0e7c20c193befb7ad97b2e1 Simplify upgrade snippet with a link to the upgrade guide
- 8423af2732f2ef363c0a6fa53efc925a3ca12742 Swap favicon for updated version
- 47307d19cfced913b22afa6e7c9383ecfb08f9a0 Switch to mdformat to format docs
- f3512d50dfd2498e6a54e8bc6c651e8c0895a430 Switch to mkdocs-materialx
- e285f3c9322f55e11f6133548e4cf7a836d89144 The headscale service is enabled by default
- 355733342f511223277f4f737aea93b3716a68de Update config-example links
- 9baa795ddbb4eda613142b9de2f1cd15e2596a96 Update docs for auth-id changes
- 813eb2d733ff882165cc5a744adb1ab6eeb615c5 Update docs for new HA tracking
- f1494a32ce3c6221d305ddbc3ecbd79b4572a725 Update links to Tailscale documentation
- fda72ad1a322f1e2381ba1d863558a81bc08de47 Update main.md
- 68b0014871c9336f766386f2d08179005de4b8c6 Use distroless without quotes
- 163363a12a782f0226ea302a417c11f14cfcd8dd Use docs instead of KB
- 23a5f1b628559d55e338e9d2a5e9c1c953cb0d7b Use pymdownx.magiclink with its default configuration
- 97778c9930a3e19b07e75db6677730f6e9341655 all: add tests for PingRequest implementation
- 17236fd28449da7994783bd153963de9dc6f3501 all: annotate complex functions with gocyclo rationale
- 3e2aa5814edbb7c607a7c150de11bdc8310f8a21 all: annotate gosec false positives with rationale
- 93860a5c063e20a51ceb1560c5404e4dd07ead18 all: apply formatter changes
- 4cca63155d7a1bed3c806f876991a1a05e77bb73 all: apply godoc [Name] link conventions across comments
- 43afeedde22dd6d57f3249af93bcf7970ce76f4f all: apply golangci-lint 2.9.0 fixes
- ce580f824536f6c250dafd1c23e5558ca603130d all: fix golangci-lint issues (#3064)
- eccf64eb58ca1187019a4c54d059fc7eed8fee8d all: fix staticcheck SA4006 in types_test.go
- 3e0a96ec3abfdde94cfd6dea4845964e5543dce9 all: fix test flakiness and improve test infrastructure
- b113655b7172982a97d2ebd44cc666ed93fba83f all: implement PingRequest for node connectivity checking
- f905d58292866df651d0646b174cdfff4c4545c0 all: mechanical lint fixes
- 742878d17211ffa7ca597a7312c2e6a026aba580 all: regenerate generated files for new tool versions
- 010a5564c5fc65c09cc84777cad956821e2afa74 all: rephrase prose to fit codebase voice
- 36a73f8c2257e486c94770f5702e34d2a01b2a78 all: update Go dependencies
- 542cdb2cb2e53ea05e837810db48b956c1434d0b all: update Go to 1.26.1
- 70f8141abd466898d72e2713db41c58a43fc0a73 all: upgrade from Go 1.26rc2 to Go 1.26.0
- 0f6d312ada15911e3b373e6202df0974818f2cf2 all: upgrade to Go 1.26rc2 and modernize codebase
- 4a9a329339c3e955bb85651733ded5bfc91794b1 all: use lowercase log messages
- 0567cb6da3a294110bbd27f9b51c48e05938d57b app: add security headers middleware
- f7d8bb8b3f37813f2a616e313a171721ccc31bf3 app: remove gRPC reflection from remote server
- 30338441c1c95d4aa975d52b6f42a9ac7cc20b96 app: switch from gorilla to chi mux
- 8a97dd134bf0b6d0717e3f1ab428c538eed1a5a7 app: wire HA health prober into scheduled tasks
- f066d12153426c417d666ad392c5ebe78bee3d37 assets: fix logo alignment and error icon centering
- 41d70fe87b6ae75235c6ad3beda32c5c15c477f2 auth: check machine key on tailscaled-restart fast path
- cb3b6949ea0c72d45f25d5eb2e8f2ef9584af88a auth: generalise auth flow and introduce AuthVerdict
- 25ccb5a161358503cdb6e01bc8e11364a3785d5e build: update golangci-lint and gopls in flake
- eb23c125fab016b706c1bc5b7d4be6a47d3bac6d capver, types: bump to tailscale v1.98, drop LegacyDERPString
- 442fcdbd33065f56650c40d7cbd455e73cb0c64e capver: regenerate for tailscale v1.96
- 31c4331a9183cb3030041791bc42b0b5eec61480 capver: regenerate from docker tags
- 2530d86f1b4d941fa49212273a1e3068a89c7757 change: document PingRequest merge first-wins foot-gun
- 4a4032a4b0c108a43e931589ded088b1587f925d changelog: document filter rule merging
- f27298c759ff82d1e71303d460b1ac8a91945c0c changelog: document wildcard CGNAT range change Add breaking change entry for the wildcard resolution change to use CGNAT/ULA ranges instead of all IPs. Updates #3036
- 575d8ecbfde35c3d7c21f7da34d74a9f790c1c4b changelog: normalise 0.29.0 BREAKING and Changes sections
- 9621a97ebe47490bd27e535aa5d21f12f0bc2a15 ci, pre-commit: validate vendor hash via vendorhash check
- e171d30179d3a40befcf2376315aba5f819814aa ci: add build workflow for main branch
- 99a93c126bf14c21a17712d29b6d64b8115b2fb3 ci: add rolling development tag to container builds
- 795a1efe9bd9cb86310124aa6ba0a86b907381aa ci: fetch full history in golangci-lint job
- d15ec28799c422e130876dbab042367355ff3d53 ci: pin Docker to v28 to avoid v29 breaking changes
- 1b6ab52f9e2479627283a90c1626583ae3a53414 ci: regenerate integration test workflow
- 0f9729466501a083ef734165e746762966fb9b6a ci: regenerate integration test workflow
- 5c449db1258908dec8fad7ce8be7ed02faec6c6b ci: regenerate test-integration.yaml for TestSSHLocalpart
- a7d405a25564efe2b09e83a79cd68c31a1ccd6cb ci: regenerate test-integration.yaml for TestTailscaleRustAxum
- 1f9635c2ec20b362281daa9c24adcd7e008f765c ci: restrict test generator to .go files
- a76b4bd46cc193b838bba4b3353ba649e9e7141f ci: switch integration tests to ARM runners
- 4d3b56714921c5bad2e1cc6e8e14e93bed8fd02c ci: use overlay2 storage driver instead of pinning docker v28
- e00c8992198ced5d43035ebeaba8d6f53b629c99 cmd, templates, integration: extract shared production constants
- 461a0e2bea8ce5caab91dca3c4d64d604aecf7a7 cmd/dev: add local development server tool
- 1a58b77271e437cd8f92001c214182b49e7ceb03 cmd/dev: validate --port fits the derived-port range
- 6c08b49d6351aa3859e01818a7c77f2180393b9c cmd/headscale/cli: add confirmAction helper for force/prompt patterns
- aae2f7de717a1c805623aee098fe8f54ee4bd1d7 cmd/headscale/cli: add grpcRun wrapper for gRPC client lifecycle
- 7b7b270126d992e7f4c3e8e677523240726708d7 cmd/headscale/cli: add mustMarkRequired helper for init-time flag validation
- d6c39e65a59eb4671b8bf15cb83ca208c5ed0706 cmd/headscale/cli: add printListOutput to centralise table-vs-JSON branching
- 095106f49830050bd6321c2e40644e01808eb2d2 cmd/headscale/cli: convert remaining commands to RunE
- 22fccae12566514f573283f0078a2b10fa7811af cmd/headscale/cli: deduplicate expiration parsing and api-key flag validation
- 2765fd397f21844479218bf7827390b085206478 cmd/headscale/cli: drop dead flag-read error checks
- af777f44f4f28ec099cdd77f4dfe9de3b4c967e5 cmd/headscale/cli: extract bypassDatabase helper and simplify policy file reads
- 92a9accfcb7161a9c0b38f18bae05a4e15c6172e cmd/headscale/cli: mention sshTests in policy check help
- 7460bec76717ab20c8df8932e2499255ffbae818 cmd/headscale/cli: move errMissingParameter and Error type to their users
- 8891ec98358c4bd9a6c350d8cd7902199b42d9ef cmd/headscale/cli: remove deprecated output, SuccessOutput, ErrorOutput
- d72a06c6c6ccbbcd65931c1038690bd834c65420 cmd/headscale/cli: remove legacy namespace and machine aliases
- 13ebea192c280bb72334b7b8e3101ec803ea0c12 cmd/headscale/cli: remove nil resp guards and unexport HasMachineOutputFlag
- e816397d547c3c65d203113ee5acc77fd3b62ed5 cmd/headscale/cli: remove no-op Args functions from serveCmd and dumpConfigCmd
- e6546b2ceaa08ff9ece54a399b30fcf491df911f cmd/headscale/cli: silence cobra error/usage output and centralise error formatting
- e4fe216e45037878ce2700387991c9e72b8466c0 cmd/headscale/cli: switch to RunE with grpcRunE and error returns
- ca321d3c13327ff9a90e0af0235f4c924e193ec3 cmd/headscale/cli: use HeadscaleDateTimeFormat and util.Base10 consistently
- 4e0c2b855616e08f3745d4db7f0264b402cee19f cmd/headscale/cli: validate users in policy check
- e470774f6a362f66a7eed2f1c534941aea31f3a3 cmd/vendorhash: track vendor SRI in flakehashes.json
- b5090a01ecf029351ee5b5ac8d655084f3633141 cmd: use zf constants for zerolog field names
- 3f73ed5404a7b4f73ca4720bbd5686146acbb9a9 config, types: move randomize_client_port from server config to policy file
- 82958835ceb26df8554c53c773044f98a1a93b43 db: enforce strict version upgrade path
- 73613d7f5317f243b01adf4bf9e37cb041228207 db: fix database_versions table creation for PostgreSQL
- 3037e5eee0285bfdc5f34cdee718331cdb315102 db: fix slice aliasing in migration tag merge
- 0641771128601588d270b5d45cc52a67a012b5df db: guard UsePreAuthKey with WHERE used=false
- af7e7a45604dd7fe5e0c64d5e2c907a3108c7c36 db: remove unused SetApprovedRoutes and SetTags helpers
- 7c756b82019755ee8110557ff57d94a0db66a0ac db: scope DestroyUser to only delete the target user's pre-auth keys
- 93e8c7285f6207cc561b659d2e902e81dc94d915 debug: explain URLIsNoise choice in ping callback
- d5a4e6e36a8db6d2e15223d97023cc7a0034f812 debug: route statsviz through tsweb.Protected
- 84adda226be25cc9e3240d39ef5b56627ade6a47 doc: add CHANGELOG entries for SSH check and auth commands
- 585d0c01bca4814b76d1a220a2e427a36f404075 docs(config): fix typo in config-example.yaml
- 01eb5402f9b91f3bd6c948e2e7b80e234b489e7b docs(setup): fix typo in requirements.md
- 3acce2da87df7fa727027da293e7b689fc71abd1 errors: rewrite errors to follow go best practices
- 510503322420aad6277b68ada8971fa088327df3 feat: add prominent warning banner for non-standard IP prefixes
- 568baf3d021bd179ee3c230323984b81c7fc19a6 fix: align banner right-side border to consistent 64-char width
- 25adfaf341960d915440cb94d88cea04ce81a6ae flake.nix, flake.lock: bump nixpkgs and pinned tools
- 980622e9a597167ec800201e7bd6b9e3172050a4 flake.nix, go.mod: bump tailscale.com to v1.97.0-pre
- 9c3a3c5837f5e67ffafc139e983e282b6d3b3465 flake: upgrade golangci-lint to 2.9.0 and update nixpkgs
- 61a14bb0e44e784836094277d3cd243a4b64200d gen: regenerate from auth proto changes
- 570735f204b6fb86ce94f1aa37a944c0cbb84c36 gen: regenerate grpc stubs with protoc-gen-go-grpc v1.6.2
- 64f23136a29bc740a41dbf38d3ef8a3a73b78cb9 github: add needs-more-info automation workflow
- 900f4b7b75b1334b5cf8b6755a6d2c3527038e62 github: add support-request automation workflow
- 174e409da6f275904c34875c64ca3440117ce61b github: drop nu flatten in needs-more-info timer
- e0d8c3c877df8ac493dccc8cd280b688293d79b5 github: fix needs-more-info label race condition
- a7f981e30e353ccfca7fa8cb2a8452420b732fe7 github: fix needs-more-info label race condition
- ce5d1ba8f8b1c0226a4fdd73ff11304fc78dd9ec github: split nu where in needs-more-info timer
- c1b468f9f45867926e00fe7a7a700ed3ac2aad22 github: update issue template contact links
- be90910d338e69ea0d51428c79ee2e666bd965af go.mod, go.sum: bump dependencies for v0.29.0
- 1f48ebb376954aaf92796bc66c623332397eac83 go.mod: add github.com/realclientip/realclientip-go
- 2f94b80e7057bf8c770b96c699c974af4ed96fc6 go.mod: add stress tool dependency
- 0cf27eba7772d4f7a8b0f66eeb50918d7952c75f go.mod: add tstest/mts tool dependency
- 27f56413411cc2ddcad928c67bbbf7c26f23ba92 golangci: add forbidigo rule for zerolog field constants
- 0c6b9f5348f3a3523a25e404d7f574f23ebf34f6 goreleaser: remove unused ts2019 build tag
- 48cc98b787f1155cc95e1d79f83d361099152987 hscontrol, cli: add auth register and approve commands
- 52d454d0c84ca33550d5c7edb560e0fb7d16a4a0 hscontrol/db: add migration to clear user_id on tagged nodes
- 7148a690d0e4f1d84e088294d072686879354fc3 hscontrol/grpcv1: use EmbedObject and zf constants
- 53cdeff129492726dd3ce8cf2595a716d309c7d2 hscontrol/mapper: use sub-loggers and zf constants
- f74ea5b8edc5181a1f5cbebaf66901a7dc78cd0b hscontrol/policy/v2: add Grant policy format support
- bc9fb6d403f182e4441398371454edba27b088d0 hscontrol/policy/v2: reject ambiguous user references at load time
- b09af3846b9362dd99f210d77e3afad38b6ef5a3 hscontrol/poll,state: fix grace period disconnect TOCTOU race
- 4f8724151e5309e5dc2c43d5357bef8f88e6d30e hscontrol/poll: use sub-logger pattern for mapSession
- 4e73133b9f81a55544a999bf413135e7d63f53b6 hscontrol/routes: use sub-logger and zf constants
- ca7362e9aa240053cacce2dd505e3e74218d9fba hscontrol/servertest: add control plane lifecycle and consistency tests
- f87b08676d7e28313f73ad9bfc0d36daa990be80 hscontrol/servertest: add policy, route, ephemeral, and content tests
- 00c41b642227fdac7ebe174d34679073babafa6c hscontrol/servertest: add race, stress, and poll race tests
- ab4e205ce70a05f5409692176a65467553193474 hscontrol/servertest: expand issue tests to 24 scenarios, surface 4 issues
- 3d53f97c826c2f766025d611fcd3622815b15485 hscontrol/servertest: fix test expectations for eventual consistency
- dd16567c525c2fe124c66923ecfd9742db479013 hscontrol/state,db: use zf constants for logging
- 8048f10d1342c6b8e51f3ec85da3ae350f1b196b hscontrol/state: extract findExistingNodeForPAK to reduce complexity
- 1053fbb16b2b6defcb2648814628702278903887 hscontrol/state: fix online status reset during re-registration
- 2f907edf879d14daf883c6a3b45821c11a9fdaaa hscontrol/types: regenerate types_clone.go for viewer bump
- 894e6946dc7ffa5ab366df9a92a62c622ba934ce hscontrol/types: regenerate types_view.go
- 1059c678c47fa1a387323dcfbedafeee066d9a33 hscontrol/types: silence zerolog by default in tests
- e0a436cefcb8864d60cfee2a0081cb101a9e2c87 hscontrol/util/zlog/zf: add tag, authkey, and route constants
- 0288614bdfd3c7acd264060f65924880ca8cfa47 hscontrol: add servertest harness for in-process control plane testing
- 580dcad683a38686a98a3df848282eb6d22f7b2f hscontrol: add tests for SetTags user_id database persistence
- 7e8930c50748ed2ac431b36e29adcbccc0c2322e hscontrol: add tests for default node key expiry
- 1e4fc3f1797400816311040158a00b6a84d05870 hscontrol: add tests for deleting users with tagged nodes
- 75e56df9e44542b57953f4022a3023e49c1e2188 hscontrol: enforce that tagged nodes never have user_id
- c6c29c05e56b1714a3b246a40afd551a74a09f3e hscontrol: gate proxy header trust on trusted_proxies
- 42b8c779a02e5bfad6cef5967a7f2e2a107a2707 hscontrol: limit /verify request body size
- 4ad200ab7352dd9690f112338f5a7a3fc25c8843 hscontrol: preserve nil expiry on tailscaled restart
- d6dfdc100cf3f9d4bd9378b61284516478b437f8 hscontrol: route hostname handling through dnsname and NodeStore
- 91730e2a1dfdb05da12d61dcd05b1548f93f67ce hscontrol: use EmbedObject for node logging
- 99767cf805d13fd6a6a687ca966365fe0e98b747 hscontrol: validate machine key and bind src/dst in SSH check handler
- f1e5f1346d1479cc30e7c65ef7400774712ef02b integration/acl: add tag verification step to TestACLTagPropagationPortSpecific
- ebc57d9a38a278c40092a3dc3162a10c80d7a58e integration/acl: fix TestACLPolicyPropagationOverTime infrastructure
- 81b871c9b50f3c7115d78a45af0b42d7406abc81 integration/acl: replace custom entrypoints with WithPackages
- a147b0cd87e061883d774f5961cc003ad9ad6b9c integration/acl: use CurlFailFast for all negative curl assertions
- eec3844f24a28ac5c831e55dc190b2f1f692b17a integration/dockertestutil: wait for libnetwork settle on reconnect
- e638cbc9b958bd9cb63ccd22f79f904441592bae integration/tsic: accept via peer-relay in non-direct ping check
- ea968e2f5df02b5ceb4f742957d2554a4b336f5c integration/tsric: add TailscaleRustInContainer package
- 7bb86f2c162b0d77b5b370b4711f5e5ff8956642 integration: HA cable-pull lifecycle test
- a7edcf3b0f73e0c77f5870319f541b38f780d34e integration: add CI-scaled timeouts and curl helpers for flaky ACL tests
- af26bab17a080d91e2f26f6dea7004a1e4c9f021 integration: add HA ping failover test
- 3db0a483edaa87d08e02b3213972c2283d1368a6 integration: add SSH check mode tests
- 2be94ce19a040a3523c934387f0f5b6f22cd4523 integration: add TestSSHLocalpart integration test
- 775bc3a7150f0bf6e7eedb7fb92b6ec7af00e7c1 integration: add TestTailscaleRustAxum for tailscale-rs
- 9b1a6b6c05e3752b14f0ba4cfe6ecd73e6df9825 integration: add cap/relay grant peer relay lifecycle test
- bca6e6334de6e14bb4532306ee1b9f4cfcb8d44b integration: add custom subnet support and fix exit node tests
- a739862c653dc3ca0fbb5e5f302c72ed58fca222 integration: add via grant route steering tests
- 98e9ff4d363f2db5d5e29d40d164944679c889e0 integration: authenticate Docker Hub pulls and retry transient errors
- ba251e7b472ffabefdc1061eb00afc069d0ac562 integration: cover exit nodes via autogroup:internet ACL (#3212)
- ecaf56e0a0b2717779b96e1f8300c60587461387 integration: drop Force flag on docker network disconnect
- 51eed414b4a46f75d3f996ed389ce5e8cfb39995 integration: fix ACL tests for address-family-specific resolve
- 9db5fb6393a0f4b08f3a253d728592e1699bf3a8 integration: fix error message assertion for invalid ACL action
- be4fd9ff2dd80366e059b3cc3822be07c090515e integration: fix tag tests for tagged nodes with nil user_id
- bfb6fd80dfa9c1f4946a3d4c87fc2aeec8c64cc1 integration: fixup test
- dfcc96d808058a9b0819b626aa018b5531c7264f integration: harden ACL test ergonomics
- acb8cfc7eed520ed0e98f2e71d7d906bd06aa47b integration: make docker execute and ping timeouts CI-aware
- 27c9113af83548830b654cebcb6f8cf354081cff integration: regenerate workflow for HA docker disconnect test
- 574a61852a3efc3f9cbed07d8e1fd63fde76d362 integration: reject failing sshTests at headscale policy set
- 3a4af8cf874c77ef9fe264b3c13e7ad62d5b0c2b integration: remove --accept-routes from via steering routers
- b762e4c350ca5106c1f4b4d78af010dbe1c5738c integration: remove exit node via grant tests
- 78fd6efb38166d33c5f8cd82d321df56c68931d7 integration: replace ad-hoc test timeouts with named constants
- 155e42f892f28d1c9f17e1c0c3209a4f74fb49dc integration: retry transient docker network ops
- a9a2001ae7a716a3a21d6bb1aa6bcce5b932272a integration: scale remaining hardcoded timeouts and replace pingAllHelper
- d1443a431cdcb4cfb160bc653eceaf0480aa1415 integration: skip subpackage tests in workflow generator
- e5ebe3205a485f8575f46b4feac963373a69fb14 integration: standardize test infrastructure options
- e44b402fe461b4bb90115b3751ee0ea39aa9952b integration: update TestSubnetRouteACL for filter merging and IPProto
- 210f58f62e72db916a823ee4ff22a6630121ac95 integration: use CI-scaled timeouts for all EventuallyWithT assertions
- a345a22a3be98893347dac9feddb6138c3cc5c62 mapper, app: ship MagicDNS Routes as empty slices, not nil
- b3f795f0b4be7759136f2f045a05738b37319c11 mapper, policy/v2: stamp suggest-exit-node on Peer.CapMap when exit routes approved
- 6fcff9e3527d20c6139898bb544ed40b65f173a9 mapper, state: deliver nodeAttrs through MapResponse and harden nextdns DoH rewrite
- 2d549e579fe7e585b7f5ce3574dbc3434013b0e7 mapper/batcher: add regression tests for M1, M3, M7 fixes
- 8e26651f2c0558b4d94b8c2d5a69a938538280a7 mapper/batcher: add regression tests for timer leak and Close lifecycle
- 9b24a3994340658dd42e84a8da84c4f85c8412f0 mapper/batcher: add scale benchmarks
- 21e02e5d1f4c9a45806cf80dcc40d09d741ce95e mapper/batcher: add unit tests and benchmarks
- feaf85bfbca1bbb00990faa3feef51c2b420df32 mapper/batcher: clean up test constants and output
- 50e8b21471fca399d5175d2264c5f5c2d7e59487 mapper/batcher: fix pointer retention, done-channel init, and connected-map races
- da33795e79dd6da29609819d3835f6d63da45c03 mapper/batcher: fix race conditions in cleanup and lookups
- 86e279869ec7f41105ac5f9182c2cc31a9804239 mapper/batcher: minor production code cleanup
- 57a38b56789332887dd5b3ac40b07b9373e69f4a mapper/batcher: reduce hot-path log verbosity
- 3ebe4d99c1abcbc0aceec7fa65d4022dcd9f0218 mapper/batcher: reduce lock contention with two-phase send
- afd3a6acbcf5eb8c6079d181499b60aabe55df7e mapper/batcher: remove disabled X-prefixed test functions
- 87b8507ac9d3510ee15074478e235ab62b1ba872 mapper/batcher: replace connected map with per-node disconnectedAt
- 3276bda0c0abef446dd5956d285dfdd55a4c5c4d mapper/batcher: replace time.After with NewTimer to avoid timer leak
- 57070680a588dd6971025d36eb5f623a2f3c2421 mapper/batcher: restructure internals for correctness
- 82c7efccf8b0dccc2539293f5628eb9d21d44fe4 mapper/batcher: serialize per-node work to prevent out-of-order delivery
- 60317064fd493b974a54387fea29b282b00cf585 mapper/batcher: serialize per-node work to prevent out-of-order delivery
- 051a38a4c4807dc3a612ade8ecf0977def5dd5d6 mapper/batcher: track worker goroutines and stop ticker on Close
- 3daf45e88aa2af8c0e46857f292a73d32551e506 mapper: close stale map channels after send timeouts
- 7881f6535843c2caa2652a7cb7a687d5ebf9b8cd mapper: extract node connection types to node_conn.go
- 9371b4ee2889fc4d605d89a299cec3cb6a925710 mapper: fix empty Peers list not clearing client peer state
- 3587225a88d1ca85b1b7111430f8a301f423b291 mapper: fix phantom updateSentPeers on disconnected nodes
- b81d6c734d071bdaf00039b717c31e3b73c0607b mapper: handle RemoveNode after channel cleanup
- 6cd919d41144faf3373e989fb5fd89962f287800 mapper: include UserProfiles in policy-change MapResponses
- 2058343ad6caea588617f9d119bb789927781e41 mapper: remove Batcher interface, rename to Batcher struct
- 427b2f15eedb13136aaca7699d8e73b289b3f9fa matcher: clarify DestsIsTheInternet single-family semantics
- 64c398f2c2050a79ed6bf87ebd44b9a8f27d3107 metrics, policy/v2: drop unused scaffolding + nil-error returns
- 65880ecb5867da032674edf220c13ffd89b20861 nix: disable external DERP URL fetch in VM test
- 37c6a9e3a6f1a8b2c2ee20529dc95b13c74bf182 nix: sync module options and descriptions with upstream nixpkgs
- 5e332595508f77d706a53a86473084a4959b074c nix: update flake inputs
- 2109674467791570c08aa44d1a23030d5ec2dc30 nix: update flake inputs and dev shell tool versions
- c5ef1d3bb96f9f47e0a6723c08ebdca0fa900776 nix: upgrade dev shell to Python 3.14
- f20bd0cf086423d29446ef8f02ffa4ce87f2b377 node: implement disable key expiry via CLI and API
- 4d427cfe2af6bd9bb71d1e2abcc987d2b0906e20 noise: limit request body size to prevent unauthenticated OOM
- 8c6cb05ab4247f479fc900857a9f7bbdabf88deb noise: pass context to sshActionFollowUp
- e4e742c776eed9422c6a34a002ed5249d9922762 noise: pin outer RemoteAddr onto tunnel requests
- 5a7cafdf855cba84d2641200ac8b70ac9f5937f3 noise: reject non-HEAD on PingResponseHandler
- d66d3a4269a8514d8c6930cba7b731f004d7e930 oidc: add confirmation page for node registration
- 3d0f597b237ce0227a233192f39fada79969d980 oidc: handle groups claim as string or array (FlexibleStringSlice)
- 78990491da94d1136d78aa8ff05d37209186319a oidc: render HTML error pages for browser-facing failures
- adb9467f605a69a787a964f11957bf240a58e683 oidc: validate state parameter length in callback
- 107c2f2f70373c196db6c24bcd1cdb9f67a1e0eb policy, noise: implement SSH check action
- c3df84e3547f0e558ac64f073ae4d59562e24226 policy/matcher: include CapGrant.Dsts in match destinations
- 8358017dcfab48590dbc3f6a1b72f0b332911748 policy/v2,state,mapper: implement per-viewer via route steering
- 078b9e308f1a4a35d9bb9ce6d1e36be9fe9bca09 policy/v2: SaaS-derived compat tests for nodeAttrs
- 49744cd4678f5a9383b1333aeba75aca238209ec policy/v2: accept RFC 3986 bracketed IPv6 in ACL destinations
- 6c59d3e60149f46473b9af45847c274c8bcd7de6 policy/v2: add SSH compatibility testdata from Tailscale SaaS
- 2cb914df59b437b519416b648aff10d22f075b41 policy/v2: add SaaS goldens for via-grant prefix containment
- 995ed0187c21f849512b323ae3512dbbc42e1a75 policy/v2: add advertised routes to compat test topologies
- 0fa9dcaff842dfb0cf85e38f89b27452a748f898 policy/v2: add data-driven grants compatibility test with Tailscale SaaS captures
- 0acf09bdd26879867e934e980b737e59e06a489f policy/v2: add localpart:*@domain SSH user compilation
- b668c7a596164f913b24713b35db37673e363886 policy/v2: add policy unmarshal tests for bracketed IPv6
- c0774a739bc4e00c7ceb4a01c912c17199e9499e policy/v2: add policytester captures recorded from Tailscale SaaS
- 7bc701179b13b5b9413c277de5a89bdcfd2c81e7 policy/v2: add policytester compat test runner
- 26eebcea5a0621523e1ef97adcbc1caa97b55270 policy/v2: add sshtester compat runner
- 834ac27779553a5da09187ab8ac83613fcab68b0 policy/v2: add subnet routes and exit node compatibility tests
- 5cd5e5de69c2a9c24af4126d4cfd476789b90971 policy/v2: add unit tests for ViaRoutesForPeer
- 08d26e541ca9338c14c547b905dc175cd291b3b3 policy/v2: add unit tests for grant filter compilation helpers
- 6a55f7d73199ea4879ca36c80ef12f289a3288dd policy/v2: add via exit steering golden captures and tests
- affaa1a31d1bdb881b4029daf556e322dcd12a07 policy/v2: align SSH check action with SaaS wire format
- d600090f2cd4e0f39e8c9a718abdb7c07026d325 policy/v2: align SSH rule validation with Tailscale
- e4e209f9191a1f071b557fe6d0b9bdedbb380bbf policy/v2: canonicalize Protocol form during unmarshal
- abd2b15db59a9e326bd80b0a960a0d0e8724683f policy/v2: clean up dead error variables, stale TODO, and test skip reasons
- 2fb71690e8321fc6a57cde5bb2fdf3da900ae0c2 policy/v2: convert ACL compat tests to data-driven format with Tailscale SaaS captures
- 500442c8f1699618a947aa4a4acb05939807188d policy/v2: convert routes compat tests to data-driven format with Tailscale SaaS captures
- 013dea4f40716ae9f8e4869715c6a540aa2cf958 policy/v2: evaluate sshTests at write boundary
- b29ae25356c57fb0dc8c8d4507d66540c0d3b7ac policy/v2: evaluate the tests block on user-initiated writes
- f95b254ea9063e7b9ba653ed32e804219409b509 policy/v2: exclude exit routes from ReduceFilterRules
- 2e1a716a9aacf4a77bf848fd50ba146fd065cae0 policy/v2: fix empty grants/acls returning FilterAllowAll
- 8573ff915890043a2e26c15c0897655e9c57f10f policy/v2: fix grant-only policies returning FilterAllowAll
- c36cedc32f7666a0ef5d62fa89987179bcf73147 policy/v2: fix via grants in BuildPeerMap, MatchersForNode, and ViaRoutesForPeer
- 28be15f8ead74d01536b085c22bf8e4b5bd01cab policy/v2: handle autogroup:internet in via grant compilation
- 0e3acdd8ecf99170b7bfeca2b690d23019c684c7 policy/v2: implement CapGrant compilation with companion capabilities
- 687cf0882f8ad2baf42c1bea4d97077d04a96108 policy/v2: implement autogroup:danger-all support
- 4f040dead2badfae47ccaf41a8d62072e19fc540 policy/v2: implement grant validation rules matching Tailscale SaaS
- 54db47badca4d7ecf76d28b3d4382325aa42d192 policy/v2: implement via route compilation for grants
- d5b2837231d4b79f16d4081efcd434c44106d45e policy/v2: match default proto set for tests with no proto
- e5fcd01ee60a7896c34beb7551992ae96e25e04d policy/v2: match via-grant destinations by prefix overlap
- a7c9721faa2f9a90544b0c8c330035fd4c62e099 policy/v2: overhaul compat test infrastructure
- a4f05b0962141213393f1c26f196d8e8bc3e57a4 policy/v2: parse, validate, and compile nodeAttrs
- ebe0f4078dcca09b008ee174f41e1ee6719ac55f policy/v2: preserve non-wildcard source IPs alongside wildcard ranges
- 9f7aa556890927d780c29dad8d4a9a080bf28648 policy/v2: refactor alias resolution to use ResolvedAddresses
- dda35847b020f9b9ac787caccf11dac18d8f6d9a policy/v2: reorder ACL self grants to match Tailscale rule ordering
- 2b7f15abaa079039660a1415ef357344372bc1fb policy/v2: surface autogroup:internet via grants on exit nodes
- e05f45cfb12b71a66660b5809f4624b2a5c9c561 policy/v2: use approved node routes in wildcard SrcIPs
- 927ce418d2bd936a9e286a3ce0ac54f5997f2d19 policy/v2: use bare IPs in autogroup:self DstPorts
- ccd284c0a58fbb51480309ab52d5edb639c31cfc policy/v2: use per-node filter compilation for via grants
- 6a0a297c7f8669f291b6e12f8e05ffac9e202e6f policy/v2: validate sshTests at parse
- f172dba0e33b7a4140f805fbd9444eb67baf139b policy/v2: validate tests block at parse boundary
- b051e7b2bc76bfaaf700a92c4ccb7519680728dd policy/v2: wire PolicyManager through compiledGrant
- f735502eae8459f3f6be0f714853b7079bbdb510 policy: add ICMP protocols to default and export constants When ACL rules don't specify a protocol, Headscale now defaults to [TCP, UDP, ICMP, ICMPv6] instead of just [TCP, UDP], matching Tailscale's behavior. Also export protocol number constants (ProtocolTCP, ProtocolUDP, etc.) for use in external test packages, renaming the string protocol constants to ProtoNameTCP, ProtoNameUDP, etc. to avoid conflicts. This resolves 78 ICMP-related TODOs in the Tailscale compatibility tests, reducing the total from 165 to 87.
- 53d17aa321485a276f1251288b6e4df706db9fb7 policy: add comprehensive Tailscale ACL compatibility tests Add extensive test coverage verifying Headscale's ACL policy behavior matches Tailscale's coordination server. Tests cover: - Source/destination resolution for users, groups, tags, hosts, IPs - autogroup:member, autogroup:tagged, autogroup:self behavior - Filter rule deduplication and merging semantics - Multi-rule interaction patterns - Error case validation Key behavioral differences documented: - Headscale creates separate filter entries per ACL rule; Tailscale merges rules with identical sources - Headscale deduplicates Dsts within a rule; Tailscale does not - Headscale does not validate autogroup:self source restrictions for ACL rules (only SSH rules); Tailscale rejects invalid sources Tests are based on real Tailscale coordination server responses captured from a test environment with 5 nodes (1 user-owned, 4 tagged).
- 835b7eb9605e6005d1c81b20b62504f872bc3a41 policy: autogroup:internet does not generate packet filters
- 14f833bdb9874075f888b4a01f3d1d327f6b338b policy: fix autogroup:self handling for tagged nodes Skip autogroup:self destination processing for tagged nodes since they can never match autogroup:self (which only applies to user-owned nodes). Also reorder the IsTagged() check to short-circuit before accessing User() to avoid potential nil pointer access on tagged nodes.
- 95b1fd636eaf08d2c114038a0699c0666f7f7ee4 policy: fix wildcard DstPorts format and proto:icmp handling
- 93d79d8da90f05399e4e5e69a85d7e336617ed09 policy: include IPv6 in identity-based alias resolution
- 0b1727c3378f69340149d3dc7470d7d969384f2b policy: merge filter rules with identical SrcIPs and IPProto
- c7a0ca709f677f25bec400b9679beb2efa9d7537 policy: surface exit nodes via autogroup:internet (#3212)
- 29aa08df0ed593368a04b6a071b9f9e686f2e558 policy: update test expectations for merged filter rules
- 8baa14ef4abd765cba571a32c7439a5bd1fa2f29 policy: use CGNAT/ULA ranges for wildcard resolution Change Asterix.Resolve() to use Tailscale's CGNAT range (100.64.0.0/10) and ULA range (fd7a:115c:a1e0::/48) instead of all IPs (0.0.0.0/0 and ::/0). This better matches Tailscale's security model where wildcard (*) means "any node in the tailnet" rather than literally "any IP address on the internet". Updates #3036
- 08fe2e4d6c4577017532ce569b9584f65a308e28 policy: use CIDR format for autogroup:self destinations
- ebdbe0363924fb222bcbd61a2c46c0b42e1b7db6 policy: validate autogroup:self sources in ACL rules Tailscale validates that autogroup:self destinations in ACL rules can only be used when ALL sources are users, groups, autogroup:member, or wildcard (). Previously, Headscale only performed this validation for SSH rules. Add validateACLSrcDstCombination() to enforce that tags, autogroup:tagged, hosts, and raw IPs cannot be used as sources with autogroup:self destinations. Invalid policies like
tag:client → autogroup:self:*are now rejected at validation time, matching Tailscale behavior. Wildcard () is allowed because autogroup:self evaluation narrows it per-node to only the node's own IPs. - ded51a4d30e92d4e23fcd09636cbe5666606a2d9 policyutil: fix reduceCapGrantRule and add route reduction
- fffc58b5d04c0da9f38d07f2c53dd00e12f9907b poll: fix poll test linter violations
- 4aca9d6568aec48173a1f69a21cfca06f6c9790b poll: stop stale map sessions through an explicit teardown hook
- dc0e52a960e72e86cc96985e4a5dab248f7edce4 proto: add AuthRegister and AuthApprove RPCs
- 56146de3774263e0c83503614d8c5376670892e9 proto: add CheckPolicy RPC
- a8f7fedced74b0c7259948fecfd481174ec610f0 proto: add disable_expiry field to ExpireNodeRequest
- 786ce2dce870ce96b882bac4f8d0950a8577062c routes: add health dimension to HA primary route election
- 863fa2f8157bf974b153a2d8f19eee12ad58bb3d servertest, integration: cover HA both-offline recovery
- 0378e2d2c60b8f49eed30ddfa19d9a1ab0b46038 servertest: add HA health probing tests
- 164d659dd28eb44beaf7117e5355d28166f92265 servertest: add TestViaGrantHACompat for via+HA compat tests
- 436d3db28e8bcf446639beb1bc2394b4bc55f84f servertest: add dynamic HA failover tests
- 0431039f2a427403129cf2eb3fe4e01f39fd3146 servertest: add regression tests for via grant filter rules
- 7d104b8c8dde06435460cd812f45efa959b436fe servertest: add via grant map compat tests
- b5b786f5197a6bae3904bdefab2b978367fc4672 servertest: cover broader-dst via grant in filter test
- 76ee29352b46659fc3ef16d8464bb50eb23ba497 servertest: cover via-grant exit-node visibility end-to-end
- 53b8a81d480bfc0be76eadb312552e683c935d83 servertest: support tagged pre-auth keys in test clients
- ff29af63f665869ffe4e9a35b3e8f8482b02c476 servertest: use memnet networking and add WithNodeExpiry option
- 7bab8da36625f6be7d2d245d1f54febe31d367f8 state, policy, noise: implement SSH check period auto-approval
- e2f2f9211f1bcf1ac59855edc26eeba2050302a8 state, servertest: property-test HA election + invariant catalogue
- 3ca4ff8f3ff2df43abacfe9b12f5ce54a2faef6f state,servertest: add grant control plane tests and fix via route ReduceRoutes filtering
- 90e65ccd6364f68b602ec47b2ab145366b0fab36 state: add HA health prober
- b1196baf6d30094808714e9cd8d8a4f281f7a5b2 state: add regression test for Node slice persistence
- 6337a3dbc4b958c9ad1965b2a44ef57881a566f6 state: apply default node key expiry on registration
- a2c3ac095e6569c66d32fd7e1ebd5d4b8ea134b5 state: auto-bump GivenName on collision and add SetGivenName
- 01e548e030fbe2326608657f43b316bc75a4ce0a state: avoid nil deref in registration handlers when old user is missing
- de6be71a8630f00d84e78af056700ef83519132c state: batch HA probe results so dual-disconnect cannot flap primary
- 9f7c8e9a07bd6682e78ea7ddec57215375c6d802 state: clear Unhealthy when node leaves HA candidate set
- da927eb01881a088ad2bd4a574bbb0dab899e646 state: compute primary routes inside NodeStore snapshot
- fb8eecae2530aebdfcc25003cf8fdc819d479afa state: defer HA failover when probe target reconnected mid-cycle
- 66ac785c22631289e4d3bf7111c59e28418f2779 state: delete routes package, port primary route tests
- 842f36225e6a2ff32d90e42c14fa8f36c6351528 state: drain pending pings on Close
- ccddeceeec951a4eb08a08794c2718a37b40f181 state: fix GORM not persisting user_id=NULL on tagged node conversion
- 6ae182696f33b77bd6cff96efd37f6e63976cea3 state: fix policy change race in UpdateNodeFromMapRequest
- 82bb4331f5b639905cf71f82af21acc51776e1e5 state: fix routesChanged mutating input Hostinfo
- c7630b505b2b757dd14284273ee5e84bb580c987 state: leave prefix unmapped when all primary candidates unhealthy
- de60982d83f93b9ae8d44bdf9be80b19c3c3adf3 state: note tagged-path coverage and self-healing behaviour for #3199
- 94ec607bcabd1b7b92abb30051f2e57df4d809fc state: per-goroutine deadline in HA probe cycle
- 0e10ca4e9a27d0eb379207c2939e76dc789484ce state: preserve nil expiry on user owned registration when no default is configured
- 3d5c0af4e703482aff8f979ff5bde330a7bf97f5 state: preserve previous primary when all HA advertisers unhealthy
- 0d4f2293ffb8c9f1bcc394b68881e4a015e64783 state: replace zcache with bounded LRU for auth cache
- 437754aeeaef6bd91736c8be9c35dbf43a50a8f2 state: switch consumers to NodeStore primary routes
- 978f1e3947c3543fca2ec5aa73bc50bf110fef4b state: tie-break ResolveNode by GivenName then lowest NodeID
- 380f5313421a68f34c1dc90a4c4ff7b801952cd3 state: trigger PolicyChange on every Connect and Disconnect
- 0e5569c3fc99d8cdca6e7b2d3bff1008c3b8e64c templates: add detailsBox collapsible component
- c15caff48c2d7437e8e75f1ad4ca5a88c7831238 templates: add error box component and error page template
- f3eb9a7bba3631845d012f3667cd2b976ae98823 templates: escape query value in ping page
- 4a7e1475c09418ac75c6a7f6fcec6757e7c265a7 templates: generalise auth templates for web and OIDC
- 814226f327a05c060d1b9c04e82b26ced8b14e1a templates: improve accessibility, dark mode, and typography
- c9dbea5c1878aca5bd7e6008d9a15701c461e83f templates: improve ping page spacing and design system usage
- 3918020551eb1a7a44c57dd702df9dc35d8f6baa templates: use CSS variables in all shared components
- de5b1eab68ca2f303e09fda25f17d4dfefa8058c templates: use table layout for registration confirm details
- f34dec2754394609995ad81dd26244091eb094d3 testcapture: add typed capture format package
- f49c42e71688b3e8315337aaff2405f8423431ca testdata: add SaaS captures for compat tests
- 30dce30a9d83c909aaea5fb8c03620b7f3c93371 testdata: convert .json to .hujson with header comments
- 9482cdf5906019b66b67a7e2fa113ec74b0b73d6 testdata: drop unused uppercase SSH-*.hujson fixtures
- 835db974b5cd69028b6dca0c441acde25005469b testdata: strip unused fields from all test data files (23MB -> 4MB)
- d243adaedd85bc06fd6bb5d21a2e80f67e1f5c9e types,mapper,integration: enable Taildrive and add cap/drive grant lifecycle test
- 2a2d5c869aeee6792209abc24874e57cd0e23e00 types/change: fix slice aliasing in Change.Merge
- cef5338cfe80a636f07a3c16d7c137669a3e1403 types/change: panic on Merge with conflicting TargetNode values
- 64d13f77e8d2c0613ed63daf239f916ce48eff18 types/config, types/node: model default-auto-update from auto_update.enabled
- 5ebc53c29e05e20037eedc1e62c5da326d5aa12a types/node, mapper, policy/v2: assemble self CapMap inside TailNode
- 5d502bfb8836b8fd09fb284f6ff494527e11098a types/node, mapper: strip own IPv4 from emission when node has disable-ipv4 cap
- 8ea4cd3faab314f536b973597fe361f12965b8d3 types/node, policy/v2: drop taildrive caps from baseline emission
- cf3d30b6f61ad685054bce900e46c23062bbad98 types: add MarshalZerologObject to domain types
- 1fe682b141b1e50135fc6295c8669c58177644e3 types: add Unhealthy and SessionEpoch fields to Node
- 4d0b273b9013796ec0d7cb99a7243b340a640313 types: add node.expiry config, deprecate oidc.expiry
- 610c1daa4dfd1ccff642e066d0740231a25e3079 types: avoid NodeView clone in CanAccess
- b01e67e8e5bb3e701b2580c687fac7fdaa56f09c types: consider subnet routes as source identity in ACL matching
- 3529fe0da16e9f79b34ce9d13cfcaf4035b9c751 types: fix OIDC identifier path traversal dropping subject
- 4064f13bda2929aeff6f9b8ac5d3f1b8dd5fb946 types: fix nil panics in Owner() and TailscaleUserID() for orphaned nodes
- 15c1cfd77859f1435e3a582903a139eec7eb30ef types: include ExitRoutes in HasNetworkChanges
- 942313a10ae84ef6ade7fb96ef53ce74bb5b1ed8 types: move DebugRoutes from routes to types
- a3c4ad2ca3159318bf2f9a1fbd829c7f97a8cf0b types: omit secret fields from JSON marshalling
- 7a20db9f4963cf366d0dce1872be3d181fb4d651 types: persist Node JSON slices via named IsZero types
- 58020696fee08981f6da197998661e7951366f87 zlog: add utility package for safe and consistent logging