Skip to content

Fix Headscale installer enrollment flow on macOS#4

Open
godsonten wants to merge 3 commits into
mainfrom
issues/510-vpn-runbook-fix
Open

Fix Headscale installer enrollment flow on macOS#4
godsonten wants to merge 3 commits into
mainfrom
issues/510-vpn-runbook-fix

Conversation

@godsonten
Copy link
Copy Markdown
Contributor

@godsonten godsonten commented May 25, 2026

Summary by CodeRabbit

  • Documentation

    • Updated VPN setup instructions across platforms with clearer server/user/key parameter usage and macOS note about one-time System Settings approval during enrollment
  • Enhancements

    • Installers now accept a server URL flag in addition to the environment variable
    • Improved macOS enrollment flow, readiness checks, retry/verification messaging, and clearer install/verification output

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 25, 2026

📝 Walkthrough

Walkthrough

Adds explicit server flag handling to install scripts and README, maps CLI --server/-Server into HEADSCALE_URL when present, and refactors macOS enrollment with new helper functions that locate the macOS Tailscale CLI, run it with backend access, wait for readiness, and verify the connection.

Changes

VPN Setup Parameter Support and macOS Enrollment

Layer / File(s) Summary
Parameter interface and documentation updates
README.md, vpn/install.ps1, vpn/install.sh
README install examples updated to use positional YOUR_NAME and document --server as an alternative; PowerShell param(...) and bash argument parsing updated to accept -Server / --server and prefer that value over HEADSCALE_URL.
macOS setup instructions and user messaging
vpn/install.sh
macos_setup_instructions() rewritten to present an approval → wait → verify flow with conditional text depending on whether an auth key is used.
macOS Tailscale helper functions and installation detection
vpn/install.sh
New functions added to find the macOS Tailscale CLI, invoke it with backend CLI enabled, wait for readiness, and verify backend state via status --json. macOS detection now uses discovered CLI; non-macOS uses tailscale version and removes stale wrappers.
macOS enrollment flow and backend verification
vpn/install.sh
macOS branch refactored to require CLI discovery, open the app, wait for CLI readiness, run tailscale up (auth-key or SSO path with distinct failure messages), poll until backend is running, then print verification commands and IP.

Sequence Diagram(s)

sequenceDiagram
  participant InstallerScript
  participant TailscaleApp
  participant MacOSCLI
  participant HeadscaleServer
  InstallerScript->>TailscaleApp: open app (open -a Tailscale)
  TailscaleApp->>MacOSCLI: expose embedded CLI binary
  InstallerScript->>MacOSCLI: poll readiness (wait_for_macos_cli_ready)
  MacOSCLI->>InstallerScript: ready status
  InstallerScript->>MacOSCLI: run `tailscale up` (macos_tailscale up --authkey / browser)
  MacOSCLI->>HeadscaleServer: enroll & fetch config
  HeadscaleServer->>MacOSCLI: return auth/connection state
  MacOSCLI->>InstallerScript: status --json (verify_macos_connection)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A little flag hops into place,
Scripts and docs now speak the same base.
macOS wakes the CLI with care,
Waits and checks the network there.
Hooray — the installer ties the trace!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: fixing the Headscale installer enrollment flow on macOS, which is the primary focus of the substantial refactoring in vpn/install.sh.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issues/510-vpn-runbook-fix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@vpn/install.sh`:
- Around line 33-36: The --server|-s) case currently assigns SERVER_URL="$2"
without validating availability; update the case for "--server|-s)" to verify
that $2 exists and does not begin with '-' (indicating another flag) before
assigning SERVER_URL and shifting 2, and if the check fails print a clear
error/usage message and exit non‑zero so callers cannot pass a flag as the
server URL.
- Around line 247-249: The one-shot call to verify_macos_connection may race
with macOS state transitions and should be retried briefly before calling error;
modify the block around verify_macos_connection to implement a short retry loop
(e.g., retry every 0.5–1s for up to 5–10s) so that verify_macos_connection is
invoked repeatedly until success or timeout, and only call error "Tailscale app
is installed..." if the retries all fail; keep the existing error message and
ensure the retry loop is cancellable/limited to avoid hanging the installer.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 57f003e6-d59d-4909-bee8-845f7172c5a3

📥 Commits

Reviewing files that changed from the base of the PR and between dc3b877 and 0de67bc.

📒 Files selected for processing (3)
  • README.md
  • vpn/install.ps1
  • vpn/install.sh

Comment thread vpn/install.sh
Comment thread vpn/install.sh Outdated
Comment thread vpn/install.sh
echo " - Click Tailscale menu bar icon > Settings"
echo " - Go to 'CLI' tab"
echo " - Click 'Enable CLI'"
echo ""
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reveiw why it's taking out the CLI installation instructions

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old instructions told users to manually open Tailscale → Settings → CLI → "Enable CLI" (and on some setups, create a /usr/local/bin/tailscale symlink by hand). That step is gone
because the script now drives the macOS app's embedded CLI programmatically — it no longer needs the user to click anything in the menu bar.

Concretely, three new functions in vpn/install.sh replace the manual step:

  • find_macos_cli() locates the binary inside the app bundle (/Applications/Tailscale.app/Contents/MacOS/Tailscale)
  • macos_tailscale() wraps every CLI call with TAILSCALE_BE_CLI=1 — that env var is Tailscale's documented way to invoke the embedded CLI without the GUI "Enable CLI" toggle
  • wait_for_macos_cli_ready() polls tailscale status until the backend is responsive

So the removed instructions weren't dropped — they were automated. Users no longer have to enable the CLI or set up symlinks manually.

Comment thread vpn/install.sh
}

wait_for_macos_cli_ready() {
local i
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

manually test this this if you can to be sure it works.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both candidate paths the function checks exist and are executable:

$ ls -l /Applications/Tailscale.app/Contents/MacOS/Tailscale
-rwxr-xr-x ... Tailscale

$ ls -l /Applications/Tailscale.app/Contents/MacOS/tailscale
-rwxr-xr-x ... tailscale

(They're the same inode — one is a case-different reference to the other.)

The function returns 1 if neither is present. The hard-error path that surfaces a clear message is verify_macos_installation() (around line 229), which calls find_macos_cli with if
! find_macos_cli; then error "..."; fi.

End-to-end: ran the installer through the macOS path; find_macos_cli correctly resolved TAILSCALE_CLI to the bundled binary, and subsequent macos_tailscale up calls worked without
any manual CLI-enable step. Happy to record a session if you want to see it.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
vpn/install.sh (1)

184-184: 💤 Low value

Redundant stderr redirection.

&>/dev/null already redirects both stdout and stderr, making the trailing 2>&1 redundant.

Suggested fix
-        if tailscale version &>/dev/null 2>&1; then
+        if tailscale version &>/dev/null; then
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@vpn/install.sh` at line 184, The conditional checking whether the tailscale
binary exists uses redundant redirection in the command "if tailscale version
&>/dev/null 2>&1; then"; update the if-statement so stderr/stdout are redirected
only once (e.g., keep "&>/dev/null" or use ">/dev/null 2>&1") for the "tailscale
version" invocation inside vpn/install.sh to remove the redundant "2>&1" while
preserving the same behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@vpn/install.sh`:
- Line 184: The conditional checking whether the tailscale binary exists uses
redundant redirection in the command "if tailscale version &>/dev/null 2>&1;
then"; update the if-statement so stderr/stdout are redirected only once (e.g.,
keep "&>/dev/null" or use ">/dev/null 2>&1") for the "tailscale version"
invocation inside vpn/install.sh to remove the redundant "2>&1" while preserving
the same behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a79cab39-36d7-41a3-a436-451a3673d233

📥 Commits

Reviewing files that changed from the base of the PR and between aee483c and bc7ecc6.

📒 Files selected for processing (1)
  • vpn/install.sh

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