What Is the Tailcat Address Format? Structure, Encoding, and Parsing Guide

The Tailcat address format is a URL-safe string starting with "tc" followed by a base64url-encoded CBOR payload containing server connection metadata such as public keys and DERP region information.

The Tailcat address format enables compact, portable server discovery within the tailscale/tailcat codebase. These addresses encode cryptographic identity and network path details into a single string that clients can parse to establish WireGuard connections. Understanding this format is essential for developers building on the Tailcat protocol.

Structure and Encoding of Tailcat Addresses

Every Tailcat address follows a strict binary-to-text encoding scheme defined in the core library.

The "tc" Prefix and Base64url Encoding

The format begins with the literal prefix tc, immediately followed by the base64url encoding (RFC 4648 §5) of a CBOR-serialized ConnInfo structure. This design produces strings like tcomFwWC... that remain unambiguous in URLs and JSON payloads.

The type definition and documentation reside in main/tailcat.go:

// Addr is a compact, URL‑safe tailcat address that a server gives to clients
// so they can connect. It is the "tc"-prefixed base64url encoding of a
// CBOR-encoded [ConnInfo]. A typical Addr looks like "tcomFwWC…". 
type Addr string

source

The CBOR Payload Fields

When decoded, the CBOR payload reveals a ConnInfo struct containing the following fields:

  • ServerPublic – The server’s WireGuard node public key (wrapped as NodePublic).
  • ServerDiscoPublic – A distinct public key used for path-discovery (disco) packets.
  • PresharedKey – An optional WireGuard pre-shared key included by default for enhanced security.
  • Region or RegionID – Either a full DERP region description (generating a longer address) or a compact numeric region ID (shorter address).

How to Generate a Tailcat Address

Servers expose their connection details through the TailcatAddr method, which serializes the server's current state into the address format.

// s is a *tailcat.Server that has already been started.
addr := s.TailcatAddr()          // Addr type, e.g., "tcJ5Bv…"
fmt.Println("Tailcat address:", addr)

source

This method handles the CBOR serialization and base64url encoding internally, ensuring the output always conforms to the specification.

How to Parse a Tailcat Address

Clients decode these strings using the ParseAddr function, which reverses the encoding process and restores implicit fields such as full region data when a RegionID is provided.

var client tailcat.Client
client.Server = addr                       // the address string from the server
ci, err := tailcat.ParseAddr(addr)         // ci is a ConnInfo struct
if err != nil {
    log.Fatalf("invalid address: %v", err)
}
fmt.Printf("Server public key: %s\n", ci.ServerPublic)

source

The function signature confirms the operation:

// ParseAddr decodes an [Addr] back into a [ConnInfo], restoring
// the implicit fields that [ParseAddr] synthesizes (region and …)
func ParseAddr(addr Addr) (ConnInfo, error)

source

CLI Usage and Real-World Examples

The Tailcat command-line interface leverages this format for server discovery. When starting a server with the --full-address flag, the CLI prints the generated address to stdout.


# Start a server that prints its address

$ tailcat serve --full-address
tailcat address: tcFh9K…

# Connect a client to that address

$ tailcat client tcFh9K…

This implementation resides in the CLI handler at main/cmd/tailcat/tailcat.go:

// serve command handling with --full-address flag

source

Web clients also consume these addresses, as demonstrated in main/web/main_js.go, where the address is passed to browser-based WebAssembly clients through the addr field.

Summary

  • Tailcat addresses use a "tc" prefix followed by base64url-encoded CBOR data, ensuring URL safety and compactness.
  • The ConnInfo payload contains cryptographic keys (ServerPublic, ServerDiscoPublic), optional PresharedKey, and DERP region routing information.
  • Server.TailcatAddr() generates valid addresses from running server instances.
  • ParseAddr() decodes addresses into structured ConnInfo objects, handling both full region objects and compact region IDs.
  • The format is integrated throughout the codebase, from the core library in main/tailcat.go to CLI tools and WebAssembly clients.

Frequently Asked Questions

What does the "tc" prefix signify in Tailcat addresses?

The "tc" prefix serves as a format identifier that distinguishes Tailcat addresses from other URI types. According to the source code comments in main/tailcat.go, this literal prefix ensures parsers can immediately identify the string as a Tailcat address before attempting base64url decoding.

How does the encoding differ from standard base64?

Tailcat addresses use base64url encoding (RFC 4648 §5) rather than standard base64. This variant replaces the + and / characters with - and _ respectively, and omits padding characters (=), making the result safe for use in URL paths and JSON strings without additional escaping.

What security information is exposed in a Tailcat address?

The address exposes the server's public keys (WireGuard and disco) and DERP region data. However, it does not contain private keys or the optional pre-shared key itself in plaintext—the PresharedKey field in the CBOR payload indicates presence or configuration, but the actual key material is established out-of-band during the WireGuard handshake.

Can a Tailcat address be parsed manually without the library?

While possible, manual parsing requires implementing CBOR decoding and handling the implicit field resolution that ParseAddr performs (such as mapping numeric RegionID values to full region structs). The tailscale/tailcat source code provides the authoritative reference implementation for this logic.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →