Skip to content

fix(host): restart Ace Helper by itself when Tailscale starts or changes tailnet - #665

Merged
iamnbutler merged 3 commits into
mainfrom
fix-tailnet-pickup
Oct 22, 2026
Merged

iamnbutler merged 3 commits into
mainfrom
fix-tailnet-pickup

Conversation

@iamnbutler

@iamnbutler iamnbutler commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Ace Helper reads the owner's login, the channels' tailnet and its tailnet listeners once, at startup. If Tailscale started later, or switched to another tailnet, the Helper stayed off it until someone pressed Restart.

  • The host now checks tailscale status every 10 seconds. When Tailscale runs on a tailnet other than the one the host started on for two checks in a row, Ace Helper shuts down cleanly and exits with status 75.
    • launchd's KeepAlive { SuccessfulExit = false } starts the Helper again, so it serves on the new tailnet. This is the same restart as Settings' Restart button: workers close without aborting and resume, and open terminals close.
    • This is a self-restart, not an in-process switch. The login, catalog.tailnet and the tailnet listeners are module-level values read at startup, and changing them live would mean reconnecting every client as a new participant.
  • Tailscale stopping never triggers a restart, because the host's channels stay on the tailnet it started on.
  • ace serve has no supervisor, so it prints Tailscale is on <tailnet> now. Restart ace serve to use it. instead.
  • For up to 20 seconds before the restart, Settings → Team's Tailscale row still says Restart needed (from fix(host): say when Ace Helper needs a restart to use Tailscale #555). Creating a channel in that window says Ace Helper is restarting to use Tailscale. Try again in a moment.

Map: ^6, ^101. Part of #539: the Helper restarts itself rather than picking up the tailnet in-process, so whether that closes the issue is left to the issue.

Smoke: this lane's host served with config.helper = true on its own ACE_HOME, with --loopback because a fake address can't be bound. A loop stood in for launchd's relaunch-on-failure, and a stub tailscale on PATH returned status --json from a file. The stub was needed because switching this Mac's real Tailscale would cut off other work on it.

  • Started with Tailscale Stopped, ace new failed with "Start Tailscale first". After the state switched to Running on fake-w2.ts.net, the Helper exited 75 within 20 s and the loop restarted it. ace new then created a channel, and ace ls listed it.
  • With the host on fake-w2.ts.net, switching Tailscale to Stopped did not restart it over 25 s.
  • Plain ace serve printed the restart line after a switch to another tailnet and kept running.
  • Not tried: the real Ace Helper under launchd (not restarted on this machine) or the real Tailscale switching tailnets.

Built in Ace

No: made from Claude Code (a fix-train worker), using the CLI for the smoke test. Dogfooding gap tracked in #5.

…ges tailnet

Why: apps/host/src/gateway.ts#function serve | Ace Helper's tailnet self-restart exits non-zero because launchd's KeepAlive relaunches only a failed exit
Why: apps/host/src/tailnet.ts#function watch | Tailscale stopping must not restart the host; only a running tailnet other than the one the host started on does
@iamnbutler
iamnbutler merged commit 9e3e1f1 into main Oct 22, 2026
1 check passed
@iamnbutler
iamnbutler deleted the fix-tailnet-pickup branch October 22, 2026 21:43
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