What Is the Meow Handshake in Tailcat? A Deep Dive into the WireGuard Bootstrap Protocol

The Meow handshake is a lightweight, stateless DERP-level protocol that bootstraps WireGuard peer relationships by exchanging node and disco public keys between Tailcat clients and servers.

Tailcat, an experimental networking component within the Tailscale ecosystem, implements a custom bootstrapping mechanism to dynamically establish encrypted connections. Unlike traditional WireGuard deployments that require static peer configuration, Tailcat uses the Meow handshake to provision peers on-the-fly when a client first contacts a server over the DERP (Detour Encrypted Routing Protocol) relay infrastructure.

Why the Meow Handshake Is Necessary

When a Tailcat client initiates contact with a Tailcat server, neither side possesses WireGuard configuration data for the other. Static configuration files are impractical for ephemeral or dynamic mesh networks where nodes frequently join and leave.

The Meow handshake solves this by performing a stateless, two-packet exchange directly over DERP. It carries each side’s node public key (long-term identity) and disco public key (discovery mechanism) in a compact binary format. This allows the server to create a WireGuard peer entry for the client without any prior configuration, enabling immediate encrypted communication.

How the Meow Handshake Works

The handshake consists of a ping request from the client and a pong acknowledgment from the server. Both packets travel as raw DERP payloads, bypassing higher-level protocol framing.

Client to Server: Sending the MeowPing

The client builds the initial handshake packet using EncodeMeowPing() defined in disco.go. The packet structure begins with a 4-byte magic string "meow", followed by a 1-byte type identifier (0x01 for ping), and the concatenated public keys.

In tailcat.go, the client constructs and transmits the packet as a raw DERP frame:

pkt := tailcat.EncodeMeowPing(c.lb.pub, mc.DiscoPublicKey())
c.lb.derpConn.Write(pkt) // raw DERP packet, not disco-framed

This sends the node public key (c.lb.pub) and disco public key (mc.DiscoPublicKey()) to the server.

Server Processing and Peer Registration

On the server side, incoming DERP packets are inspected in locoBackend.handleDERP within tailcat.go. The server identifies Meow packets using IsMeowPacket():

if tailcat.IsMeowPacket(pkt) {
    nodeKey, discoKey, ok := tailcat.ParseMeowPing(pkt)
    if ok && b.onMeow(nodeKey, discoKey) {
        b.mc.SendDERPPacketTo(src, regionID, tailcat.EncodeMeowed())
    }
    return true
}

The onMeow method (located at line 1347-1351 in tailcat.go) performs three critical actions:

  1. Validates the client’s access rights
  2. Allocates a unique WireGuard node ID and registers the peer in the server’s client map
  3. Rebuilds the network map and pushes it to MagicSock for immediate use

Server to Client: The Meowed Acknowledgment

If the client passes access checks, the server responds with a Meowed acknowledgment packet (type 0x02). The server calls EncodeMeowed() to create a minimal response indicating successful peer registration:

b.mc.SendDERPPacketTo(src, regionID, tailcat.EncodeMeowed())

The client recognizes this acknowledgment via IsMeowedPacket(), which checks for the meowTypePong constant (0x02). Upon receipt, the client considers the handshake complete. The WireGuard engine lazily learns the new peer during the next traffic exchange.

Packet Structure and Key Implementation Files

The Meow protocol implementation spans several files in the tailscale/tailcat repository:

disco.go

Contains the wire format definitions and parsing logic. Key symbols include:

  • meowMagic: The 4-byte header []byte("meow")
  • EncodeMeowPing(): Builds the initial handshake packet (lines 30-38)
  • ParseMeowPing(): Extracts node and disco keys from inbound packets
  • IsMeowPacket() and IsMeowedPacket(): Packet type validators (lines 25-27)

tailcat.go

Implements the handshake orchestration and peer lifecycle:

  • onMeow(): Server-side handler for peer registration (lines 1347-1351)
  • Client-side transmission logic around line 1765
  • DERP packet routing and state management

wire.go

Provides low-level DERP I/O primitives:

  • SendDERPPacketTo(): Transmits raw packets to specific DERP regions
  • ReadDERPPacket(): Receives incoming DERP frames

Code Example: Complete Server-Side Handler

The following snippet from tailcat.go demonstrates the complete server-side handshake flow:

func (b *locoBackend) onMeow(src key.NodePublic, discoPub key.DiscoPublic) bool {
    // Access control checks omitted for brevity
    id := len(b.clients) + 2 // allocate unique WireGuard node ID
    b.clients[src] = &tailcfg.Node{
        ID:       tailcfg.NodeID(id),
        Key:      src,
        DiscoKey: discoPub,
        // Address assignment and endpoint configuration...
    }
    // Rebuild network map and push to MagicSock
    b.rebuildNetworkMap()
    return true // Signal to send "meowed" ack
}

After onMeow returns true, the server transmits the acknowledgment, and both sides maintain the peer mapping for subsequent encrypted communication over DERP or direct WireGuard tunnels.

Summary

  • The Meow handshake is a custom DERP-level protocol in Tailcat that enables dynamic WireGuard peer creation without static configuration.
  • Packet format uses a 4-byte "meow" magic header, 1-byte type field (0x01 for ping, 0x02 for pong), and 64 bytes of public key material.
  • Implementation resides primarily in disco.go (wire format) and tailcat.go (handshake logic).
  • Stateless operation requires only two packets (MeowPing and Meowed), ensuring minimal latency during connection bootstrap.
  • Security relies on WireGuard’s existing cryptokey routing; the handshake merely transmits public keys that would otherwise require out-of-band distribution.

Frequently Asked Questions

What does the name "Meow" refer to in Tailcat?

The name derives from the 4-byte magic constant []byte("meow") that prefixes every handshake packet. This magic string identifies the protocol as a Tailcat-specific DERP extension rather than standard Tailscale disco traffic or regular WireGuard packets.

Is the Meow handshake encrypted?

The handshake itself is sent as a raw DERP packet, which benefits from DERP’s transport-layer encryption (usually TLS). However, the Meow payload contains only public keys, which are non-sensitive by design. The actual WireGuard session keys are derived separately using the Noise protocol after the handshake completes.

How does Tailcat handle handshake failures or packet loss?

The Meow handshake is stateless and idempotent. If the MeowPing is lost, the client will retransmit it during subsequent connection attempts. If the Meowed acknowledgment is lost, the client may retry, and the server will regenerate the peer entry (overwriting the previous allocation) since onMeow simply re-registers the node key if it sees a duplicate request.

Can the Meow handshake be used over direct connections?

No. The Meow handshake is specifically designed for DERP relay communication when direct UDP connectivity is unavailable. The packet format and SendDERPPacketTo API assume DERP region routing. Once the handshake completes and WireGuard peers are configured, Tailcat attempts to establish a direct WireGuard tunnel using standard NAT traversal techniques.

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 →