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, withclavis.exe: one file that needs only Windows's own DLLs. - Linux:
clavis-cli-1.0.0-linux-x64.tar.gz, withclavis. The machine needs glibc 2.34 or later and the system'slibsodiumandlibcurl(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 variable | Option | Value |
|---|---|---|
CLAVIS_BASE_URL | --base-url | the API address on the Connect software page |
CLAVIS_APP_ID | --app-id | the App ID |
CLAVIS_APP_KEY | --app-key | the app's public key |
CLAVIS_STATE_DIR | --state-dir | the folder that keeps the device key, the session and the offline token (default .clavis) |
CLAVIS_LANG | --lang | en or vi for error sentences |
CLAVIS_LICENSE | --key | the 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 code | Means |
|---|---|
| 0 | done |
| 1 | refused: the key, the license, the account, the device… (error.code says which) |
| 2 | bad usage: unknown command, a setting missing |
| 3 | try again later: no network, timeout, rate limit, maintenance |
| 4 | the 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
- On the Connect software page, select Make a test key.
- Save the whole file below as
k1.ps1(ork1.shfor bash, from the section above), set the settings with the tool's path inCLAVIS_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.0and 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 activatewith the key the customer typed, then reads the exit code: 0 carries on, 1 showserror.messageto the customer, 3 asks to try again once online. - Set
--state-dirwhere the user can write, one per user (%APPDATA%\YourAppfor 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_PASSWORDrather than--password: another user's process on the machine can see a command line. - Pass
--app-versionas 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.