# Core Components of Tailcat's Architecture: How the P2P Tunneling Tool Works

> Explore Tailcat's architecture. Discover its control-plane-free P2P tunneling, leveraging Tailscale's data plane for direct, secure connections without accounts or daemons.

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

---

**Tailcat is a control-plane-free, peer-to-peer TCP tunneling utility that leverages Tailscale's data plane—including WireGuard encryption, DERP relays, and magicsock NAT traversal—to establish direct connections without accounts, persistent daemons, or centralized coordination.**

Tailcat is a lightweight "netcat-like" tool developed by Tailscale that creates encrypted TCP tunnels between peers using only the data plane. Unlike standard Tailscale deployments that require a control server for network management, Tailcat's architecture operates autonomously through compact connection tokens and pre-configured DERP maps. This minimal design enables instant, ephemeral networking suitable for debugging, file transfers, or ad-hoc remote access scenarios.

## Components of Tailcat's Architecture

Tailcat's implementation is deliberately minimal, consisting of tightly coupled components that fit within a single process. The architecture eliminates the need for background services by embedding the entire networking stack—including WireGuard, NAT traversal, and TCP handling—directly into the application binary.

### Server Component

The **Server** acts as the listening endpoint in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) (lines 69–78). It initializes a WireGuard tunnel bootstrapped through a DERP relay and exposes TCP handlers via callback functions.

- **`OnTCP`** — Registers a handler function that accepts inbound TCP connections on specific ports. When a client connects, the server invokes this callback with a standard `net.Conn`.
- **`OnTCPForward`** — Optional callback that enables the server to act as an exit node, forwarding traffic to alternate destinations rather than terminating it locally.

The server generates a **ConnBlob** token upon startup, which encodes its public key and DERP region details required for client connection.

### Client Component

The **Client** ([`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go), lines 99–108) initiates connections using a parsed ConnBlob token. It implements a lazy initialization pattern where the WireGuard engine starts only upon the first dial attempt.

Key APIs include:

- **`Dial`** and **`DialTCP`** — Establish TCP connections to the remote server through the encrypted tunnel.
- **`Ping`** — Verifies connectivity and latency to the server before initiating data transfer.

The client executes a "meow" handshake protocol over DERP to authenticate itself to the server before upgrading to a direct WireGuard connection.

### locoBackend: The Local Backend

**`locoBackend`** ([`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go), lines 105–120) replaces Tailscale's standard `LocalBackend` to provide a control-plane-free experience. This component owns all per-process state, including:

- WireGuard private keys and peer configurations
- DERP map and region selection
- Network map and endpoint discovery state

It drives the underlying **WireGuard engine**, **magicsock** for UDP NAT traversal, and **netstack** for TCP handling. The backend processes "meow" handshake packets via `locoBackend.onMeow`, adding authenticated clients as WireGuard peers without external coordination.

### WireGuard Engine and Netstack

**`wgengine`** is the userspace WireGuard implementation created via `createEngine` ([`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go), lines 57–67). It handles encryption, decryption, and NAT traversal at the UDP layer.

**Netstack** ([`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go), lines 46–55) embeds gVisor's lightweight userspace IP stack within the same process. This allows Tailcat to accept TCP listeners and route traffic without requiring kernel-level WireGuard interfaces or root privileges. The netstack bridges between the WireGuard tunnel and standard Go `net.Conn` interfaces.

### DERP Map and ConnBlob Tokens

The **DERP Map** describes available relay regions for bootstrap connectivity. Rather than querying a control server, Tailcat embeds this information directly into **ConnBlob** tokens.

A **ConnBlob** is a compact, base64url-encoded CBOR token generated in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) (lines 33–43) and parsed in lines 52–66. It contains:

- The server's public WireGuard key
- Region ID for DERP relay selection
- Optional embedded DERP region details for offline operation

This design allows clients to connect using only a short string token, eliminating the need for DNS, mDNS, or external discovery services.

### Discovery and MagicSock

**MagicSock** and the **Discovery (disco)** protocol handle NAT hole punching. When the initial DERP connection establishes, both sides exchange "call-me-maybe" packets advertising their UDP endpoints ([`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go), lines 111–119).

This endpoint advertisement enables the underlying WireGuard engine to upgrade from the relayed DERP connection to a direct peer-to-peer UDP path, minimizing latency and relay bandwidth once the tunnel stabilizes.

### Command-Line Interface and Web Assembly

The **CLI** ([`cmd/tailcat/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/tailcat.go)) provides a thin wrapper around the library, parsing flags to instantiate either a Server or Client and forwarding traffic accordingly.

A **Web Demo** ([`webdemo/webdemo.go`](https://github.com/tailscale/tailcat/blob/main/webdemo/webdemo.go)) compiles the same core library to WebAssembly, demonstrating that the architecture functions within browser sandboxes using the identical Go networking code.

## Connection Establishment Workflow

Tailcat establishes connections through a five-phase handshake that requires no control server:

1. **Bootstrap** — The server initializes via `Server.Start()`, creating the `locoBackend`, WireGuard engine, and netstack. It publishes a ConnBlob encoding its public key and DERP region.

2. **Discovery** — The client receives the ConnBlob and calls `c.ensureStarted()` → `c.initLocked()` → `c.ci.Expand()` to parse the token and expand the DERP map if necessary.

3. **Handshake** — Both sides exchange "meow" and "meowed" packets over the DERP relay. The server invokes `locoBackend.onMeow` to add the client as a validated WireGuard peer.

4. **Direct Path** — MagicSock advertises UDP endpoints through "call-me-maybe" disco packets, allowing WireGuard to establish a direct peer-to-peer tunnel bypassing the relay.

5. **Traffic** — The netstack forwards TCP traffic through the WireGuard tunnel. The server invokes `OnTCP` handlers for incoming connections or `OnTCPForward` for exit-node functionality.

## Implementation Examples

### Running a Tailcat Server

The following minimal implementation starts a server and handles SSH connections on port 22:

```go
package main

import (
    "log"
    "net"
    "tailscale.com/tailcat"
)

func main() {
    var s tailcat.Server
    
    if err := s.Start(); err != nil {
        log.Fatalf("Server.Start: %v", err)
    }
    defer s.Close()

    // Display the connection token for clients
    log.Printf("ConnBlob: %s", s.ConnBlob())

    // Handle TCP connections on port 22
    s.OnTCP = func(port uint16) func(net.Conn) {
        if port == 22 {
            return func(c net.Conn) {
                // Handle SSH traffic here
                defer c.Close()
            }
        }
        return nil // Returns RST for other ports
    }

    select {} // Block forever
}

```

*Key calls*: `s.Start()` initializes the networking stack, while `s.ConnBlob()` produces the base64url-encoded connection token. Source: [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) lines 52–58.

### Connecting a Tailcat Client

Clients use the ConnBlob token to dial specific ports on the server:

```go
package main

import (
    "context"
    "log"
    "tailscale.com/tailcat"
)

func main() {
    // Token obtained from the server
    blob := tailcat.ConnBlob("tc...") // Insert actual token
    
    c := tailcat.NewClient(blob)
    
    // Dial port 22 (SSH) through the encrypted tunnel
    conn, err := c.DialTCPPort(context.Background(), "127.0.0.1:22")
    if err != nil {
        log.Fatalf("DialTCPPort: %v", err)
    }
    defer conn.Close()
    
    // Use conn as standard net.Conn
}

```

*Key calls*: `c.DialTCPPort` triggers lazy initialization via `c.ensureStarted`, which invokes `c.initLocked` and `c.ci.Expand` to bootstrap the WireGuard engine. Source: `Client.ensureStarted` in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) lines 94–102.

### Using the Command-Line Interface

```bash

# Server mode: listen on port 22 and print connection token

tailcat -l -port 22

# Client mode: connect using server token and forward local port

tailcat -connect tcABcd... -dial 127.0.0.1:22

```

The CLI implementation in [`cmd/tailcat/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/tailcat.go) maps these flags directly to the `Server` and `Client` structs described above.

## Key Source Files

Understanding Tailcat's architecture requires familiarity with these specific files in the `tailscale/tailcat` repository:

- **[`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)** — Core library containing `Server`, `Client`, and `locoBackend` implementations. Defines the ConnBlob generation/parsing logic and high-level orchestration.
- **[`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go)** — Minimal CBOR wire format definitions (lines 25–60) for serializing ConnBlob data structures containing server public keys and DERP regions.
- **[`cmd/tailcat/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/tailcat.go)** — Command-line interface that translates flags into library calls for server and client modes.
- **[`webdemo/webdemo.go`](https://github.com/tailscale/tailcat/blob/main/webdemo/webdemo.go)** — WebAssembly target demonstrating browser-based operation of the same networking stack.
- **[`disco.go`](https://github.com/tailscale/tailcat/blob/main/disco.go)** — Wrapper around Tailscale's discovery protocol for endpoint advertisement and NAT traversal coordination.

## Summary

- **Tailcat's architecture eliminates the control plane** by embedding DERP maps and public keys directly into ConnBlob tokens, enabling connection without accounts or background daemons.
- **The `locoBackend` replaces Tailscale's LocalBackend** to manage WireGuard keys, peer state, and network maps entirely within the process.
- **Connection bootstrap uses CBOR-encoded tokens** (base64url) rather than DNS or mDNS, allowing offline-capable peer discovery.
- **MagicSock and D relays enable NAT traversal**, automatically upgrading from relayed connections to direct peer-to-peer WireGuard tunnels after the "meow" handshake completes.
- **Netstack integration** provides userspace TCP handling without requiring kernel modules or root privileges, making the tool portable across operating systems and WASM environments.

## Frequently Asked Questions

### Does Tailcat require a Tailscale account or control server?

No. Tailcat operates entirely without a control plane. The `locoBackend` implementation manages keys and peer state locally, while ConnBlob tokens (generated in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) lines 33–43) embed all necessary connection information. This allows two peers to establish encrypted tunnels without authentication servers, persistent daemons, or network infrastructure beyond the DERP relay required for initial NAT traversal.

### What is contained within a ConnBlob token?

A ConnBlob is a compact, base64url-encoded CBOR structure defined in [`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go) (lines 25–60). It minimally contains the server's public WireGuard key and DERP region identifier. Optionally, it may embed complete DERP region details, allowing clients to connect without fetching external DERP maps. Clients parse these tokens via `ConnInfo.Expand` to reconstruct the network parameters needed for dialing.

### How does Tailcat differ from standard netcat or SSH port forwarding?

Unlike traditional netcat, Tailcat encrypts all traffic using WireGuard and automatically handles NAT traversal. Unlike SSH, it requires no installed daemon, no authentication keys beyond the ephemeral WireGuard keypairs, and no listening ports on the public internet. The "meow" handshake and magicsock layer in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) (lines 111–119) coordinate direct P2P connections that bypass intermediate servers once established, whereas SSH tunnels typically route through intermediaries.

### Can Tailcat run in a web browser?

Yes. The [`webdemo/webdemo.go`](https://github.com/tailscale/tailcat/blob/main/webdemo/webdemo.go) file compiles the complete Tailcat library—including WireGuard, netstack, and DERP client—to WebAssembly. This demonstrates that the architecture's userspace networking approach (gVisor netstack rather than kernel interfaces) functions within browser security sandboxes, enabling P2P TCP tunnels directly from web applications without plugins or native code installation.