# What Information Is Contained in a Tailcat ConnBlob?

> Discover what information is in a Tailcat ConnBlob. This URL-safe string contains server public key and DERP region for client authentication and connection without extra network calls.

- Repository: [Tailscale/tailcat](https://github.com/tailscale/tailcat)
- Tags: deep-dive
- Published: 2026-08-30

---

**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](https://github.com/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 name **`p`**. This field is always present and forms the cryptographic basis for connection authentication.
- **Region Data**: Either a full `Region` list or a lightweight `RegionID`. The `Region` field contains complete DERP region objects (serialized as **`r`**), while `RegionID` stores a numeric reference to a known Tailscale-provided region (serialized as **`i`**). 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`](https://github.com/tailscale/tailcat/blob/main/wire.go)**:

- `wireConnInfo` (lines 25-30): Maps to the top-level structure with fields `p` (public key), `r` (regions), and `i` (region ID).
- `wireRegion` (lines 32-38): Represents DERP regions using compact field keys `i` (ID), `c` (code), `m` (name), and `N` (nodes).
- `wireNode` (lines 40-58): Defines individual DERP nodes with single-character fields including `n` (name), `h` (hostname), `4` (IPv4), `6` (IPv6), `s` (port), `d` (cert name), and `x` (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:

1. **Struct Conversion**: The high-level `ConnInfo` converts to `wireConnInfo` (and nested `wireRegion`/`wireNode` types), stripping redundant data.
2. **CBOR Encoding**: The wire structure serializes using CBOR (Concise Binary Object Representation).
3. **Base64 Encoding**: The CBOR bytes undergo base64-URL encoding without padding.
4. **Prefixing**: The resulting string receives a `"tc"` prefix, producing the final `ConnBlob` format (e.g., `tc...`).

### Parsing and Field Reconstruction

Decoding reverses this pipeline through the `ParseConnBlob` function implemented in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/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 a `RegionID` was 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):

```go
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:

```go
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:

```go
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:

```go
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 `ConnInfo` structure with the server's public key (`p`) and either full DERP regions (`r`) or a region ID (`i`).
- The wire format defined in [`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go) uses single-character field names to minimize size.
- **ParseConnBlob** in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) handles 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`](https://github.com/tailscale/tailcat/blob/main/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`](https://github.com/tailscale/tailcat/blob/main/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`](https://github.com/tailscale/tailcat/blob/main/wire.go)** (lines 25-58).