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

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 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, 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) 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) 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:

// 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:

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:

// 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 — Contains the core type definitions including ConnInfo, PrivateKey, Server, and Client (lines 1–1700+).
  • wire.go — Implements low-level wire-format structures for CBOR encoding of the public key types.
  • cmd/tailcat/tailcat.go — Provides the CLI implementation that wires the public API into a command-line interface.
  • 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 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 within the tailscale/tailcat repository. Low-level wire encoding logic resides in wire.go, while the command-line tool that exercises these types is implemented in cmd/tailcat/tailcat.go.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →