Skip to content

hscontrol: rewrite hostname logic to match Tailscale SaaS#3202

Merged
kradalby merged 3 commits into
juanfont:mainfrom
kradalby:kradalby/hostname-cleanroom
Apr 18, 2026
Merged

hscontrol: rewrite hostname logic to match Tailscale SaaS#3202
kradalby merged 3 commits into
juanfont:mainfrom
kradalby:kradalby/hostname-cleanroom

Conversation

@kradalby
Copy link
Copy Markdown
Collaborator

Two disagreeing ingest paths, three duplicate regexes, and a
random-suffix collision fallback that drifted from Tailscale SaaS.
Apostrophes, spaces, dots, @, and non-ASCII were alternately
rejected or stored as invalid-<rand>. Same class of bug has shipped
six times since 2022.

Replace custom sanitiser with tailscale.com/util/dnsname.SanitizeHostname
and ValidLabel. Collision bumping moves inside NodeStore.PutNode /
UpdateNode — serialised through the single writer goroutine, so
concurrent registrations cannot duplicate. Admin rename uses the new
SetGivenName, which rejects collisions with ErrGivenNameTaken
rather than silently rewriting.

Empty-input fallback is now the literal node. MagicDNS names change
on upgrade for nodes previously stored with a random suffix; raw
Hostname is unchanged.

If dnsname drifts from SaaS later, the client has drifted too — fix
is a tailscale version bump, not new code here.

Fixes #3188
Fixes #2926
Fixes #2343
Fixes #2762
Fixes #2449
Fixes #2177
Fixes #2121
Fixes #363
Updates #2803
Closes #2976
Closes #3189
Closes #3195

Generated with the help of an AI assistant

Comment thread hscontrol/state/node_store.go Outdated
Comment thread hscontrol/state/node_store.go Outdated
Comment thread hscontrol/state/node_store.go Outdated
@kradalby kradalby force-pushed the kradalby/hostname-cleanroom branch 2 times, most recently from d3fabed to 85c45be Compare April 17, 2026 13:08
@kradalby kradalby marked this pull request as ready for review April 17, 2026 13:10
@kradalby kradalby force-pushed the kradalby/hostname-cleanroom branch 2 times, most recently from 759bda6 to 86edc5d Compare April 17, 2026 14:47
NodeStore's writer goroutine now resolves GivenName collisions inside
applyBatch: on PutNode/UpdateNode the landing label gets -N appended
until unique, matching Tailscale SaaS. Empty labels fall back to the
literal "node".

SetGivenName exposes the admin-rename path: validates via
dnsname.ValidLabel and rejects on collision with ErrGivenNameTaken,
so renames do not silently rewrite behind the caller.

Updates juanfont#3188
Updates juanfont#2926
Updates juanfont#2343
Updates juanfont#2762
Ingest (registration and MapRequest updates) now calls
dnsname.SanitizeHostname directly and lets NodeStore auto-bump on
collision. Admin rename uses dnsname.ValidLabel + SetGivenName so
conflicts are surfaced to the caller instead of silently mutated.

Three duplicate invalidDNSRegex definitions, the old NormaliseHostname
and ValidateHostname helpers, EnsureHostname, InvalidString,
ApplyHostnameFromHostInfo, GivenNameHasBeenChanged, generateGivenName
and EnsureUniqueGivenName are removed along with their tests.
ValidateHostname's username half is retained as ValidateUsername for
users.go.

The SaaS-matching collision rule replaces the random "invalid-xxxxxx"
fallback and the 8-character hash suffix; the empty-input fallback is
the literal "node". TestUpdateHostnameFromClient now exercises the
rewrite end-to-end with awkward macOS/Windows names.

Fixes juanfont#3188
Fixes juanfont#2926
Fixes juanfont#2343
Fixes juanfont#2762
Fixes juanfont#2449
Updates juanfont#2177
Updates juanfont#2121
Updates juanfont#363
Summarise the ingest rewrite, the SaaS-matching collision rule, and the
BREAKING change from random-suffix to numeric-suffix collision labels
and from "invalid-<rand>" to the literal "node" fallback.

Updates juanfont#3188
@kradalby kradalby force-pushed the kradalby/hostname-cleanroom branch from 86edc5d to 62330ae Compare April 17, 2026 15:37
@kradalby kradalby changed the title hscontrol: rewrite hostname ingest to match Tailscale SaaS hscontrol: rewrite hostname logic to match Tailscale SaaS Apr 17, 2026
@kradalby kradalby merged commit 4e1d83e into juanfont:main Apr 18, 2026
158 checks passed
@ykhrustalev
Copy link
Copy Markdown

are you guys going to cut a release soon on that?

@kradalby
Copy link
Copy Markdown
Collaborator Author

kradalby commented Apr 19, 2026

Not for this particular, it will go into 0.29.

I hope that we can start doing betas in less than a month.

kradalby added a commit to kradalby/headscale that referenced this pull request May 20, 2026
Move HA subnet router health probing into the feature description block
above BREAKING so the layout matches the rest of the file. Drop the
ad-hoc **User deletion**: / **Node Expiry**: topic prefixes — the ####
subgroup heading already labels each bullet's topic. Fill missing PR
refs on the hostname rewrite (juanfont#3202), sshTests + SSH rule validation
(juanfont#3263), HA probe (juanfont#3194), randomize_client_port removal (juanfont#3251), and
trusted_proxies (juanfont#3268).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
kradalby added a commit to kradalby/headscale that referenced this pull request May 20, 2026
Move HA subnet router health probing above BREAKING so the layout
matches every other release. Drop **User deletion**: / **Node Expiry**:
bold prefixes redundant with the #### subgrouping. Fill missing PR
refs: juanfont#3202 (hostname rewrite), juanfont#3263 (sshTests + SSH rule validation),
juanfont#3194 (HA probe), juanfont#3251 (randomize_client_port removal), juanfont#3268
(trusted_proxies).
kradalby added a commit that referenced this pull request May 20, 2026
Move HA subnet router health probing above BREAKING so the layout
matches every other release. Drop **User deletion**: / **Node Expiry**:
bold prefixes redundant with the #### subgrouping. Fill missing PR
refs: #3202 (hostname rewrite), #3263 (sshTests + SSH rule validation),
#3194 (HA probe), #3251 (randomize_client_port removal), #3268
(trusted_proxies).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment