# What Is a Tailcat Connection Token (ConnBlob)? Format and Usage Guide

> Learn what a Tailcat connection token ConnBlob is. This guide explains its format and usage for secure client connections without network discovery.

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

---

**A Tailcat connection token (ConnBlob) is a compact, URL-safe string that encodes a server's public key and DERP relay region in a "tc"-prefixed, Base64-URL-encoded CBOR format, enabling clients to establish secure connections without additional network discovery.**

Tailscale's Tailcat project (available at `tailscale/tailcat`) provides a wire protocol for proxying network traffic between nodes. The **ConnBlob** serves as the fundamental connection token that bridges server advertisement and client initiation, embedding all necessary routing and cryptographic identity within a single portable string.

## Understanding the ConnBlob Structure and Format

### Type Definition and Encoding Scheme

In [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go), the connection token is defined as a distinct string type:

```go
type ConnBlob string

```

According to the source comments in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) (lines 133-136), the ConnBlob format follows a strict encoding scheme: the literal prefix `"tc"` followed by Base64-URL-encoded CBOR data representing a serialized **ConnInfo** structure. This design ensures the token remains URL-safe and compact enough for command-line arguments or DNS TXT records.

### Internal Data Structure (ConnInfo)

The CBOR payload encapsulates a **ConnInfo** structure containing:

- **ServerPublic** – The server's public key (`NodePublic`) for cryptographic identity.
- **Region / RegionID** – The DERP relay region identifier(s) for routing.
- **Optional DERP details** – Hosts, ports, and other relay metadata when embedded for self-containment.

These fields map to wire-format structs defined in [`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go) (`wireConnInfo`, `wireRegion`, and `wireNode`), which strip unnecessary fields to minimize blob size.

## How ConnBlob Serialization Works

### Server-Side Token Generation

The serialization logic resides in `ConnInfo.ConnBlob()` (tailcat.go, lines 58-89). This method performs four critical steps:

1. **Structure Conversion**: Transforms the internal `ConnInfo` into wire-format structs to drop unused fields.
2. **CBOR Encoding**: Serializes the wire struct using CBOR for efficient binary representation.
3. **Base64-URL Encoding**: Encodes the CBOR bytes using URL-safe Base64 (no padding).
4. **Prefixing**: Prepends the `"tc"` identifier to create the final `ConnBlob` string.

When a server generates its token, `Server.ConnBlob()` delegates to `locoBackend.connBlob()` (tailcat.go, lines 607-613), which constructs the `ConnInfo` and invokes the serialization method.

### Client-Side Token Parsing

Clients recover connection parameters using `ParseConnBlob` (tailcat.go, lines 744-747). This function:

1. Strips the `"tc"` prefix.
2. Base64-URL decodes the remaining string.
3. CBOR-decodes the result into a `ConnInfo` structure.

The recovered `ConnInfo` provides the client's `NewClient` function (tailcat.go, lines 1455-1471) with the server's public key and DERP region required to establish the connection.

## Resolving Short vs. Self-Contained Tokens

Tailcat supports **short tokens** containing only a `RegionID` and **full tokens** with embedded DERP details. The `ConnBlob.Resolve()` method (tailcat.go, lines 91-99) handles this distinction:

- **Self-contained blobs** return immediately with full routing information.
- **Minimal blobs** trigger a DERP map fetch to expand the token into a complete, fully-embedded connection string.

This resolution step ensures clients can connect using lightweight tokens while maintaining the flexibility to cache complete routing information when needed.

## Practical Code Examples

### Generating a ConnBlob on the Server

```go
// Create and start a Tailcat server instance.
srv, err := tailcat.NewServer(nil)
if err != nil {
    log.Fatal(err)
}
srv.Start(context.Background())

// Obtain the connection token for distribution to clients.
blob := srv.ConnBlob()
fmt.Println("Server connection token:", blob)

```

### Parsing a ConnBlob on the Client

```go
// Receive token from CLI argument, DNS TXT record, or URL parameter.
blob := tailcat.ConnBlob("tcM...") // Full token shortened for brevity

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

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

```

### Establishing a Connection Using the Token

```go
// Initialize client with the connection token.
client := tailcat.NewClient(blob)
defer client.Close()

// Open a TCP connection to port 80 on the server.
conn, err := client.DialTCPPort(context.Background(), 80)
if err != nil {
    log.Fatalf("dial failed: %v", err)
}
defer conn.Close()

fmt.Fprintln(conn, "GET / HTTP/1.1\r\nHost: example\r\n\r\n")

```

### Resolving a Short Token to Full Form

```go
// Expand a minimal token into a self-contained blob with DERP details.
resolved, err := blob.Resolve(context.Background())
if err != nil {
    log.Fatalf("resolve failed: %v", err)
}
fmt.Println("Resolved token:", resolved)

```

## Key Source Files and Architecture

The ConnBlob implementation spans several critical files in the `tailscale/tailcat` repository:

- **[`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)** – Core type definitions (`ConnBlob`, `ConnInfo`), serialization methods (`ConnInfo.ConnBlob`), parsing functions (`ParseConnBlob`), and high-level API wrappers (`Server.ConnBlob`, `NewClient`).
- **[`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go)** – CBOR wire definitions (`wireConnInfo`, `wireRegion`, `wireNode`) that define the minimal on-wire representation of connection data.
- **[`tailcat_test.go`](https://github.com/tailscale/tailcat/blob/main/tailcat_test.go)** – Comprehensive tests validating ConnBlob generation, parsing, and round-trip correctness.
- **[`cmd/tailcat/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/tailcat.go)** – Command-line interface handling for `<addrblob>` arguments, including parsing and resolution logic.

## Summary

- A **Tailcat connection token (ConnBlob)** is a `type ConnBlob string` consisting of `"tc"` plus Base64-URL-encoded CBOR data.
- The token encapsulates **ConnInfo** containing the server's public key (`NodePublic`) and DERP relay region ID.
- Serialization occurs via `ConnInfo.ConnBlob()` (tailcat.go, lines 58-89), which converts to wire structs, CBOR-encodes, and Base64-URL-encodes.
- **Server-side generation** uses `Server.ConnBlob()` (lines 607-613), while **client-side parsing** uses `ParseConnBlob` (lines 744-747).
- **Short tokens** can be resolved to full tokens via `ConnBlob.Resolve()` (lines 91-99) by fetching DERP map details when necessary.

## Frequently Asked Questions

### What format does a Tailcat ConnBlob use?

A ConnBlob uses a string format starting with the literal prefix `"tc"` followed by Base64-URL-encoded (RFC 4648, no padding) CBOR data. The CBOR payload represents a `ConnInfo` structure containing the server's public key and DERP region information, making the token both compact and URL-safe for embedding in DNS records or command-line arguments.

### How is the ConnBlob token generated programmatically?

Servers generate tokens by calling `Server.ConnBlob()`, which delegates to internal methods that construct a `ConnInfo` struct and invoke `ConnInfo.ConnBlob()` (tailcat.go, lines 58-89). This method converts the internal representation to wire format structs from [`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go), CBOR-encodes the result, applies Base64-URL encoding, and prepends the `"tc"` prefix.

### What information does a ConnBlob contain?

The CBOR payload inside a ConnBlob contains a serialized `ConnInfo` with the **ServerPublic** key (the server's `NodePublic` identity) and **RegionID** (the DERP relay region for routing). Optional fields may include specific DERP hostnames and ports when the blob is self-contained, allowing clients to connect without external DERP map lookups.

### Can short ConnBlob tokens be resolved to full tokens?

Yes. The `ConnBlob.Resolve()` method (tailcat.go, lines 91-99) handles resolution of minimal tokens containing only a RegionID. When called, it fetches the DERP map from the network if necessary and returns a fully-embedded ConnBlob containing complete routing details, enabling clients to cache complete connection parameters.