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

> Understand the gVisor netstack in Tailcat, a userspace TCP/IP implementation for secure kernel-less networking. Learn how it runs networking in Go without root.

- Repository: [Tailscale/tailcat](https://github.com/tailscale/tailcat)
- Tags: deep-dive
- Published: 2026-09-06

---

**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`](https://github.com/tailscale/tailcat/blob/main/tailcat.go), the server struct holds a pointer to the netstack implementation:

```go
// 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`](https://github.com/tailscale/tailcat/blob/main/tailcat.go#L94-L95)**: Import statement establishing the gVisor netstack dependency
- **[`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go#L885-L888)**: Helper that extracts the underlying TCP/IP stack from the netstack abstraction
- **[`internal/buildtags/buildtags.go`](https://github.com/tailscale/tailcat/blob/main/internal/buildtags/buildtags.go#L27-L31)**: 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:

```go
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:

```go
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`](https://github.com/tailscale/tailcat/blob/main/README.md#L996-L1028), 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`](https://github.com/tailscale/tailcat/blob/main/web/main_js.go#L214), the browser-based demo reads directly from the netstack when users interact with the UI:

```go
// 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`](https://github.com/tailscale/tailcat/blob/main/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`](https://github.com/tailscale/tailcat/blob/main/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`](https://github.com/tailscale/tailcat/blob/main/tailcat.go#L339) 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`](https://github.com/tailscale/tailcat/blob/main/tailcat.go#L885-L888).

### 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.