Browse the library
Documentation librarySecurity
The two protection modes
Every app runs one of two modes. Standard Protection is the default and has nothing to set. Advanced Protection adds the methods you pick. This guide explains both modes, every method and how to move between them.
13 min read
Which mode to use
| Standard Protection | Advanced Protection | |
|---|---|---|
| Made for | Sellers who want to add licensing and sell | Sellers who want to fight cracking and key sharing themselves |
| What you set | Nothing | The methods you turn on, in five groups |
| Your work | Add the SDK and the public key | The same, plus picking methods and building the Protection Kit |
| Answers to your software | No change | Can change, depending on the methods you turn on |
| Default | Yes | Your choice |
Both modes are on every plan. Inside Advanced, three things need Premium: Graduated Enforcement acting on its own, alerts through Discord, Telegram and webhooks, and choosing how long offline tokens last.
The Methods tab
Open an app and choose App security › Protection, then the Methods tab. From top to bottom, it shows:
- Two cards, Standard Protection and Advanced Protection, marked In use or Use this mode
- Standard Protection: the ten methods, labelled Always on
- Advanced Protection: the methods in five groups, as a list of names until you turn this mode on
- Signing keys: prepare and activate a new signing key, in Advanced only, with a sign-in from the last 15 minutes in production
- Change history
Every workspace member can open this tab, but only the Admin and Owner roles can change anything. The Overview tab shows the mode in use and how many methods are on, with a link back here.
Moving between modes
- On the Methods tab, click Use this mode on the card you want
- Moving to Standard asks Go back to Standard Protection?. Click Use Standard to confirm
Changes apply to your software's next requests, within about 10 seconds.
Going back to Standard keeps your saved choices. They stop working, and they return when you turn Advanced on again. This covers every switch and every access rule.
Standard Protection
Every app has these ten methods. There is nothing to set, and nobody can turn them off, the Owner included.
| Method | What it does |
|---|---|
| Server-Side Enforcement | The server decides who gets in, your software only asks |
| Response Signing | The server signs every answer, and the official SDK refuses answers that are unsigned, signed by an unknown key, meant for another request or too old |
| Replay Protection | Each nonce is accepted once, so capturing and resending an old request achieves nothing |
| No Embedded Secrets | A build needs only the app id and a public key, never an API key or a shared password |
| TLS with HSTS | Traffic stays on HTTPS, browsers refuse plain HTTP |
| Device Binding (Node Locking) | A key works only on the devices it was activated on, and a machine is recognised after a reinstall or a small hardware change |
| Device-Bound Sessions | A session belongs to the device key that created it, so a leaked session id is refused on any other machine |
| Rate Limiting | Each IP gets at most 20 wrong keys or passwords per 15 minutes per app, then receives too_many_attempts |
| Encryption at Rest | Signing keys, webhook secrets and secret variables are encrypted with AES-256-GCM, and license keys are stored only as an HMAC |
| Audit Logging | Sign-ins, and refusals at sign-in, are recorded with a reason |
A few details worth knowing:
- Standard ignores every saved switch and all access rules
- Security events are still written to the log, but there is no bell notification, no email or chat alert and no automatic response
- What you set on a license plan or the Releases page keeps running, and so do device blocks and IP blocks
- For Rate Limiting, clients on the CLV1.1 protocol get the same
invalid_credentialscode for every reason a key is refused (unknown, suspended, revoked, expired), after the same minimum delay, so someone guessing keys cannot tell a real key from a fake one. The exception is a machine that already holds a seat on that key: it still gets the detailed reason, so your software can explain the problem to the user. - More detail: the protocol specification for Response Signing and Replay Protection, and Devices, HWID and transfers for Device Binding (Node Locking)
Advanced Protection
Everything in Standard, plus the methods below. A method has a switch on the Methods tab, or a link to the page where you set it. Every switch is off until you turn it on.
License Enforcement
| Method | Set on | What it does | Keep in mind |
|---|---|---|---|
| Seat Transfer Policy | License plan | How often a customer can move a key to a new device | A tight limit annoys customers who change machines |
| Concurrent Session Limit | License plan | How many devices can run one key at the same time | A dead session holds its place until it expires |
| Short-Lived Session Lease | Switch | Sessions last minutes, so a blocked key stops quickly | A short lease means more calls to the server |
| Signed Offline License | License plan, Premium to set the length | Every plan gets a signed token for 7 days. Premium chooses how long | A revoked license can keep working offline for up to that window, unless Online-Only Mode is on |
| Online-Only Mode | Switch | No offline token is ever issued | Without a network, the software does not run |
| Clock Tampering Detection | Protection Kit | Notices a device clock moved back to stretch a license | No offline check is fully safe |
| Minimum Version Enforcement | Releases | Lower versions are refused at sign-in, if your software reports its version | Customers must update to keep going |
Access Control
| Method | Set on | What it does | Keep in mind |
|---|---|---|---|
| IP Allowlist and Blocklist | Access Rules tab | Block or allow addresses and networks | VPN and NAT make an IP less exact |
| Geo-Blocking | Access Rules tab | Block or allow whole countries | Shows only when the server sits behind Cloudflare, and a VPN gets around it |
| Device Denylist | Devices tab of End users › Devices & sessions | Stop one device from signing in | New hardware can look like a new machine |
| Team MFA Enforcement | Switch | Teammates need two-factor or a passkey to open this app | Applies only to people in the workspace |
Access rules only run in Advanced Protection. IP blocks made by hand or by Graduated Enforcement, and device blocks, apply in both modes. See Access control and Devices, HWID and transfers.
Code Protection
Most of these run inside your own software, through the Protection Kit. That makes an attack harder, never impossible, and it does not replace a sound design.
| Method | Set on | What it does | Keep in mind |
|---|---|---|---|
| Build Integrity Verification | Switch | Flags or refuses a build whose hash you did not release. Your software must send the hash | A patched build can report a fake hash |
| Code Signing | Protection Kit | Buyers can check the software really comes from you | Proves origin, does not stop cracking |
| Code Obfuscation | Not in the Kit yet | Makes compiled code harder to read | Can always be bypassed, it only adds effort |
| Redundant License Checks | Protection Kit | Checks in several places, so one patch is not enough | It only adds effort |
| Anti-Debugging | Not in the Kit yet | Notices a debugger attached to your software | Almost impossible to stop fully, reports by default |
| Virtual Machine Detection | Not in the Kit yet | Notices your software running in a virtual machine | Can catch honest customers who use virtual machines, reports by default |
| Certificate Pinning | Not in the Kit yet, needs this server's TLS pins | Trusts only your API's key, even behind a proxy | A wrong key change locks out every customer |
Detection and Response
| Method | Set on | What it does | Keep in mind |
|---|---|---|---|
| Threat Signals | Overview tab | Odd behaviour is scored and written to the log | Signals sent by the client can be faked |
| Graduated Enforcement | Switch | Warn, ask to sign in again, then suspend as risk rises | Watch it for a while before you let it act. Acting on its own needs Premium |
| Forensic Watermarking | Switch | Each license carries a hidden mark that traces a leak | It traces, it does not prevent. State it in your terms of sale |
| Alert Routing | Monitoring & alerts › Security, tab Alerts | Send alerts to email, Discord, Telegram or a webhook | Every channel except email needs Premium |
Key and Release Integrity
| Method | Set on | What it does | Keep in mind |
|---|---|---|---|
| Signing Key Rotation | Switch | Set how often to replace the signing key. The keys card tells you when it is due | A reminder only, and your software must get the new key first |
| Signed Releases | Releases | Each release is signed, so your software can verify an update | If your key leaks, the signature means nothing |
Turning a method on
- Click the switch on the right of the row. The change is saved at once and you see Saved
- Methods with settings show their fields once they are on. A number field saves when you leave it, and a value outside the allowed range is ignored
- For Build Integrity Verification and Graduated Enforcement, click the mode you want. The choice is saved at once
Changes apply to your software's next requests, within about 10 seconds. The methods with a switch:
| Method | Settings (default) |
|---|---|
| Short-Lived Session Lease | Pulse every 60 to 3600 seconds (300) |
| Online-Only Mode | None |
| Team MFA Enforcement | None |
| Build Integrity Verification | Warn only or Block (Warn only) |
| Graduated Enforcement | Alerts only or Act on its own (Alerts only); Alert at, Sign out at, Suspend at 10 to 100 (40, 60, 80) |
| Forensic Watermarking | None |
| Signing Key Rotation | Rotate every 30 to 730 days (180) |
Short-Lived Session Lease
This method shortens sessions: a session expires after three missed pulses, which is 15 minutes at the default 300 seconds. With it off, a session lives for 1 hour after the last sign-in or heartbeat. The server announces the pulse through session.heartbeatSeconds and nextCheckSeconds in the sign-in answer and securityHints.heartbeatSec in init(). The SDK does not send heartbeats by itself, so your software calls it on that rhythm:
const grant = await clavis.activate(licenseKey);
// Lease on: use the server's pulse. Lease off: sessions last 1 hour, every 30 minutes is plenty
const everySec = grant.session.heartbeatSeconds ?? 30 * 60;
setInterval(() => {
clavis.heartbeat().catch(onSessionLost); // onSessionLost is your own function
}, everySec * 1000);
Online-Only Mode
This method turns offline use off: the server stops issuing offlineToken and init() sends maxOfflineSec: 0, so the SDK trusts no offline token at all. A revocation or suspension therefore lands on the next connection. A token already sitting on a machine that never manages to call init() still runs until its own exp. Tokens come on every plan, so this is the only way to turn them off. See Offline licenses.
Team MFA Enforcement
This method requires every member, the Owner included, to use 2FA or a passkey before they can open this app. A member with neither gets two_factor_required on every page and API call for the app. So that you cannot lock yourself out, the server only lets you turn it on once your own account has one of them; otherwise you see Turn on 2FA for your own account first. Turn 2FA on or add a passkey in Account & organization › Account security. See Dashboard account security.
Build Integrity Verification
This method checks the running binary against your releases. The SDK sends appVersion and buildHash (the SHA-256 of the running file, 64 lowercase hex characters) when you set them:
const clavis = new ClavisClient({
baseUrl: "https://api.example.com",
appId: "app_…",
appPublicKey: "…",
device: { fingerprint: hashComponent(machineId) },
appVersion: "1.4.0",
buildHash: await sha256OfRunningFile(),
});
A build is unknown when its hash matches no file in Licensing & releases › Releases and the app has at least one published release. An unknown build always raises an Unknown build signal, in either mode. This method decides the rest: Warn only still lets it sign in, Block refuses it with unknown_build. With it on, init() also sends reportTamperSignals: true to the SDK. See Signed releases and updates.
Anti-Debugging and Virtual Machine Detection
The Kit has no module for this yet, so the detection is your own code. Your software can report what it detects. Each report raises a Tampering detected signal, which adds risk but never blocks anyone on its own. It needs a live session, and each session counts at most 5 reports an hour:
if (debuggerDetected()) {
await clavis.request("/v1/client/report", { kind: "debugger" }); // or "hook", "vm", "patched"
}
Graduated Enforcement
This method lets Protect 24/7 respond on its own to the risk score (0 to 100) of each license, device and IP:
- Alerts only: when a risk score crosses Alert at, Protect records an Alerted action on the incident and leaves the customer alone
- Act on its own: adds automatic actions. A license crossing Sign out at has all its sessions ended; a license reaching Suspend at is suspended. A device that keeps raising Key sharing or Unknown build is blocked. An IP guessing keys is blocked for 24 hours.
Act on its own is a Premium feature by default. On a plan without it you can still select it, but the page shows Acting on its own runs on Premium, your plan alerts only and Protect only alerts. Automatic suspensions and blocks can all be undone from Protect 24/7.
Forensic Watermarking
This method stamps a buyer mark on every successful sign-in: the license.mark field, shaped <10 hex>.<6 hex>, where the first half names the license and the second half names the machine. Offline tokens carry the same mark in their wm field. Show the mark in your About box, a log header or the files users save; when a copy leaks, the mark tells you which license on which machine it came from.
const grant = await clavis.activate(licenseKey);
if (grant.license.mark) aboutDialog.setFooter(`Licensed: ${grant.license.mark}`);
To trace a leak, paste the mark into the search box of Licenses, or send it to the Seller API's POST /seller/v1/licenses/lookup as { "mark": "…" }, which also names the machine when the second half matches its current device key. A mark found in a file is only a lead: anyone can copy a mark into a file.
Signing Key Rotation
This method puts key rotation on a schedule: the Signing keys section of the tab shows Rotation due with a date, which turns into Rotation overdue once it passes. Rotation never runs by itself: click Prepare a new key, ship a build that carries both keys, then click Activate the new key; the old key stays trusted for 14 more days. See Signing keys and rotation.
The Protection Kit
The Code Protection methods go into your software through App security › Protection Kit. Pick a language and the methods you want, then build and download the kit. You can start from three presets: Core, Recommended or Full.
When a method needs something on the server, the page lists it under Turn on server side. Turning a switch on from there moves the app to Advanced Protection. See Protection Kit.
Change history
Change history lists the 20 most recent saves, newest first, each with the name (or email) of the person and when it happened:
- Moved to {mode}: the app changed mode
- Changed {items}: the methods that changed, for example Changed Short-Lived Session Lease, Graduated Enforcement
Every save is also written to the workspace audit log as security.updated or security.mode. Until anyone saves, the section reads No changes yet.