Browse the library
Documentation libraryGet started by language
Get started with C++
This guide takes a C++ program from nothing to its first activated license, and on to the build you hand to customers: install the SDK and the three libraries it needs, a few lines of code, a test run, the result on the dashboard, errors, and release.
6 min read
What you need
- A C++17 compiler and CMake 3.21 or later. On Windows, Visual Studio 2022 brings both CMake and vcpkg.
- Three libraries: libsodium, libcurl and nlohmann-json. Windows gets them through vcpkg; Debian and Ubuntu install them with
sudo apt install g++ cmake pkg-config libsodium-dev libcurl4-openssl-dev nlohmann-json3-dev. The SDK has not been built on macOS 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. Install the SDK
Download clavisauh-cpp-0.1.0.zip from Downloads, check its SHA-256 as that page shows, and unpack it: you get a clavisauh-cpp-0.1.0 folder with the SDK's sources. The SDK builds with your project through add_subdirectory. A CMakeLists.txt for your own project, with the unpacked folder beside it (CLAVISAUH_DIR lets you put it elsewhere):
cmake_minimum_required(VERSION 3.21)
project(your_app LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CLAVISAUH_DIR "${CMAKE_CURRENT_SOURCE_DIR}/clavisauh-cpp-0.1.0" CACHE PATH "The ClavisAUH C++ SDK")
add_subdirectory("${CLAVISAUH_DIR}" clavisauh)
add_executable(your_app main.cpp)
target_link_libraries(your_app PRIVATE clavisauh::clavisauh)
if(MSVC)
target_compile_options(your_app PRIVATE /utf-8)
endif()
On Windows, declare the three libraries in a vcpkg.json next to CMakeLists.txt; vcpkg downloads and builds them on the first configure (the baseline is the vcpkg version the SDK was tested with):
{
"dependencies": ["libsodium", "curl", "nlohmann-json"],
"builtin-baseline": "9eec002df1cd109f8f104cd966a763daf9751489"
}
Configure and build, with the path to the unpacked SDK folder:
# Windows, in a Developer PowerShell for VS 2022 (it has cmake and VCPKG_ROOT)
cmake -B build "-DCMAKE_TOOLCHAIN_FILE=$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" -DCLAVISAUH_DIR=C:/path/to/clavisauh-cpp-0.1.0
cmake --build build --config Release
# Linux
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCLAVISAUH_DIR=/path/to/clavisauh-cpp-0.1.0
cmake --build build
The SDK is not on vcpkg or Conan yet; once it is, this guide will show that way too.
2. The minimal code
The blocks below are parts of one file; the whole file, with its helpers, is in step 3.
Create the client once, when your software starts. The API address, App ID and public key are the three values on the Connect software page: the sample reads them from environment variables, your software writes them in its code. The device key lives in a file (sealed with DPAPI on Windows): sessions and offline tokens are bound to it, while the seat on the license follows the machine's hashed identifiers. The sample keeps this in settings.hpp, shared by the five scenarios, with the helpers that read the settings and show an error:
// What every scenario reads from the environment, and the client they all start from.
#pragma once
#include <cstdlib>
#include <iostream>
#include <optional>
#include <stdexcept>
#include <string>
#include "clavisauh/clavisauh.hpp"
namespace sample {
inline std::optional<std::string> optional(const char* name) {
const char* value = std::getenv(name);
return value ? std::optional<std::string>(value) : std::nullopt;
}
inline std::string env(const char* name) {
auto value = optional(name);
if (!value) throw std::runtime_error(std::string(name) + " is not set");
return *value;
}
inline std::string lang() { return optional("CLAVIS_LANG").value_or("en"); }
inline std::string state() { return optional("CLAVIS_STATE_DIR").value_or(".clavis"); }
inline clavisauh::Client make_client(std::optional<std::string> app_version = std::nullopt) {
clavisauh::Options options;
options.base_url = env("CLAVIS_BASE_URL");
options.app_id = env("CLAVIS_APP_ID");
options.app_public_keys = {env("CLAVIS_APP_KEY")};
options.device = clavisauh::default_device();
// The same key every run: sessions and offline tokens stay with this machine.
options.device_key = clavisauh::load_or_create_device_key(state() + "/device.key");
options.app_version = std::move(app_version);
return clavisauh::Client(options);
}
// The sentence for the person using the software, then a line for the CI to read.
inline int fail(const clavisauh::ClavisError& e) {
std::cout << e.user_message(lang()) << "\n" << "FAIL " << e.code << "\n";
return 1;
}
} // namespace sample
Activate the customer's key. Without a network, the offline token saved last time keeps the software running:
std::optional<clavisauh::json> grant;
try {
client.init();
grant = client.activate(sample::env("CLAVIS_LICENSE"));
} catch (const clavisauh::ClavisError& e) {
// No answer (network_error, timeout...): a token saved last time keeps the software working.
std::ifstream file(saved);
std::stringstream token;
token << file.rdbuf();
if (!e.retryable() || !file || !client.verify_offline(token.str())) return sample::fail(e);
std::cout << "Offline: using the license saved on this machine\n";
}
The public key only checks signatures, so embedding it in your software is safe. Never embed any secret key.
3. Run it
The quickest way is the starter project, with the SDK and your app's settings already wired in:
- On the Connect software page, pick C++, select Make a test key, then Download the starter (.zip), and unpack it.
- Run
./run.sh <test key>on Linux, or.\run.cmd <test key>in a Developer PowerShell for VS 2022 on Windows. The first time on Windows, vcpkg downloads and builds the three libraries, which takes a few minutes.
It prints License active until <date>, then OK k1 active on the last line. Run it again on the same machine and it still succeeds without using another seat: the server knows the machine by its hashed identifiers (Devices, HWID and transfers). The device key, which signs the requests, stays in the .clavis folder under the folder you run it from (or in CLAVIS_STATE_DIR). A container started afresh for each run looks like a new machine every time, see Questions. In a project of your own instead: save the whole K1 below as main.cpp, with settings.hpp beside it and the CMakeLists.txt above, build, set the four CLAVIS_* settings (CLAVIS_BASE_URL, CLAVIS_APP_ID, CLAVIS_APP_KEY for the public key, CLAVIS_LICENSE) and run your_app. The whole K1 scenario:
// K1 · License key only: activate on this machine, keep the session alive, work offline for a while.
#include <fstream>
#include <sstream>
#include "settings.hpp"
int main() {
auto client = sample::make_client("1.0.0");
const std::string saved = sample::state() + "/offline.token";
std::optional<clavisauh::json> grant;
try {
client.init();
grant = client.activate(sample::env("CLAVIS_LICENSE"));
} catch (const clavisauh::ClavisError& e) {
// No answer (network_error, timeout...): a token saved last time keeps the software working.
std::ifstream file(saved);
std::stringstream token;
token << file.rdbuf();
if (!e.retryable() || !file || !client.verify_offline(token.str())) return sample::fail(e);
std::cout << "Offline: using the license saved on this machine\n";
}
if (grant) {
const auto& license = (*grant)["license"];
std::cout << "License " << license["status"].get<std::string>() << " until "
<< (license["expiresAt"].is_string() ? license["expiresAt"].get<std::string>() : "forever") << "\n";
// Every few minutes while the software runs; sessions end after an hour without one.
client.heartbeat();
if (client.offline_token()) std::ofstream(saved) << *client.offline_token();
client.logout();
}
std::cout << "OK k1 " << (grant ? (*grant)["license"]["status"].get<std::string>() : "offline") << "\n";
return 0;
}
4. See it on the dashboard
- Integration › Connect software: step 4 turns to Connected, with
cpp/0.1.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.
5. Handle errors
Every refusal is a clavisauh::ClavisError with three things you need:
e.code: a code from the error codes table, such asinvalid_credentialsordevice_limit.e.user_message("en"): a sentence for the person using your software, in English or"vi", ready to show.e.retryable():truewhen no answer arrived (no network, too slow, a busy server); trying again later may work.
An answer without your app's valid signature is refused with a clavisauh::ClavisVerificationError (also a ClavisError), code unsigned_response, invalid_response_signature, unknown_key or stale_response. Treat it as no answer at all; usually the API address or the public key in your software is wrong.
While your software runs, send a heartbeat regularly to keep the session, every 5 to 30 minutes or so: a session ends after 60 minutes without one.
// Every few minutes while the software runs; sessions end after an hour without one.
client.heartbeat();
Save the latest offline token, so the software still opens next time without a network:
if (client.offline_token()) std::ofstream(saved) << *client.offline_token();
Offline 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. To see every call while tracking a problem down, set a function as options.debug: the SDK hands it each call and its outcome, keys show only their first and last four characters, and passwords never appear.
6. Package and release
- Write the API address, App ID and public key in your code or in a config file shipped with the build. Customers' machines need no environment variables.
- Put the state folder (device key, offline token) where the user can write, one per user:
%APPDATA%\YourAppon Windows,~/.config/your-appon Linux. Never in the install folder. - Set
options.app_versionto 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. - Windows: for one
.exethat needs nothing installed, configure with-DVCPKG_TARGET_TRIPLET=x64-windows-staticand setCMAKE_MSVC_RUNTIME_LIBRARYtoMultiThreaded, as ClavisAUH's ownclaviscommand line tool does. With the default triplet, ship the DLLs vcpkg copies next to the executable and the Visual C++ Redistributable. - Linux: customers' machines need the system's
libsodiumandlibcurl(Debian, Ubuntu:apt install libsodium23 libcurl4). - Run the release build to
OK k1 activeon a clean machine before you release it. - 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
Does the software need a network every time it opens? Activation and heartbeats do. Without a network, the saved token keeps the software running for 7 days by default, or the length chosen on Premium, checked on the machine without the server.
Does it work with Qt or MFC? The SDK is plain C++17 with no interface library; any C++17 project built with CMake can call it. Each call waits for the network, so keep it off the interface thread.
When should I use the C library instead? The C library is this same SDK behind C functions, for other languages (Delphi, Lua, Python ctypes…) or when you just want a ready-made clavis.dll. C++ projects use the SDK directly.
How do I limit features by license plan? client.has("feature_name") tells whether the license grants that feature; create features in License plans and entitlements.
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.