Skip to content

fix(tailscale): debounce misleading first-boot auth-failed status#494

Open
mvanhorn wants to merge 1 commit into
almeidapaulopt:mainfrom
mvanhorn:fix/478-misleading-authfailed-firstboot
Open

fix(tailscale): debounce misleading first-boot auth-failed status#494
mvanhorn wants to merge 1 commit into
almeidapaulopt:mainfrom
mvanhorn:fix/478-misleading-authfailed-firstboot

Conversation

@mvanhorn

Copy link
Copy Markdown
Contributor

Summary

On every first boot, tsdproxy logs a scary, terminal-sounding status: tailscale is in NeedsLogin state without an auth URL. This indicates stale tsnet state ... Restart tsdproxy to auto-recover (#478). The node is actually authenticating fine and reaches Running moments later, so the message is misleading.

The cause is in internal/proxyproviders/tailscale/status_watcher.go. During the brief first-boot window, lc.Status() reports backend state NeedsLogin with an empty authURL because the auth key has not been processed yet. classifyState maps that combination straight to a terminal model.ProxyStatusAuthFailed event, which is what surfaces as the alarming message.

This change keeps classifyState pure and adds a small debounce in StatusWatcher.Watch: it counts consecutive NeedsLogin-with-empty-authURL polls and emits the benign ProxyStatusStarting status until the streak persists past a grace threshold (firstBootAuthGraceCycles, default 3 poll cycles). Only once the threshold is crossed does it forward ProxyStatusAuthFailed. Any other observed state (or a non-empty authURL) resets the counter. The default is an internal constant, so NewStatusWatcher and the node_lifecycle.go call sites need no signature change.

Why this matters

The maintainer flagged this twice in the thread: the node was confirmed to be authenticating correctly, and the conclusion was that the status watcher polls during the normal first-boot NeedsLogin window and emits a misleading message that should be improved. Genuinely-bad auth keys are not masked, because a NeedsLogin-with-no-authURL state that persists past the grace window still surfaces AuthFailed with the existing error message.

Testing

  • go test ./internal/proxyproviders/tailscale/... (and -race) pass.
  • Added watcher tests covering: transient first-boot window that clears to Running emits no AuthFailed; a persisted NeedsLogin-no-authURL state still surfaces AuthFailed; an alternating sequence resets the streak and never trips the threshold prematurely.
  • The existing pure classifyState table tests are unchanged and still pass.

Refs #478

During first boot Tailscale briefly reports NeedsLogin with no auth URL
while the auth key is still being processed. classifyState maps that
combination directly to a terminal AuthFailed event, surfacing a
misleading 'stale state / auth failed' message even though the node
reaches Running moments later (almeidapaulopt#478).

Add a small grace window in StatusWatcher.Watch: count consecutive
NeedsLogin-with-empty-authURL polls and emit a benign Starting status
until the streak persists past firstBootAuthGraceCycles, only then
forwarding AuthFailed. Any other observed state resets the streak, so
genuinely-bad auth keys are still surfaced. classifyState stays pure.

Refs almeidapaulopt#478
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant