# How Is a Tailcat Connection Token Encoded? The ConnBlob Format Explained

> Discover how Tailcat connection tokens are encoded. Learn about the ConnBlob format, CBOR serialization, and base64-URL encoding used to create compact, URL-safe strings for Tailscale.

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

---

**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`](https://github.com/tailscale/tailcat/blob/main/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`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) source file.

### 1. Field Pruning for Size Optimization

Before serialization, the `ConnInfo.ConnBlob()` method (implemented around **lines 58–78** of [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/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`, and `RegionName` fields.
- Clears each node's `RegionID` to avoid duplication.
- Omits the node's `Name` field when a `HostName` is 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`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)) reverses the encoding pipeline:

1. Strips the `"tc"` prefix and validates the format.
2. Decodes the base64-URL string back into a byte slice.
3. Unmarshals the CBOR data into a `wireConnInfo` structure.
4. Reconstructs the full `ConnInfo` object, 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:

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

```go
// 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()` within **[`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)**, which prunes redundant fields before serialization.
- The wire schema defined in **[`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go)** utilizes single-character CBOR field names (e.g., `"p"` for `ServerPublic`) 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 the `Resolve()` 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`](https://github.com/tailscale/tailcat/blob/main/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.