What Is the gVisor Netstack in Tailcat? A Deep Dive into Userspace TCP/IP Networking

The gVisor netstack in Tailcat is a userspace TCP/IP implementation—derived from Google's gVisor project—that enables secure, kernel-less networking by running a complete network stack inside the Go process without requiring root privileges or host network configuration changes.

Tailcat, an experimental sibling project to Tailscale, embeds this netstack at its core to provide end-to-end encrypted TCP connections over WireGuard without ever touching the operating system's kernel networking layer. This architecture enables Tailcat to run sandboxed, portable binaries that work identically across Linux, macOS, Windows, and even WebAssembly environments.

How Tailcat Implements the gVisor Netstack Architecture

The netstack integration lives in tailscale.com/wgengine/netstack, a dependency that Tailcat imports and wraps for its specific use cases. Unlike traditional networking tools that create TUN/TAP devices or modify routing tables, Tailcat instantiates the netstack entirely within user memory.

In tailcat.go, the server struct holds a pointer to the netstack implementation:

// tailcat.go L339
ns *netstack.Impl

The netstack is created during server startup via newNetstack, configured with IP addresses derived from the WireGuard public key. This eliminates the need for static IP assignment or DHCP negotiation.

Key Source File References

Tailcat's netstack integration spans several critical files:

  • tailcat.go: Import statement establishing the gVisor netstack dependency
  • tailcat.go: Helper that extracts the underlying TCP/IP stack from the netstack abstraction
  • internal/buildtags/buildtags.go: Build tag declarations ensuring netstack code is always linked, including WebAssembly builds

The build tags are particularly significant—the ts_omit_netstack tag exists but is intentionally not used in Tailcat releases, guaranteeing the netstack remains available across all compilation targets.

Internal Packet Flow Through the Netstack

Understanding how packets move through Tailcat requires tracing four distinct stages:

1. Server Initialization

Tailcat creates a WireGuard engine with an ephemeral keypair, then calls netstack.Create to obtain a *netstack.Impl. The resulting netstack instance receives default IP addresses computed from the WireGuard public key, enabling automatic, deterministic addressing without configuration.

2. Transport Layer Encryption

The magicsock component—shared with the production Tailscale client—handles all packet transport. Magicsock selects between direct UDP connections and DERP relays for NAT traversal, feeding encrypted WireGuard packets into the netstack's receive path.

3. TCP State Machine Processing

Incoming packets pass through the gVisor-derived TCP implementation, which maintains full RFC-compliant connection state. When a listener binds to a port via the OnTCP callback, the netstack invokes the user-provided handler with a standard net.Conn interface.

4. Outbound Connection Handling

Client-side dials through DialTCPPort create new TCP connections within the netstack, with traffic flowing symmetrically through the WireGuard tunnel back to the server's netstack instance.

This design decouples the TCP/IP protocol implementation from the underlying transport, allowing Tailcat to tunnel arbitrary TCP traffic without kernel involvement.

Practical Code Examples

Minimal Echo Server

This server responds with a greeting on any TCP port:

package main

import (
	"fmt"
	"log"
	"net"

	"github.com/tailscale/tailcat"
)

func main() {
	s := &tailcat.Server{
		OnTCP: func(port uint16) func(net.Conn) {
			return func(c net.Conn) {
				fmt.Fprintf(c, "hello from port %v\n", port)
				c.Close()
			}
		},
	}
	if err := s.Start(); err != nil {
		log.Fatal(err)
	}
	fmt.Println(s.TailcatAddr()) // prints the tailcat address
	select {}                     // block forever
}

The OnTCP callback registers a factory function that returns connection handlers—the netstack invokes this whenever a remote peer connects to a listened port.

Minimal Client Connection

This client connects to a Tailcat server and reads the response:

package main

import (
	"context"
	"io"
	"log"
	"os"

	"github.com/tailscale/tailcat"
)

func main() {
	// Pass the tailcat address as the first CLI argument.
	cl := tailcat.NewClient(tailcat.Addr(os.Args[1]))
	defer cl.Close()

	c, err := cl.DialTCPPort(context.Background(), 80) // connect to port 80
	if err != nil {
		log.Fatal(err)
	}
	io.Copy(os.Stdout, c) // prints "hello from port 80"
}

Both examples appear in the official repository's README at README.md, demonstrating the library's intended public API surface.

WebAssembly Portability via Pure Go Netstack

A critical advantage of gVisor's userspace implementation is complete portability to WebAssembly. Because the netstack contains no cgo dependencies or platform-specific syscalls, Tailcat compiles to GOOS=js GOARCH=wasm without modification.

In web/main_js.go, the browser-based demo reads directly from the netstack when users interact with the UI:

// web/main_js.go L214 - netstack integration in WASM builds

This enables Tailcat to run as an in-browser peer, participating in the same encrypted mesh network as native CLI clients—a capability impossible with kernel-dependent networking stacks.

Security and Privilege Model

The gVisor netstack fundamentally reshapes Tailcat's security posture:

  • No root required: The binary operates without elevated privileges, eliminating a common deployment barrier
  • Host network isolation: The process never modifies routing tables, DNS configuration, or firewall rules
  • Containment boundaries: Network processing occurs in memory-safe Go code rather than kernel C code
  • Predictable attack surface: The TCP/IP implementation is a specific, auditable version rather than the host kernel's potentially variant stack

Summary

  • The gVisor netstack in Tailcat is a userspace TCP/IP implementation providing complete network protocol handling without kernel dependencies
  • Core integration lives in tailcat.go, with the netstack pointer stored at line 339 and creation logic in newNetstack
  • Magicsock and WireGuard provide the encrypted transport layer, while the netstack terminates TCP connections
  • Build tags in internal/buildtags/buildtags.go ensure netstack availability across all targets including WebAssembly
  • The architecture enables privileged-free operation, cross-platform portability, and browser-based deployment impossible with traditional networking approaches

Frequently Asked Questions

Does Tailcat require root privileges because it implements its own TCP/IP stack?

No. Because the gVisor netstack runs entirely in userspace, Tailcat operates without root privileges or capabilities. The stack processes packets in Go memory rather than through kernel networking APIs, eliminating the traditional requirement for elevated permissions when creating network interfaces or modifying routing tables.

How does the netstack differ from using a TUN device?

A TUN device would require kernel cooperation and typically elevated privileges to create. The gVisor netstack bypasses this entirely—packets flow directly from magicsock into the userspace TCP implementation without ever entering the host kernel's network stack. This enables deployment in restricted environments like containers without CAP_NET_ADMIN or browsers without any device access.

Can I use Tailcat's netstack with my own WireGuard implementation?

The *netstack.Impl type in tailcat.go is specifically wired to Tailscale's magicsock and WireGuard engine. While the underlying tailscale.com/wgengine/netstack package exposes generic interfaces, Tailcat's integration couples these components for seamless operation. Direct reuse would require reimplementing the glue logic found in tailcat.go.

Why gVisor specifically rather than another userspace stack?

Google's gVisor project provides a production-tested, RFC-compliant TCP/IP implementation written in Go with explicit sandboxing goals. Its netstack component offers the memory safety, licensing compatibility, and active maintenance that align with Tailscale's requirements. The gVisor netstack also sees wide deployment in Google's production infrastructure, providing confidence in its correctness under load.

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 →