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 includingConnInfo,PrivateKey,Server, andClient(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
ConnInfoandConnBlobmodel connection metadata and its URL-safe serialization format.NodePublic,DiscoPublic, andPrivateKeyhandle cryptographic identities using compact CBOR encoding.DERPMapURLandDERPMapCacheconfigure how the library discovers and caches relay server information.ServerandClientprovide the high-level API for accepting and initiating peer-to-peer connections.PingResultdelivers 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →