ADR-007: Use golang.org/x/sys/windows/svc for Windows Service registration¶
Status: superseded by ADR-010
Context¶
v2.0 adds native Windows Service support so operators can register cee-exporter with the Windows Service Control Manager (SCM) instead of using NSSM or manual startup.
Three implementation approaches were evaluated:
golang.org/x/sys/windows/svc+svc/mgr— the official Go extended stdlib packages for Windows service lifecycle and SCM management.github.com/kardianos/service— a cross-platform wrapper that delegates tox/sys/windows/svcon Windows and to systemd/launchd on other platforms.- NSSM (Non-Sucking Service Manager) — an external executable that wraps any binary as a Windows Service.
Arguments against option 2 (kardianos/service):
kardianos/serviceis a thin wrapper aroundgolang.org/x/sys/windows/svc. It adds no functionality that the underlying package does not already provide.x/sysis already ingo.mod(v0.31.0) as a dependency of the existing Win32 EventLog writer. Usingkardianos/serviceadds a new transitive dependency for zero functional gain.- The cross-platform abstraction (Linux/macOS service support) is not a goal for cee-exporter: systemd integration on Linux is handled by a static unit file (ADR-implicit), and macOS is not a supported platform.
Arguments against option 3 (NSSM):
- Breaks the single-artifact deployment contract: the binary is no longer self-sufficient; operators must install NSSM separately.
- Not reproducible in CI/CD pipelines that build and package the daemon.
Decision¶
Use golang.org/x/sys/windows/svc and golang.org/x/sys/windows/svc/mgr directly.
The main binary gains install, uninstall, start, stop, and status
subcommands implemented in a cmd/cee-exporter/service_windows.go build-tag file.
At startup, svc.IsWindowsService() detects SCM context and delegates to
svc.Run("cee-exporter", handler).
Consequences¶
- No new external dependencies added to
go.mod. - All SCM code is confined to
_windows.gobuild-tag files; Linux build is unaffected. golang.org/x/sysis an official Go project; breaking changes follow the Go compatibility promise.- Service installation requires Administrator privileges (
mgr.Connect()needs them); this must be documented clearly. - Recovery actions (auto-restart on failure) are set via
mgr.RecoveryActionswith a 5-second delay — equivalent toRestart=on-failure, RestartSec=5sin systemd.