# What Are the Components of a Tailcat Address? ServerPublic, ServerDiscoPublic, and PresharedKey Explained

> Understand tailcat address components like ServerPublic, ServerDiscoPublic, and PresharedKey. Learn how they enable secure tunnel establishment without DNS exposure.

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

---

**A tailcat address is a compact, URL-safe string that encodes a server's WireGuard public key (NodeKey), a discovery public key (DiscoKey), an optional pre-shared key (PSK), and DERP routing metadata to enable secure tunnel establishment without DNS exposure.**

A **tailcat address** is the primary connection credential generated by the Tailcat server when it starts. According to the tailscale/tailcat source code, this identifier encapsulates everything a client needs to authenticate the server and route packets through the correct DERP relay. Understanding the individual components—**ServerPublic** (NodeKey), **ServerDiscoPublic** (DiscoKey), and **PresharedKey** (PSK)—is essential for both operators deploying servers and developers integrating the protocol.

## Core Cryptographic Components

### ServerPublic (NodeKey)

The **ServerPublic** component, referred to as `NodeKey` in the source code, is the server's WireGuard node-public key. This is a 32-byte Curve25519 public key that serves as the primary authentication material for the tunnel.

When a client connects, it uses this key to derive the shared secret that encrypts the data channel. In [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go), the `type Addr` struct defines this field (lines 146–166), and the key is populated via the `tcAddrForKey` function when the server generates its identity.

### ServerDiscoPublic (DiscoKey)

The **ServerDiscoPublic** (or `DiscoKey`) is a separate Curve25519 public key used exclusively for the discovery protocol. This key is exchanged out-of-band via the address string itself, allowing the client to reach the server's DERP relay without exposing the sensitive node key in DNS lookups.

According to the comments in `Addr.MarshalBinary` (lines 174–182), this separation allows the server to prove ownership of the address without leaking the `NodeKey` itself, providing an additional layer of security during the initial handshake phase.

### PresharedKey (PSK)

The **PresharedKey** (PSK) is an optional 32-byte random value included to harden the tunnel against key-compromise attacks. When enabled via the `--psk` flag (which defaults to true), the PSK ensures that even if the node key were leaked, an attacker would still need this additional secret to derive the correct shared secret.

The source code conditionally appends this value in `Addr.MarshalBinary` (lines 188–196) only when `a.PSK != nil`. The PSK is deliberately omitted when targeting compatibility with very old clients (≤ v0.5.0).

## Routing and Metadata Fields

### DERP Region and Embedded Maps

Beyond cryptographic keys, the tailcat address contains routing information for the DERP (Designated Encrypted Relay for Packets) network. This is either a numeric **region ID** (e.g., `1` for "us-west") or a full **DERP map** listing relay nodes.

When using the `--full-address` or `--embed-derp-map` flags, the server bakes this data directly into the address, removing the need for a separate map fetch and speeding up connection setup. The parsing logic resides in `parseTailcatAddr` around lines 1030–1080, which handles region validation and emits "invalid tailcat address" errors when parsing fails.

### Version Prefix and Compatibility

Every tailcat address begins with the literal prefix `tc`, followed by a version byte (currently `0`). This versioning scheme, checked early in `ParseTailcatAddr` (lines 1030–1035), guarantees forward compatibility by allowing older clients to reject newer address formats they cannot parse.

## Parsing and Decoding Examples

You can inspect the components of any tailcat address using the CLI or programmatically via the Go API.

```bash

# Generate a new server key with full address embedding and PSK enabled

$ tailcat genkey --full-address --psk

# Output: tc-1B2C3D4E5F6G7H8I9J0K...

# Decode the address to inspect its components

$ tailcat decode tc-1B2C3D4E5F6G7H8I9J0K...

```

```json
{
  "nodeKey": "e6c8e3a6...",
  "discoKey": "c7d2f1...",
  "psk": "7f4b...",
  "regionID": 2,
  "derpMap": null
}

```

To parse addresses programmatically:

```go
import "github.com/tailscale/tailcat"

addrStr := "tc-ABCD..." // Address obtained from server output
addr, err := tailcat.ParseTailcatAddr(addrStr)
if err != nil {
    log.Fatalf("invalid tailcat address: %v", err)
}

fmt.Printf("Server node key: %x\n", addr.NodeKey)
fmt.Printf("Disco key: %x\n", addr.DiscoKey)
if addr.PSK != nil {
    fmt.Printf("PSK present (hex): %x\n", addr.PSK)
}

```

## Summary

- A **tailcat address** encodes the server's `NodeKey` (WireGuard public key), `DiscoKey` (discovery public key), and optional `PSK` into a single URL-safe string.
- The **NodeKey** (defined in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) lines 146–166) provides primary tunnel authentication, while the **DiscoKey** (serialized in `MarshalBinary` lines 174–182) enables secure DERP routing without DNS exposure.
- The **PresharedKey** (conditionally appended in lines 188–196) adds a layer of post-quantum resistance and is controlled via the `--psk` CLI flag.
- **DERP region IDs** or embedded maps (parsed in lines 1030–1080) tell clients which relay to contact for initial connection.
- The **version prefix** (`tc` + version byte) ensures backward compatibility as specified in `ParseTailcatAddr`.

## Frequently Asked Questions

### What is the difference between NodeKey and DiscoKey in a tailcat address?

The **NodeKey** is the server's Curve25519 WireGuard public key used for encrypting the actual data tunnel, while the **DiscoKey** is a separate Curve25519 key used only for the discovery protocol to locate the server via DERP relays. This separation prevents exposure of the encryption key during the routing discovery phase.

### Is the PresharedKey (PSK) mandatory for tailcat connections?

No, the PSK is optional but enabled by default. When the `--psk` flag is set (the default behavior), a 32-byte random key is appended to the address. You can disable it with `--psk=false` for compatibility with legacy clients (version 0.5.0 and earlier), though this removes the additional security layer against key-compromise attacks.

### How does the DERP region information help establish a connection?

The DERP region ID or embedded map tells the client which encrypted relay server to contact initially when a direct peer-to-peer connection is not yet established. By embedding this data directly in the tailcat address (via `--embed-derp-map`), clients can connect immediately without fetching a separate configuration file from a coordination server.

### Can I decode a tailcat address without the tailcat CLI?

Yes, you can use the Go API by importing `github.com/tailscale/tailcat` and calling `ParseTailcatAddr(addrStr)`. This function, located in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) around line 1030, returns a struct containing the `NodeKey`, `DiscoKey`, `PSK`, and `RegionID` fields as byte slices and integers.