How the Tailcat Meow Discovery Handshake Works: A Deep Dive into WireGuard Bootstrapping

The Tailcat Meow discovery handshake is a lightweight, two-packet protocol that bootstraps WireGuard tunnels by exchanging public keys over raw DERP packets, enabling peer registration and verification in a single round-trip.

The Meow discovery handshake is the foundation of Tailcat's peer-to-peer connection establishment. Unlike standard Tailscale discovery, this protocol operates directly on raw DERP payloads to register clients as WireGuard peers before any full network map exchange occurs.

Overview of the Meow Discovery Protocol

The handshake consists of three distinct phases executed over the same UDP relay infrastructure used by Tailscale. The protocol uses a 4-byte magic prefix "meow" followed by packet-type identifiers to distinguish control messages. According to the tailscale/tailcat source code, the entire exchange completes in a single round-trip while accomplishing both peer registration and cryptographic verification.

Step-by-Step Handshake Implementation

Step 1: Client Initiates Meow Ping

The client begins by constructing a MeowPing packet containing its node public key and derived disco public key. In tailcat.go at lines 1670-1671, the EncodeMeowPing function serializes these keys into a raw DERP payload.

The packet structure follows a strict binary format:

  • 4-byte magic prefix: meow
  • 1-byte type identifier: 0x01
  • Client node public key
  • Client disco public key

The client transmits this packet via MagicSock's SendDERPPacketTo method, targeting the server's node public key.

func (c *Client) pingMeow(ctx context.Context) (PingResult, error) {
    // Prepare a channel that will be closed when the server replies
    meowWait := make(chan struct{})
    c.meowWait = meowWait

    // Build the MeowPing packet (client's node key + disco key)
    pkt := tailcat.EncodeMeowPing(c.lb.pub, mc.DiscoPublicKey())

    // Send it over DERP to the server's node public key
    sent, err := mc.SendDERPPacketTo(dstNode, derpRegion, pkt)
    if err != nil { return zero, fmt.Errorf("sending meow: %w", err) }
    if !sent { return zero, fmt.Errorf("meow not sent") }

    // Wait for the Meowed acknowledgment or a timeout
    select {
    case <-meowWait:
        return PingResult{time.Since(t0)}, nil
    case <-ctx.Done():
        return zero, ctx.Err()
    }
}

Step 2: Server Processes the Ping

When the server's MagicSock receives the DERP packet, it forwards the payload to locoBackend.onMeow. The server first validates the packet using IsMeowPacket in disco.go (lines 25-27), then extracts the keys via ParseMeowPing (lines 49-62).

If the client is authorized (or allowedClients is nil), the server registers the client as a WireGuard peer in its network map:

func (b *locoBackend) onMeow(src key.NodePublic, discoPub key.DiscoPublic) bool {
    // Register the client as a WireGuard peer (if not already present)
    if _, ok := b.clients[src]; !ok {
        id := len(b.clients) + 2 // IDs start at 2 for clients
        b.clients[src] = &tailcfg.Node{
            ID: tailcfg.NodeID(id),
            Key: src,
            DiscoKey: discoPub,
            // … other fields omitted …
        }
    }

    // Update the MagicSock network map so the client can see its peer
    mc := b.sys.MagicSock.Get()
    mc.SetNetworkMap(b.nm.SelfNode, b.nm.Peers)

    // Send the Meowed (ack) packet back to the client
    mc.SendDERPPacketTo(src, regionID, tailcat.EncodeMeowed())
    return true
}

Step 3: Server Responds with Meow Pong

After updating the network map and MagicSock state, the server sends a Meow Pong (acknowledgment) packet. The EncodeMeowed function generates the response, transmitted via SendDERPPacketTo at tailcat.go:428-430.

Completion: Client Receives Acknowledgment

The client's pingMeow routine waits on the meowWait channel. When the MagicSock layer receives the Meow Pong packet—recognized by IsMeowedPacket in disco.go (lines 64-66)—it closes the channel at tailcat.go:1502-1504, unblocking the waiting goroutine.

The client then returns a successful PingResult, confirming the handshake completed at tailcat.go:1679-1684.

Key Source Files and Architecture

The Meow discovery handshake spans four critical files in the tailscale/tailcat repository:

  • disco.go — Defines the Meow packet format, magic constants, and serialization functions (EncodeMeowPing, EncodeMeowed, ParseMeowPing, IsMeowPacket, IsMeowedPacket).

  • tailcat.go — Implements the client-side pingMeow method, server-side onMeow handler, and MagicSock callback wiring.

  • tailcat_test.go — Contains integration tests verifying that clients receive Meowed packets after sending Meow pings.

  • wire.go — Provides low-level DERP packet transport utilities used by MagicSock for raw packet transmission.

Summary

  • The Meow discovery handshake bootstraps WireGuard tunnels using raw DERP packets rather than wrapped disco protocol messages.
  • The protocol requires exactly two packets: Meow Ping (client→server) and Meow Pong (server→client).
  • Peer registration and verification occur simultaneously in a single round-trip.
  • The disco.go file handles packet format constants and parsing, while tailcat.go manages the state machine and network map updates.
  • Channels (meowWait) coordinate asynchronous packet receipt with synchronous handshake completion.

Frequently Asked Questions

What is the Meow protocol in Tailcat?

The Meow protocol is a lightweight discovery mechanism that initializes WireGuard peer relationships before standard Tailscale disco protocol negotiation begins. It uses raw DERP packets with a "meow" magic prefix to exchange public keys and register clients with servers efficiently.

How does the Meow handshake differ from standard Tailscale discovery?

Standard Tailscale discovery relies on the higher-level disco protocol wrapped in DERP framing, whereas the Meow handshake uses raw DERP payloads. This allows Tailcat to establish peer mappings and verify connectivity with minimal overhead before engaging full network map synchronization.

What is the purpose of the "meow" magic prefix?

The 4-byte "meow" prefix serves as a protocol discriminator in disco.go, allowing the packet receiver to quickly identify Meow control packets without parsing the entire payload. This magic constant precedes a 1-byte type field (0x01 for ping, 0x02 for pong) that determines the packet's semantic meaning.

Where is the Meow handshake implemented in the Tailcat source code?

The handshake implementation is distributed across tailcat.go (client pingMeow and server onMeow logic) and disco.go (packet encoding/decoding). Key line references include the ping construction at tailcat.go:1670-1675, acknowledgment handling at tailcat.go:1502-1504, and packet validation utilities at disco.go:25-66.

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 →