Skip to Content
New release 12 available 🎉

C++ integration

Compile a C++ application against the Babel native SDK’s public C header. This guide explains the C++ example , a direct compiler command and an equivalent CMake configuration.

Babel Licensing Native SDK 12.2.0 (C ABI 2) is available for macOS arm64, Linux x64 and Windows x64. The setup script downloads the pinned archive for your platform and verifies its SHA-256 before installation.

Select your platform

The SDK distributes one runtime library for each platform. Botan is embedded; users do not install or ship a Botan or SQLite library. Windows also receives a link-time import library for MSVC/Rust; that file is not another runtime DLL.

Platform packageRuntime fileBuild environment
macOS arm64, macOS 15+.sdk/lib/libBabelLicensingNative.dylibApple Silicon toolchain; Xcode command-line tools for compiled clients
Linux x64, glibc.sdk/lib/libBabelLicensingNative.sox86_64 compiler/toolchain; Ubuntu 22.04 is the initial build baseline
Windows x64.sdk/bin/BabelLicensingNative.dllx64 process; MSVC import library in .sdk/lib/BabelLicensingNative.lib

The installer selects the current OS/architecture, verifies that platform’s archive checksum and metadata, and rejects a different package. Linux checks its minimum glibc version; a musl/Alpine package is not provided. The native runtime uses the OS certificate trust store and system facilities. The release archives are available from the 12.2.0 SDK release .

Linux: install, build and run

Install Python 3.9 or later and the language toolchain. Rust needs Cargo/rustc 1.85 or newer plus a C linker; Go needs Go 1.22 or newer plus GCC/Clang for cgo; C++ needs a C++20 compiler. On Ubuntu, build-essential supplies the C/C++ compiler and linker. Python clients need no C/C++ compiler. The OS needs its normal CA certificate package for HTTPS.

git clone https://github.com/babelfornet/native-licensing-examples.git cd native-licensing-examples python3 scripts/setup-sdk.py python3 scripts/run-example.py cpp

The shared runner builds the selected client and runs it from the repository root. It does not build the SDK or download dependencies. A successful offline run prints License verified. Feature Pro enabled. The equivalent direct command is:

mkdir -p .build c++ -std=c++20 -Isdk/include cpp/main.cpp -L.sdk/lib -lBabelLicensingNative \ -Wl,-rpath,"$PWD/.sdk/lib" -o .build/cpp-example ./.build/cpp-example

The Rust build script and Go cgo directives set the runtime path on Linux. C++ explicitly supplies -L for link-time lookup and -Wl,-rpath for runtime lookup. Python selects .so in runtime_path(). If you deploy a compiled client elsewhere, ship the single .so in your application layout and configure an appropriate runtime path, such as $ORIGIN/../lib for a sibling lib/ directory.

Windows: install, build and run

Use x64 Python 3.9 or later. For C++ or Rust/MSVC, install Visual Studio’s Desktop development with C++ workload and open an x64 Native Tools terminal. Rust uses the x86_64-pc-windows-msvc toolchain. Go/cgo additionally needs a MinGW-compatible GCC or Clang toolchain, such as MSYS2 UCRT64 or LLVM-MinGW; cl.exe is not Go’s cgo C compiler.

From PowerShell in the example checkout:

python scripts/setup-sdk.py python scripts/run-example.py cpp

The Python runner makes .sdk/bin available to the child process through its PATH. The C++ runner uses cl when available, Rust links the MSVC import .lib, and Go links the C ABI with its cgo compiler. To build and run directly:

New-Item -ItemType Directory -Force .build | Out-Null $env:PATH = "$PWD\.sdk\bin;$env:PATH" cl /nologo /std:c++20 /EHsc /MT /Isdk/include cpp/main.cpp /Fe:.build\cpp-example.exe /link /LIBPATH:.sdk\lib BabelLicensingNative.lib & .\.build\cpp-example.exe

For deployment, copy BabelLicensingNative.dll next to the executable, or put its directory on the application’s controlled DLL search path. Python loads its explicit absolute DLL path through ctypes.CDLL, using the C calling convention. Do not copy a Linux .so or macOS .dylib onto Windows, and do not distribute the MSVC import .lib as a runtime dependency.

The numbered sections below show the macOS reference build and explain the shared integration code. Offline, activation and floating logic is identical on all three systems; the loader and linker settings above account for the platform differences.

1. Check the compiler

The sample builds with a C++20 compiler. On macOS, install the Xcode command-line tools if they are not already available. Python 3 is needed for SDK setup; CMake is optional if you use the direct compiler command.

xcode-select -p c++ --version python3 --version uname -m

Use an arm64 compiler and process for the supplied arm64 library. The public API uses C types, not C++ classes, so your application does not include Botan headers or depend on its C++ interface. C++20 is the tested sample configuration, not a C++ language requirement imposed on Python, Go or Rust callers of this C ABI.

2. Install the runtime and find the header

git clone https://github.com/babelfornet/native-licensing-examples.git cd native-licensing-examples python3 scripts/setup-sdk.py

Run python3 scripts/setup-sdk.py to download the pinned platform archive. To use a previously downloaded ZIP, add --archive /absolute/path/to/sdk.zip. Both paths verify SHA-256.

The build uses these paths:

sdk/include/babel/babel_licensing.h public C declarations .sdk/lib/libBabelLicensingNative.dylib native implementation cpp/main.cpp example application

The C++ source includes:

#include <babel/babel_licensing.h>

The include search path must therefore be sdk/include, not sdk/include/babel. The SDK archive also carries a matching header under .sdk/include.

Run from the repository root:

mkdir -p .build ROOT="$(pwd -P)" c++ -std=c++20 \ -Isdk/include \ cpp/main.cpp \ -L.sdk/lib -lBabelLicensingNative \ -Wl,-rpath,"$ROOT/.sdk/lib" \ -o .build/cpp-example ./.build/cpp-example
ArgumentPurpose
-std=c++20Selects the language mode used by the sample.
-Isdk/includeFinds the public header during compilation.
cpp/main.cppCompiles the application source.
-L.sdk/libFinds the native library during linking.
-lBabelLicensingNativeLinks the library by its base name.
-Wl,-rpath,...Records a search path for the macOS dynamic loader.
-o .build/cpp-exampleNames the resulting executable.

The include path does not locate the library, and the link path alone does not tell the loader where to find it at runtime. All three path settings have a separate role.

./run.sh cpp performs the same compile-and-run operation. It does not add another licensing implementation.

4. Use CMake instead

The example repository includes this CMakeLists.txt. It links the platform SDK and copies the single Babel runtime beside the executable:

CMakeLists.txt
cmake_minimum_required(VERSION 3.20) project(BabelNativeExample LANGUAGES CXX) set(BABEL_NATIVE_SDK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.sdk" CACHE PATH "Directory containing the native SDK") add_library(BabelLicensingNative SHARED IMPORTED) if(WIN32) set_target_properties(BabelLicensingNative PROPERTIES IMPORTED_IMPLIB "${BABEL_NATIVE_SDK_DIR}/lib/BabelLicensingNative.lib" IMPORTED_LOCATION "${BABEL_NATIVE_SDK_DIR}/bin/BabelLicensingNative.dll") else() find_library(BABEL_NATIVE_LIBRARY NAMES BabelLicensingNative PATHS "${BABEL_NATIVE_SDK_DIR}/lib" NO_DEFAULT_PATH REQUIRED) set_target_properties(BabelLicensingNative PROPERTIES IMPORTED_LOCATION "${BABEL_NATIVE_LIBRARY}") endif() add_executable(native-example cpp/main.cpp) target_compile_features(native-example PRIVATE cxx_std_20) target_include_directories(native-example PRIVATE "${BABEL_NATIVE_SDK_DIR}/include") target_link_libraries(native-example PRIVATE BabelLicensingNative) if(MSVC) set_property(TARGET native-example PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") elseif(APPLE) set_target_properties(native-example PROPERTIES BUILD_RPATH "@loader_path") else() set_target_properties(native-example PROPERTIES BUILD_RPATH "$ORIGIN") endif() # The deployed app needs only the Babel runtime next to its executable. add_custom_command(TARGET native-example POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$<TARGET_FILE:BabelLicensingNative>" "$<TARGET_FILE_DIR:native-example>" VERBATIM)

Then configure, build and run:

cmake -S . -B .build/cmake -DCMAKE_BUILD_TYPE=Release cmake --build .build/cmake --config Release ./.build/cmake/native-example

On Windows, run these commands in an x64 Visual Studio developer terminal. A multi-configuration generator places the executable under Release; the build copies the DLL beside it:

cmake -S . -B .build/cmake cmake --build .build/cmake --config Release & .\.build\cmake\Release\native-example.exe

Ninja is a single-configuration generator; with -G Ninja -DCMAKE_BUILD_TYPE=Release, run .build/cmake/native-example.exe instead. macOS and Linux record a loader-relative runtime path. When distributing the example, copy the executable and its adjacent Babel runtime together; no Botan runtime is needed. Keep the signed application license and trusted publisher key in your application’s expected locations.

NO_DEFAULT_PATH prevents the search from selecting an unrelated library elsewhere on the machine. If the SDK is installed outside the checkout, configure it explicitly:

cmake -S . -B .build/cmake \ -DCMAKE_BUILD_TYPE=Release \ -DBABEL_NATIVE_SDK_DIR=/absolute/path/to/the/sdk

The directory must contain include/babel/babel_licensing.h and lib/libBabelLicensingNative.dylib. The CMake variable configures SDK discovery, not the location of your customer license file.

5. Read bounded input and check the ABI

The full main.cpp  opens each input in binary mode, checks its size before allocation and rejects embedded NUL bytes. Its limits are 1 MiB for XML and 16 KiB for the publisher public key.

Before using a file, it checks:

if (babel_abi_version() != 2) { throw std::runtime_error("This example requires native ABI v2"); }

The native functions return babel_status. The example’s success helper throws a local C++ exception when a status is not BABEL_OK:

static void success(babel_status status, const char* operation) { if (status != BABEL_OK) { throw std::runtime_error( std::string(operation) + " failed (status " + std::to_string(status) + ")"); } }

These exceptions are application error handling. The native C API reports its failures through status codes rather than exposing C++ exceptions across the ABI.

6. Keep one owner for the native handle

Loading produces an opaque handle through an output parameter:

babel_license* raw = nullptr; success(babel_license_load(text.c_str(), &raw), "License parsing"); std::unique_ptr<babel_license, decltype(&babel_license_free)> handle(raw, babel_license_free);

The unique_ptr custom deleter calls the library’s own deallocator. Do not call delete or std::free on the opaque pointer. unique_ptr releases it on success and when a later check throws, so the main validation flow does not need separate cleanup branches.

std::string::c_str() provides NUL-terminated bytes while its string is alive. The example keeps its text and key variables alive across the calls. It passes the key separately from the XML: the publisher trust anchor is never selected from the document being checked.

7. Authenticate and validate before using Pro

success( babel_license_verify(handle.get(), "RSA", key.c_str()), "Publisher signature verification"); success( babel_license_validate(handle.get()), "Runtime license validation"); if (!babel_license_feature_enabled(handle.get(), "Pro")) { throw std::runtime_error("Requested feature is absent or expired"); }

Verification proves the publisher signature against the supplied trusted key. Runtime validation checks license expiry and refuses restrictions that the native implementation does not support. The feature query also enforces feature expiry.

Parsing success does not enable a feature. A failed verification clears previous authentication. Do not continue after a failed check, and do not use diagnostic getters as proof that a license is valid.

The demo takes the key path as an argument to let you try your publisher. For a product, pin the key in the application or trusted configuration. The private signing key belongs only on the publisher’s machine.

8. Run the resulting application

The default run uses demo/perpetual.xml, demo/publisher-key.txt, algorithm RSA and feature Pro:

./.build/cpp-example

Expected output:

License verified. Feature Pro enabled.

Select another file or your own publisher:

./.build/cpp-example demo/trial.xml ./.build/cpp-example demo/perpetual.xml ./.build/cpp-example \ /absolute/path/customer.xml /absolute/path/publisher-key.txt RSA Pro ./.build/cpp-example demo/expired.xml ./.build/cpp-example demo/no-feature.xml ./.build/cpp-example demo/restricted.xml

The last three commands are expected to fail with a nonzero exit status. Status 10 means license expiry; 4 means an unsupported restriction. A missing Pro feature is refused even if the file has a valid signature.

Run from the repository root for the default relative file paths, or supply absolute paths when launching from another directory. You do not need a compiler or .NET to start the built executable, but its configured native runtime must remain available.

Runtime files and troubleshooting

Inspect the linked binary and runtime:

otool -L .build/cpp-example otool -l .build/cpp-example file .build/cpp-example .sdk/lib/libBabelLicensingNative.dylib

otool -L shows the library reference; otool -l includes the LC_RPATH entries used to resolve @rpath. Distribute libBabelLicensingNative.dylib at the configured runtime path. Botan is embedded in this file; only macOS system libraries appear as runtime dependencies.

The development command uses an absolute runtime path to the checkout. Moving only the executable does not package the SDK with it. A distributable application must install the native libraries and configure its runtime search path for that layout; rebuild the application if its development SDK location changes.

ErrorLikely configuration problem
babel/babel_licensing.h not foundWrong -I path or missing header.
library not found for -lBabelLicensingNativeWrong -L path or SDK not installed.
Library not loadedMissing runtime rpath or libBabelLicensingNative.dylib.
Architecture mismatchIntel application and arm64 SDK, or the reverse.
Status 3Wrong publisher key or altered signed data.

For publisher setup, see Issue your own files. For all sample checks, run python3 scripts/test-examples.py from the repository root.

Run activation and floating licensing

The online modes use the same application and the same single SDK runtime as the offline example. They need a service endpoint, a customer user key and a publisher public key pinned by your application. Do not supply the license used to evaluate or purchase Babel Licensing itself: these are licenses for your own application.

For a self-contained local protocol demonstration, start the fixture in a separate terminal from the repository root:

python3 scripts/demo-service.py

The fixture listens only on 127.0.0.1:8765, reads signed demo payloads, and allocates one disposable floating seat. It needs Python 3, but no .NET runtime, database or signing private key. It is a protocol fixture, not a deployable Licensing Service.

In your application terminal, run:

export BABEL_SERVICE_URL=http://127.0.0.1:8765/ export BABEL_ALLOW_LOOPBACK_HTTP=1 export BABEL_MACHINE_ID=native-example-machine export BABEL_PUBLISHER_KEY_FILE="$PWD/demo/publisher-key.txt" export BABEL_ALGORITHM=RSA export BABEL_CLIENT_ID=native-example-cpp export BABEL_USER_KEY=demo-activation ./run.sh cpp --online activation export BABEL_USER_KEY=demo-floating ./run.sh cpp --online floating

Activation prints a verified grant, validates it again with the service, then deactivates it. Floating prints a verified grant, renews it twice, then releases its seat. Both exit with an error when acquisition, signature verification, machine validation, feature entitlement or a network operation fails.

Use your actual Licensing Service

Configure an activation or floating license for your application in Babel Licensing Service, with a supported RSA or ECDSA P-256 publisher key and a Pro feature (or adapt the feature name). The service must return a valid signed XML or compact license. Stateful trial run counts/days-from-first-use, domain, usage and custom restrictions remain unsupported; an absolute expiry is supported.

unset BABEL_ALLOW_LOOPBACK_HTTP BABEL_MACHINE_ID export BABEL_SERVICE_URL=https://licensing.example.com/ export BABEL_PUBLISHER_KEY_FILE=/absolute/path/your-publisher-public-key.txt export BABEL_ALGORITHM=RSA # Supply BABEL_USER_KEY from your application's secure configuration. ./run.sh cpp --online activation # Or use a floating user key and --online floating.

Replace the endpoint with your own service root. The SDK appends v1/auth/login and v1/license/*. It authenticates with the user key, then uses a bearer token for service requests. Credentials are read from the environment in these examples and are not printed or passed on the command line. Production applications should use their own secure configuration mechanism.

With BABEL_MACHINE_ID unset, the example calls babel_hardware_key. Keep the machine identity and BABEL_CLIENT_ID stable for an activation; choose distinct client identities for concurrently running floating clients. The fixed machine ID above exists only to match the signed local fixture. Pin the public key in your application or trusted configuration; never trust the key supplied inside a server response.

HTTPS always verifies certificates and hostnames; redirects are not followed. Plain HTTP requires the explicit loopback flag and cannot be used with a remote hostname. The sample timeout is 15 seconds per HTTP request; authentication and the following service request each have their own bound.

Integration code and lifecycle

// Error checking and exception cleanup are in cpp/online.hpp. success(babel_online_activate(client, userKey.c_str()), "Activate"); success(babel_online_check(client), "Check before work"); if (babel_online_feature_enabled(client, "Pro") != 1) throw std::runtime_error("Pro is not licensed"); success(babel_online_validate_activation(client), "Validate with service"); success(babel_online_deactivate(client), "Deactivate");

cpp/main.cpp includes cpp/online.hpp; keep that file next to the main source. The same C++20 command and CMake target shown above compile all three licensing modes. No extra include directory, linker flag or runtime library is needed for the online API. std::unique_ptr<babel_online_client, decltype(&babel_online_free)> owns the native session. The exception path attempts cleanup before the owner frees it.

For floating, the code calls babel_online_request_floating, reads babel_online_heartbeat_after_ms, waits with std::this_thread::sleep_for(std::chrono::milliseconds(delay)), then calls babel_online_heartbeat and babel_online_check. It repeats twice before babel_online_release.

The session API retains the verified license and floating token internally. Use babel_online_check immediately before protected work and babel_online_feature_enabled for feature access. Do not extract or cache a Boolean from the initial acquisition and use it indefinitely.

For a running floating application, schedule heartbeats before the returned deadline throughout the protected session. The two-heartbeat loop is only the executable demonstration. Stop protected work on any heartbeat/check failure. There is no background renewal, offline grace or persistent activation cache in this version. A failed heartbeat invalidates the local grant immediately; a lease cannot be revived after its deadline and must be acquired again.

Release floating seats or deactivate activations explicitly when the application workflow requires it. The examples do this when they exit so repeated evaluations do not consume seats. A product can retain an activation between runs by keeping stable identities and activating/validating it again at startup; this SDK does not save a signed license or token to disk. Freeing a handle releases memory and performs no network request.

If release/deactivation fails, local use is still disabled. Retry cleanup with the same live handle while it exists; a floating token eventually expires at the service. Handle destruction, process termination and network failure do not prove that the server has received cleanup.

Online refusal scenarios

StatusMeaning and application action
2 / 3Malformed response/license or bad publisher signature: refuse protected work.
4Unsupported algorithm, curve or restriction: adjust the issuing template, never bypass the check.
10Signed license expired: obtain a valid application license.
11Network, TLS, timeout or response-size failure: stop protected work and report the failure.
12 / 13 / 14Credentials rejected, HTTP/service failure or grant refused: check service policy and seat availability.
15No active grant, released/expired lease or failed heartbeat: stop use and acquire a new lease as appropriate.
16Signed hardware binding does not match the configured machine: refuse activation.

Run python3 scripts/test-online-examples.py to build/run all four languages against local activation, floating renewal/release, denied grant, hardware mismatch and unsupported-restriction cases. The native SDK separately tests malformed JSON, tampered signed payloads, oversized responses, timeout, redirects, invalid TLS and lease loss. These protocol-fixture checks do not replace acceptance tests against your deployed Licensing Service.

Online commands in Windows PowerShell

The service and user key settings have the same meaning on every OS. For the supplied loopback fixture, open one terminal for python scripts/demo-service.py, then use another:

$env:BABEL_SERVICE_URL = "http://127.0.0.1:8765/" $env:BABEL_ALLOW_LOOPBACK_HTTP = "1" $env:BABEL_MACHINE_ID = "native-example-machine" $env:BABEL_PUBLISHER_KEY_FILE = "$PWD\demo\publisher-key.txt" $env:BABEL_ALGORITHM = "RSA" $env:BABEL_CLIENT_ID = "native-example-cpp" $env:BABEL_USER_KEY = "demo-activation" python scripts/run-example.py cpp --online activation $env:BABEL_USER_KEY = "demo-floating" python scripts/run-example.py cpp --online floating

For your actual HTTPS Licensing Service, remove the loopback flag and fixed demo machine identity, then supply your service root, publisher key and application user key. Run the same --online commands. Linux uses the shell export commands in the online section and can use python3 scripts/run-example.py instead of ./run.sh.

Remove-Item Env:BABEL_ALLOW_LOOPBACK_HTTP -ErrorAction SilentlyContinue Remove-Item Env:BABEL_MACHINE_ID -ErrorAction SilentlyContinue $env:BABEL_SERVICE_URL = "https://your-licensing-service.example/" $env:BABEL_PUBLISHER_KEY_FILE = "C:\YourApplication\publisher-public-key.txt" # Supply BABEL_USER_KEY through your application's secure configuration. python scripts/run-example.py cpp --online activation

Run python scripts/test-examples.py and python scripts/test-online-examples.py on Windows (or python3 on Linux/macOS) to exercise the same accepted/refused paths in all four bindings.

Last updated on