What Is a Tailcat Connection Token (ConnBlob)? Format and Usage Guide
A Tailcat connection token (ConnBlob) is a compact, URL-safe string that encodes a server's public key and DERP relay region in a "tc"-prefixed, Base64-URL-encoded CBOR format, enabling clients to establish secure connections without additional network discovery.
Tailscale's Tailcat project (available at tailscale/tailcat) provides a wire protocol for proxying network traffic between nodes. The ConnBlob serves as the fundamental connection token that bridges server advertisement and client initiation, embedding all necessary routing and cryptographic identity within a single portable string.
Understanding the ConnBlob Structure and Format
Type Definition and Encoding Scheme
In tailcat.go, the connection token is defined as a distinct string type:
type ConnBlob string
According to the source comments in tailcat.go (lines 133-136), the ConnBlob format follows a strict encoding scheme: the literal prefix "tc" followed by Base64-URL-encoded CBOR data representing a serialized ConnInfo structure. This design ensures the token remains URL-safe and compact enough for command-line arguments or DNS TXT records.
Internal Data Structure (ConnInfo)
The CBOR payload encapsulates a ConnInfo structure containing:
- ServerPublic – The server's public key (
NodePublic) for cryptographic identity. - Region / RegionID – The DERP relay region identifier(s) for routing.
- Optional DERP details – Hosts, ports, and other relay metadata when embedded for self-containment.
These fields map to wire-format structs defined in wire.go (wireConnInfo, wireRegion, and wireNode), which strip unnecessary fields to minimize blob size.
How ConnBlob Serialization Works
Server-Side Token Generation
The serialization logic resides in ConnInfo.ConnBlob() (tailcat.go, lines 58-89). This method performs four critical steps:
- Structure Conversion: Transforms the internal
ConnInfointo wire-format structs to drop unused fields. - CBOR Encoding: Serializes the wire struct using CBOR for efficient binary representation.
- Base64-URL Encoding: Encodes the CBOR bytes using URL-safe Base64 (no padding).
- Prefixing: Prepends the
"tc"identifier to create the finalConnBlobstring.
When a server generates its token, Server.ConnBlob() delegates to locoBackend.connBlob() (tailcat.go, lines 607-613), which constructs the ConnInfo and invokes the serialization method.
Client-Side Token Parsing
Clients recover connection parameters using ParseConnBlob (tailcat.go, lines 744-747). This function:
- Strips the
"tc"prefix. - Base64-URL decodes the remaining string.
- CBOR-decodes the result into a
ConnInfostructure.
The recovered ConnInfo provides the client's NewClient function (tailcat.go, lines 1455-1471) with the server's public key and DERP region required to establish the connection.
Resolving Short vs. Self-Contained Tokens
Tailcat supports short tokens containing only a RegionID and full tokens with embedded DERP details. The ConnBlob.Resolve() method (tailcat.go, lines 91-99) handles this distinction:
- Self-contained blobs return immediately with full routing information.
- Minimal blobs trigger a DERP map fetch to expand the token into a complete, fully-embedded connection string.
This resolution step ensures clients can connect using lightweight tokens while maintaining the flexibility to cache complete routing information when needed.
Practical Code Examples
Generating a ConnBlob on the Server
// Create and start a Tailcat server instance.
srv, err := tailcat.NewServer(nil)
if err != nil {
log.Fatal(err)
}
srv.Start(context.Background())
// Obtain the connection token for distribution to clients.
blob := srv.ConnBlob()
fmt.Println("Server connection token:", blob)
Parsing a ConnBlob on the Client
// Receive token from CLI argument, DNS TXT record, or URL parameter.
blob := tailcat.ConnBlob("tcM...") // Full token shortened for brevity
ci, err := tailcat.ParseConnBlob(blob)
if err != nil {
log.Fatalf("invalid connection token: %v", err)
}
fmt.Printf("Server public key: %s\n", ci.ServerPublic)
fmt.Printf("DERP region ID: %d\n", ci.RegionID)
Establishing a Connection Using the Token
// Initialize client with the connection token.
client := tailcat.NewClient(blob)
defer client.Close()
// Open a TCP connection to port 80 on the server.
conn, err := client.DialTCPPort(context.Background(), 80)
if err != nil {
log.Fatalf("dial failed: %v", err)
}
defer conn.Close()
fmt.Fprintln(conn, "GET / HTTP/1.1\r\nHost: example\r\n\r\n")
Resolving a Short Token to Full Form
// Expand a minimal token into a self-contained blob with DERP details.
resolved, err := blob.Resolve(context.Background())
if err != nil {
log.Fatalf("resolve failed: %v", err)
}
fmt.Println("Resolved token:", resolved)
Key Source Files and Architecture
The ConnBlob implementation spans several critical files in the tailscale/tailcat repository:
tailcat.go– Core type definitions (ConnBlob,ConnInfo), serialization methods (ConnInfo.ConnBlob), parsing functions (ParseConnBlob), and high-level API wrappers (Server.ConnBlob,NewClient).wire.go– CBOR wire definitions (wireConnInfo,wireRegion,wireNode) that define the minimal on-wire representation of connection data.tailcat_test.go– Comprehensive tests validating ConnBlob generation, parsing, and round-trip correctness.cmd/tailcat/tailcat.go– Command-line interface handling for<addrblob>arguments, including parsing and resolution logic.
Summary
- A Tailcat connection token (ConnBlob) is a
type ConnBlob stringconsisting of"tc"plus Base64-URL-encoded CBOR data. - The token encapsulates ConnInfo containing the server's public key (
NodePublic) and DERP relay region ID. - Serialization occurs via
ConnInfo.ConnBlob()(tailcat.go, lines 58-89), which converts to wire structs, CBOR-encodes, and Base64-URL-encodes. - Server-side generation uses
Server.ConnBlob()(lines 607-613), while client-side parsing usesParseConnBlob(lines 744-747). - Short tokens can be resolved to full tokens via
ConnBlob.Resolve()(lines 91-99) by fetching DERP map details when necessary.
Frequently Asked Questions
What format does a Tailcat ConnBlob use?
A ConnBlob uses a string format starting with the literal prefix "tc" followed by Base64-URL-encoded (RFC 4648, no padding) CBOR data. The CBOR payload represents a ConnInfo structure containing the server's public key and DERP region information, making the token both compact and URL-safe for embedding in DNS records or command-line arguments.
How is the ConnBlob token generated programmatically?
Servers generate tokens by calling Server.ConnBlob(), which delegates to internal methods that construct a ConnInfo struct and invoke ConnInfo.ConnBlob() (tailcat.go, lines 58-89). This method converts the internal representation to wire format structs from wire.go, CBOR-encodes the result, applies Base64-URL encoding, and prepends the "tc" prefix.
What information does a ConnBlob contain?
The CBOR payload inside a ConnBlob contains a serialized ConnInfo with the ServerPublic key (the server's NodePublic identity) and RegionID (the DERP relay region for routing). Optional fields may include specific DERP hostnames and ports when the blob is self-contained, allowing clients to connect without external DERP map lookups.
Can short ConnBlob tokens be resolved to full tokens?
Yes. The ConnBlob.Resolve() method (tailcat.go, lines 91-99) handles resolution of minimal tokens containing only a RegionID. When called, it fetches the DERP map from the network if necessary and returns a fully-embedded ConnBlob containing complete routing details, enabling clients to cache complete connection parameters.
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 →