# Key Types in the Tailcat Go Library: Server, Client, and Connection Primitives

> Explore the Tailcat Go library's key types like Server, Client, and ConnInfo. Understand connection encoding, WireGuard keys, and peer-to-peer networking primitives for your projects.

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

---

**The Tailcat Go library exposes ten core types—including `ConnInfo`, `PrivateKey`, `Server`, and `Client`—that handle connection encoding, WireGuard key material, and high-level peer-to-peer networking.**

The `tailscale/tailcat` repository provides a lightweight Go library for establishing peer-to-peer connections over WireGuard and DERP relay servers. Understanding the key types in the Tailcat Go library is essential for implementing the full client-server flow, from generating cryptographic identities to exchanging URL-safe connection tokens.

## Connection and Key Types

### ConnInfo and ConnBlob

`ConnInfo` is the central payload describing how to reach a Tailcat server. Defined in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) at lines 141–170, it contains public keys, DERP region identifiers, and metadata required to establish a secure connection.

`ConnBlob` represents the URL-safe, CBOR-encoded serialization of a `ConnInfo`. Implemented at lines 136–140 in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go), this is the "tc-…" string exchanged between peers to share connection details without requiring a coordination server.

### NodePublic and DiscoPublic

`NodePublic` (lines 171–176) is a thin wrapper around `key.NodePublic` that provides a compact CBOR representation without the standard "np" prefix. Similarly, `DiscoPublic` (lines 177–182) wraps `key.DiscoPublic` to store the raw 32-byte discovery key used in connection blobs. These wrappers ensure efficient binary serialization while maintaining compatibility with Tailscale's key types.

### PrivateKey

The `PrivateKey` type (defined at lines 222–228 in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)) holds a node's private key together with its associated `ConnInfo`. Before the key is usable, you must set the DERP region via the `Public` field. This type is typically instantiated using the `NewPrivateKey()` function and serves as the identity for both `Server` and `Client` instances.

## DERP Map Abstractions

### DERPMapURL

`DERPMapURL` is a string option (lines 101–102) that allows callers to specify an alternate URL for fetching the DERP map. This is useful when operating in environments that require custom relay infrastructure rather than Tailscale's default servers.

### DERPMapCache

Defined at lines 112–122, `DERPMapCache` is an interface used by `ConnInfo.Expand` and `FetchDERPMap` to cache DERP map data. Implementations can provide in-memory, on-disk, or distributed caching strategies to avoid repeated fetches of the relay configuration.

## High-Level Networking Types

### Server

The `Server` type (lines 315–326 in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)) represents a high-level server object that runs a Tailcat instance. It handles incoming WireGuard packets, discovery handshakes, and DERP forwarding. The struct requires a `PrivateKey` and optionally accepts a `DERPMapCache` for custom caching behavior.

### Client

`Client` (lines 1490–1502) provides the high-level client API for connecting to a remote Tailcat server. It accepts either a `ConnBlob` or a fully populated `ConnInfo` to establish the connection. Once started, the client manages the WireGuard tunnel and DERP fallback routing automatically.

### PingResult

When verifying connectivity, `Client.Ping` returns a `PingResult` (lines 1677–1680). This struct contains latency measurements and any error information from the round-trip probe, allowing applications to verify reachability before transmitting application data.

## Practical Usage Examples

Generating a new key pair and encoding it for sharing:

```go
// Generate a fresh private key (includes public keys)
pk := tailcat.NewPrivateKey()

// Populate the DERP region for the client (example: region 2)
pk.Public.RegionID = 2

// Encode the connection info into a URL-safe blob
blob, err := pk.Public.MarshalBinary()
if err != nil {
    log.Fatalf("marshal: %v", err)
}
connBlob := tailcat.ConnBlob(base64.URLEncoding.EncodeToString(blob))
fmt.Println("Share this with peers:", connBlob)

```

Starting a server with the generated key:

```go
srv := &tailcat.Server{
    // Provide the private key generated above
    PrivateKey: pk,
    // Optional: custom DERP map cache
    DERPMapCache: tailcat.NewOnDiskCache("derp-cache"),
}
if err := srv.Start(); err != nil {
    log.Fatalf("server start: %v", err)
}
defer srv.Close()

```

Connecting as a client and verifying latency:

```go
// Decode the received blob back into ConnInfo
var ci tailcat.ConnInfo
if err := ci.UnmarshalBinary(receivedBytes); err != nil {
    log.Fatalf("decode blob: %v", err)
}

// Create and start the client
cl := &tailcat.Client{
    ConnInfo: ci,
}
if err := cl.Start(); err != nil {
    log.Fatalf("client start: %v", err)
}
defer cl.Close()

// Verify connectivity
res, err := cl.Ping(context.Background())
if err != nil {
    log.Fatalf("ping failed: %v", err)
}
fmt.Printf("Round-trip latency: %v\n", res.Latency)

```

## Source File Organization

The public API is distributed across several files according to their abstraction level:

- **[`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)** — Contains the core type definitions including `ConnInfo`, `PrivateKey`, `Server`, and `Client` (lines 1–1700+).
- **[`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go)** — Implements low-level wire-format structures for CBOR encoding of the public key types.
- **[`cmd/tailcat/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/tailcat.go)** — Provides the CLI implementation that wires the public API into a command-line interface.
- **[`readme.go`](https://github.com/tailscale/tailcat/blob/main/readme.go)** — Houses documentation and usage examples embedded directly in the repository.

## Summary

- **`ConnInfo`** and **`ConnBlob`** model connection metadata and its URL-safe serialization format.
- **`NodePublic`**, **`DiscoPublic`**, and **`PrivateKey`** handle cryptographic identities using compact CBOR encoding.
- **`DERPMapURL`** and **`DERPMapCache`** configure how the library discovers and caches relay server information.
- **`Server`** and **`Client`** provide the high-level API for accepting and initiating peer-to-peer connections.
- **`PingResult`** delivers latency metrics to verify connectivity between peers.

## Frequently Asked Questions

### What is the difference between ConnInfo and ConnBlob in Tailcat?

`ConnInfo` is the structured Go struct containing keys and DERP region data, while `ConnBlob` is the URL-safe base64 string (prefixed with "tc-") that you share with peers. According to the source in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) lines 136–170, `ConnBlob` is the CBOR-encoded representation of `ConnInfo` designed for easy transmission via copy-paste or QR codes.

### How does the PrivateKey type relate to Server and Client?

`PrivateKey` is the identity primitive required by both sides of a connection. As defined at lines 222–228, it encapsulates the node's private key and its public `ConnInfo`. Both the `Server` struct (line 315) and the `Client` struct (line 1490) accept a `PrivateKey` in their configuration to authenticate WireGuard handshakes and sign discovery messages.

### What is the purpose of the DERPMapCache interface?

`DERPMapCache` allows applications to cache DERP map data locally rather than fetching it repeatedly from the network. The interface at lines 112–122 is consumed by `ConnInfo.Expand` to resolve region IDs into full relay configurations, enabling offline-capable or low-latency startup scenarios when an on-disk cache is provided.

### Where are the core type definitions located in the repository?

All primary types—including `Server`, `Client`, `ConnInfo`, and key wrappers—are defined in the root file [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) within the `tailscale/tailcat` repository. Low-level wire encoding logic resides in [`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go), while the command-line tool that exercises these types is implemented in [`cmd/tailcat/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/tailcat.go).