Syncthing — Structure#

Layout pattern#

Standard Go Layout with lib/ as primary library root (non-standard naming)

Syncthing follows the standard Go layout in spirit (cmd/ for binaries, internal/ for private code) but uses lib/ rather than pkg/ for its reusable packages. This is an intentional architectural choice: lib/ signals “library-quality packages that external GUI wrappers and tools may import,” whereas internal/ is reserved for a small set of truly private utilities. The result is a bimodal package hierarchy with ~40 public-ish packages in lib/ and 7 strictly private packages in internal/.

Directory map#

syncthing/
├── assets/              # Status tray icon images (platform-specific icons)
│   └── statusicons/
├── cmd/                 # All binary entry points
│   ├── syncthing/       # Main syncthing application + embedded CLI
│   │   └── cli/         # REST API CLI client (subcommand of main binary)
│   ├── stdiscosrv/      # Global discovery server (production service)
│   ├── strelaysrv/      # Relay server (production service)
│   ├── dev/             # Developer tools (10 small utilities)
│   └── infra/           # Infrastructure services (crash, relay pool, upgrades, usage)
│       ├── stcrashreceiver/
│       ├── strelaypoolsrv/
│       ├── stupgrades/
│       └── ursrv/
├── etc/                 # System integration (init scripts, service files)
│   ├── linux-systemd/   # systemd unit files
│   ├── macos-launchd/   # launchd plist
│   ├── freebsd-rc/      # FreeBSD rc.d
│   └── ...              # upstart, runit, solaris-smf, firewall-ufw
├── gui/                 # Web UI static assets (4 color themes)
│   ├── default/         # Default theme (AngularJS SPA + vendor JS)
│   ├── dark/
│   ├── light/
│   └── black/
├── internal/            # Private packages (not importable outside module)
│   ├── blob/            # Binary blob storage abstraction
│   ├── db/              # Database interface (SQLite-backed)
│   ├── gen/             # Code generation helpers
│   ├── itererr/         # Iterator with error propagation
│   ├── protoutil/       # Protobuf encoding/decoding utilities
│   ├── slogutil/        # Structured logging (slog) helpers
│   └── timeutil/        # Time utility functions
├── lib/                 # Core application library (~40 packages)
│   ├── api/             # REST API server (HTTP handlers, auth, CORS)
│   ├── assets/          # Embedded GUI asset serving
│   ├── beacon/          # UDP broadcast beacon for local device discovery
│   ├── build/           # Build metadata (version, tags, build info)
│   ├── config/          # Configuration loading, validation, migration
│   ├── connections/     # Connection management (QUIC, TCP, relay transport)
│   ├── dialer/          # Low-level connection dialing
│   ├── discover/        # Device discovery (local UDP + global HTTPS)
│   ├── events/          # Internal event bus (typed events, subscriptions)
│   ├── fs/              # Filesystem abstraction (real, fake, case-folding, MTime)
│   ├── geoip/           # MaxMind GeoIP database integration (for relay/disco)
│   ├── httpcache/       # HTTP caching layer
│   ├── ignore/          # .stignore pattern matching
│   ├── locations/       # Platform-specific config/data/log paths
│   ├── model/           # Core sync engine (the largest and most critical package)
│   ├── nat/             # NAT traversal coordination
│   ├── netutil/         # Network utility functions
│   ├── osutil/          # OS-level utilities (atomic file ops, etc.)
│   ├── pmp/             # NAT-PMP protocol client
│   ├── protocol/        # Block Exchange Protocol (BEP) implementation
│   ├── rand/            # Cryptographic random number utilities
│   ├── rc/              # REST client library (used by CLI subcommand)
│   ├── relay/           # Relay protocol client
│   ├── scanner/         # File scanner, block hasher, metadata collector
│   ├── semaphore/       # Counting semaphore primitive
│   ├── signature/       # Code signature/update verification
│   ├── sliceutil/       # Generic slice utilities
│   ├── stats/           # Per-device and per-folder statistics
│   ├── stringutil/      # String utilities
│   ├── structutil/      # Struct reflection utilities
│   ├── stun/            # STUN client for NAT traversal
│   ├── svcutil/         # Service supervisor utilities (suture helpers)
│   ├── syncthing/       # Top-level application assembly (wires lib/* together)
│   ├── syncutil/        # Sync primitives (mutex with tracing, etc.)
│   ├── testutil/        # Shared test helpers
│   ├── tlsutil/         # TLS certificate generation and loading
│   ├── upgrade/         # Auto-upgrade mechanism
│   ├── upnp/            # UPnP NAT port mapping
│   ├── ur/              # Usage reporting client
│   ├── versioner/       # File versioning strategies (trash can, staggered, etc.)
│   └── watchaggregator/ # Filesystem watcher event aggregation and debouncing
├── man/                 # Man page source (refreshed by script)
├── meta/                # Project metadata (compatibility declarations)
├── proto/               # Protocol Buffer definitions
│   ├── bep/             # Block Exchange Protocol messages
│   ├── apiproto/        # REST API protobuf types
│   ├── dbproto/         # Database record types
│   ├── discoproto/      # Discovery protocol messages
│   └── discosrv/        # Discovery server service definition
├── relnotes/            # Per-version release notes
├── script/              # Build helper Go scripts (authors, copyrights, etc.)
├── test/                # Integration test infrastructure
│   ├── h1/ h2/ h3/ h4/  # Test host configurations (pre-configured Syncthing homes)
│   └── logs/
├── build.go             # Custom build script (go run build.go ...)
├── build.sh             # Shell wrapper for build.go
├── buf.yaml             # Buf (protobuf toolchain) configuration
├── buf.gen.yaml         # Buf code generation configuration
└── Dockerfile*          # Multiple Dockerfiles, one per service

Entry points#

BinaryFilePurpose
syncthingcmd/syncthing/main.goMain application: file sync daemon + embedded web UI
syncthing clicmd/syncthing/cli/main.goREST API CLI client (subcommand of syncthing binary)
stdiscosrvcmd/stdiscosrv/main.goGlobal discovery server (production; maps device ID → address)
strelaysrvcmd/strelaysrv/main.goRelay server (TCP relay for devices behind NAT)
stcrashreceivercmd/infra/stcrashreceiver/main.goInfrastructure: crash report collection service
strelaypoolsrvcmd/infra/strelaypoolsrv/main.goInfrastructure: relay pool registry service
stupgradescmd/infra/stupgrades/main.goInfrastructure: binary update server
ursrvcmd/infra/ursrv/main.goInfrastructure: anonymous usage reporting server
stfinddevicecmd/dev/stfinddevice/main.goDev tool: locate device on local network
stfindignoredcmd/dev/stfindignored/main.goDev tool: show which files are ignored
stdiscocmd/dev/stdisco/main.goDev tool: query discovery server
stfileinfocmd/dev/stfileinfo/main.goDev tool: show BEP file info for a path
stwatchfilecmd/dev/stwatchfile/main.goDev tool: test filesystem watcher
steventscmd/dev/stevents/main.goDev tool: stream events via REST API
stsigtoolcmd/dev/stsigtool/main.goDev tool: sign/verify release artifacts
stcompdirscmd/dev/stcompdirs/main.goDev tool: compare two directory trees
stvanitycmd/dev/stvanity/main.goDev tool: generate device ID with prefix
stgenfilescmd/dev/stgenfiles/main.goDev tool: generate test file trees

Package organization#

  • Internal packages (internal/): 7 packages that are strictly private to the module — db (SQLite-backed storage), blob (binary object storage), gen (codegen), itererr (iterator error handling), protoutil (protobuf encoding), slogutil (structured logging helpers), timeutil (time utilities). These are the true implementation internals, not meant for external consumers.

  • Public packages (lib/): 40 packages structured as a cohesive application library. The most critical are:

    • lib/model — Core sync engine; largest package in the codebase. Manages folder state, device connections, file transfers.
    • lib/protocol — BEP (Block Exchange Protocol) implementation; the on-wire format for all sync traffic.
    • lib/fs — Filesystem abstraction with multiple implementations (basic, case-sensitive wrapper, MTime-tracking). Essential for cross-platform correctness.
    • lib/config — Configuration with live-reload, migration, and validation.
    • lib/connections — Transport management (QUIC, TCP, relay-based connections).
    • lib/discover — Both local (UDP beacon) and global (HTTPS) device discovery.
    • lib/syncthing — Application assembly layer that wires model, api, connections, discover, config, events together into the running application.
  • Layering: The package dependency graph has a clear layering:

    1. Primitives (rand, semaphore, syncutil, sliceutil, stringutil, structutil, osutil, netutil) — no cross-dependencies
    2. Domain utilities (fs, ignore, scanner, versioner, events, stats, tlsutil) — depend on primitives
    3. Protocol/Network (protocol, beacon, dialer, relay, stun, nat, upnp, pmp, discover, connections) — depend on domain utilities
    4. Application core (model, api, config) — depend on most of the above
    5. Assembly (lib/syncthing) — wires all core components together under a supervisor tree
    6. Binaries (cmd/syncthing) — thin shell that calls into lib/syncthing

Build system#

  • Build tool: Custom Go build script (go run build.go), wrapped by build.sh. No Makefile. The build script (build.go) uses the //go:build tools tag and handles compilation, testing, packaging, and release artifact production for all target platforms.
  • Key targets:
    • go run build.go — builds syncthing binary
    • go run build.go -goos <OS> -goarch <ARCH> — cross-compile
    • go run build.go test — run test suite
    • go run build.go install — install to GOPATH
    • Individual infra services built separately by their Dockerfiles
  • Docker: Yes — 8 Dockerfiles at the repo root, one per service (Dockerfile, Dockerfile.builder, Dockerfile.stdiscosrv, Dockerfile.strelaysrv, Dockerfile.stcrashreceiver, Dockerfile.strelaypoolsrv, Dockerfile.stupgrades, Dockerfile.ursrv). Multi-stage builds are used.
  • Protobuf: Managed via buf toolchain (buf.yaml, buf.gen.yaml); proto sources live in proto/ and generated Go code lives alongside the lib/ packages.

Notable structural decisions#

  1. lib/ over pkg/ naming: Syncthing predates the widespread adoption of the pkg/ convention and uses lib/ to signal “importable library.” This has stayed stable since the early days and enables the ecosystem of third-party GUI wrappers (Syncthing-GTK, Syncthing-macOS, etc.) that import lib/ packages directly.

  2. lib/syncthing as an explicit assembly package: Rather than doing all wiring in cmd/syncthing/main.go, Syncthing introduces lib/syncthing as a dedicated package that constructs and starts the supervised service tree. This makes the main binary trivially thin and enables alternative entry points (e.g., embedding syncthing in other applications).

  3. Infrastructure services co-located with the application: stdiscosrv, strelaysrv, and all infra/ services live in the same repository. Each has its own Dockerfile and can be deployed independently. This is unusual — most projects would split these into separate repositories — but it ensures protocol compatibility is maintained in lockstep.

  4. Multiple Dockerfiles at the root: 8 Dockerfiles at the repository root is a deliberate trade-off: simple and self-documenting, but visually noisy. Each service’s build context is the full repository, which is required because they all import from lib/.

  5. proto/ with domain-partitioned subdirectories: Protocol Buffer definitions are split by semantic domain (bep, dbproto, discoproto, apiproto, discosrv), keeping the wire formats modular and allowing independent evolution of each protocol component.

  6. GUI as static assets in 4 themes: The web UI (AngularJS SPA) ships as embedded static assets in 4 color variants. The script/genassets.go tool generates a Go file embedding them; this avoids runtime filesystem dependencies and keeps the binary self-contained.

  7. test/h1-h4 pre-configured host directories: Integration tests spin up multiple real Syncthing instances using pre-seeded home directories (test/h1/ through test/h4/), testing actual sync behavior rather than mocked components. This reflects a culture of integration testing over unit testing for the core sync path.