# Tailcat Connection Establishment Flow: From Compact Address to WireGuard Tunnel

> Explore the Tailcat connection establishment flow. Discover how Tailcat creates secure WireGuard tunnels from compact addresses using DERP for path discovery and a peer-to-peer handshake.

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

---

**Tailcat establishes encrypted connections through a three-phase process—address generation, DERP-assisted path discovery, and WireGuard handshake—that converts a compact Base64-URL address into a peer-to-peer WireGuard tunnel without external coordination.**

Tailcat is a standalone secure networking library in the `tailscale/tailcat` repository that creates WireGuard tunnels without requiring a Tailscale account or centralized control plane. The connection establishment flow in Tailcat transforms a concise, shareable address string into an end-to-end encrypted tunnel through a carefully orchestrated sequence of key exchange, NAT traversal, and protocol upgrade.

## Phase 1: Address Generation and ConnInfo Encoding

The server initiates the connection establishment flow by generating a compact, self-contained address that encodes all necessary cryptographic material. In [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go), the `type ConnInfo struct` (lines 51-88) aggregates the server's **WireGuard node public key**, a separate **disco public key** for path discovery, an optional pre-shared key, and the target DERP region ID.

This structure is **CBOR-encoded** and then **Base64-URL-encoded** into a `type Addr string` (lines 46-50), producing a compact string like `tcomFwWC…` that can be distributed to clients via any out-of-band channel. The address contains everything required to locate and authenticate the server, eliminating the need for external coordination.

## Phase 2: DERP Discovery and Path Discovery

When the client receives the address, it parses the string back into a `ConnInfo` via `ConnInfo.Expand` (lines 103-110), which fetches the DERP map if the address does not contain the full region list. The client then connects to the specified **DERP relay** using the `tailscale.com/disco` package.

Through this relay, the client and server exchange **disco packets** that convey the server's `ServerDiscoPublic` key and UDP endpoints. The server registers its dialer with `tsdial.NewDialer`, while the client listens on the DERP connection. This phase, implemented around lines 660-682 in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go), establishes a fallback relay path and gathers the network intelligence needed for NAT traversal.

## Phase 3: WireGuard Handshake and Direct Path Upgrade

With cryptographic material exchanged, both sides instantiate a **WireGuard engine** using `wgengine.NewEngine` (lines 520-560). The WireGuard handshake completes over the established DERP channel, creating the first encrypted tunnel immediately.

Once the handshake succeeds, the **magicsock** layer attempts to upgrade the connection from relayed DERP to a direct peer-to-peer UDP path. The function `ns.GetUDPHandlerForFlow` (lines 663-682) manages this transition, testing direct connectivity between the discovered UDP endpoints. If NAT traversal succeeds, traffic shifts to the direct path for lower latency; if it fails, the connection continues using the DERP relay as a fallback, maintaining the encrypted WireGuard tunnel throughout.

## Key Source Files and Functions

The connection establishment flow spans several critical files in the `tailscale/tailcat` repository:

- **[`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)**: Core implementation containing `ConnInfo` address parsing (`ParseAddr`), server initialization (`NewServer`), client creation (`NewClient`), and the magicsock upgrade logic.
- **[`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go)**: CBOR wire format definitions that determine how `ConnInfo` structures serialize into the compact `Addr` format.
- **[`disco.go`](https://github.com/tailscale/tailcat/blob/main/disco.go)**: Path-discovery packet definitions used during the DERP exchange phase.
- **[`cmd/tailcat/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/tailcat.go)**: Command-line interface that demonstrates the complete flow from address generation to tunneled connection.

## Server and Client Implementation Example

The following example demonstrates the complete connection establishment flow using the Tailcat API:

```go
// Server side: generate an address containing all connection parameters
srv, _ := tailcat.NewServer(tailcat.ServerOptions{
    // optionally set RegionID to let the client pick a DERP region
})
addr := srv.Addr() // produces a string like "tcomFwWC…"
fmt.Println("Give this address to your client:", addr)

// Client side: parse address and establish secure tunnel
ci, err := tailcat.ParseAddr(addr) // decodes CBOR back into ConnInfo
if err != nil { log.Fatal(err) }
client, err := tailcat.NewClient(ci) // expands DERP map, performs discovery
if err != nil { log.Fatal(err) }
conn, err := client.Dial(context.Background(), "example.com:22") // encrypted WireGuard tunnel
if err != nil { log.Fatal(err) }
fmt.Println("Connected to server over WireGuard!")

```

## Summary

- **Tailcat uses a three-phase connection establishment** that encodes connection parameters into a compact address, discovers paths via DERP relays, and upgrades to direct WireGuard connections.
- **The `ConnInfo` struct** in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) encapsulates all cryptographic identity and network location data, serialized via CBOR into a portable `Addr` string.
- **DERP relays provide the initial bootstrap path** for NAT traversal and key exchange, with the `tailscale.com/disco` package handling endpoint discovery.
- **Magicsock upgrades connections** from relayed to direct UDP paths automatically, falling back to DERP if direct connectivity fails, ensuring resilient encrypted tunnels without external coordination.

## Frequently Asked Questions

### What information is encoded in a Tailcat address?

A Tailcat address is a Base64-URL-encoded CBOR payload containing the `ConnInfo` struct. This includes the server's WireGuard public key (`ServerPublic`), a separate disco public key (`ServerDiscoPublic`) for path discovery, an optional pre-shared key, and the DERP region ID. This self-contained design allows clients to establish connections using only the address string, without querying external directories.

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

Tailcat leverages **DERP (Designated Encrypted Relay for Packets)** relays and the `tailscale.com/disco` protocol for NAT traversal. The client and server use the DERP relay to exchange UDP endpoint information through encrypted disco packets. After learning each other's public endpoints, the magicsock layer attempts direct UDP hole punching; if successful, the connection migrates from the relay to the direct path transparently.

### Is the connection encrypted during the initial DERP relay phase?

Yes. While DERP provides the transport for initial packets, the actual **WireGuard handshake** establishes an encrypted tunnel immediately. The DERP relay only forwards opaque WireGuard packets and cannot decrypt the payload. Once the handshake completes in phase 3, all application traffic flows through the WireGuard tunnel, regardless of whether it traverses the DERP relay or a direct UDP path.

### What happens if direct peer-to-peer connection fails?

If NAT traversal fails and no direct UDP path can be established, Tailcat maintains the connection through the **DERP relay**. The magicsock layer, specifically via `ns.GetUDPHandlerForFlow` in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go), gracefully handles this fallback, keeping the encrypted WireGuard tunnel active over the relay indefinitely. This ensures connectivity even in restrictive network environments that block direct UDP communication.