Joins an exe.dev VM to the IV Tailscale tailnet.
Usage
Run the helper. It does everything, including attaching and detaching the credential:
~/.agents/skills/join-tailnet/join-tailnet.sh <vm-name>
Then verify the VM appears in tailscale status. After it joins, use
ssh <vm> (Tailscale SSH) for everything else.
Why the script, and not the raw commands
api-tailscale (formerly tailscale-api) is attached in two lanes (settled
2026-09-15; the contract lives in iv-provision tailnet.md):
- Private dev VMs carry a standing grant through the exe.dev tag
tailnet, because the browser-drivencreate-vmskill can only fix things atnewtime (its token cannot attach or detach). A dev VM is private, so this is the same exposure as the VM itself. - Anything internet-facing, and every prod-lane deployment target, gets a
time-boxed per-VM grant —
ssh exe.dev integrations attach api-tailscale vm:<vm> --for 30m— never the tag. This is what the 2026-07-28 remediation was about:auto:allhad put key-minting on the public-facingrss-feedandtelnyx-vm(token exchange fromrss-feedreturned HTTP 200), and thetailnettag had quietly put it back on them between 2026-08-19 and 2026-09-15. Removed again 2026-09-15 (tag -d <vm> tailneton the four public VMs).
This script is the third path — a one-off join from the mini for a VM that has neither: it attaches for the duration of the join and detaches after.
This is the control-plane variant: it needs exe.dev access to attach and
detach, which a VM does not have. The VM-side variant — for a VM that already
carries api-tailscale through the tailnet tag — is iv-provision's
skills-local/join-tailnet, which also covers the preflight check and why the
vendored tailscale skill's join advice does not apply on this fleet. Same
name, different callers; keep them separate rather than syncing one over the
other.
The script now:
- Checks whether
api-tailscaleis already attached to the VM. If so, it leaves the attachment exactly as found and skips step 4. - Attaches
api-tailscaletovm:<name>. - SSHes in over
*.exe.xyz, ensurestailscaledis running, exchanges an OAuth token through the proxy, mints a one-use ephemeral preauthorized key against the public API, and runstailscale up. - Detaches
api-tailscaleon exit — via a trap that fires on error and interrupt, not only on success.
So the authority exists only while it is being used. If a detach ever fails the
script warns loudly; ssh exe.dev integrations list will show the stray
vm: attachment.
Do not hand-run the old inline curl commands. Without an attachment the
token exchange returns no access_token, and the natural next move — attaching
api-tailscale by hand and forgetting to detach — is exactly the standing
authority this change removed.
Prerequisites
- The VM exists and is reachable at
<vm>.exe.xyz. curlandjqare present on the VM (stock exeuntu and iv-image both have them; a slim/exeslim base may not — check before relying on this skill there).tailscaledis enabled and started by the script. Stock exeuntu ships it disabled, so don't assume it is already running.- You are running from
klundstedt-mini, which owns exe.dev control-plane mutations.
Notes
- Keys are minted
ephemeral:true, so a node that goes offline long enough is removed from the tailnet and must be re-joined by re-running this script. For a long-lived appliance that must survive outages, run withIV_TAILSCALE_EPHEMERAL=false(added 2026-09-15 for the AgentsView collector rebuild): the node then persists, and retiring the VM must delete the node in the admin console (iv-provisionretiring.md§5). --tag=ivat VM creation is no longer sufficient or required for tailnet joining. It still governs othertag:ivintegrations.- Prod-lane images self-join at boot (since exeslim 2026-08-23). The
exeslim(notexeslim-dev) image shipsiv-tailnet-join.service: at first boot it probes theapi-tailscaleproxy and, if attached, mints atag:prodkey and joins.api-tailscaleis attached to the exe.dev tagtailnet, sonew --tag=tailneton an exeslim VM means it is on the tailnet astag:prodseconds after boot, and this helper then exits early ("already on the tailnet"). To gettag:devon such a VM:sudo tailscale logouton it, then run this helper. Seen oniv-llm-relay2026-09-15. - Prod-lane nodes are minted non-ephemeral since exeslim 2026-09-15, so an
appliance never needs the API after its first boot; an older ephemeral prod
node that gets reaped after a long outage needs a 30-minute attach and
systemctl start iv-tailnet-joinover the.exe.xyzedge (or this script, which would make ittag:dev).
SSH discipline
- One SSH attempt at a time. Never launch parallel SSH to
*.exe.xyz. - If SSH fails, wait 30–60s before one more attempt.