Go app to connect automatically into your private Tailscale network via an auth key generated in your admin console.
Built on tsnet — Tailscale's embedded networking library — connectit registers any Linux host as a node on your tailnet programmatically, with no Tailscale daemon or manual browser login required.
Standard Tailscale installation runs a background daemon (tailscaled) and requires interactive authentication. connectit bypasses both: it embeds the Tailscale network stack directly into the process via tsnet, authenticates using a pre-shared auth key, and keeps the node connected until it receives a shutdown signal.
This makes it suitable for:
- Automated infrastructure provisioning
- Ephemeral test environments (VMs, containers)
- Nodes that must join a tailnet without human interaction
| Requirement | Notes |
|---|---|
Go >= 1.21 |
Tested with 1.22 |
| Tailscale account | tailscale.com |
| Auth key | Generate one in the admin console |
| Linux host | Needs a TUN-capable kernel (standard on any modern distro) |
root or CAP_NET_ADMIN |
Required to create the TUN network interface |
connectit/
├── bin/ # Compiled binary (git-ignored)
│ └── connectit
├── endpointstate/ # Tailscale node identity — persists between runs (git-ignored)
├── infra/
│ └── Vagrantfile # Ubuntu 22.04 VM for isolated integration testing
├── main.go
├── go.mod
├── go.sum
└── README.md
Note:
bin/andendpointstate/should both be in your.gitignore. Theendpointstate/directory holds the node's private key and tailnet identity — committing it would expose your node's credentials.
# Build for your current platform
go build -o bin/connectit .
# Build explicitly for Linux amd64 (required before Vagrant testing)
GOOS=linux GOARCH=amd64 go build -o bin/connectit .tsnet is pure Go with no cgo dependencies, so the binary is fully statically linked — no shared libraries to worry about on the target host.
export TS_AUTH_KEY="tskey-auth-<your-key-here>" #OR source your .env
sudo ./bin/connectit -hostname <your-chosen-hostname>| Flag | Default | Description |
|---|---|---|
-hostname |
(required) | Name the node will appear as in the Tailscale admin console |
AUTH_KEY is read from the environment — never pass it as a CLI argument, as arguments are visible in ps output and shell history.
2024/01/15 12:00:00 Endpoint connected successfully
Ip address : [100.x.x.x]
2024/01/15 12:00:00 Running... press Ctrl+C to disconnect
The node will appear in your Tailscale admin console within a few seconds. On Ctrl+C, connectit logs out the node cleanly before exiting.
connectit stores node identity under endpointstate/ in the working directory it's launched from. This is intentional:
- First run: creates the directory, registers a new node, obtains a Tailscale IP.
- Subsequent runs: reuses the existing identity — the node reconnects as the same device with the same IP rather than registering a new one.
Always run the binary from the same working directory if you want consistent identity between restarts.
The infra/ directory contains a Vagrantfile that boots an isolated Ubuntu 22.04 VM for integration testing — verifying that the binary correctly registers a node on a real tailnet without touching your host machine's network.
# 1. Build a Linux binary on your host
GOOS=linux GOARCH=amd64 go build -o bin/connectit .
# 2. Boot the VM (mounts bin/ into the guest, marks binary executable)
cd infra
vagrant up
# 3. SSH into the VM
vagrant ssh
# 4. Set your auth key inside the guest shell (never touches a file)
export AUTH_KEY="tskey-auth-<your-key-here>"
# 5. Run the binary
sudo /vagrant_bin/connectit -hostname connectit-test-vm
# 6. Check your Tailscale admin console — the node should appear within seconds
# 7. Ctrl+C to disconnect, then clean up
exit
vagrant destroyThe VM has no internet exposure beyond what Tailscale requires — there are no open ports, no public IP forwarding, and the default Vagrant synced folder is explicitly disabled so only bin/ is mounted.
- Auth keys are credentials. Treat them like passwords — use environment variables, never hardcode them in files or pass them as CLI flags.
- Reusable vs. ephemeral keys: For repeated testing, generate a reusable auth key in the admin console. For production nodes, prefer one-time keys and rotate them.
endpointstate/is sensitive. It contains your node's private key. Add it to.gitignoreimmediately and never commit it.sudoscope:connectitneeds root only to create the TUN interface at startup. A production hardening step would be grantingCAP_NET_ADMINto the binary specifically rather than running the whole process as root.
connectit
│
├── tsnet.Server{} ──► userspace WireGuard stack (no tailscaled daemon needed)
│ │
│ └── srv.Up(ctx) ──► authenticates via AUTH_KEY
│ registers node with Tailscale coordination server
│ creates TUN interface, assigns 100.x.x.x IP
│
└── signal.Notify() ──► blocks until SIGINT / SIGTERM
│
└── lc.Logout(ctx) ──► cleanly deregisters node before exit