Volt CLI: source-available under AGPSL v5.0
Complete infrastructure platform CLI: - Container runtime (systemd-nspawn) - VoltVisor VMs (Neutron Stardust / QEMU) - Stellarium CAS (content-addressed storage) - ORAS Registry - GitOps integration - Landlock LSM security - Compose orchestration - Mesh networking Copyright (c) Armored Gates LLC. All rights reserved. Licensed under AGPSL v5.0
This commit is contained in:
95
pkg/license/fingerprint.go
Normal file
95
pkg/license/fingerprint.go
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
Volt Platform — Machine Fingerprint Generation
|
||||
Creates a unique, deterministic identifier for the current node
|
||||
*/
|
||||
package license
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GenerateFingerprint creates a machine fingerprint by hashing:
|
||||
// - /etc/machine-id
|
||||
// - CPU model from /proc/cpuinfo
|
||||
// - Total memory from /proc/meminfo
|
||||
// Returns a 32-character hex-encoded string
|
||||
func GenerateFingerprint() (string, error) {
|
||||
machineID, err := readMachineID()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read machine-id: %w", err)
|
||||
}
|
||||
|
||||
cpuModel, err := readCPUModel()
|
||||
if err != nil {
|
||||
// CPU model is best-effort
|
||||
cpuModel = "unknown"
|
||||
}
|
||||
|
||||
totalMem, err := readTotalMemory()
|
||||
if err != nil {
|
||||
// Memory is best-effort
|
||||
totalMem = "unknown"
|
||||
}
|
||||
|
||||
// Combine and hash
|
||||
data := fmt.Sprintf("volt-fp:%s:%s:%s", machineID, cpuModel, totalMem)
|
||||
hash := sha256.Sum256([]byte(data))
|
||||
|
||||
// Return first 32 hex chars (16 bytes)
|
||||
return fmt.Sprintf("%x", hash[:16]), nil
|
||||
}
|
||||
|
||||
// readMachineID reads /etc/machine-id
|
||||
func readMachineID() (string, error) {
|
||||
data, err := os.ReadFile("/etc/machine-id")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(string(data)), nil
|
||||
}
|
||||
|
||||
// readCPUModel reads the CPU model from /proc/cpuinfo
|
||||
func readCPUModel() (string, error) {
|
||||
f, err := os.Open("/proc/cpuinfo")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.HasPrefix(line, "model name") {
|
||||
parts := strings.SplitN(line, ":", 2)
|
||||
if len(parts) == 2 {
|
||||
return strings.TrimSpace(parts[1]), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("model name not found in /proc/cpuinfo")
|
||||
}
|
||||
|
||||
// readTotalMemory reads total memory from /proc/meminfo
|
||||
func readTotalMemory() (string, error) {
|
||||
f, err := os.Open("/proc/meminfo")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.HasPrefix(line, "MemTotal:") {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) >= 2 {
|
||||
return fields[1], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("MemTotal not found in /proc/meminfo")
|
||||
}
|
||||
Reference in New Issue
Block a user