.NET SDK — device fingerprints
Activation binds a seat to a device fingerprint — a stable identifier for the install. The fingerprint is what lets the platform tell “the same machine reconnecting” from “a new machine claiming a seat”, so a restart or redeploy reuses its seat instead of consuming a new one.
Why stability matters
Section titled “Why stability matters”Seats are counted per fingerprint. If the fingerprint changes between runs, each run looks like a new device: the entitlement fills up and further activations are refused. A fingerprint that stays constant across process restarts is therefore the single most important property — prefer a value you can guarantee is stable over one derived from volatile hardware.
Where the fingerprint comes from
Section titled “Where the fingerprint comes from”The SDK resolves it through INodeIdProvider:
- From configuration — the default provider reads
Oclavex:LicenseConsumer:NodeId. Set it to a value that is stable for the install (for example, a per-deployment id you persist). - Derived from the device — a machine-derived identifier (machine name and network adapter). Convenient, but on machines with VPNs or virtual adapters the “first” adapter can vary between runs, so it is best treated as a fallback rather than the primary identity.
Compose one from device traits
Section titled “Compose one from device traits”DeviceIdDeviceFingerprint composes a fingerprint from device traits and hashes it on
the device — raw values never leave the machine. Pass it to UseNodeId on the
fluent builder, or register
it as the INodeIdProvider:
var fingerprint = new DeviceIdDeviceFingerprint{ FingerprintMethod = DeviceIdDeviceFingerprint.FingerprintMethods.MacAddress | DeviceIdDeviceFingerprint.FingerprintMethods.MachineName | DeviceIdDeviceFingerprint.FingerprintMethods.OsVersion};The default is MacAddress | MachineName. Available traits: MachineName,
OsVersion, MacAddress, UserName.
The selected values are concatenated and hashed with SHA-256, then encoded Base64Url. That digest is the fingerprint; the raw traits stay on the machine. If none of the selected traits yields a value, the machine name is used so the provider always returns something rather than an empty fingerprint.
MacAddress picks one adapter and holds it for the life of the process. The choice
ignores loopback, tunnel and dial-up adapters and the usual virtual ones, and does not
depend on enumeration order, so a VPN or container adapter coming up mid-session does not
change the fingerprint underneath a running install.
Supply your own
Section titled “Supply your own”For a value you control — a tenant id, a provisioned device id, a stored GUID — register
a scoped INodeIdProvider before AddMonetizeItLicenseConsumer:
builder.Services.AddScoped<INodeIdProvider>(_ => StaticNodeId.Create(deploymentId));In a multi-tenant host, resolve the fingerprint from the current tenant so each tenant’s
seats are counted independently — pair this with the per-tenant ILicenseKeyProvider from
install and configure.
Short fingerprints for manual / air-gapped entry
Section titled “Short fingerprints for manual / air-gapped entry”A hashed fingerprint is normally moved as a file or an API call, not typed by hand. When the
only channel available is a person reading a value off one screen and entering it on
another — a fully air-gapped machine, a value read aloud over the phone — a 40+ character
hash is impractical. StaticNodeId.CreateShort produces a short, grouped alternative instead:
builder.Services.AddScoped<INodeIdProvider>(_ => StaticNodeId.CreateShort(deploymentId));The result looks like 7K4M-QX2P-9J — an 8-character payload grouped for readability plus a
2-character checksum, so a mistyped fingerprint is rejected rather than silently activating
the wrong device.
If you build your own entry box for that value, check it with TryCanonicalize and send back
what it gives you:
if (!ShortFingerprintSigner.Instance.TryCanonicalize(typed, out var fingerprint)) return "That is not a valid device fingerprint.";It accepts any case and any separators, and forgives the look-alike substitutions people most
often make copying a code by hand (O/0, I/L/1) — the same tolerance an activation code
has. What it returns is the exact value CreateShort produced. Send that, not what was typed:
a bare fingerprint is matched by exact equality — send the typed text and your check
passes, but the server never matches the device.
Component hashes and drift matching
Section titled “Component hashes and drift matching”Beyond the composite hash, the provider also surfaces each trait as its own one-way
hash (Components), keyed MachineName, MacAddress, OsVersion and UserName.
Sending components lets the server recognise a device whose fingerprint drifted, such as
a swapped network card or an OS upgrade, instead of treating it as a new device and
taking another seat.
How much drift is tolerated is the entitlement’s choice, not the client’s:
| Strategy | A returning device is recognised when |
|---|---|
MatchAll |
the whole fingerprint is identical. This is the default. |
MatchAny |
at least one component still matches |
MatchTwo |
at least two components still match |
MatchMost |
a majority of components still match |
Two things follow from the strategy living on the server. The tolerant strategies
apply only when the client sends components, so a client that reports a bare
fingerprint is always matched exactly, whatever the entitlement says. And sending
components never loosens anything on its own: an entitlement left on MatchAll keeps
exact matching. Both halves have to agree before drift is forgiven, which is why this is
safe to adopt gradually.
Sending components is additive, so existing clients keep working unchanged.
Where to find it
Section titled “Where to find it”In the portal, open an entitlement and go to the Policies tab. The setting is Fingerprint component matching.
When activation is refused
Section titled “When activation is refused”An activation code can carry rules about the fingerprint, which surface as these errors:
- “A device fingerprint is required to activate this license.” — the code requires the
device to identify itself and the request carried no fingerprint. Check that your
INodeIdProvideris returning a value. - “This activation code is locked to a different device.” — the code is bound to one machine’s fingerprint and this is not that machine. If it should be, the bound value on the code needs updating; a fingerprint that changed on its own points at an unstable provider, which why stability matters covers.
Both are described from the vendor’s side in Locking a code to one device.
Server and cloud workloads
Section titled “Server and cloud workloads”Where hardware traits are meaningless (containers, autoscaling), derive a stable logical fingerprint instead:
using Revenusion.MonetizeIt.Client.LicenseConsumer;
builder.Services.AddScoped<INodeIdProvider>(_ => StaticNodeId.Create(customerIdentifier));Same identifier, same fingerprint — one seat per customer environment across restarts and instances.