Tailscale — Structure#
Note on analysis scope: The local checkout is a sparse clone — approximately 1,438 of the full ~2,083
.gofiles are present on disk. Missing directories (wgengine/,tsnet/,types/,util/,tsd/,tsweb/,tstest/,tstime/,tool/,version/,wf/,wif/,tsconsensus/,tsconst/,words/) were inspected viagit showandgit ls-tree. The structure analysis covers the full repository.
Layout pattern#
Custom flat-domain layout (monorepo product suite)
Tailscale does not follow the standard Go layout (cmd/ + internal/ + pkg/). Instead, every domain area lives as a top-level or shallow directory — net/, ipn/, control/, derp/, tka/, tailcfg/, etc. — all exported at tailscale.com/<name>. The internal/ directory exists but is tiny (2 sub-packages). This is a deliberate choice: the vanity module path tailscale.com means the entire module is treated as a stable public library API. Third-party projects and Tailscale’s own mobile apps all import packages directly from this module.
The repo also functions as a monorepo for the complete Tailscale product: VPN daemon, CLI, DERP relay server, Kubernetes operator, SSH server, OIDC provider, embedded library (tsnet), and dozens of ancillary tools — all in one go.mod.
Directory map#
tailscale/
├── cmd/ # 50+ binary entry points (tailscale, tailscaled, derper, k8s-operator, …)
├── ipn/ # IPN daemon: state machine, local backend, server, API
│ ├── ipnlocal/ # LocalBackend — the central daemon orchestrator
│ ├── ipnserver/ # Unix socket server, IPN protocol
│ ├── localapi/ # LocalAPI HTTP server (used by tailscale CLI)
│ ├── store/ # State persistence backends (file, AWS, kube, mem)
│ ├── ipnauth/ # Authentication helpers
│ ├── ipnext/ # Extension points for daemon subsystems
│ └── conffile/ # Declarative config file support
├── wgengine/ # WireGuard engine interface + userspace implementation [git only]
│ ├── magicsock/ # MagicSock: multi-path UDP with DERP fallback
│ ├── filter/ # Packet filter (firewall)
│ ├── netstack/ # Userspace network stack integration
│ ├── router/ # OS kernel route management
│ ├── wgcfg/ # WireGuard configuration types
│ └── wgint/ # WireGuard internal helpers
├── control/ # Control plane client
│ ├── controlclient/ # Long-poll client to Tailscale coordination server
│ ├── controlhttp/ # HTTP transport for control protocol
│ ├── controlbase/ # Noise-based session layer (ts2021 protocol)
│ └── ts2021/ # ts2021 protocol implementation
├── derp/ # DERP relay protocol (Designated Encrypted Relay for Packets)
│ ├── derpserver/ # DERP server implementation
│ ├── derphttp/ # DERP over HTTP(S) client/server
│ ├── derpconst/ # Protocol constants
│ └── xdp/ # XDP/eBPF acceleration for DERP
├── net/ # Networking library (~40 sub-packages)
│ ├── dns/ # DNS manager + resolver
│ ├── netcheck/ # NAT type detection / reachability
│ ├── portmapper/ # UPnP, NAT-PMP, PCP port mapping
│ ├── tstun/ # TUN device wrapper
│ ├── tsdial/ # Tailscale dialer
│ ├── netmon/ # Network interface monitor
│ ├── art/ # Allotment Routing Table (IP route lookup)
│ ├── packet/ # Packet parsing
│ └── stun/ # STUN protocol
├── tailcfg/ # Wire format types: NodeView, NetworkMap, DERPMap, …
├── tsd/ # System container: dependency wiring for all subsystems [git only]
├── tsnet/ # Embedded Tailscale library for user applications [git only]
├── tka/ # Tailscale Key Authority (distributed trust log)
├── types/ # Core shared types: key, logger, netmap, prefs, … [git only]
├── util/ # ~50 utility packages: deephash, eventbus, lru, dnsname, … [git only]
├── feature/ # Compile-time feature flag system
│ ├── buildfeatures/ # Generated _enabled/_disabled file pairs (~100 features)
│ └── condregister/ # Conditional feature registration via blank imports
├── k8s-operator/ # Kubernetes operator implementation
│ ├── apis/ # CRD API types
│ ├── reconciler/ # Kubernetes reconcilers
│ └── sessionrecording/ # SSH session recording integration
├── kube/ # Kubernetes client and support packages
├── ssh/tailssh/ # Tailscale SSH server
├── client/ # API clients
│ ├── tailscale/ # Go client library for the Tailscale HTTP API
│ ├── local/ # Local daemon IPC client
│ └── web/ # Web client (browser UI)
├── appc/ # App Connectors subsystem
├── drive/driveimpl/ # Tailscale Drive (file sharing) implementation
├── sessionrecording/ # SSH session recording
├── logtail/ # Structured log streaming to log.tailscale.io
├── logpolicy/ # Logging policy and log ID management
├── log/ # Log sink implementations
├── health/ # Health tracker subsystem
├── envknob/ # Environment-variable feature knobs
├── hostinfo/ # Host metadata collection
├── disco/ # Disco protocol (peer discovery handshake)
├── doctor/ # Network diagnostics framework
├── posture/ # Device posture checking
├── prober/ # Service probing (used internally)
├── safeweb/ # Hardened HTTP server wrapper
├── safesocket/ # Cross-platform Unix socket with auth
├── syncs/ # Sync primitives (typed atomics, mutexes)
├── chirp/ # Chirp UDP protocol (lightweight IPC)
├── tempfork/ # Patched vendored copies of upstream libraries
├── omit/ # Omit-tag stubs (pair with feature/ for dead-code elim)
├── tsweb/ # Internal web utilities [git only]
├── tstest/ # Test helpers [git only]
├── version/ # Version embedding and parsing [git only]
├── release/ # Packaging: deb, rpm, dist
├── docs/ # Documentation and example configs
└── scripts/ # Maintenance scriptsEntry points#
The cmd/ directory contains 50+ binaries. The most significant:
| Binary | Purpose |
|---|---|
cmd/tailscaled | Primary daemon — the Tailscale node agent (Linux, Windows, macOS, BSDs) |
cmd/tailscale | CLI client — controls and queries tailscaled via LocalAPI |
cmd/derper | DERP relay server binary |
cmd/k8s-operator | Kubernetes operator for Tailscale |
cmd/k8s-proxy | Kubernetes proxy (egress/ingress) |
cmd/k8s-nameserver | Kubernetes MagicDNS nameserver |
cmd/tsidp | Tailscale OIDC identity provider |
cmd/tsshd | Tailscale SSH server daemon |
cmd/containerboot | Container bootstrap for Tailscale in Docker/K8s |
cmd/natc | NAT connector binary |
cmd/stund | STUN server daemon |
cmd/derpprobe | DERP health probe |
cmd/vnet | Virtual network for testing |
cmd/tsconnect | Browser/WASM-based Tailscale connection |
cmd/sniproxy | SNI proxy for subnet routing |
cmd/nginx-auth | nginx auth helper for Tailscale |
cmd/pgproxy | PostgreSQL proxy |
cmd/derper, cmd/xdpderper | DERP + XDP-accelerated DERP |
cmd/featuretags | Prints build tags for feature selection |
cmd/mkversion, cmd/mkpkg | Build/packaging tooling |
Most cmd/ subdirectories do not have a main.go at the root; instead they have domain-named *.go files that are package main (e.g., cmd/tailscaled/tailscaled.go).
Package organization#
Internal packages:
internal/client/tailscale(private API client),internal/tooldeps(tool dependency tracking). Minimal use ofinternal/— Tailscale chose public packages everywhere to enable broad embedding.Public packages (pkg-equivalent, all at module root):
tailcfg— wire protocol types; the lingua franca between daemon and control planetypes/*— key, logger, netmap, prefs, views, ipproto, dnstype, …net/*— 40+ networking sub-packages used as a standard library extensionwgengine— Engine interface + userspace WireGuardipn— IPN state machine typesderp— DERP protocol librarycontrol/controlclient— control plane client librarytka— Key Authority librarytsnet— embedded Tailscale library (the “tsnet” package for app embedding)util/*— ~50 generic utilitiesclient/tailscale— public Go API client for Tailscale’s HTTP API
Layering: No strict clean-architecture layering. Dependencies flow as follows:
cmd/tailscaled └── tsd (system wiring) ├── wgengine (WireGuard engine) │ └── magicsock (UDP + DERP relay) ├── ipn/ipnlocal (local backend orchestrator) │ ├── control/controlclient │ ├── tailcfg (wire types) │ └── net/* (networking) └── ipn/ipnserver (socket server) └── ipn/localapi (REST API)tailcfgandtypes/*are leaf packages with minimal external dependencies.net/*depends ontailcfgandtypes.wgenginewrapsnet/*.ipn/*wraps all of the above.tsdwires them together.cmd/*are at the top.
Build system#
Build tool: GNU Make (
Makefile) orchestrating a pinned Go toolchain via./tool/go(a wrapper script that downloads and caches the exact toolchain version specified ingo.toolchain.rev)Key targets:
make vet—go vet ./...make staticcheck— staticcheck analysismake depaware— dependency awareness check (validates no unexpected imports)make buildwindows/build386/buildlinuxarm/buildwasm— cross-compilation testsmake check— comprehensive pre-merge checkmake generate— code generation (go generate ./...)make kube-generate-all— regenerate Kubernetes CRD codemake sshintegrationtest— Docker-based SSH integration testsmake updatedeps— update depaware lockfiles
Docker: Yes, multi-stage.
Dockerfilebuildstailscaled,tailscale, and other binaries then packages them in Alpine. Actual published images usebuild_docker.sh+ the externalmkctrtool for multi-architecture builds. The Makefilepublishdevimagetarget drivesbuild_docker.sh.Toolchain pinning: The repository pins its Go toolchain via
go.toolchain.rev,go.toolchain.version, and an SRI hash. The./tool/goscript enforces this — no system Go is used for builds. This is unusually strict but ensures reproducible builds across contributors and CI.Release packaging:
release/contains Debian (.deb) and RPM (.rpm) packaging logic.cmd/distis a distribution build tool.cmd/mkpkgbuilds platform packages.
Notable structural decisions#
Flat public namespace with vanity module path. Using
tailscale.comas the module path and exporting almost everything (no broadinternal/) is an architectural commitment: Tailscale is as much a library as an application. Thetsnetpackage is a first-class embedded-Tailscale API specifically for this purpose.Feature flag architecture as physical structure. The
feature/buildfeatures/directory contains ~100+ feature pairs (feature_X_enabled.go/feature_X_disabled.go). Combined with build tags and theomit/stub package, this lets the same codebase produce a minimal mobile build or a full-featured server build with dead-code elimination — without#ifdef-style guards scattered through the code.tsdas explicit dependency injection container. Rather than passing individual subsystems around (or using a DI framework), thetsd.Systemstruct holds all daemon subsystems asSubSystem[T]values. This was an intentional 2023 redesign to unify initialization across tailscaled, Windows service, macOS GUI, tsnet, and WASM — documented in the package comment.tempfork/as a managed vendor-patch area. Dependencies that needed patching (e.g.,gliderlabs/ssh,acme) live intempfork/rather than invendor/. This is more honest than silently patching vendor files and makes upstream-porting efforts visible.wgengine/magicsockas the connectivity core. MagicSock is arguably the most complex and critical package: it manages multiple UDP endpoints per peer, falls back to DERP relay when direct paths fail, implements STUN-based NAT traversal, and supports XDP acceleration. Its placement underwgengine/reflects that it is conceptually part of the WireGuard layer, not the OS networking layer.Kubernetes integration at product scale.
k8s-operator/,kube/, and thecmd/k8s-*binaries form a substantial product sub-system. The operator uses the standard controller-runtime reconciler pattern and has its own CRD types ink8s-operator/apis/. The presence of asessionrecording/sub-package within the operator reflects the complexity of the Kubernetes SSH session recording feature.