Activating a License on a Device: How Hardware IDs Work
What actually happens when an app calls activate(): hardware fingerprinting, activation limits, and reactivation.

Originally published on the PermitCore blog.
You've generated a license key. Now a real customer installs your app, pastes that key, and the app calls activate() for the first time. Here's what actually happens in that call: hardware fingerprinting, activation limits, and why reinstalling on the same machine shouldn't cost a seat.
Validate vs. Activate: not the same call
These two are easy to mix up, and they do genuinely different things:
- Validate checks that the key is real and still active. It doesn't touch the activation count, so it's safe to call on every app launch.
- Activate does everything Validate does, plus permanently ties the key to a device and increments
CurrentActivations. Call it once, the first time a customer installs your app.
What "device" actually means
For a node-locked license, the SDK needs a stable identifier for the machine it's running on: something that survives reboots and app updates but isn't trivial to spoof. There's no universal API for this across operating systems, so each platform needs its own approach:
private static string GetHardwareId()
{
// Windows: motherboard serial + CPU ID, hashed together
if (OperatingSystem.IsWindows())
{
var board = GetWmiValue("Win32_BaseBoard", "SerialNumber");
var cpu = GetWmiValue("Win32_Processor", "ProcessorId");
return Sha256Hex(board + cpu);
}
// macOS: IOPlatformUUID
if (OperatingSystem.IsMacOS())
return ReadMacOsPlatformUuid();
// Linux: /etc/machine-id
return File.ReadAllText("/etc/machine-id").Trim();
}
All 8 official PermitCore SDKs (.NET, Node.js, Python, Java, PHP, C++, Go, Unity) include a working implementation of this, so you only write it yourself if you're integrating over raw REST.
Calling Activate
var result = await client.ActivateAsync(new ActivateRequest
{
LicenseKey = enteredKey,
Version = "1.0",
DeviceId = GetHardwareId(),
DeviceName = Environment.MachineName
});
if (!result.IsValid)
{
// result.ErrorCode is one of: NotFound, ProductInactive, Expired, VersionBlocked,
// IpBlocked, RegionBlocked, VendorSubscriptionLapsed, PoolExhausted, SeatsExhausted, WrongProduct
ShowActivationError(result.ErrorCode);
return;
}
SaveLicenseLocally(result);
What you see on the admin side
Every successful activation shows up immediately on the license's detail page: the request's user agent and timestamp, and (when available) a rough geographic location resolved from the request IP. That's exactly what your support team needs when a customer writes "it says I'm out of activations but I only installed it once".
Reactivating the same device
Customers reinstall software constantly: OS reinstalls, a new hard drive, a factory reset. PermitCore recognizes when an activation request comes from a hardware ID that's already recorded against that license and treats it as a reactivation, not a new seat. It doesn't consume another slot out of MaxActivations, even if the license is already fully seated.
Why this matters: without that exemption, a customer who reinstalls Windows twice on a 2-seat license would be locked out on the third install, having never used more than one machine at a time. It's a small detail, but it's the difference between licensing that gets out of your way and a support queue full of angry emails.
Deactivating a device
When a customer replaces a machine, you (or they, via the self-service Customer Portal) can remove one specific device from a license, freeing that seat for a new activation without touching the other devices on the same key.
What's next
Activation assumes your app can reach the license server. The follow-up on the PermitCore blog covers offline grace mode: keeping your app working without a connection, and how to stop a customer from editing a local file to stay "licensed" forever.
How do you identify devices in your own apps? Hashed hardware IDs, machine-id, or something else? I'd love to hear what's worked for you.
About the author
- Software licensing with a built-in branded Store. https://permitcore.dev
Comments
Join the discussionNo comments yet. Be the first to comment.