# How a Tailcat Server Manages Incoming WireGuard Clients: Deep Dive into User-Space Relay Authentication

> Discover how a Tailcat server manages incoming WireGuard clients using CBOR tokens and public key validation. Understand the authentication process for secure tunnel establishment.

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

---

**A Tailcat server authenticates WireGuard clients by issuing CBOR-encoded connection tokens that embed the server's public key and DERP region map, then validates client public keys against a mutex-protected `allowedClients` map before allowing tunnel establishment.**

Tailcat is a lightweight, user-space relay implementation from `tailscale/tailcat` that combines DERP (Designated Encrypted Relay for Packets) functionality with WireGuard networking. Unlike traditional kernel-space implementations, this architecture handles cryptographic handshakes and packet forwarding entirely within the application layer. Understanding how the server manages incoming WireGuard clients requires examining the token-based authentication flow and the in-memory authorization mechanisms defined in the source code.

## Core Architecture: The locoBackend Structure

The server centers around the `locoBackend` struct defined in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) (lines 15-30). This structure maintains the server's cryptographic identity and access control state:

- **Server private key**: The WireGuard private key used for all cryptographic operations
- **DERP map (`lb.dm`)**: A mapping of region IDs to relay nodes for NAT traversal
- **Authorized clients map (`allowedClients`)**: A map of client public keys to boolean values, protected by `lb.mu` mutex

When the server starts via `NewServer()`, it initializes this backend with a fresh WireGuard key pair and an empty `allowedClients` map. All subsequent client authentication flows reference this central authority.

## Token-Based Client Authentication

### Generating the Connection Token

After server initialization, `Server.ConnBlob` constructs the authentication credential. As implemented in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) (lines 7-13), this method:

1. Creates a `ConnInfo` struct containing the server's public key and a copy of the DERP region map
2. Encodes the structure using CBOR (Concise Binary Object Representation) serialization
3. Prefixes the result with `"tc"` to create a compact string token

This token serves as the single source of truth for client configuration, eliminating the need for separate key distribution mechanisms.

### Parsing and Validation

When a WireGuard client receives the token, `ParseConnBlob` (lines 44-78 in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)) performs the inverse operation:

- Decodes the CBOR payload to restore the server's public key
- Reconstructs the DERP region map for routing decisions
- Validates the token format begins with the `"tc"` prefix

The extracted DERP map allows the client to route packets through the correct relay without additional network lookups.

### Public Key Authorization

During the WireGuard handshake phase, the server extracts the client's public key from the cryptographic exchange. According to the source code, the server then:

1. Acquires `lb.mu` to ensure thread-safe access
2. Stores the client public key in the `allowedClients` map
3. Releases the mutex

Every incoming encrypted packet undergoes validation against this map. The server checks the packet's source public key against `allowedClients`; if the key is absent, the packet is dropped silently, preventing unknown peers from establishing tunnels.

## WireGuard Data Path Processing

The server runs a user-space WireGuard engine via `wireguard-go` bound to a UDP socket. The packet processing flow follows this sequence:

1. **Ingress**: Encrypted WireGuard packets arrive at the UDP socket
2. **Decryption**: The `wireguard-go` engine decrypts the packet layer
3. **Authorization**: The source public key is checked against `allowedClients` (protected by `lb.mu`)
4. **Forwarding**: Valid packets are routed to the local Tailscale network or attached process

Because the DERP map is embedded directly in the connection token, the server routes packets to other peers without external STUN queries or DNS resolution. This design minimizes latency and eliminates external dependencies during the data path phase.

## Practical Implementation Examples

### Starting a Server and Generating Tokens

```go
s, _ := tailcat.NewServer()
s.Start(ctx)               // launches the DERP+WireGuard listener
blob := s.ConnBlob()       // give this string to clients
fmt.Println("ConnBlob:", blob)

```

### Client Connection Using the Token

```go
c, _ := tailcat.NewClient(blob) // parses the blob, extracts DERP map
c.Start(ctx)                     // runs wireguard-go and connects to server

```

### Internal Authorization Check

```go
lb := s.lb                     // locoBackend inside the server
lb.mu.Lock()
ok := lb.allowedClients[clientPubKey]
lb.mu.Unlock()
if !ok {
    log.Fatalf("unauthorised client")
}

```

## Summary

- The `locoBackend` struct centralizes server keys, DERP routing tables, and the mutex-protected `allowedClients` authorization map in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go).
- `Server.ConnBlob` generates CBOR-encoded tokens prefixed with `"tc"` that bundle the server public key and complete DERP region information.
- `ParseConnBlob` decodes connection tokens to establish cryptographic context without external network lookups.
- Every client public key is validated against `allowedClients` before packets enter the WireGuard tunnel, enforcing strict peer authorization.
- User-space packet forwarding leverages `wireguard-go` with DERP routing embedded directly in the initial connection token.

## Frequently Asked Questions

### What prevents unauthorized WireGuard clients from connecting to a Tailcat server?

The server maintains an `allowedClients` map that stores authorized public keys. When a client completes the WireGuard handshake, the server extracts its public key and verifies its presence in the map under protection of `lb.mu`. If the key is not found, the connection is rejected before any IP traffic traverses the tunnel.

### How does Tailcat eliminate external lookups during peer connection?

The `ConnBlob` token encodes the complete DERP region map using CBOR serialization as defined in [`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go) (types `wireConnInfo`, `wireRegion`, and `wireNode`). When the client parses this token via `ParseConnBlob`, it immediately possesses all routing information needed to reach DERP relays, removing dependencies on STUN queries or DNS resolution during connection establishment.

### Where is the client authorization logic implemented in the codebase?

The core authorization logic resides in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go), specifically within the `locoBackend` struct (lines 15-30) and the handshake handling code. The `allowedClients` map is checked on every packet ingress to verify the source public key. The CBOR wire types used for token serialization are defined separately in [`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go).

### Why does the server use a mutex-protected map for client authorization?

The `lb.mu` mutex protects the `allowedClients` map because the user-space WireGuard engine processes packets on multiple goroutines. Concurrent read and write operations on the authorization map would cause race conditions without this synchronization primitive, potentially allowing unauthorized packets to slip through during map updates.