# How to Resolve a Tailcat Address to a Self‑Contained Form: CLI and Go API Guide

> Resolve a tailcat address to its self-contained form using the CLI expand subcommand or Go API ConnInfo.Expand(). Embed full DERP region data easily.

- Repository: [Tailscale/tailcat](https://github.com/tailscale/tailcat)
- Tags: how-to-guide
- Published: 2026-09-06

---

**Use the `tailcat expand` subcommand or the `ConnInfo.Expand()` method to resolve a short tailcat address into a self‑contained form that embeds full DERP region data.**

A **tailcat address** is a compact, URL‑safe string encoding a `[ConnInfo]` structure used by Tailscale's peer‑to‑peer networking tool. By default, addresses contain only a DERP region ID, requiring clients to fetch a DERP map separately. Resolving the address to a **self‑contained form** embeds the full DERP region information directly into the address string, eliminating external lookups.

## Understanding Tailcat Address Expansion

The `tailcat` repository defines two address forms:

- **Short form**: Contains `RegionID` only (e.g., `tc:a1b2c3d4…`). Clients must resolve the region ID to actual relay endpoints.
- **Self‑contained (full) form**: Embeds complete `Region` data including embedded IPv4/IPv6 addresses, eliminating runtime dependencies on DERP map fetching.

The expansion process populates the `Region` field of `ConnInfo` with full DERP data, transforming a short address into a long, portable one.

## The Expand Method in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)

The core expansion logic resides in the `ConnInfo` type. According to the source code, the method signature is:

```go
func (ci *ConnInfo) Expand(ctx context.Context, opts ...any) error

```

`Expand` queries the Tailscale DERP map to resolve `RegionID` into full `Region` data, mutating `ci` in place. Once expanded, `ci.TailcatAddr()` returns the self‑contained address string.

Key implementation details from [[`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)](https://github.com/tailscale/tailcat/blob/main/tailcat.go):

- The method accepts `context.Context` for cancellation and timeout control.
- Optional variadic parameters allow customizing DERP map sources.
- Expansion is idempotent: calling `Expand` on an already‑resolved address returns immediately.

## Using the `tailcat expand` Subcommand

The CLI provides a dedicated subcommand for address resolution. Located in [[`cmd/tailcat/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/tailcat.go)](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/tailcat.go), the `expand` command parses a short address, calls `ConnInfo.Expand()`, and outputs JSON.

Example:

```bash

# Resolve a short address to self‑contained form

$ tailcat expand tc:a1b2c3d4e5f6g7h8i9j0k...
{
  "ServerPublic": "key:...",
  "ServerDiscoPublic": "key:...",
  "PresharedKey": "base64:...",
  "Region": [
    {
      "RegionID": 1,
      "EmbeddedIPv4": "1.2.3.4",
      "EmbeddedIPv6": "[2001:db8::1]",
      "Name": "us-east"
    }
  ],
  "RegionID": 0
}

```

The subcommand handles base64 decoding, CBOR deserialization via [`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go), DERP map fetching, and JSON re‑serialization.

## The `--full-address` Flag on `tailcat serve`

For server operations, the `--full-address` flag automatically emits self‑contained addresses. The flag is defined in [[`cmd/tailcat/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/tailcat.go)](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/tailcat.go) as:

```go
flagFullAddress bool

```

When enabled, the server:

1. Generates a fresh `ConnInfo` with `RegionID` set.
2. Calls `ConnInfo.Expand()` before encoding.
3. Prints the long address containing embedded DERP data.

Example:

```bash

# Start server with self‑contained address output

$ tailcat serve --full-address --psk
tailcat address: tcomFwWCA…[long-base64-string]

```

This flag eliminates the need for clients to run `expand` separately.

## Programmatic Expansion in Go

For custom tooling, use the `tailcat` package directly:

```go
package main

import (
	"context"
	"log"

	"tailscale.com/tailcat"
)

func main() {
	// Parse a short address string
	ci, err := tailcat.ParseAddr("tc:a1b2c3d4e5…")
	if err != nil {
		log.Fatal(err)
	}

	// Resolve to self‑contained form
	if err := ci.Expand(context.Background()); err != nil {
		log.Fatal(err)
	}

	// Re‑encode as full address
	fullAddr, err := ci.TailcatAddr()
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("Self‑contained address: %s", fullAddr)
}

```

Key API points:

- **`tailcat.ParseAddr(string)`**: Decodes base64 and CBOR into `*ConnInfo`.
- **`(*ConnInfo).Expand(context.Context, ...any)`**: Fetches and embeds DERP region data.
- **`(*ConnInfo).TailcatAddr()`**: Encodes expanded `ConnInfo` back to string format.

## When to Use Each Approach

| Approach | Use Case |
|----------|----------|
| **`tailcat expand`** | One‑off address conversion, debugging, or script automation. |
| **`--full-address` flag** | Server deployment where emitted addresses must work without client‑side DERP lookups. |
| **Go API (`Expand`)** | Custom clients, testing, or integration with existing Go services. |

## Key Source Files

| File | Purpose |
|------|---------|
| [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) | `ConnInfo` struct, `Expand()` method, and `TailcatAddr()` encoding. |
| [`cmd/tailcat/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/tailcat.go) | CLI implementation including `expand` subcommand and `--full-address` flag. |
| [`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go) | CBOR serialization/deserialization for address encoding. |
| [`derpmap.go`](https://github.com/tailscale/tailcat/blob/main/derpmap.go) (implied) | DERP map fetching for region resolution. |

## Summary

- **Short tailcat addresses** contain only region IDs and require external DERP map lookup.
- **Self‑contained addresses** embed full region data, making them portable and offline‑capable.
- Use `tailcat expand <addr>` for CLI conversion, `--full-address` for server output, or `ConnInfo.Expand()` programmatically.
- All three approaches converge on the same implementation in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go).

## Frequently Asked Questions

### What is the difference between a short and self‑contained tailcat address?

A **short address** encodes only `ServerPublic`, `ServerDiscoPublic`, `PresharedKey`, and `RegionID`. A **self‑contained address** additionally includes the full `Region` structure with embedded IPv4/IPv6 endpoints, eliminating the need for clients to fetch DERP maps.

### Does `Expand()` modify the original address string?

No. `Expand()` mutates the `*ConnInfo` struct in memory. You must call `TailcatAddr()` to obtain the new encoded string. The original address remains valid but unexpanded.

### Can I expand an address without network access?

No. `Expand()` requires network access to fetch the DERP map from Tailscale's coordination server or a custom source. The self‑contained output, however, works entirely offline once generated.