# How Tailcat Achieves Control-Plane-Free WireGuard Tunneling

> Discover how Tailcat achieves control-plane-free WireGuard tunneling by embedding WireGuard configuration within an HTTPS handshake CBOR blob. Learn more about this innovative approach.

- Repository: [Tailscale/tailcat](https://github.com/tailscale/tailcat)
- Tags: internals
- Published: 2026-08-30

---

**Tailcat establishes direct WireGuard tunnels without any external control plane by embedding all necessary configuration—including public keys and DERP relay addresses—inside a compact CBOR blob exchanged during the initial HTTPS handshake.**

Tailcat is an experimental project from Tailscale that demonstrates how to create secure WireGuard connections without relying on the traditional Tailscale coordination server. Unlike standard Tailscale deployments that require a control plane to distribute keys and network topology, **control-plane-free WireGuard tunneling** embeds the entire connection metadata into a self-contained payload exchanged out-of-band.

## The Self-Contained Configuration Architecture

Traditional WireGuard deployments rely on external services to coordinate public key exchange and endpoint discovery. Tailcat eliminates this dependency by compressing all required state into a single **CBOR-encoded blob** that travels over the initial HTTPS connection.

### Embedding WireGuard State in CBOR

The architecture centers on the `ConnInfo` structure, which encapsulates the server's WireGuard public key, discovery (disco) key, and DERP region information. According to the source code in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go), this structure is serialized using custom wire types defined in [`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go), including `wireConnInfo`, `wireRegion`, and `wireNode`. The struct tags use single-character CBOR field names to minimize payload size.

When a client connects, the server returns this blob via an HTTP response—typically from the `/info` endpoint implemented in the `ServeHTTP` method. No subsequent coordination with an external control plane occurs after this initial exchange.

## Server-Side Implementation: Generating and Encoding

The server bootstrap process involves two critical phases: cryptographic key generation and compact serialization.

### Key Generation Without External Coordination

In [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go), the `initLocked` function generates the server's WireGuard private/public key pair alongside a separate disco key pair used for path discovery. These keys populate the `ConnInfo` structure. As noted in [`tailcat_ssh.go`](https://github.com/tailscale/tailcat/blob/main/tailcat_ssh.go), "Authentication is not required — the WireGuard tunnel provides identity," eliminating the need for pre-shared credentials or OAuth flows with a coordination server.

### Compact Wire Format Serialization

The conversion logic resides in [`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go), which provides mapping functions like `wireRegionOf` and `(*wireRegion).derpRegion` to transform internal structures into portable wire formats. The CBOR encoding ensures the payload remains small enough to transmit inline within an HTTP response header or body, avoiding the latency of multiple round-trips to a configuration service.

## Client-Side Implementation: Decoding and Tunnel Establishment

The client receives the configuration blob and configures its local WireGuard engine without querying external services.

### Parsing the Connection Blob

The client retrieves the CBOR payload from the server's `/info` endpoint and processes it through `ParseConnBlobRaw` (found in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)). This function decodes the compact binary format back into a `ConnInfo` structure, extracting the server's public key, disco key, and DERP endpoint addresses.

### Local Engine Configuration

With the parsed `ConnInfo`, the client invokes `wgengine.Engine.SetPeerConfigFunc` (implemented around line 1319 in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)) to install a per-peer configuration source. This callback supplies the WireGuard engine with the server's public key and DERP relay address dynamically:

```go
// Simplified from tailcat.go
cfgFunc := func() *wgengine.PeerConfig {
    return &wgengine.PeerConfig{
        PublicKey:   ci.ServerPublic,
        Endpoints:   []netaddr.IPPort{ci.DERPEndpoint},
        DiscoKey:    ci.ServerDiscoPublic,
    }
}
wgEngine.SetPeerConfigFunc(cfgFunc)

```

The WireGuard engine then performs the standard cryptographic handshake directly with the server, using the embedded DERP relay for NAT traversal. Because the DERP region information travels inside the initial blob, no additional discovery step or control plane query occurs.

## Eliminating Control Plane Dependencies

The comment at line 17 in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) explicitly states the design goal: "WireGuard tunnel with no Tailscale account or coordination server required." By embedding the full network topology—including relay addresses and cryptographic identities—within the CBOR payload, Tailcat removes the traditional requirement for a persistent connection to a coordination server.

This approach works because the HTTPS handshake that delivers the CBOR blob provides the necessary encryption and authentication for the initial key exchange. Once both peers possess each other's public keys and DERP coordinates, the WireGuard protocol operates autonomously, handling key rotation and session management without external state management.

## Summary

- **CBOR encapsulation**: All WireGuard configuration data is compressed into a compact binary blob using one-character field names defined in [`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go).
- **In-band key distribution**: The server generates keys locally in `initLocked` and transmits them via the `/info` HTTP endpoint, eliminating the need for a separate control plane.
- **Local engine configuration**: Clients configure their WireGuard engines using `SetPeerConfigFunc` with data from `ParseConnBlobRaw`, enabling tunnel establishment without external coordination.
- **Zero external dependencies**: The architecture requires no Tailscale account, no OAuth flows, and no persistent connection to a coordination server after the initial HTTPS exchange.

## Frequently Asked Questions

### How does Tailcat handle NAT traversal without a control plane?

Tailcat embeds DERP (Designated Encrypted Relay for Packets) region information directly within the CBOR configuration blob. Because both the client and server receive each other's DERP endpoint addresses during the initial HTTPS handshake, they can route traffic through Tailscale's DERP relays immediately without querying a separate discovery service.

### What makes CBOR better than JSON for this use case?

CBOR provides a binary encoding that is significantly more compact than JSON while maintaining schema flexibility. The [`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go) implementation uses single-character field keys to minimize payload size, ensuring the configuration blob remains small enough to transmit efficiently within an HTTP response without fragmentation.

### Can Tailcat work without any internet access at all?

No—Tailcat requires network connectivity to exchange the initial CBOR blob (typically over HTTPS) and to reach the DERP relays embedded in the configuration. However, once the blob is exchanged, the WireGuard tunnel operates independently without requiring access to Tailscale's coordination servers or the public internet if both peers are on the same local network.

### Why doesn't Tailcat require traditional authentication?

As stated in [`tailcat_ssh.go`](https://github.com/tailscale/tailcat/blob/main/tailcat_ssh.go), "Authentication is not required — the WireGuard tunnel provides identity." The cryptographic properties of WireGuard's key exchange provide mutual authentication; if a client can successfully complete the Noise protocol handshake with the server using the public keys exchanged in the CBOR blob, identity is cryptographically verified without requiring passwords, certificates, or OAuth tokens.