# Tailcat Connection Token Format: Structure, Encoding, and Parsing

> Understand the Tailcat connection token format. Learn its structure, encoding, and how to parse the URL-safe string containing WireGuard keys and DERP relay metadata.

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

---

**A Tailcat connection token is a URL-safe string consisting of the literal prefix `tc` followed by a base64-url-encoded CBOR payload containing the server's WireGuard public key and DERP relay metadata.**

The Tailcat connection token (also referred to as a **ConnBlob**) enables clients to establish WireGuard tunnels over DERP relays without prior configuration. According to the `tailscale/tailcat` source code, this compact format encodes all necessary connection parameters into a single string suitable for URLs, command-line arguments, or QR codes.

## Token Structure and Encoding

### The tc Prefix and ConnBlob Type

Every valid token begins with the literal ASCII characters `tc`. This prefix identifies the string as a Tailcat connection blob before the payload is decoded. In **[`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)** (lines 36‑40), the `ConnBlob` type is defined as a string alias, while the underlying `ConnInfo` struct holds the deserialized data.

### CBOR Wire Format

The payload following the `tc` prefix is the result of **base64-url-encoding** (specifically `base64.RawURLEncoding` without padding) a binary **CBOR** (Concise Binary Object Representation) blob. The CBOR schema is defined in **[`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go)**, which declares the `wireConnInfo` structure using compact field keys:

- **`p`** — `ServerPublic` (`NodePublic`): The server's 32-byte raw WireGuard public key (no "nodekey:" prefix).
- **`k`** — `ServerDiscoPublic` (`*DiscoPublic`): Optional separate discovery public key used for NAT traversal path discovery.
- **`i`** — `RegionID` (`int64`): Numeric DERP region ID used when the token does **not** embed full region details.
- **`r`** — `Region` (`[]*wireRegion`): Full DERP region description (list of relays) embedded when the token contains complete region data, including relay hostnames and ports.

## Generating a Tailcat Connection Token

Servers generate tokens by populating a `ConnInfo` struct and calling the `ConnBlob()` method. The implementation in **[`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)** (lines 44‑77) handles CBOR marshaling and base64 encoding.

```go
// Create and start a Tailcat server.
srv := &tailcat.Server{}
if err := srv.Start(); err != nil {
    log.Fatalf("Server start failed: %v", err)
}

// Obtain the self-contained connection token.
blob := srv.ConnBlob()  // Returns ConnBlob type (string alias)
fmt.Println("Tailcat token:", blob)

```

The `ConnBlob()` method internally invokes `lb.connBlob()` to build the `ConnInfo` and serialize it (see **[`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)** lines 93‑99 and 174‑35). By default, this produces a minimal token containing only the `RegionID` (`i` field) rather than full relay addresses.

## Parsing and Validating Tokens

Clients decode tokens using `ParseConnBlob()`, implemented in **[`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)** (lines 332‑370). This function validates the `tc` prefix, base64-url-decodes the payload, and CBOR-unmarshals it into a `wireConnInfo` struct before converting back to the high-level `ConnInfo` type.

```go
// Assume token is received from the server.
token := tailcat.ConnBlob("tcABCdef...")

ci, err := tailcat.ParseConnBlob(token)
if err != nil {
    log.Fatalf("Invalid token: %v", err)
}

fmt.Printf("Server public key: %s\n", ci.ServerPublic)
fmt.Printf("DERP region ID: %d\n", ci.RegionID)

```

The `ParseConnBlob` function delegates CBOR decoding to `parseWire` (see **[`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)** lines 332‑340), which restores omitted fields and handles backward compatibility for various token versions.

## Full vs. Minimal Token Variants

Tailcat supports two token densities controlled by the `-full-address` CLI flag defined in **[`cmd/tailcat/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/tailcat.go)**.

- **Minimal tokens** contain only `ServerPublic`, optional `ServerDiscoPublic`, and `RegionID`. These are compact but require the client to already possess the DERP map for that region ID.
- **Full tokens** embed the complete `Region` array (`r` field) containing `wireRegion` and `wireNode` structures with relay hostnames and ports, resulting in a longer but self-contained token.

```go
// Server-side: Request a full-address token.
full := true  // Corresponds to -full-address flag
if full {
    // ConnBlob() will populate ci.Region instead of only ci.RegionID,
    // embedding the entire DERP region description.
}
fmt.Println(srv.ConnBlob())

```

When the full flag is set, `lb.connBlob()` populates `ci.Region` with the complete relay list (see **[`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)** lines 173‑228), producing a token that requires no external DERP configuration to connect.

## Summary

- **Format**: Literal `tc` prefix + base64-url-encoded CBOR payload.
- **Encoding**: `base64.RawURLEncoding` of CBOR-serialized `wireConnInfo`.
- **Key Fields**: `p` (server public key), `k` (disco key), `i` (region ID), `r` (full region).
- **Generation**: Use `ConnInfo.ConnBlob()` in **[`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)** (lines 44‑77).
- **Parsing**: Use `ParseConnBlob()` in **[`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)** (lines 332‑370).
- **Variants**: Minimal (region ID only) vs. full (embedded relay addresses).

## Frequently Asked Questions

### What data is encoded inside a Tailcat connection token?

The CBOR payload encodes the server's WireGuard public key (`ServerPublic`), an optional discovery public key (`ServerDiscoPublic`), and either a DERP region ID or a full region description. This data allows the client to locate the server and establish a WireGuard tunnel over the correct relay.

### How is the token encoded to ensure URL safety?

After CBOR binary serialization, the payload is encoded using Go's `base64.RawURLEncoding`, which produces URL-safe strings without padding characters. The resulting string is prefixed with `tc`, ensuring the entire token can be safely transmitted in URLs, JSON strings, or scanned from QR codes.

### What is the difference between minimal and full connection tokens?

Minimal tokens contain only a `RegionID` integer, requiring the client to already know the DERP region's relay addresses. Full tokens embed the complete `Region` structure with explicit relay hostnames and ports, making them self-contained but significantly longer. The `tailcat` CLI `-full-address` flag toggles this behavior.

### Where are the token format definitions located in the repository?

The high-level types (`ConnBlob`, `ConnInfo`) and serialization logic reside in **[`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)** (lines 36‑40 and parsing at lines 332‑370). The low-level CBOR wire structures (`wireConnInfo`, `wireRegion`, `wireNode`) and their conversion helpers are defined in **[`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go)**.