How Is a Tailcat Connection Token Encoded? The ConnBlob Format Explained
Tailcat connection tokens are compact, URL-safe strings created by pruning server metadata, serializing the remainder into CBOR, encoding the binary data with unpadded base64-URL, and prefixing the result with the literal "tc".
The tailscale/tailcat repository implements a lightweight tunneling protocol that relies on these compact connection tokens to share server details between peers. Understanding how a Tailcat connection token is encoded reveals a carefully optimized pipeline designed to minimize transfer size while preserving essential connectivity information like public keys and DERP region data.
The ConnBlob Wire Format Definition
The internal representation of a connection token is defined in wire.go, where the wireConnInfo struct serves as the canonical schema for serialization. This structure contains the server’s public key (ServerPublic) and an optional list of DERP regions (Region).
The CBOR field names are deliberately minimized to single-character strings to reduce payload size. For example, "p" represents ServerPublic, "r" represents Region, and "i" represents RegionID. While JSON tags exist on these structs, they are used strictly for debugging display, not for the actual token encoding.
The Encoding Pipeline
The transformation from a full ConnInfo structure to a shareable token occurs in four distinct phases within the tailcat.go source file.
1. Field Pruning for Size Optimization
Before serialization, the ConnInfo.ConnBlob() method (implemented around lines 58–78 of tailcat.go) aggressively strips fields that are not strictly required for the client to establish a connection. This pruning process:
- Zeroes out the region’s
RegionID,RegionCode, andRegionNamefields. - Clears each node's
RegionIDto avoid duplication. - Omits the node's
Namefield when aHostNameis already present.
This reduction ensures the token contains only the minimal data necessary for connectivity, significantly reducing the final string length.
2. CBOR Marshalling
Once pruned, the wireConnInfo structure is marshalled using cbor.Marshal. The implementation treats this operation as deterministic—any marshalling error results in a panic because the wire types should always be serializable. CBOR was chosen over JSON specifically for its compact binary representation, which produces smaller tokens than text-based alternatives.
3. Base64-URL Encoding
The resulting byte slice from CBOR encoding is transformed into a URL-safe string using base64.RawURLEncoding.EncodeToString. The "Raw" variant is critical here, as it omits the standard base64 padding characters (=), producing a cleaner string suitable for URLs and command-line arguments without requiring additional escaping.
4. Prefixing with the "tc" Identifier
The final step prepends the literal string "tc" to the base64-encoded data. This prefix serves as a magic identifier that allows the parser to quickly distinguish valid Tailcat tokens from random strings. The complete token follows the pattern tc<base64-data>, resulting in values like tcAbCdEfGh....
Decoding and Parsing Tokens
When a client receives a token, the ParseConnBlob() function (also located in tailcat.go) reverses the encoding pipeline:
- Strips the
"tc"prefix and validates the format. - Decodes the base64-URL string back into a byte slice.
- Unmarshals the CBOR data into a
wireConnInfostructure. - Reconstructs the full
ConnInfoobject, filling in the previously pruned fields (region IDs, codes, node names) so the server can operate with complete metadata.
The optional ConnBlob.Resolve() method can fetch missing DERP region information from network sources if the pruned token lacks complete routing data.
Working with Tailcat Connection Tokens in Go
The following examples demonstrate creating and parsing connection tokens using the tailscale/tailcat package.
Create a token from a running server:
// Create a connection token (ConnBlob) from a running Server.
s := tailcat.NewServer(privKey) // privKey is a tailnet private key
s.Start(context.Background())
token := s.ConnBlob() // e.g. "tcAbCdEfGh..."
fmt.Println("Connect with:", token)
Parse a token on the client side:
// Parse a token on the client side.
blob := tailcat.ConnBlob(token)
// Resolve any missing DERP region information (optional).
resolvedBlob, err := blob.Resolve(context.Background())
if err != nil {
log.Fatalf("resolve failed: %v", err)
}
// Decode back into a ConnInfo structure.
ci, err := tailcat.ParseConnBlob(resolvedBlob)
if err != nil {
log.Fatalf("parse failed: %v", err)
}
fmt.Printf("Server public key: %s\n", ci.ServerPublic)
fmt.Printf("Embedded DERP regions: %d\n", len(ci.Region))
Summary
- Tailcat connection tokens use the ConnBlob format, prefixed with
"tc"for quick identification. - The encoding process is implemented in
ConnInfo.ConnBlob()withintailcat.go, which prunes redundant fields before serialization. - The wire schema defined in
wire.goutilizes single-character CBOR field names (e.g.,"p"forServerPublic) to minimize payload size. - Tokens are serialized using CBOR, then encoded with base64-URL without padding to ensure URL safety.
- Clients decode tokens via
ParseConnBlob()and reconstruct complete metadata using theResolve()method.
Frequently Asked Questions
What is the ConnBlob format in Tailcat?
The ConnBlob is Tailcat's compact connection token format. It is a URL-safe string that encodes a server's public key and DERP region information using CBOR serialization and base64-URL encoding, prefixed with "tc". This format allows servers to share connection details through compact, copy-paste-friendly strings.
Why does Tailcat use CBOR instead of JSON for connection tokens?
Tailcat uses CBOR (Concise Binary Object Representation) because it produces significantly smaller binary payloads compared to JSON text. Given that connection tokens may be shared via chat, QR codes, or command-line arguments, minimizing byte size is critical for usability, making CBOR the optimal choice over verbose text formats.
How does Tailcat reduce the token size before encoding?
Before calling cbor.Marshal, the ConnBlob() method (around lines 58–78 in tailcat.go) prunes the server metadata by zeroing out region identifiers (RegionID, RegionCode, RegionName), clearing node-specific region IDs, and omitting node names when hostnames are present. This aggressive trimming removes redundant data that can be reconstructed by the client during the Resolve() phase.
What does the "tc" prefix signify in a Tailcat connection token?
The literal "tc" prefix acts as a format magic number that allows ParseConnBlob() to immediately validate the input string and distinguish it from other token types or random data. This prefix ensures that Tailcat can reject malformed tokens early in the parsing process before attempting expensive decoding operations.
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 →