Skip to main content

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

fingerprint.py
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

fingerprint.ts
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.

LevelBehaviorUse Case
PER_LICENSEFingerprint 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_POLICYFingerprint is unique across all licenses under the same policy.Prevent one device from holding multiple licenses of the same type.
PER_ACCOUNTFingerprint 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

activate.py
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 False

TypeScript

activate.ts
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.

container.py
# 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)
Kubernetes tip
Use 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.py
# 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}")