Tailcat

2026-09-04 · page · source ↗ · source summary

Tailcat is Tailscale's data plane stripped of its control plane: a netcat-like pipe that gives you point-to-point WireGuard-encrypted tunnels with NAT traversal, packaged as a Go library plus a CLI. One side prints a short tc… address; the other dials it. No account, no coordination server, no root, no changes to routing tables or DNS. It matters because it isolates the two halves of Tailscale that are usually fused — encryption + hole-punching (the hard, reusable part) from identity, ACLs, and coordination (the part you might not want) — and proves the data plane alone is a useful product.


Architecture

One Go module, github.com/tailscale/tailcat, built on tailscale.com as a normal library dependency (not a fork). Three layers:

The tailcat address is the core abstraction. ConnInfo (tailcat.go:154) holds four things, CBOR-encoded then base64url-ed behind a tc prefix:

  1. ServerPublic — the server's WireGuard node key (Curve25519).
  2. ServerDiscoPublic — a separate path-discovery key (see below).
  3. PresharedKey — an independent 256-bit WireGuard pre-shared key.
  4. Region (embedded DERP node metadata, long but self-contained) or RegionID (an integer into the default DERP map, short but requires a fetch).

wire.go defines the wire types with single-character CBOR field names (p, k, q, r, i, N, n, h, t, 4, 6, s, d, x) so a typical address is ~140 bytes; a test (TestWireFieldNames) locks the field names as a wire format, keeping it independent of upstream tailcfg churn.

Key techniques

Design decisions

Optimized for ephemeral, zero-trust use. The default is a fresh key generated in memory, an address nobody has ever seen, dead the moment the process exits. Saved keys (genkey) are the opt-in; the magic name default silently becomes the persistent identity once it exists, and the startup line tells you which happened.

The address is the whole security model. No usernames, no ACLs beyond an optional --allow allowlist of node keys. That's both the elegant part and the limitation: it's a point-to-point primitive, not a mesh — one server, N clients, no client-to-client routing, no revocation beyond never sharing the address again.

Short vs. self-contained is an explicit user choice. --full-address / resolve embed DERP metadata so clients skip a map fetch; the default integer region ID keeps addresses short but costs a round-trip. --fixed-region exists so a DNS-published address stays valid across restarts.

No stability promises, loudly. Go API, CLI flags/output, and wire format may all change; the public DERP relays have no SLA. The readme subcommand embeds the README into the binary (via readme.go) specifically so people — and AI agents — with only a binary can learn to use it without web access.

Comparison notes

The obvious frame is Tailscale itself: tailcat is the data plane (magicsock + WireGuard + netstack + DERP) with the control plane deleted, and it uses Tailscale as a library rather than a fork.

Against Headscale, the contrast is sharp: Headscale replaces the coordination server with a self-hosted one so official clients keep working; tailcat removes the coordination layer entirely and moves identity into the bearer-capability address. Headscale keeps the tailnet's multi-node, ACL'd, named-identity model; tailcat gives up all of that for a single encrypted pipe with no server at all.

Tunnet is the closest modern sibling — it bundles mesh, serve, tunnel, send, and SSH under one identity system and policy engine, with an optional control plane. Tailcat is the opposite pole on the same axis: no identity system, no policy engine, no membership database, just an address that is the credential. Where Tunnet's Direct mode still needs CRDT membership and a pre-shared-key challenge-response to join a network, tailcat's join protocol is one ping and one ack.

RustDesk shares the NAT-traversal-plus-relay-fallback shape (race direct paths, fall back to an encrypted relay), but applies it to remote desktop; tailcat is the same trick generalized into a netcat — stdin/stdout, ports, UDP, files, SSH.

Agentcookie and Domenic Denicola's Agentic Coding Setup are consumers of the full Tailscale stack (tailnet, accounts, coordination). Tailcat is the underlying primitive beneath such tools — the "encrypted pipe to a machine" without the account ceremony — which is why it deliberately notes its appeal to AI agents in the readme embedding.

#tool #project #networking #security #vpn #p2p


Sources: raw/tailcat, Tailcat Last updated: 2026-09-04