What Information Is Contained in a Tailcat ConnBlob?
A Tailcat ConnBlob is a compact, URL-safe string that encodes a CBOR-serialized ConnInfo structure containing the server's public key and DERP region information, enabling clients to locate and authenticate to a Tailcat server without additional network calls.
A ConnBlob serves as a self-contained connection credential within the tailscale/tailcat repository. This binary-to-text format packages everything a client needs to establish an authenticated connection while minimizing payload size and network round trips.
Internal Structure and Data Model
The ConnInfo Core Fields
At the heart of every ConnBlob lies a ConnInfo structure that carries two essential data categories:
- ServerPublic: The server's public key (
key.NodePublic), serialized with the CBOR field namep. This field is always present and forms the cryptographic basis for connection authentication. - Region Data: Either a full
Regionlist or a lightweightRegionID. TheRegionfield contains complete DERP region objects (serialized asr), whileRegionIDstores a numeric reference to a known Tailscale-provided region (serialized asi). Including region data allows clients to skip separate DERP map fetches.
Certain fields like RegionCode, RegionName, and redundant node names are intentionally omitted during encoding to reduce blob size. The ParseConnBlob function restores these implicit fields during decoding by cross-referencing the embedded region ID against the known DERP map.
Wire Format Definitions in wire.go
The CBOR wire types that define the binary layout are declared in wire.go:
wireConnInfo(lines 25-30): Maps to the top-level structure with fieldsp(public key),r(regions), andi(region ID).wireRegion(lines 32-38): Represents DERP regions using compact field keysi(ID),c(code),m(name), andN(nodes).wireNode(lines 40-58): Defines individual DERP nodes with single-character fields includingn(name),h(hostname),4(IPv4),6(IPv6),s(port),d(cert name), andx(test flag).
These abbreviated field names minimize the final encoded payload size.
Encoding and Decoding Process
Creating a ConnBlob
The generation follows a strict pipeline to ensure compactness and URL safety:
- Struct Conversion: The high-level
ConnInfoconverts towireConnInfo(and nestedwireRegion/wireNodetypes), stripping redundant data. - CBOR Encoding: The wire structure serializes using CBOR (Concise Binary Object Representation).
- Base64 Encoding: The CBOR bytes undergo base64-URL encoding without padding.
- Prefixing: The resulting string receives a
"tc"prefix, producing the finalConnBlobformat (e.g.,tc...).
Parsing and Field Reconstruction
Decoding reverses this pipeline through the ParseConnBlob function implemented in tailcat.go (lines 744-747). The function:
- Strips the
"tc"prefix and base64-URL decodes the payload - CBOR-decodes the bytes into the wire types
- Reconstructs the full
ConnInfo, re-injecting omitted fields (such as region codes and names) when only aRegionIDwas provided
This ensures that clients receive a complete connection description regardless of which encoding optimization the server chose.
Working with ConnBlob in Go
Generating a ConnBlob
To create a ConnBlob with an embedded DERP region (larger payload):
import (
"github.com/tailscale/tailcat"
"tailscale.com/tailcfg"
)
func makeBlobWithRegion(pub tailcat.NodePublic, derp *tailcfg.DERPRegion) tailcat.ConnBlob {
ci := tailcat.ConnInfo{
ServerPublic: pub,
Region: []*tailcfg.DERPRegion{derp},
}
return ci.ConnBlob()
}
To generate a minimal ConnBlob using only a region ID:
func makeBlobWithRegionID(pub tailcat.NodePublic, id int) tailcat.ConnBlob {
ci := tailcat.ConnInfo{
ServerPublic: pub,
RegionID: id,
}
return ci.ConnBlob()
}
Parsing a ConnBlob
Extract connection details from a blob string:
func parseBlob(blob tailcat.ConnBlob) (tailcat.ConnInfo, error) {
ci, err := tailcat.ParseConnBlob(blob)
if err != nil {
return tailcat.ConnInfo{}, err
}
// ci now holds ServerPublic + either Region or RegionID
return ci, nil
}
Initializing a Client
Pass a ConnBlob directly to the client constructor:
func newClientFromBlob(blobStr string) *tailcat.Client {
blob := tailcat.ConnBlob(blobStr)
client := tailcat.NewClient(blob)
return client
}
Summary
- A Tailcat ConnBlob is a URL-safe string starting with
"tc"that encodes connection parameters. - Internally, it contains CBOR-encoded data representing a
ConnInfostructure with the server's public key (p) and either full DERP regions (r) or a region ID (i). - The wire format defined in
wire.gouses single-character field names to minimize size. - ParseConnBlob in
tailcat.gohandles decoding and reconstructs omitted fields from the known DERP map. - Clients use this blob to authenticate and locate servers without fetching additional configuration.
Frequently Asked Questions
What does the "tc" prefix indicate in a ConnBlob?
The "tc" prefix identifies the string as a Tailcat ConnBlob and distinguishes it from other base64-encoded payloads. After removing this two-character prefix, the remaining string represents base64-URL-encoded CBOR data containing the connection information.
How does a ConnBlob minimize its size?
The encoding omits optional fields like region codes, region names, and duplicate node hostnames when they match the node name. The wire format in wire.go further reduces size by mapping struct fields to single-character CBOR keys (e.g., p for public key, i for region ID). When the client already knows the DERP map, the server can encode only a RegionID instead of full region objects.
What is the difference between Region and RegionID in a ConnBlob?
Region embeds a complete list of DERP region objects, making the blob self-contained but larger. RegionID stores only an integer reference to a Tailscale-hosted DERP region, producing a shorter blob that assumes the client possesses the corresponding DERP map. Both are serialized with distinct CBOR field keys (r vs i) in the wireConnInfo structure.
Where is the ConnBlob parsing logic implemented?
The ParseConnBlob function is implemented in tailcat.go (lines 744-747), which handles base64-URL decoding, CBOR deserialization into wireConnInfo, and reconstruction of any stripped fields. The ConnBlob() method that generates the string resides in the same file, while the underlying wire types are defined in wire.go (lines 25-58).
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 →