machineid

Reads a cross-platform machine ID without admin privileges and hashes it for safe, app-specific device identification.

Library
Go
vv1.0.1
1,177stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
40/100Fair
Development Activity0
Maintenance0
Community60
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
56/100Fair
Architecture72
Code Quality60
Innovation35
Learning Curve55

machineid is a small Go library for reading the OS-native machine identifier — dbus machine-id on Linux, IOPlatformUUID on macOS, MachineGuid from the registry on Windows, and /etc/hostid (with a kenv fallback) on BSD variants — without requiring administrator or root privileges. It exposes two functions: ID() returns the raw platform identifier, and ProtectedID(appID) returns an HMAC-SHA256 hash of that ID keyed by an application-specific string, so applications can derive a stable per-machine identifier without exposing or storing the actual hardware/OS UUID.

The package is a pure standard-library implementation on Unix (only Windows pulls in golang.org/x/sys/windows/registry), with platform-specific files gated by Go build tags, plus a small companion CLI (cmd/machineid) for reading the ID from the command line. It’s commonly used for license enforcement, device fingerprinting, and per-installation opt-out/telemetry keys where a stable, non-invasive machine identifier is needed.

What You Get

  • ID() function - returns the raw OS-native machine identifier (dbus machine-id, IOPlatformUUID, registry MachineGuid, or /etc/hostid depending on platform)
  • ProtectedID() function - returns an HMAC-SHA256 hash of the machine ID keyed by an app-specific string, so the real ID is never exposed
  • Cross-platform build-tag implementations - dedicated machineID() implementations for linux, darwin, windows, and BSD variants (freebsd, netbsd, openbsd, dragonfly, solaris)
  • Standalone CLI - cmd/machineid prints the machine ID (or a protected/hashed variant with —appid) directly from the command line

Common Use Cases

  • License key binding - tie a software license to a specific machine by hashing the machine ID with an app-specific key, invalidating the license if copied to another host
  • Device fingerprinting for fraud/abuse prevention - generate a stable per-device identifier for rate limiting or duplicate-account detection without collecting hardware data
  • Telemetry opt-out keys - derive a consistent anonymous per-installation ID for opt-in/opt-out preference tracking across app restarts
  • CLI-based machine identification in scripts - use the bundled machineid binary to fetch the current host’s ID from shell scripts or CI provisioning steps

Under The Hood

Architecture The package is a flat, single-package Go library with no internal layering or dependency injection: id.go exposes the two public functions (ID, ProtectedID), which delegate to an unexported machineID() function whose actual implementation is selected at compile time via Go build tags across id_linux.go, id_darwin.go, id_windows.go, and id_bsd.go — effectively a compile-time strategy pattern keyed on GOOS. Shared plumbing (exec wrapping, HMAC hashing, file reading, string trimming) lives in helper.go and is reused by every platform file. Because every implementation must satisfy the same machineID() (string, error) contract, changing that signature would require touching all four platform files in lockstep, but the tiny, well-bounded surface keeps that risk low.

Tech Stack Written in pure Go with no go.mod (predates Go modules, using the legacy // import comment convention), the library depends only on the standard library on Linux, macOS, and BSD (crypto/hmac, crypto/sha256, encoding/hex, io/ioutil, os/exec, strings) and pulls in golang.org/x/sys/windows/registry solely for the Windows build. The companion CLI in cmd/machineid uses the standard flag package. Build/test orchestration is a minimal Makefile (make build, make test); there is no CI configuration in the repository.

Code Quality Test coverage is present and reasonably thorough for the package’s size: table-driven tests for the trim() helper, HMAC round-trip verification (with a deliberate tamper case) for protect(), exec-wrapper tests covering both success and “executable not found” paths, platform-gated tests for the darwin ioreg-output parser, and Example()-based doctests for both ID() and ProtectedID(). Error handling is idiomatic Go (explicit error returns wrapped with context via fmt.Errorf), though the code still uses the deprecated io/ioutil package rather than its os equivalents, reflecting its pre-modules vintage. No linter or CI configuration is checked in.

What Makes It Unique The library’s specific technical choice is avoiding hardware-derived identifiers entirely (MAC address, BIOS serial, CPU ID) in favor of OS-assigned installation identifiers, which it argues are more stable across virtualized and cloned environments. Its other notable design choice is exposing ProtectedID() as the recommended default API rather than the raw ID — pushing consumers toward an HMAC-derived, non-reversible identifier by default rather than requiring them to remember to hash it themselves. Neither idea is architecturally novel, but the combination is a deliberate, security-conscious API design rather than a generic ID accessor.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search