# What Is the Role of DERP in Tailcat's Connection Establishment?

> Discover DERP's role in Tailcat connection establishment. DERP acts as a bootstrap and NAT traversal fallback, enabling WireGuard key exchange for secure peer-to-peer paths.

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

---

**DERP (Designated Encrypted Relay for Packets) functions as the bootstrap mechanism and NAT traversal fallback for Tailcat, enabling the initial exchange of WireGuard keys via WebSocket-capable relays before direct peer-to-peer paths are established.**

Tailcat, an experimental mesh networking project from the `tailscale/tailcat` repository, integrates Tailscale’s DERP infrastructure to solve connectivity challenges between peers located behind restrictive firewalls and NAT devices. Unlike traditional VPN relays that handle all data plane traffic, DERP in Tailcat operates exclusively as a **control-plane** service for the initial handshake and as a reliable fallback when UDP hole punching fails.

## How DERP Bootstraps Initial Connections

Tailcat utilizes DERP relays solely for the *initial bootstrap* phase of connection establishment. This process involves encoding relay information into compact addresses, resolving abstract region identifiers to concrete endpoints, and carrying the first discovery packets between peers.

### Encoding DERP Regions in ConnInfo

When a Tailcat server starts, it advertises its bootstrap location through the `RegionID` field in the `ConnInfo` structure. According to the source in [`main/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/main/tailcat.go) (lines 71-88), this field contains either a numeric region identifier or a full `DERPRegion` description, telling the client exactly which DERP relay to contact first.

The compact `Addr` type, generated by `ci.Encode()`, embeds this minimal region metadata into a shareable string that clients can use to initiate connections. This design ensures that clients know where to find the rendezvous point without requiring prior knowledge of the server's network topology.

### Resolving Regions via DERP Maps

If the address contains only a `RegionID`, the client must fetch the public DERP map to resolve the abstract identifier to a concrete relay endpoint. As implemented in [`main/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/main/tailcat.go) (lines 101-111), the client retrieves this map from `DefaultDERPMapURL` or a custom URL supplied via the `DERPMapURL` option.

The package comment in [`main/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/main/tailcat.go) (lines 8-15) explicitly describes this "bootstrap-only" usage: the chosen DERP relay carries the two peers' discovery packets, allowing each side to learn the other's public WireGuard keys and NAT-traversal endpoints before any direct UDP path exists.

## DERP as a Fallback Relay

While Tailcat attempts to establish direct peer-to-peer connections after the initial handshake, DERP remains available as a fallback pathway. The implementation emphasizes that when NAT traversal fails—such as when both peers sit behind symmetric NATs—the established DERP tunnel continues to carry traffic reliably.

This fallback behavior is documented in [`main/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/main/tailcat.go) (lines 12-15), which notes that DERP serves as the fallback relay when direct paths cannot be formed. In this mode, the WebSocket-capable DERP server continues to encrypt and forward packets, ensuring connectivity even in challenging network environments where UDP hole punching is impossible.

## Implementation Architecture

The Tailcat source code provides several mechanisms to optimize DERP discovery and reduce latency during the bootstrap phase.

### Automatic Region Detection

When the server initializes, it can automatically detect the optimal DERP region through a netcheck request. This logic, located near the top of [`main/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/main/tailcat.go) (lines 99-108), analyzes network conditions and stores the resulting region identifier in `ConnInfo.RegionID`. This auto-detection ensures that clients connect to the geographically closest or lowest-latency relay without manual configuration.

### DERP Map Caching

To minimize external dependencies and reduce network traffic during bootstrap, Tailcat implements the `DERPMapCache` interface defined in [`main/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/main/tailcat.go) (lines 13-32). This cache allows a process to reuse a previously fetched DERP map for up to one hour, eliminating redundant HTTP requests to the map URL during transient reconnections or multiple connection attempts.

### Wire Format Serialization

For efficient address encoding, Tailcat serializes minimal DERP data into a compact wire format. The `wireRegion` and `wireNode` structs, defined in [`main/wire.go`](https://github.com/tailscale/tailcat/blob/main/main/wire.go) (lines 34-65), handle the binary representation of region IDs, ports, and node lists when constructing the `Addr` type. This serialization ensures that DERP metadata remains compact enough to fit in URLs or QR codes while preserving all necessary relay information.

### Browser and Test Implementations

In WebAssembly environments, the browser client implementation in [`main/web/main_js.go`](https://github.com/tailscale/tailcat/blob/main/main/web/main_js.go) demonstrates how JavaScript passes the `derpMapURL` parameter to the Go runtime, enabling DERP bootstrap from browser-based peers. Integration tests in [`main/web/wasm_test.go`](https://github.com/tailscale/tailcat/blob/main/main/web/wasm_test.go) verify this behavior by spinning up local DERP and STUN servers to validate both the bootstrap handshake and fallback scenarios.

## Practical Code Examples

The following examples demonstrate how to configure DERP bootstrap behavior when implementing Tailcat clients and servers.

### Creating a ConnInfo with DERP Region

To advertise a specific DERP region to clients, populate the `RegionID` field when constructing server configuration:

```go
// Create a ConnInfo that advertises DERP region 2
ci := tailcat.ConnInfo{
    ServerPublic:      myNodePublic,
    ServerDiscoPublic: myDiscoPublic,
    RegionID:          2, // Directs client to use DERP region 2
}

// Encode into a shareable tailcat address
addr := ci.Encode() // Returns tailcat.Addr

```

### Expanding Addresses with Custom DERP Maps

On the client side, resolve the compact address to concrete endpoints using the `Expand` method. You can use the default Tailcat DERP map or specify a custom source:

```go
// Expand the address for client use with a custom DERP map URL
err := ci.Expand(ctx, 
    tailcat.ExpandForClient, 
    tailcat.DERPMapURL("https://example.com/derpmap.json"))
if err != nil { 
    log.Fatal(err) 
}

// After expansion, ConnInfo contains concrete DERP relay endpoints
// The client can now start the handshake; DERP carries the first packets

```

### Implementing Persistent DERP Caching

For long-running CLI applications or embedded clients, implement the `DERPMapCache` interface to persist DERP maps across restarts:

```go
// Custom disk-based cache implementing DERPMapCache
cache := &myDiskCache{path: "/tmp/derp-cache"}

err := ci.Expand(ctx, tailcat.DERPMapCache(cache))
if err != nil { 
    log.Fatalf("failed to fetch DERP map: %v", err) 
}

```

## Summary

- **DERP serves as the control-plane bootstrap** for Tailcat, not the primary data plane, handling only the initial WireGuard key exchange and NAT traversal coordination.
- **Region identifiers** embedded in `ConnInfo` (defined in [`main/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/main/tailcat.go)) direct clients to specific DERP relays before direct paths are established.
- **Fallback capability** ensures connectivity persists through DERP relays when direct peer-to-peer UDP connections fail due to symmetric NATs or restrictive firewalls.
- **Caching and auto-detection** mechanisms in [`main/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/main/tailcat.go) optimize performance by reusing DERP maps for up to one hour and automatically selecting optimal regions.
- **Wire format types** in [`main/wire.go`](https://github.com/tailscale/tailcat/blob/main/main/wire.go) enable compact serialization of DERP metadata for efficient address sharing.

## Frequently Asked Questions

### What does DERP stand for in the context of Tailcat?

DERP stands for **Designated Encrypted Relay for Packets**. In Tailcat, these relays are WebSocket-capable servers that facilitate the initial connection bootstrap by carrying discovery packets between peers, allowing them to exchange WireGuard public keys and NAT traversal endpoints when no direct path exists.

### Does Tailcat route all traffic through DERP relays?

No. Tailcat uses DERP relays **only for the initial bootstrap** and as a fallback mechanism. Once peers establish direct peer-to-peer connectivity through UDP hole punching, application traffic flows directly between endpoints. DERP carries traffic only when NAT traversal fails or during the first moments of connection establishment.

### How does Tailcat determine which DERP region to use?

Tailcat determines the optimal DERP region through **automatic netcheck detection** on the server side, which stores the result in `ConnInfo.RegionID` (implemented in [`main/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/main/tailcat.go)). Clients then resolve this region ID to concrete relay endpoints by fetching the DERP map from `DefaultDERPMapURL` or a custom URL specified via the `DERPMapURL` option.

### Can I use a private DERP relay with Tailcat instead of Tailscale's public relays?

Yes. Tailcat supports custom DERP infrastructure through the `DERPMapURL` configuration option, allowing you to specify a private JSON endpoint that returns your own DERP region definitions. Additionally, you can implement the `DERPMapCache` interface to cache these definitions locally, reducing dependency on external services as shown in [`main/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/main/tailcat.go).