# How gVisor Netstack Powers Tailcat’s Userspace Networking Architecture

> Discover how gVisor Netstack empowers Tailcat with kernel-independent, userspace networking over WireGuard tunnels using a pure-Go TCP/IP implementation.

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

---

**gVisor Netstack provides Tailcat with a pure-Go TCP/IP implementation that runs entirely in userspace, enabling kernel-independent networking over encrypted WireGuard tunnels while exposing standard Go `net.Conn` interfaces.**

Tailcat leverages the `tailscale/tailcat` repository to build a portable networking layer that operates without host OS dependencies. The **gVisor Netstack** serves as the foundational transport layer, handling TCP and UDP protocols atop Tailscale’s WireGuard data plane through the `gvisor.dev/gvisor/pkg/tcpip` packages.

## Core Role of gVisor Netstack in Tailcat

Tailcat constructs a complete userspace network stack on top of Tailscale’s encrypted WireGuard tunnel. Rather than using the host kernel’s networking stack, Tailcat imports `gvisor.dev/gvisor/pkg/tcpip/stack` to implement TCP and UDP transport protocols directly in Go. This design centers on the `netstack.Impl` type, which wraps gVisor’s `*stack.Stack` and exposes it through the `gonet` adapter.

## Implementing the gVisor Netstack

The integration of gVisor Netstack into Tailcat follows three architectural patterns: stack instantiation, interface adaptation, and lifecycle management.

### Creating the Userspace Stack Instance

In [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go), the `newNetstack` function instantiates the network layer by calling `netstack.Create()`. This constructor wires the gVisor TCP/IP implementation with Tailcat’s logging, DNS resolution, and routing tables.

```go
// tailcat.go - Instantiating the gVisor-based netstack
func newNetstack(logf logger.Logf, sys *tsd.System) (*netstack.Impl, error) {
    // netstack.Create wires together the gVisor TCP/IP stack,
    // DNS resolver, and routing tables.
    return netstack.Create(logf,
        sys,
        // optional configuration fields omitted for brevity
    )
}

```

The returned `*netstack.Impl` serves as the primary handle for all subsequent networking operations within the encrypted tunnel.

### Bridging to Standard Go Networking Interfaces

Tailcat exposes gVisor endpoints as standard Go interfaces through the `gonet` adapter located at `gvisor.dev/gvisor/pkg/tcpip/adapters/gonet`. This translation layer allows high-level features—such as the SSH server, SOCKS proxy, and port-forwarding logic—to operate using familiar `net.Conn` and `net.Listener` abstractions without kernel involvement.

The test suite in [`tailcat_test.go`](https://github.com/tailscale/tailcat/blob/main/tailcat_test.go) demonstrates TCP dialing through the netstack:

```go
// tailcat_test.go - Dialing TCP via gVisor's gonet adapter
clientStack := ... // *netstack.Impl obtained from newNetstack
addr := tcpip.FullAddress{
    NIC:    1,
    Addr:   netip.AddrFrom4([4]byte{127, 0, 0, 1}),
    Port:   8080,
}
conn, err := gonet.DialTCP(clientStack, addr)
if err != nil { log.Fatalf("dial failed: %v", err) }
defer conn.Close()

```

For incoming traffic, Tailcat binds listeners using `netstack.ListenTCP`, which returns objects implementing `net.Listener`:

```go
// Listening for incoming SSH connections through the netstack
ln, err := netstack.ListenTCP(ns, 1, netip.AddrPortFrom(netip.AddrFrom4([4]byte{0,0,0,0}), 22))
if err != nil { log.Fatalf("listen failed: %v", err) }
for {
    c, err := ln.Accept()
    if err != nil { break }
    go handleSSH(c) // `c` implements net.Conn
}

```

### Managing Connection Lifecycles

Tailcat implements graceful shutdown by directly manipulating the gVisor stack’s connection tables. The `DrainTCP` method, defined in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go), iterates through active TCP streams to close them gracefully during server termination.

```go
// tailcat.go - Graceful shutdown via DrainTCP
func (s *Server) Close() error {
    // DrainTCP walks the netstack’s connection tables and closes them.
    if err := s.ns.DrainTCP(context.Background()); err != nil {
        return err
    }
    return s.ns.Close()
}

```

For low-level operations such as packet injection or statistics gathering, the helper `tcpipStackOf(ns *netstack.Impl) *stack.Stack` extracts the underlying gVisor `*stack.Stack` from the Tailcat wrapper.

## Architectural Benefits of gVisor Netstack

Delegating transport handling to gVisor Netstack provides Tailcat with specific platform and protocol advantages:

- **Kernel-independent operation**: All TCP/UDP processing executes in userspace through the `netstack.Impl` layer, eliminating platform-specific networking code and enabling WebAssembly compilation targets.
- **Full TCP semantics**: The gVisor implementation provides congestion control, retransmission logic, and socket options matching Linux kernel behavior, ensuring RFC-compliant transport protocols.
- **Network isolation**: The stack operates exclusively atop the encrypted Tailscale WireGuard tunnel, maintaining strict separation between Tailcat traffic and the host’s native networking interfaces.
- **Cross-platform portability**: As pure Go code within `gvisor.dev/gvisor/pkg/tcpip`, the netstack compiles identically for Windows, macOS, Linux, Android, and WebAssembly without conditional compilation.

## Critical Source Files

Understanding Tailcat’s networking requires examining these specific files in the `tailscale/tailcat` repository:

- **[`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)**: Contains the `newNetstack` constructor, `tcpipStackOf` helper for accessing raw gVisor primitives, and the `DrainTCP` lifecycle management function.
- **[`tailcat_test.go`](https://github.com/tailscale/tailcat/blob/main/tailcat_test.go)**: Demonstrates netstack usage patterns via `gonet.DialTCP` and verifies proper connection cleanup behavior.
- **[`cmd/tailcat/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/tailcat.go)**: Shows how the CLI leverages the netstack for SSH server functionality, port-forwarding, and the "pipe" mode networking.
- **`tailscale.com/wgengine/netstack/*`** (vendor directory): Houses the actual `netstack.Create` implementation and gVisor integration logic.

## Summary

- **gVisor Netstack** provides Tailcat’s complete TCP/IP implementation in userspace, enabling kernel-independent operation over WireGuard tunnels.
- The `netstack.Impl` type in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) wraps gVisor’s `*stack.Stack` and exposes standard Go networking interfaces via the `gonet` adapter from `gvisor.dev/gvisor/pkg/tcpip/adapters/gonet`.
- Connection lifecycle management uses `DrainTCP` to gracefully terminate active streams by walking the gVisor connection tables during shutdown.
- This architecture allows Tailcat to offer full TCP/UDP networking on any platform supporting Go, including WebAssembly, while maintaining complete isolation from host network stacks.

## Frequently Asked Questions

### Why does Tailcat use gVisor Netstack instead of the host kernel?

Tailcat uses gVisor Netstack to achieve **kernel-independent networking** that operates entirely within the userspace process. According to the `tailscale/tailcat` source code, this design allows the application to run on platforms without native TCP/IP support—such as WebAssembly—and ensures all traffic remains isolated within the encrypted WireGuard tunnel, separate from the host’s network configuration.

### How does Tailcat handle TCP congestion control without kernel assistance?

The gVisor Netstack implements **full TCP semantics** including congestion control algorithms, retransmission timers, and socket options equivalent to the Linux kernel. As implemented in the `gvisor.dev/gvisor/pkg/tcpip/stack` package imported by Tailcat, these protocols operate entirely within the Go runtime, providing RFC-compliant transport behavior without system calls.

### Can Tailcat’s networking stack run on WebAssembly?

Yes. Because gVisor Netstack is implemented in pure Go without CGO or kernel dependencies, Tailcat compiles to WebAssembly targets where standard OS networking is unavailable. The `gonet` adapter ensures that higher-level code continues to use standard `net.Conn` interfaces regardless of the underlying platform constraints.

### How does Tailcat gracefully close active connections during shutdown?

Tailcat calls the `DrainTCP` method on the `netstack.Impl` instance in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go), which iterates through the gVisor stack’s internal connection tables to close all active TCP streams. This ensures proper TCP teardown sequences—sending FIN packets and waiting for acknowledgments—before the process terminates, preventing connection resets for connected clients.