Browse the library
Documentation librarySecurity
Activity log
Every time your software activates a key, signs in, starts a trial or downloads an update, ClavisAUH writes one line: what happened, whether it was allowed or denied, and why. When a customer says "it won't let me in", this is the first page to open.
10 min read
Opening the log
- Go to Workspace › Apps and open the app.
- Choose App security › Activity log. Inside an app you can also press
gtheny.
Every member of the workspace can view this page. The newest line is on top, the list refreshes itself every 20 seconds and loads 50 lines at a time; click Load older at the bottom to keep going.
To look at one key only, open Licensing & releases › Licenses and click the key's row (or View details in the row menu). The Recent SDK calls section of the drawer lists the latest 10 log lines for that license, with the reason when one was denied.
What each line records
| Field | Content |
|---|---|
| Event | The kind of request, such as license.activate (see the table below) |
| Outcome | ok, denied or error |
| Reason | The reason code for a denial or error, the same err.code the SDK receives |
| License | The license involved, once the server has identified it |
| User | The end-user account, for successful registrations, sign-ins and redemptions |
| Device | The machine involved, for successful requests |
| IP | The request's IP address |
| Country | Country code of requests from the software, only when the API server runs behind Cloudflare |
| Time | When the line was written |
The table on the page shows an outcome dot, Event, Reason, IP (hidden on narrow screens) and Time as a relative time; hover over the time for the exact date and hour. License, user, device and country are stored with each line but have no column yet; to see activity per key, use the Recent SDK calls section mentioned above.
The three outcomes:
- Allowed (green dot): the request succeeded.
- Denied (red dot): the request was well formed but the license state or your rules refused it. These lines can carry the license involved, but never a user or a device.
- Errors (amber dot): something failed unexpectedly on the server. The reason is always
internal_error(Server error) and no license is attached.
Events that are recorded
| Event | Dashboard label | When |
|---|---|---|
license.activate | Key activation | clavis.activate(key) |
trial.start | Trial | clavis.trial() |
user.register | User registration | clavis.register(...) |
user.login | User sign-in | clavis.login(...) |
user.redeem | Key redeemed | clavis.redeem(key) |
device.release | Seat released by the buyer | clavis.transfer() |
session.logout | Sign-out | A successful clavis.logout() |
release.check | Update check | clavis.checkUpdates() when the channel has a release |
release.download | Update downloaded | The software downloads a file through a link from the update manifest |
client.report | SDK tamper report | The SDK reports tampering or answers it could not verify |
portal.device_release | (shown as the raw code) | The buyer removes a machine in the customer portal |
The first six events are logged with all three outcomes. The other five are logged only when they succeed. Download links you open from the dashboard do not create a release.download line. For client.report, Allowed means the server accepted the report, not that the machine is clean; at most 5 reports per session per hour are logged.
What the log does not contain
init(),heartbeat()andvariables()write no line, even when they fail (for examplesession_expiredon a heartbeat). Session errors oncheckUpdates()are not logged either.- Requests stopped at the protocol layer, before any handler runs: missing headers, a bad signature, a replayed request, a wrong clock (
clock_skew), a malformed body, a paused app, an IP blocked by Protect, or a rule on the Access Rules tab (access_blocked). The software still gets the exact error code, and most of these are recorded by Protect 24/7 as signals such as Forged signature, Replayed request, Clock drift or Blocked by an access rule. Forclock_skew, the JavaScript SDK corrects its clock from the server and retries once on its own. - The codes
unsigned_response,unknown_key,invalid_response_signatureandstale_responseare raised inside the SDK when it cannot verify an answer, so they never appear as a reason in the log. On the next answer that does verify, the JavaScript SDK reports them to the server and you see an SDK tamper report line.
Filtering
The bar at the top of the page has four options: All, Allowed, Denied and Errors. Your choice is kept in the URL (for example ?outcome=denied), so you can bookmark it or send a teammate a link that opens straight onto the denials. Today the page filters by outcome only.
When a customer reports a problem, the quickest route is:
- Open App security › Activity log and choose Denied.
- Find the line by the time they tried and their IP.
- Read the Reason column and look it up below.
- If you know the key, open that license for its Recent SDK calls and its devices.
Denial reasons
The Label column is the text shown under Reason. A code without a label is shown as is. The protocol's full list of error codes is in the protocol reference.
Keys, licenses and sign-in
| Code | Label | Meaning |
|---|---|---|
invalid_credentials | These credentials are not valid | Wrong username or password. With the JavaScript SDK (protocol CLV1.1), a key that is unknown, suspended, revoked or expired also answers this code, unless the asking machine already holds a seat on that key, so someone guessing keys cannot tell which ones exist |
invalid_key | Unknown key | The key does not belong to this app; only older CLV1 clients receive this code |
license_expired | License expired | Past its expiry date and the plan's grace days |
license_suspended | License suspended | The key was suspended with Suspend |
license_revoked | License revoked | The key was revoked with Revoke, or deleted |
too_many_attempts | Too many attempts | One IP entered a wrong key or password 20 times for this app; that IP waits up to 15 minutes, other IPs are unaffected |
mode_not_allowed | wrong sign-in door | The software called something the app's sign-in mode does not accept; see Sign-in modes and trials |
Devices and sessions
| Code | Label | Meaning |
|---|---|---|
device_limit | Device limit reached | The key is on as many machines as it allows; see Devices, HWID and transfers |
device_blocked | device blocked | The machine is blocked in this app |
session_limit | too many sessions at once | The key reached its concurrent session limit |
session_device_mismatch | session from another machine | A session ID arrived from a machine with a different device key, usually a copied session |
transfer_limited | moves limited for now | No transfers left, or still in the cooldown |
transfer_not_allowed | buyer moves not allowed | The plan does not let buyers move machines themselves |
Protect 24/7 counts device_limit and session_device_mismatch as Key sharing signals, and session_limit as Session overflow.
End-user accounts
| Code | Label | Meaning |
|---|---|---|
user_banned | User banned | The account was banned with Ban |
no_license | Account without license | The account has no license attached |
username_taken | Username taken | The username is already used in this app |
license_already_registered | License already registered | The key is already linked to another account |
user_limit | account limit reached | The app holds as many end users as the workspace plan allows; plans with soft limits (Premium by default) do not refuse here |
account_required | account sign-in needed | redeem() was called without an account session |
already_redeemed | key already on this account | The account redeemed the key it already uses |
Trials
| Code | Label | Meaning |
|---|---|---|
trial_unavailable | no trial offered | No plan of the app has Trial days above 0 |
trial_used | trial already used on this machine | This machine used its trial and later activated a purchased key |
trial_expired | trial ended | The trial is over and the plan's After the trial is set to Stop |
trial_denied | risky machine, no trial | The machine's IP has a high risk score in Protect 24/7 |
Versions and maintenance
| Code | Label | Meaning |
|---|---|---|
update_required | update required | The version is below the Minimum version set in Licensing & releases › Releases |
build_blocked | build blocked | The running version or file belongs to a release marked with Block this build |
unknown_build | unknown build | Build Integrity Verification is set to Block, the app has published releases, and the running file's hash matches none of their files |
maintenance | under maintenance | Maintenance mode is on |
Reading denial reasons in your software
The code in the Reason column is the err.code the SDK throws. Use it to show customers a clear message, and to match their report with the log when you help them.
import { ClavisError, ClavisVerificationError } from "@clavisauh/sdk";
const MESSAGES: Record<string, string> = {
invalid_credentials: "This key or account is not valid.",
device_limit: "This key is on all the machines it allows. Remove an old one and try again.",
license_expired: "Your license has expired.",
too_many_attempts: "Too many failed attempts. Please wait a few minutes.",
update_required: "Please update to the latest version.",
};
try {
await clavis.activate(licenseKey);
} catch (err) {
if (err instanceof ClavisVerificationError) {
// The answer could not be verified: treat it as no connection, change nothing
} else if (err instanceof ClavisError) {
// Show the code so the customer can quote it and you can find the line in the Activity log
showMessage(`${MESSAGES[err.code] ?? err.message} (${err.code})`);
}
}
Overview charts
Workspace › Overview sums up the log of every app in the workspace:
- The Sign-ins · 30 days tile adds up the allowed sign-ins of the last 30 days (the same numbers as the chart below), with a small trend line.
- The Sign-ins chart plots each day (UTC) of the last 30 days: Allowed as a filled area, Denied as a dashed line. Only three events count: key activations, user sign-ins and user registrations. Trials, redemptions, sign-outs and updates are left out.
- The Why access was denied card lists up to 6 of the most frequent reasons over the last 7 days, across all events. Denials without a code are grouped under Other.
Each app's Overview page has the same Sign-ins chart for that app alone. The Active licenses, Unused keys and Live sessions tiles come from licenses and sessions, not from the log.
The log also feeds Protect 24/7: its hourly chart counts allowed and denied requests, and the Denial spike signal fires when one hour has 20 or more denials, well above the app's usual rate over the previous 14 days.
How long the log is kept
The log follows your workspace plan's Log retention (days) limit, shown in Account & organization › Plan & usage. By default, Basic keeps 7 days and Premium keeps 90. Compare plans on the pricing page.
Cleanup runs every night at 03:30 Vietnam time and never deletes silently:
- When the oldest line in the workspace is less than 3 days from passing the retention, every Owner of the workspace gets an email saying logs older than the retention will be cleared in 3 days. Nothing is deleted that night.
- Once that email is at least 3 days old, each nightly run deletes what is older than the retention.
- While cleanup keeps running, the reminder email goes out again at most once every 30 days, and after each reminder deletion waits another 3 days.
The same retention applies to Seller API request logs, Protect 24/7 signals and finished webhook deliveries. Audit logs are never touched.