Upstream, unmodified
The official Tailscale clients on every device. Headscale 0.29.4 for control, and derper built from the locked Tailscale source. No forks and no patches.
Open-source overlay network
Tailgate runs the official Tailscale clients against your own Headscale server and your own relays. It adds the rest: checked config, pinned builds, private HTTPS, and a console that decides which device reaches which service.

Watch it work
The stock app signs in to your server. Three devices, all yours. From the public internet: nothing.
# the stock app signs in to your server
tailscale login --login-server=https://tailgate.example.com
Success.
# three devices, all yours
tailscale status
100.120.0.7 laptop macOS
100.120.0.10 homelab linux
100.120.0.11 nas linux
# a private app with a real certificate
curl -sI https://photos.apps.example.com
HTTP/2 200
# from the public internet: nothing
curl -m 5 https://photos.apps.example.com
curl: (28) Connection timed out after 5001 ms
Why upstream
The hard parts already exist. Tailgate does not rewrite them. It runs them as they ship, pins the exact versions and checks the checksums. You only have to read the code Tailgate adds, and there is not much of it.

The official Tailscale clients on every device. Headscale 0.29.4 for control, and derper built from the locked Tailscale source. No forks and no patches.
The control plane and the relays run on hosts you choose. There is no hosted coordinator, and no relay map is fetched from Tailscale.
Validated config, pinned and checksummed artifacts, private HTTPS, and a small console for access. No networking code. No crypto.
Deploy
Write down what you want in one file. Tailgate checks it before it touches anything. Then it puts the pieces on your host.

Name your domain, relays, devices and services in one file, and Tailgate tells you whether it holds together.
$ python3 tools/tailgate.py validate tailgate.jsonHeadscale, the relays, the certificates and the console go onto the host you chose.
Installer coming soon Until then, the bundle is rendered and installed by hand.
You bring a host, a domain, and DNS that can answer a certificate challenge.
# describe it and check it
python3 tools/tailgate.py validate examples/pilot.json
Input valid
# render the bundle; nothing is installed
python3 tools/tailgate.py render examples/pilot.json \
--output build/pilot
Bundle rendered
ls build/pilot
README.txt headscale.json records.json
controller.nginx.conf manifest.json tailgate.service
derp.json policy.json tailgate-exit.service
Private apps
Each service listens on its overlay address, port 443, with a publicly trusted certificate from a DNS challenge. Browsers trust it and the public internet cannot reach it. There is no public listener to find.

# check the app list, then render it
python3 tools/applications.py validate \
examples/application.json \
examples/application-platform.json
Valid application and platform
python3 tools/applications.py render … \
--output build/sample-app
95b4b222363fdc099c34306202a1c62c…
# same input, same plan hash
ls build/sample-app
README.txt plan.json private-service.json
compose.yaml private-nginx.conf
Connection path
Devices talk to each other directly, encrypted with WireGuard under the Tailscale client. When there is no direct road, the packets go through a relay you own, still encrypted. The control plane makes introductions. It does not carry traffic.
# first through your own relay
tailscale ping homelab
pong from homelab via DERP(tg-fra) in 41ms
pong from homelab via DERP(tg-fra) in 39ms
# then the two devices find each other
pong from homelab via 203.0.113.24:41641 in 6ms

Access
A device that joins gets an address and nothing else. You grant it each service and port in the console. Signing in with Google proves who you are. It does not open anything.
{"grants":[]}.Coexistence
A server can stay on hosted Tailscale and run a second Tailgate client in its own network namespace, and the two stay apart. An optional gateway bridges only the users you list. An empty list means nobody.
Open source
Tailgate is open source. The part it adds is small, and it is meant to be read.
Public repository coming soon

Limits
You bring the servers and the cloud accounts. Tailgate configures what is already there.
It runs Tailscale's protocol through upstream code and does not replace it.
The official clients run on many systems, and Tailgate does not certify each one.
The rendered bundle holds references to secrets, never the values, so the values stay with you.