The CLV1 protocol is now publicRead the spec
Browse the library

Documentation libraryGet started by language

Get started with the command line tool

`clavis` is ClavisAUH's command line tool, for scripts, installers, batch files and any language that can start a program but not call C. Every command prints exactly one line of JSON and ends with a fixed exit code. The C++ SDK is inside, so it signs every request and checks every answer as the SDKs do.

5 min read

What you need

  • Windows x64 or Linux x64. There is no macOS or ARM build yet.
  • An app on the dashboard. Its Integration › Connect software page has the API address, the App ID, the public key and a Make a test key button. The API address ends in /api, with no /v1: https://<your ClavisAUH site>/api.

1. Download the tool

Download from Downloads and check the SHA-256:

  • Windows: clavis-cli-1.0.0-windows-x64.zip, with clavis.exe: one file that needs only Windows's own DLLs.
  • Linux: clavis-cli-1.0.0-linux-x64.tar.gz, with clavis. The machine needs glibc 2.34 or later and the system's libsodium and libcurl (Debian, Ubuntu: apt install libsodium23 libcurl4).

Unpack it, then put the folder on the PATH or call the tool by its path. Try clavis version; it answers {"ok":true,"version":"1.0.0"}.

2. Settings and answers

Your app's settings come from environment variables or command line options:

Environment variableOptionValue
CLAVIS_BASE_URL--base-urlthe API address on the Connect software page
CLAVIS_APP_ID--app-idthe App ID
CLAVIS_APP_KEY--app-keythe app's public key
CLAVIS_STATE_DIR--state-dirthe folder that keeps the device key, the session and the offline token (default .clavis)
CLAVIS_LANG--langen or vi for error sentences
CLAVIS_LICENSE--keythe license key

The session is kept in the state folder, so later commands (heartbeat, variables, logout, offline) run in other processes and still use that session. The exit code gives the outcome without reading the JSON:

Exit codeMeans
0done
1refused: the key, the license, the account, the device… (error.code says which)
2bad usage: unknown command, a setting missing
3try again later: no network, timeout, rate limit, maintenance
4the answer could not be trusted (unsigned, wrong key, forged update)

A successful answer, shortened (keys in alphabetical order; result holds what the SDKs return):

{"command":"activate","ok":true,"result":{"license":{"status":"active", …},"session":{"id":"ses_…", …}, …}}

On failure the JSON line is {"ok":false,"error":{"code","num","status","retryable","message"}}: code and num come from the error codes table, message is the sentence for the user in --lang. Non-ASCII characters are written as \u00e1, so the output is the same on every console; any JSON reader decodes them. clavis errors lists every code.

3. K1 in bash

Activate, then read the exit code and the JSON:

answer=$("$clavis" activate)
if [ $? -ne 0 ]; then
  echo "FAIL activate $(echo "$answer" | field code)"
  exit 1
fi
echo "activated: $(echo "$answer" | field status)"

Keep the session in later runs, read a variable, then end the session:

# A later command, another process: the session was kept in the state folder.
"$clavis" heartbeat > /dev/null || { echo "FAIL heartbeat"; exit 1; }
echo "motd: $("$clavis" variables | field motd)"
"$clavis" logout > /dev/null || { echo "FAIL logout"; exit 1; }

A key that cannot be used gives exit code 1 with its error code:

# Exit code 1: refused (the key, the license...); 3: try again later; 4: the answer was not trusted.
# A mistyped key, from another state folder: a machine that holds no seat on it.
refused=$("$clavis" activate --key "${CLAVIS_LICENSE%????}ZZZZ" --state-dir "$state-other")
code=$?
rm -rf "$state-other"
echo "refused: $code $(echo "$refused" | field code)"
[ "$code" -eq 1 ] || { echo "FAIL refused"; exit 1; }

The whole bash script, with the small field helper that reads one field of a JSON line:

#!/usr/bin/env bash
# K1 with the command line tool from bash: activate, heartbeat, read a variable (motd, empty when the app has no such
# variable), end the session; then a refused key and a run without network. CLAVIS_CLI is the path of clavis; the
# other settings are the CLAVIS_* variables, the state folder defaulting to .clavis. Ends with "OK cli". Each answer
# is one line of JSON, so sed is enough here (jq works too).
set -u
clavis="$CLAVIS_CLI"
state="${CLAVIS_STATE_DIR:-.clavis}"
field() { sed -n "s/.*\"$1\":\"\\([^\"]*\\)\".*/\\1/p" | head -1; }

answer=$("$clavis" activate)
if [ $? -ne 0 ]; then
  echo "FAIL activate $(echo "$answer" | field code)"
  exit 1
fi
echo "activated: $(echo "$answer" | field status)"

# A later command, another process: the session was kept in the state folder.
"$clavis" heartbeat > /dev/null || { echo "FAIL heartbeat"; exit 1; }
echo "motd: $("$clavis" variables | field motd)"
"$clavis" logout > /dev/null || { echo "FAIL logout"; exit 1; }

# Exit code 1: refused (the key, the license...); 3: try again later; 4: the answer was not trusted.
# A mistyped key, from another state folder: a machine that holds no seat on it.
refused=$("$clavis" activate --key "${CLAVIS_LICENSE%????}ZZZZ" --state-dir "$state-other")
code=$?
rm -rf "$state-other"
echo "refused: $code $(echo "$refused" | field code)"
[ "$code" -eq 1 ] || { echo "FAIL refused"; exit 1; }

# No network: the offline token saved by activate answers on this machine (exit 0). Without one, as on a plan with
# no offline time, the answer is offline_unavailable (exit 1): going online helps, waiting does not.
offline=$("$clavis" offline --base-url "http://127.0.0.1:9")
code=$?
echo "offline: $code $(echo "$offline" | sed -n 's/.*"ok":\(true\|false\).*/\1/p')"
[ "$code" -le 1 ] || { echo "FAIL offline"; exit 1; }
echo "OK cli"

4. K1 in PowerShell

The same steps, with ConvertFrom-Json reading the answers (the whole script is in step 5):

$answer = & $clavis activate | ConvertFrom-Json
if ($LASTEXITCODE -ne 0) {
    Write-Output $answer.error.message
    Write-Output "FAIL activate $($answer.error.code)"
    exit 1
}
Write-Output "activated: $($answer.result.license.status)"
# A later command, another process: the session was kept in the state folder.
$beat = & $clavis heartbeat | ConvertFrom-Json
if ($LASTEXITCODE -ne 0) { Write-Output "FAIL heartbeat $($beat.error.code)"; exit 1 }
$variables = & $clavis variables | ConvertFrom-Json
Write-Output "motd: $($variables.result.motd)"
& $clavis logout | Out-Null
if ($LASTEXITCODE -ne 0) { Write-Output "FAIL logout"; exit 1 }
# Exit code 1: refused (the key, the license...); 3: try again later; 4: the answer was not trusted.
# A mistyped key, from another state folder: a machine that holds no seat on it.
$key = $env:CLAVIS_LICENSE
$refused = & $clavis activate --key "$($key.Substring(0, $key.Length - 4))ZZZZ" --state-dir "$state-other" |
    ConvertFrom-Json
$code = $LASTEXITCODE
Remove-Item -Recurse -Force "$state-other" -ErrorAction SilentlyContinue
Write-Output "refused: $code $($refused.error.code)"
if ($code -ne 1) { Write-Output "FAIL refused"; exit 1 }

5. Run it

  1. On the Connect software page, select Make a test key.
  2. Save the whole file below as k1.ps1 (or k1.sh for bash, from the section above), set the settings with the tool's path in CLAVIS_CLI, and run it:
# K1 with the command line tool from PowerShell: activate, heartbeat, read a variable (motd, empty when the app has no
# such variable), end the session; then a refused key and a run without network. CLAVIS_CLI is the path of clavis.exe;
# the other settings are the CLAVIS_* variables, the state folder defaulting to .clavis. Ends with "OK cli".
$clavis = $env:CLAVIS_CLI
$state = if ($env:CLAVIS_STATE_DIR) { $env:CLAVIS_STATE_DIR } else { ".clavis" }

$answer = & $clavis activate | ConvertFrom-Json
if ($LASTEXITCODE -ne 0) {
    Write-Output $answer.error.message
    Write-Output "FAIL activate $($answer.error.code)"
    exit 1
}
Write-Output "activated: $($answer.result.license.status)"

# A later command, another process: the session was kept in the state folder.
$beat = & $clavis heartbeat | ConvertFrom-Json
if ($LASTEXITCODE -ne 0) { Write-Output "FAIL heartbeat $($beat.error.code)"; exit 1 }
$variables = & $clavis variables | ConvertFrom-Json
Write-Output "motd: $($variables.result.motd)"
& $clavis logout | Out-Null
if ($LASTEXITCODE -ne 0) { Write-Output "FAIL logout"; exit 1 }

# Exit code 1: refused (the key, the license...); 3: try again later; 4: the answer was not trusted.
# A mistyped key, from another state folder: a machine that holds no seat on it.
$key = $env:CLAVIS_LICENSE
$refused = & $clavis activate --key "$($key.Substring(0, $key.Length - 4))ZZZZ" --state-dir "$state-other" |
    ConvertFrom-Json
$code = $LASTEXITCODE
Remove-Item -Recurse -Force "$state-other" -ErrorAction SilentlyContinue
Write-Output "refused: $code $($refused.error.code)"
if ($code -ne 1) { Write-Output "FAIL refused"; exit 1 }

# No network: the offline token saved by activate answers on this machine (exit 0). Without one, as on a plan with
# no offline time, the answer is offline_unavailable (exit 1): going online helps, waiting does not.
$offline = & $clavis offline --base-url "http://127.0.0.1:9" | ConvertFrom-Json
$code = $LASTEXITCODE
Write-Output "offline: $code $($offline.ok)"
if ($code -gt 1) { Write-Output "FAIL offline"; exit 1 }
Write-Output "OK cli"
$env:CLAVIS_CLI = "C:\path\to\clavis-cli-1.0.0-windows-x64\clavis.exe"
$env:CLAVIS_BASE_URL = "<API address>"
$env:CLAVIS_APP_ID = "<App ID>"
$env:CLAVIS_APP_KEY = "<public key>"
$env:CLAVIS_LICENSE = "<test key>"
powershell -NoProfile -ExecutionPolicy Bypass -File .\k1.ps1

It prints activated: active, motd: …, refused: 1 invalid_credentials (the mistyped key: exit code 1 and one code for any key that cannot be used), offline: …, then OK cli on the last line; the script stops with FAIL … instead when a step answers otherwise. The motd: line shows the app's variable named motd and stays empty until you create one (App variables). The offline: line reads 0 True when this machine holds a usable offline token, and 1 False without one (offline_unavailable, for example with Online-Only Mode on). -ExecutionPolicy Bypass lets this script run even where Windows blocks PowerShell scripts (the default on personal machines). On Linux, run bash k1.sh with the same variables (export ...).

6. See it on the dashboard

  • Integration › Connect software: step 4 turns to Connected, with cli/1.0.0 and the machine's operating system.
  • Licensing & releases › Licenses: the key goes from Unused to Active.
  • End users › Devices & sessions: the machine that ran it, and the session it opened.

7. In installers and releases

  • An installer (Inno Setup, NSIS, WiX…) runs clavis activate with the key the customer typed, then reads the exit code: 0 carries on, 1 shows error.message to the customer, 3 asks to try again once online.
  • Set --state-dir where the user can write, one per user (%APPDATA%\YourApp for example), and use that same folder for every later command: the session and the offline token live there, bound to the device key in it.
  • Pass a password (account scenarios) through CLAVIS_PASSWORD rather than --password: another user's process on the machine can see a command line.
  • Pass --app-version as the version you release: the server uses it to block builds that are too old and to announce new ones, see Signed releases and updates.
  • Before you sell, turn on Publicly released in Integration › App settings. Every TEST key stops working, so a test key that got out cannot be used.

Questions

What about no network? clavis offline answers on the machine: while the offline token saved by the last activation or heartbeat is still good, ok is true (exit code 0). Without a usable token it answers offline_unavailable with exit code 1: going online helps, waiting does not. Tokens come on every plan and last 7 days by default; Premium chooses the length, and Online-Only Mode turns them off. See Offline licenses.

My language can call C functions. Then the C library is lighter: no new process for every call.

What if a customer reinstalls the system or changes machines? After a reinstall on the same machine, the server still recognizes it from its hashed hardware identifiers. A new machine takes a new seat; when none is left the customer gets device_limit, and you Reset devices on the Licenses page. See Devices, HWID and transfers.

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