Guides
Device Fingerprinting
Lock licenses to specific devices using hardware-based fingerprints.
How It Works
A fingerprint is a unique device identifier generated by hashing hardware attributes: CPU model, disk serial number, hostname, and MAC address. The SDK generates this automatically — you do not need to compute it yourself.
Python
from licentric import Licentric
client = Licentric(api_key="lk_live_your_key_here")
# The SDK generates a fingerprint from hardware attributes:
# CPU model, disk serial, hostname, MAC address
fingerprint = client.fingerprint()
# => "a1b2c3d4e5f6..."TypeScript
import { Licentric } from "@licentric/sdk";
const client = new Licentric({ apiKey: "lk_live_your_key_here" });
// The SDK generates a fingerprint from hardware attributes:
// CPU model, disk serial, hostname, MAC address
const fingerprint = client.fingerprint();
// => "a1b2c3d4e5f6..."Uniqueness Levels
Fingerprint uniqueness controls how strictly devices are tracked across your licenses.
| Level | Behavior | Use Case |
|---|---|---|
| PER_LICENSE | Fingerprint is unique per license key. Same device can be activated on multiple licenses. | Most common. Users can run multiple products on the same device. |
| PER_POLICY | Fingerprint is unique across all licenses under the same policy. | Prevent one device from holding multiple licenses of the same type. |
| PER_ACCOUNT | Fingerprint is unique across your entire account. | Strictest. One device can only ever hold one license from your organization. |
Machine Activation Flow
When a license is validated with a fingerprint for the first time, the API returns FINGERPRINT_NOT_FOUND. Your app should then call the activate endpoint to register the device.
Python
def activate_machine(license_key: str) -> bool:
"""Full machine activation flow."""
fingerprint = client.fingerprint()
# Step 1: Validate the license with fingerprint
result = client.validate(
key=license_key,
fingerprint=fingerprint
)
if result.valid:
# Already activated on this device
return True
if result.code == "FINGERPRINT_NOT_FOUND":
# Step 2: This device is not yet activated — activate it
machine = client.machines.activate(
license_id=result.license_id,
fingerprint=fingerprint,
name=get_hostname()
)
print(f"Activated device: {machine.name}")
return True
if result.code == "MACHINE_LIMIT_EXCEEDED":
print("Maximum devices reached. Deactivate another device first.")
return False
print(f"Cannot activate: {result.code}")
return FalseTypeScript
async function activateMachine(licenseKey: string): Promise<boolean> {
const fingerprint = client.fingerprint();
const result = await client.validate({
key: licenseKey,
fingerprint,
});
if (result.valid) {
return true;
}
if (result.code === "FINGERPRINT_NOT_FOUND") {
await client.machines.activate({
licenseId: result.licenseId,
fingerprint,
name: os.hostname(),
});
return true;
}
if (result.code === "MACHINE_LIMIT_EXCEEDED") {
throw new Error("Maximum devices reached. Deactivate another device first.");
}
throw new Error(`Cannot activate: ${result.code}`);
}Container Environments
Docker and Kubernetes environments lack stable hardware identifiers. The SDK detects the LICENTRIC_FINGERPRINT environment variable and uses it instead of hardware-based fingerprinting.
# Docker / Kubernetes environments
# Set environment variables for stable fingerprints:
#
# docker run -e LICENTRIC_FINGERPRINT="container-abc-123" myapp
# --- or in Kubernetes ---
# env:
# - name: LICENTRIC_FINGERPRINT
# valueFrom:
# fieldRef:
# fieldPath: metadata.uid
#
# The SDK auto-detects LICENTRIC_FINGERPRINT and uses it
# instead of hardware-based fingerprinting.
import os
from licentric import Licentric
client = Licentric(api_key="lk_live_your_key_here")
# In containers, fingerprint() reads LICENTRIC_FINGERPRINT
fingerprint = client.fingerprint()
# => "container-abc-123" (from env var)metadata.uid from the downward API for stable pod fingerprints, or a StatefulSet pod name for consistent identity across restarts.Deactivating Devices
When a user reaches their machine limit, they can deactivate an old device to free up a slot.
# Deactivate a device to free up a machine slot
def deactivate_machine(license_id: str, machine_id: str):
"""Remove a device from the license."""
client.machines.deactivate(
license_id=license_id,
machine_id=machine_id
)
print("Device deactivated successfully.")
# List all machines on a license
machines = client.machines.list(license_id="lic_abc123")
for machine in machines:
print(f"{machine.name} — {machine.fingerprint}")