The CLV1 protocol is now publicRead the spec
Browse the library

Documentation libraryLicensing

Devices, HWID and transfers

Every license key runs on a limited number of machines. This guide explains how ClavisAUH recognises a machine, what happens when a key is full, how buyers move to a new machine under the rules you set, and how you watch, block or end sign-ins from the dashboard.

8 min read

How a machine is identified

Every activation and sign-in carries a description of the device:

  • fingerprint: a stable hash that stands for the whole machine (the HWID).
  • components: hashed hardware parts, each under its own name such as cpu, disk or board.
  • name and platform: the machine name and operating system, shown in the dashboard.
  • The running version and build hash, when you set appVersion and buildHash in the SDK.

Raw hardware values never leave the machine. The JavaScript SDK ships hashComponent, which trims, lowercases, hashes with SHA-256 and keeps the first 32 hex characters. The server only ever compares hashes with hashes.

import { ClavisClient, ClavisError, hashComponent } from "@clavisauh/sdk";

const clavis = new ClavisClient({
  baseUrl: "https://api.example.com",
  appId: "app_…",
  appPublicKey: "…",
  device: {
    // machineId, cpuId… are raw values your software reads itself
    fingerprint: hashComponent(machineId),
    components: {
      cpu: hashComponent(cpuId),
      disk: hashComponent(diskSerial),
      board: hashComponent(boardSerial),
      mac: hashComponent(primaryMac),
    },
    name: hostName,
    platform: "windows",
  },
  // Keep the device key in the OS keychain so the machine keeps its identity
  deviceKey: await loadDeviceKey(),
});

Besides the HWID, each machine has a device key (an Ed25519 key pair) that signs every request. Sessions are tied to that key, so a leaked session id is useless on another machine. Persist deviceKey instead of creating a new one on every launch.

The same machine after a reinstall or a new part

When a machine activates a key, the server compares it with the machines already holding a seat, in this order:

  1. Same fingerprint: it is the known machine.
  2. A different fingerprint, but at least N components with the same name and hash: still the known machine. N is the plan's fingerprintThreshold, 3 by default (valid from 1 to 8). The plan form in the dashboard does not show this field yet.

A known machine reuses its seat, and the server records its new details (fingerprint, components, IP, version). Reinstalling the OS or swapping one drive does not cost another seat.

Devices per key

A key's machine count comes from the plan's Machines field, or from Devices per key when you create keys in Licensing & releases › Licenses. The number is written into each key when it is created: editing the plan later does not change keys already issued. Your workspace plan sets a ceiling on Devices per key, shown in Account & organization › Plan & usage.

Seats are counted inside a transaction that locks the license row, so machines activating at the same moment can never exceed the limit. Sleeping machines (see below) do not hold a seat.

When a key is full

A machine beyond the limit is refused with device_limit (HTTP 403). The refusal is written to the activity log and counts as a Key sharing signal in Protect 24/7.

try {
  await clavis.activate(licenseKey);
} catch (err) {
  if (err instanceof ClavisError && err.code === "device_limit") {
    // Tell the buyer the key is in use on all its machines: free one, or contact the seller
  }
}

See the refused machine and let it in

When a buyer writes "my new PC won't activate", there is no guesswork:

  1. Open Licensing & releases › Licenses and click the key's row (or View details in the row menu).
  2. Under Devices, below the machine closest to the refused one, you see "Machine … was refused, compared with this one" with a list of components: a tick means it matches, a cross means it differs. Only component names appear, never a serial number.
  3. If the buyer really did replace the machine, click Let that machine in. The old machine's seat is freed and the new one gets in on its next activation.

This shows in Move history as "Removed by the seller" and never uses up the buyer's moves.

Sleeping machines and freed seats

A machine that neither signs in nor sends a heartbeat for a while gives its seat back, so buyers are not stuck behind a machine that broke or was sold.

  • Set the wait in the plan's Silent machine frees its seat after (hours) field. Empty is 72 hours, 0 is never.
  • A background job runs about every 15 minutes and puts overdue machines to sleep. In the license drawer a sleeping machine has a moon icon and reads Asleep, seat released.
  • For 7 days after falling asleep, if that same machine comes back (matched by fingerprint or components), it takes a free seat again, and that is not counted as a move. If the key filled up in the meantime, it gets device_limit like any other machine.
  • After 7 days asleep the machine is removed for good. If it returns later, it needs a free seat like a new machine.

Move rules

A move frees one machine's seat so another machine can use it. The rules live in the Device transfers section of a plan, under Licensing & releases › Sign-in & license plans (click a plan, or Add plan):

FieldMeaning
Per 30 daysHow many times the buyer may move in the last 30 days
Hours between transfersThe minimum gap between two buyer moves
Seller, Buyer, ResellerWho may move machines

These fields are the Seat Transfer Policy method, and they run in both protection modes. A number field left empty uses the default: 2 moves per 30 days, 24 hours apart.

Who can move, and how:

  • You, the seller: remove one machine with the X button (Free this seat) in the license drawer, or through the Seller API. Never limited, only recorded. To clear every machine of a key at once, pick Reset devices in the row menu; all of the key's sessions end too.
  • The buyer: when the plan ticks Buyer, the buyer frees a seat from your software with clavis.transfer() or with Remove in the customer portal. Both share the same allowance and cooldown.
  • A reseller: when the plan ticks Reseller, resellers can reset the devices of keys they sold through the Reseller API. Otherwise they get transfer_not_allowed.
try {
  const { remaining } = await clavis.transfer();
  // This machine's seat is free; remaining = moves left in the last 30 days
} catch (err) {
  if (err instanceof ClavisError && err.code === "transfer_limited") {
    // Out of moves or still cooling down: err.message says when to retry
  }
}

transfer() needs a live session, so call it before the buyer leaves the old machine. A refusal is HTTP 429 with transfer_limited (no moves left, or cooling down) or transfer_not_allowed (the plan does not let buyers move). The Device transfers row in the license drawer shows how many the buyer has used. Each buyer move sends the device.transferred webhook event; a new machine on a key sends device.added.

Blocking a machine across the app

A blocked machine cannot get into this app with any key, because the server checks its fingerprint against every blocked machine in the app. Its running sessions end at once, and every new sign-in gets device_blocked. This is the Device Denylist method, and a block applies in both protection modes.

Four ways to block:

  1. Open End users › Devices & sessions, tab Devices, click Block next to the machine and give a reason.
  2. Open App security › Protection. In the Needs you list, on a device subject, click Block device and give a reason. The incident drawer of a device has the same button.
  3. Through the Seller API: POST /devices/{id}/actions with {"action": "block"}, which needs the devices:write scope.
  4. Automatically: Graduated Enforcement in Act on its own mode (runs on Premium) blocks a machine when Key sharing or Unknown build signals repeat.

Lift a block with Unblock next to the machine on the Devices tab, in the license drawer or in the incident drawer, or Undo the action in the incident history.

Sessions

Each successful sign-in opens a session tied to the machine and its device key. A session lasts 1 hour without a heartbeat; with Short-Lived Session Lease on, it lasts three times the Pulse every interval. Every heartbeat() call extends it.

The Devices & sessions page

End users › Devices & sessions has two tabs. Sessions shows the software that is signed in right now and refreshes every 15 seconds: device, license, user, location (country and IP) and last seen. Devices lists every device on a key of this app, signed in or not, with its license, last IP and last seen.

On the Sessions tab, click the X (End session) at the end of a row to end one session. The software's next call gets session_expired and must sign in again. Ending a session does not take the machine off the key: to get the seat back, free it in the license drawer; to keep the machine out, block it.

Ending many sessions at once

This page has no button to end every session of the app yet. What you can use today:

  • The whole app: the Seller API's POST /apps/{appId}/sessions/revoke-all (sessions:write scope), which returns how many sessions ended.
  • One key: Suspend ends all of its sessions; Reset devices ends them and removes every machine.
  • One user: Ban or Reset password in End users › Users ends that person's sessions.

Concurrent Session Limit

The machine count limits where a key is installed; the session cap limits where it runs at the same time. A key with 3 machines and 1 session can live on a home PC and an office PC, but only one of them can be open at any moment.

  • The plan's Sessions at once field sets the cap, in both protection modes. When it is empty, there is no cap.
  • Signing in again on the same machine replaces that machine's old session and does not count twice.
  • When live sessions on other machines already reach the cap, a new sign-in gets session_limit and Protect records a Session overflow signal.

Common error codes

CodeHTTPWhen
device_limit403The key is on all its machines
device_blocked403The machine is blocked in this app
device_revoked403The machine was removed from the license
session_limit403Too many sessions at once
session_expired401The session ended or lapsed, sign in again
transfer_limited429No moves left, or still cooling down
transfer_not_allowed429 (403 for resellers)The plan does not let this party move machines

Every refusal at sign-in appears in App security › Activity log with its reason code. The full list is in the protocol reference.

How can we help?

Frequently asked

What is ClavisAUH?

A licensing and authentication service for desktop software: license keys, device binding, end-user sign-in and signed responses. All run from one dashboard

Why can't a cracker fake your server?

The SDK only trusts answers signed with your app's private key. That key never leaves our servers, so a fake server can't sign and gets rejected

What ends up in my binary?

Your app id and public key. Nothing secret, because each device makes its own key pair on first run

Does it work offline?

Yes. Every plan gets a signed offline token for 7 days, and your app checks it locally. Premium lets you choose how long. A revoked license can keep working offline for that time, unless Online-Only Mode is on

Which languages are supported?

JavaScript and TypeScript today. C++, C# and Python are next, on the same open protocol

Is Basic really free?

Yes. No time limit, no card. Move to Premium when you need more

Get in touch