From 0c5b0d4b118eae4fa9f418896f2890e89aec0db7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Gonz=C3=A1lez=20Di=20Antonio?= Date: Sat, 14 Feb 2026 20:50:21 +0100 Subject: [PATCH 1/2] feat: add MACFilter for network interface classification Add MACFilter type (Physical, All, Virtual) to control which network interfaces contribute to the machine ID. Physical-only (default) provides the most stable IDs on bare metal; All includes virtual interfaces for maximum uniqueness; Virtual-only supports container fingerprinting. Backward compatible: WithMAC() with no args behaves identically to before. Co-Authored-By: Claude Opus 4.6 --- README.md | 33 ++++++++++++++++++++++- cmd/machineid/main.go | 25 +++++++++++++++-- darwin.go | 2 +- darwin_test.go | 2 +- doc.go | 23 +++++++++++++++- linux.go | 2 +- machineid.go | 35 +++++++++++++++++++++++- machineid_test.go | 63 +++++++++++++++++++++++++++++++++++++++++++ network.go | 32 +++++++++++++++------- network_test.go | 59 +++++++++++++++++++++++++++++++++++++--- windows.go | 2 +- 11 files changed, 257 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 914a949..3994525 100644 --- a/README.md +++ b/README.md @@ -142,12 +142,36 @@ provider := machineid.New(). WithCPU(). // processor ID and feature flags WithMotherboard(). // motherboard serial number WithSystemUUID(). // BIOS/UEFI system UUID - WithMAC(). // physical network interface MAC addresses + WithMAC(). // physical network interface MAC addresses (default filter) + WithDisk() // internal disk serial numbers id, err := provider.ID(ctx) ``` +### MAC Address Filtering + +Control which network interfaces are included in the machine ID using `MACFilter`: + +```go +ctx := context.Background() + +// Physical interfaces only (default, most stable for bare-metal) +id, _ := machineid.New().WithCPU().WithMAC().ID(ctx) + +// All interfaces including virtual (VPN, Docker, bridges) +id, _ = machineid.New().WithCPU().WithMAC(machineid.MACFilterAll).ID(ctx) + +// Only virtual interfaces (useful for container-specific fingerprinting) +id, _ = machineid.New().WithCPU().WithMAC(machineid.MACFilterVirtual).ID(ctx) +``` + +| Filter | Interfaces Included | Best For | +|---------------------|--------------------------------------------------------|--------------------------| +| `MACFilterPhysical` | `en0`, `eth0`, `wlan0` (default) | Bare-metal stability | +| `MACFilterAll` | Physical + virtual (`docker0`, `utun`, `bridge`, etc.) | Maximum uniqueness | +| `MACFilterVirtual` | `docker0`, `utun`, `bridge0`, `veth`, `vmnet`, etc. | Container fingerprinting | + ### Output Formats All formats produce pure hexadecimal strings without dashes: @@ -338,6 +362,12 @@ machineid -cpu -uuid -validate "b5c42832542981af58c9dc3bc241219e780ff7d276cfad05 # Info-level logging (fallbacks, lifecycle events) machineid -cpu -uuid -verbose +# Include only physical MACs (default) +machineid -mac -mac-filter physical + +# Include all MACs (physical + virtual) +machineid -all -mac-filter all + # Debug-level logging (command details, raw values, timing) machineid -all -debug @@ -354,6 +384,7 @@ machineid -version.long | `-motherboard` | Include motherboard serial number | | `-uuid` | Include system UUID | | `-mac` | Include network MAC addresses | +| `-mac-filter F` | MAC filter: `physical` (default), `all`, or `virtual` | | `-disk` | Include disk serial numbers | | `-all` | Include all hardware identifiers | | `-vm` | VM-friendly mode (CPU + UUID only) | diff --git a/cmd/machineid/main.go b/cmd/machineid/main.go index adc5bfc..fef88bb 100644 --- a/cmd/machineid/main.go +++ b/cmd/machineid/main.go @@ -22,6 +22,7 @@ func main() { motherboard := flag.Bool("motherboard", false, "Include motherboard serial number") uuid := flag.Bool("uuid", false, "Include system UUID") mac := flag.Bool("mac", false, "Include network MAC addresses") + macFilterFlag := flag.String("mac-filter", "physical", "MAC filter: physical, all, virtual") disk := flag.Bool("disk", false, "Include disk serial numbers") all := flag.Bool("all", false, "Include all hardware identifiers") vm := flag.Bool("vm", false, "Use VM-friendly mode (CPU + UUID only)") @@ -134,11 +135,18 @@ func main() { provider.WithSalt(*salt) } + mFilter, err := parseMACFilter(*macFilterFlag) + if err != nil { + slog.Error("invalid mac-filter", "error", err) + flag.Usage() + os.Exit(1) + } + switch { case *vm: provider.VMFriendly() case *all: - provider.WithCPU().WithMotherboard().WithSystemUUID().WithMAC().WithDisk() + provider.WithCPU().WithMotherboard().WithSystemUUID().WithMAC(mFilter).WithDisk() default: if !*cpu && !*motherboard && !*uuid && !*mac && !*disk { // Default: CPU + Motherboard + System UUID @@ -154,7 +162,7 @@ func main() { provider.WithSystemUUID() } if *mac { - provider.WithMAC() + provider.WithMAC(mFilter) } if *disk { provider.WithDisk() @@ -213,6 +221,19 @@ func parseFormatMode(format int) (machineid.FormatMode, error) { } } +func parseMACFilter(value string) (machineid.MACFilter, error) { + switch strings.ToLower(value) { + case "physical": + return machineid.MACFilterPhysical, nil + case "all": + return machineid.MACFilterAll, nil + case "virtual": + return machineid.MACFilterVirtual, nil + default: + return 0, fmt.Errorf("unsupported mac-filter %q; valid values are physical, all, virtual", value) + } +} + func handleValidate(ctx context.Context, provider *machineid.Provider, expectedID string, jsonOut bool) { valid, err := provider.Validate(ctx, expectedID) if err != nil { diff --git a/darwin.go b/darwin.go index e5413c6..b664b6f 100644 --- a/darwin.go +++ b/darwin.go @@ -76,7 +76,7 @@ func collectIdentifiers(ctx context.Context, p *Provider, diag *DiagnosticInfo) if p.includeMAC { identifiers = appendIdentifiersIfValid(identifiers, func() ([]string, error) { - return collectMACAddresses(logger) + return collectMACAddresses(p.macFilter, logger) }, "mac:", diag, ComponentMAC, logger) } diff --git a/darwin_test.go b/darwin_test.go index b069bfe..3863251 100644 --- a/darwin_test.go +++ b/darwin_test.go @@ -635,7 +635,7 @@ func TestCollectMACAddressesWithLogger(t *testing.T) { var buf bytes.Buffer logger := slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug})) - macs, err := collectMACAddresses(logger) + macs, err := collectMACAddresses(MACFilterPhysical, logger) if err != nil { t.Logf("collectMACAddresses error (may be expected): %v", err) return diff --git a/doc.go b/doc.go index 56ed8ce..ca10350 100644 --- a/doc.go +++ b/doc.go @@ -29,12 +29,32 @@ // - [Provider.WithCPU] — processor identifier and feature flags // - [Provider.WithMotherboard] — motherboard / baseboard serial number // - [Provider.WithSystemUUID] — BIOS / UEFI system UUID -// - [Provider.WithMAC] — MAC addresses of physical network interfaces +// - [Provider.WithMAC] — MAC addresses of network interfaces (filterable) // - [Provider.WithDisk] — serial numbers of internal disks // // Or use [Provider.VMFriendly] to select a minimal, virtual-machine-safe // subset (CPU + System UUID). // +// # MAC Address Filtering +// +// [Provider.WithMAC] accepts an optional [MACFilter] to control which network +// interfaces contribute to the machine ID: +// +// - [MACFilterPhysical] — only physical interfaces (default) +// - [MACFilterAll] — all non-loopback, up interfaces (physical + virtual) +// - [MACFilterVirtual] — only virtual interfaces (VPN, bridge, container) +// +// Examples: +// +// // Physical interfaces only (default, most stable) +// provider.WithMAC() +// +// // Include all interfaces +// provider.WithMAC(machineid.MACFilterAll) +// +// // Only virtual interfaces (containers, VPNs) +// provider.WithMAC(machineid.MACFilterVirtual) +// // # Output Formats // // Set the output length with [Provider.WithFormat]: @@ -159,6 +179,7 @@ // machineid -cpu -uuid // machineid -all -format 32 -json // machineid -vm -salt "my-app" -diagnostics +// machineid -mac -mac-filter all // machineid -cpu -uuid -verbose // machineid -all -debug // machineid -version diff --git a/linux.go b/linux.go index 3e7fa3c..b366036 100644 --- a/linux.go +++ b/linux.go @@ -39,7 +39,7 @@ func collectIdentifiers(ctx context.Context, p *Provider, diag *DiagnosticInfo) if p.includeMAC { identifiers = appendIdentifiersIfValid(identifiers, func() ([]string, error) { - return collectMACAddresses(logger) + return collectMACAddresses(p.macFilter, logger) }, "mac:", diag, ComponentMAC, logger) } diff --git a/machineid.go b/machineid.go index d64f534..05ed575 100644 --- a/machineid.go +++ b/machineid.go @@ -26,6 +26,32 @@ const ( Format256 ) +// MACFilter controls which network interfaces are included in MAC address collection. +type MACFilter int + +const ( + // MACFilterPhysical includes only physical network interfaces (default). + // Virtual, VPN, bridge, and container interfaces are excluded. + MACFilterPhysical MACFilter = iota + // MACFilterAll includes all non-loopback, up network interfaces (physical and virtual). + MACFilterAll + // MACFilterVirtual includes only virtual network interfaces + // (VPN, bridge, container, and hypervisor interfaces). + MACFilterVirtual +) + +// String returns the string representation of the MACFilter. +func (f MACFilter) String() string { + switch f { + case MACFilterAll: + return "all" + case MACFilterVirtual: + return "virtual" + default: + return "physical" + } +} + // Component names used as keys in DiagnosticInfo. const ( ComponentCPU = "cpu" @@ -66,6 +92,7 @@ type Provider struct { includeMotherboard bool includeSystemUUID bool includeMAC bool + macFilter MACFilter includeDisk bool } @@ -118,8 +145,14 @@ func (p *Provider) WithSystemUUID() *Provider { } // WithMAC includes network interface MAC addresses in the generation. -func (p *Provider) WithMAC() *Provider { +// An optional [MACFilter] controls which interfaces are included. +// Default is [MACFilterPhysical], which excludes virtual, VPN, bridge, +// and container interfaces for stability. +func (p *Provider) WithMAC(filter ...MACFilter) *Provider { p.includeMAC = true + if len(filter) > 0 { + p.macFilter = filter[0] + } return p } diff --git a/machineid_test.go b/machineid_test.go index 902de0d..c25c469 100644 --- a/machineid_test.go +++ b/machineid_test.go @@ -693,6 +693,69 @@ func TestFormatValidation(t *testing.T) { } } +// TestWithMACBackwardCompatibility tests that WithMAC() with no args works as before. +func TestWithMACBackwardCompatibility(t *testing.T) { + // WithMAC() with no args should work (physical filter, same as current behavior) + g := machineid.New().WithCPU().WithSystemUUID().WithMAC() + + id, err := g.ID(context.Background()) + if err != nil { + t.Fatalf("WithMAC() error = %v", err) + } + + if len(id) != 64 { + t.Errorf("WithMAC() ID length = %d, want 64", len(id)) + } +} + +// TestWithMACFilter tests WithMAC with explicit filter modes. +func TestWithMACFilter(t *testing.T) { + filters := []struct { + name string + filter machineid.MACFilter + }{ + {"physical", machineid.MACFilterPhysical}, + {"all", machineid.MACFilterAll}, + {"virtual", machineid.MACFilterVirtual}, + } + + for _, tt := range filters { + t.Run(tt.name, func(t *testing.T) { + g := machineid.New().WithCPU().WithSystemUUID().WithMAC(tt.filter) + + id, err := g.ID(context.Background()) + if err != nil { + t.Fatalf("WithMAC(%s) error = %v", tt.name, err) + } + + if len(id) != 64 { + t.Errorf("WithMAC(%s) ID length = %d, want 64", tt.name, len(id)) + } + }) + } +} + +// TestWithMACFilterAffectsID tests that different MAC filters can produce different IDs. +func TestWithMACFilterAffectsID(t *testing.T) { + physical := machineid.New().WithMAC(machineid.MACFilterPhysical).WithCPU() + all := machineid.New().WithMAC(machineid.MACFilterAll).WithCPU() + + idPhysical, err := physical.ID(context.Background()) + if err != nil { + t.Fatalf("physical ID error: %v", err) + } + + idAll, err := all.ID(context.Background()) + if err != nil { + t.Fatalf("all ID error: %v", err) + } + + // They may or may not differ depending on the system, just verify both are valid + if len(idPhysical) != 64 || len(idAll) != 64 { + t.Errorf("Expected 64-char IDs, got physical=%d, all=%d", len(idPhysical), len(idAll)) + } +} + // TestFormatPowerOfTwo verifies that all format lengths are powers of 2. func TestFormatPowerOfTwo(t *testing.T) { formats := []struct { diff --git a/network.go b/network.go index 0202b40..19fb363 100644 --- a/network.go +++ b/network.go @@ -26,9 +26,9 @@ var virtualInterfacePrefixes = []string{ "vnic", "vboxnet", } -// collectMACAddresses retrieves MAC addresses from physical network interfaces. -// Virtual, VPN, bridge, and container interfaces are excluded for stability. -func collectMACAddresses(logger *slog.Logger) ([]string, error) { +// collectMACAddresses retrieves MAC addresses from network interfaces filtered +// by the given [MACFilter]. Loopback and down interfaces are always excluded. +func collectMACAddresses(filter MACFilter, logger *slog.Logger) ([]string, error) { interfaces, err := net.Interfaces() if err != nil { return nil, err @@ -51,17 +51,31 @@ func collectMACAddresses(logger *slog.Logger) ([]string, error) { continue } - // Skip virtual/VPN/bridge interfaces that are not stable hardware. - if isVirtualInterface(i.Name) { - if logger != nil { - logger.Debug("skipping virtual interface", "interface", i.Name) + virtual := isVirtualInterface(i.Name) + + switch filter { + case MACFilterPhysical: + if virtual { + if logger != nil { + logger.Debug("skipping virtual interface", "interface", i.Name) + } + + continue } + case MACFilterVirtual: + if !virtual { + if logger != nil { + logger.Debug("skipping physical interface", "interface", i.Name) + } - continue + continue + } + case MACFilterAll: + // Include everything that passed loopback/up checks. } if logger != nil { - logger.Debug("including interface", "interface", i.Name, "mac", i.HardwareAddr.String()) + logger.Debug("including interface", "interface", i.Name, "mac", i.HardwareAddr.String(), "virtual", virtual) } macs = append(macs, i.HardwareAddr.String()) diff --git a/network_test.go b/network_test.go index 676baa7..b657d66 100644 --- a/network_test.go +++ b/network_test.go @@ -4,13 +4,66 @@ import ( "testing" ) -// TestCollectMACAddresses tests network interface MAC address collection. +// TestCollectMACAddresses tests network interface MAC address collection with default filter. func TestCollectMACAddresses(t *testing.T) { - macs, err := collectMACAddresses(nil) + macs, err := collectMACAddresses(MACFilterPhysical, nil) if err != nil { t.Logf("collectMACAddresses error (might be expected in some environments): %v", err) } - t.Logf("Found %d MAC addresses (filtered)", len(macs)) + t.Logf("Found %d physical MAC addresses", len(macs)) +} + +// TestCollectMACAddressesAllFilter tests that MACFilterAll returns >= physical count. +func TestCollectMACAddressesAllFilter(t *testing.T) { + physical, err := collectMACAddresses(MACFilterPhysical, nil) + if err != nil { + t.Logf("physical filter error: %v", err) + } + + all, err := collectMACAddresses(MACFilterAll, nil) + if err != nil { + t.Logf("all filter error: %v", err) + } + + if len(all) < len(physical) { + t.Errorf("MACFilterAll (%d) should return >= MACFilterPhysical (%d)", len(all), len(physical)) + } + + t.Logf("Physical: %d, All: %d", len(physical), len(all)) +} + +// TestCollectMACAddressesVirtualFilter tests that virtual filter excludes physical interfaces. +func TestCollectMACAddressesVirtualFilter(t *testing.T) { + physical, _ := collectMACAddresses(MACFilterPhysical, nil) + virtual, _ := collectMACAddresses(MACFilterVirtual, nil) + all, _ := collectMACAddresses(MACFilterAll, nil) + + // Virtual + physical should equal all (no overlap since classification is binary) + if len(virtual)+len(physical) != len(all) { + t.Errorf("virtual (%d) + physical (%d) != all (%d)", len(virtual), len(physical), len(all)) + } + + t.Logf("Physical: %d, Virtual: %d, All: %d", len(physical), len(virtual), len(all)) +} + +// TestMACFilterString tests the String() method on MACFilter. +func TestMACFilterString(t *testing.T) { + tests := []struct { + filter MACFilter + want string + }{ + {MACFilterPhysical, "physical"}, + {MACFilterAll, "all"}, + {MACFilterVirtual, "virtual"}, + {MACFilter(99), "physical"}, // unknown defaults to physical + } + + for _, tt := range tests { + got := tt.filter.String() + if got != tt.want { + t.Errorf("MACFilter(%d).String() = %q, want %q", tt.filter, got, tt.want) + } + } } // TestIsVirtualInterface tests virtual interface detection. diff --git a/windows.go b/windows.go index 1341970..51356b6 100644 --- a/windows.go +++ b/windows.go @@ -33,7 +33,7 @@ func collectIdentifiers(ctx context.Context, p *Provider, diag *DiagnosticInfo) if p.includeMAC { identifiers = appendIdentifiersIfValid(identifiers, func() ([]string, error) { - return collectMACAddresses(logger) + return collectMACAddresses(p.macFilter, logger) }, "mac:", diag, ComponentMAC, logger) } From a295eeaf9e27c2894a6f83cc101711c0ee6b6d5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Gonz=C3=A1lez=20Di=20Antonio?= Date: Sat, 14 Feb 2026 20:57:50 +0100 Subject: [PATCH 2/2] docs: align doc comments with go.dev/doc/comment standards - Add missing periods to all doc comment sentences - Use "reports whether" for bool-returning functions (Validate, isValidUUID, isValidSerial, isNonEmpty, isVirtualInterface) - Add [Symbol] doc links for cross-references (FormatMode, DiagnosticInfo, Provider.ID) Co-Authored-By: Claude Opus 4.6 --- linux.go | 20 ++++++++++---------- machineid.go | 26 +++++++++++++------------- network.go | 2 +- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/linux.go b/linux.go index b366036..9ff49a5 100644 --- a/linux.go +++ b/linux.go @@ -52,7 +52,7 @@ func collectIdentifiers(ctx context.Context, p *Provider, diag *DiagnosticInfo) return identifiers, nil } -// linuxCPUID retrieves CPU information from /proc/cpuinfo +// linuxCPUID retrieves CPU information from /proc/cpuinfo. func linuxCPUID(logger *slog.Logger) (string, error) { const path = "/proc/cpuinfo" @@ -72,7 +72,7 @@ func linuxCPUID(logger *slog.Logger) (string, error) { return parseCPUInfo(string(data)), nil } -// parseCPUInfo extracts CPU information from /proc/cpuinfo content +// parseCPUInfo extracts CPU information from /proc/cpuinfo content. func parseCPUInfo(content string) string { lines := strings.Split(content, "\n") var processor, vendorID, modelName, flags string @@ -100,7 +100,7 @@ func parseCPUInfo(content string) string { return fmt.Sprintf("%s:%s:%s:%s", processor, vendorID, modelName, flags) } -// linuxSystemUUID retrieves system UUID from DMI +// linuxSystemUUID retrieves system UUID from DMI. func linuxSystemUUID(logger *slog.Logger) (string, error) { // Try multiple locations for system UUID locations := []string{ @@ -111,7 +111,7 @@ func linuxSystemUUID(logger *slog.Logger) (string, error) { return readFirstValidFromLocations(locations, isValidUUID, logger) } -// linuxMotherboardSerial retrieves motherboard serial number from DMI +// linuxMotherboardSerial retrieves motherboard serial number from DMI. func linuxMotherboardSerial(logger *slog.Logger) (string, error) { locations := []string{ "/sys/class/dmi/id/board_serial", @@ -121,7 +121,7 @@ func linuxMotherboardSerial(logger *slog.Logger) (string, error) { return readFirstValidFromLocations(locations, isValidSerial, logger) } -// linuxMachineID retrieves systemd machine ID +// linuxMachineID retrieves systemd machine ID. func linuxMachineID(logger *slog.Logger) (string, error) { locations := []string{ "/etc/machine-id", @@ -131,7 +131,7 @@ func linuxMachineID(logger *slog.Logger) (string, error) { return readFirstValidFromLocations(locations, isNonEmpty, logger) } -// readFirstValidFromLocations reads from multiple locations until valid value found +// readFirstValidFromLocations reads from multiple locations until a valid value is found. func readFirstValidFromLocations(locations []string, validator func(string) bool, logger *slog.Logger) (string, error) { for _, location := range locations { data, err := os.ReadFile(location) @@ -156,17 +156,17 @@ func readFirstValidFromLocations(locations []string, validator func(string) bool return "", ErrNotFound } -// isValidUUID checks if UUID is valid (not empty or null) +// isValidUUID reports whether the UUID is valid (not empty or null). func isValidUUID(uuid string) bool { return uuid != "" && uuid != "00000000-0000-0000-0000-000000000000" } -// isValidSerial checks if serial is valid (not empty or placeholder) +// isValidSerial reports whether the serial is valid (not empty or placeholder). func isValidSerial(serial string) bool { return serial != "" && serial != biosFirmwareMessage } -// isNonEmpty checks if value is not empty +// isNonEmpty reports whether the value is not empty. func isNonEmpty(value string) bool { return value != "" } @@ -232,7 +232,7 @@ func linuxDiskSerialsLSBLK(ctx context.Context, executor CommandExecutor, logger return serials, nil } -// linuxDiskSerialsSys retrieves disk serials from /sys/block +// linuxDiskSerialsSys retrieves disk serials from /sys/block. func linuxDiskSerialsSys(logger *slog.Logger) ([]string, error) { var serials []string diff --git a/machineid.go b/machineid.go index 05ed575..54cd372 100644 --- a/machineid.go +++ b/machineid.go @@ -16,13 +16,13 @@ import ( type FormatMode int const ( - // Format64 outputs 64 hex characters (2^6), default SHA-256 output without dashes + // Format64 outputs 64 hex characters (2^6), default SHA-256 output without dashes. Format64 FormatMode = iota - // Format32 outputs 32 hex characters (2^5), truncated SHA-256 + // Format32 outputs 32 hex characters (2^5), truncated SHA-256. Format32 - // Format128 outputs 128 hex characters (2^7), double SHA-256 + // Format128 outputs 128 hex characters (2^7), double SHA-256. Format128 - // Format256 outputs 256 hex characters (2^8), quadruple SHA-256 + // Format256 outputs 256 hex characters (2^8), quadruple SHA-256. Format256 ) @@ -52,7 +52,7 @@ func (f MACFilter) String() string { } } -// Component names used as keys in DiagnosticInfo. +// Component names used as keys in [DiagnosticInfo]. const ( ComponentCPU = "cpu" ComponentMotherboard = "motherboard" @@ -78,7 +78,7 @@ type CommandExecutor interface { } // Provider configures and generates unique machine IDs. -// After the first call to ID(), the configuration is frozen and the result is cached. +// After the first call to [Provider.ID], the configuration is frozen and the result is cached. // Provider methods are safe for concurrent use after configuration is complete. type Provider struct { commandExecutor CommandExecutor @@ -98,7 +98,7 @@ type Provider struct { // New creates a new Provider with default settings. // The provider uses real system commands by default. -// Default format is Format64 (64 hex characters, 2^6). +// Default format is [Format64] (64 hex characters, 2^6). func New() *Provider { return &Provider{ commandExecutor: &defaultCommandExecutor{ @@ -116,7 +116,7 @@ func (p *Provider) WithSalt(salt string) *Provider { } // WithFormat sets the output format and length. -// Use Format64 (default), Format32, Format128, or Format256. +// Use [Format64] (default), [Format32], [Format128], or [Format256]. func (p *Provider) WithFormat(mode FormatMode) *Provider { p.formatMode = mode @@ -198,7 +198,7 @@ func (p *Provider) VMFriendly() *Provider { // ID generates the machine ID based on the configured options. // It caches the result, so subsequent calls return the same ID. -// The configuration is frozen after the first successful call to ID(). +// The configuration is frozen after the first successful call. // The provided context controls the timeout and cancellation of any // system commands executed during hardware identifier collection. // This method is safe for concurrent use. @@ -257,8 +257,8 @@ func (p *Provider) Diagnostics() *DiagnosticInfo { return p.diagnostics } -// Validate checks if the provided ID matches the current machine ID. -// The provided context is forwarded to [ID] if it needs to generate the ID. +// Validate reports whether the provided ID matches the current machine ID. +// The provided context is forwarded to [Provider.ID] if it needs to generate the ID. func (p *Provider) Validate(ctx context.Context, id string) (bool, error) { currentID, err := p.ID(ctx) if err != nil { @@ -269,7 +269,7 @@ func (p *Provider) Validate(ctx context.Context, id string) (bool, error) { } // hashIdentifiers processes and hashes the hardware identifiers with optional salt. -// Returns a hash formatted according to the specified FormatMode. +// Returns a hash formatted according to the specified [FormatMode]. func hashIdentifiers(identifiers []string, salt string, mode FormatMode) string { sort.Strings(identifiers) combined := strings.Join(identifiers, "|") @@ -284,7 +284,7 @@ func hashIdentifiers(identifiers []string, salt string, mode FormatMode) string return formatHash(rawHash, mode) } -// formatHash formats a 64-character SHA-256 hash according to the specified mode. +// formatHash formats a 64-character SHA-256 hash according to the specified [FormatMode]. // All formats produce power-of-2 lengths without dashes. func formatHash(hash string, mode FormatMode) string { if len(hash) != 64 { diff --git a/network.go b/network.go index 19fb363..18947f0 100644 --- a/network.go +++ b/network.go @@ -84,7 +84,7 @@ func collectMACAddresses(filter MACFilter, logger *slog.Logger) ([]string, error return macs, nil } -// isVirtualInterface returns true if the interface name matches a known +// isVirtualInterface reports whether the interface name matches a known // virtual, VPN, or bridge prefix. func isVirtualInterface(name string) bool { lower := strings.ToLower(name)