Skip to content

Install

View on GitHub

The C++ SDK licenses native desktop apps, games, CLI tools, Unreal Engine projects, and JUCE audio plugins. It has a header-only core with zero dependencies and verifies licenses identically to the Swift, Rust, and JavaScript SDKs (proven by cross-SDK conformance vectors). Requires C++17 or later.

include(FetchContent)
FetchContent_Declare(
keylight
GIT_REPOSITORY https://github.com/keylight-dev/keylight-cpp.git
GIT_TAG v0.1.0
)
FetchContent_MakeAvailable(keylight)
# Link against the header-only core (zero dependencies):
target_link_libraries(MyApp PRIVATE keylight::keylight)
# Optional: enable the built-in HTTPS transport (needs cpp-httplib + OpenSSL):
# set(KEYLIGHT_BUILD_HTTPLIB_TRANSPORT ON)
# target_link_libraries(MyApp PRIVATE keylight::httplib_transport)

Download keylight_single.hpp from the v0.1.0 GitHub Release and place it anywhere on your include path. No CMake required — add #include "keylight_single.hpp" and you’re done.

Terminal window
vcpkg install keylight[httplib-transport]
Terminal window
conan install --requires="keylight/0.1.0"

The core is network-agnostic. Supply any transport that satisfies the one-method concept (get(url) -> std::string). The opt-in HttplibTransport wraps cpp-httplib + OpenSSL:

set(KEYLIGHT_BUILD_HTTPLIB_TRANSPORT ON) # enable before FetchContent_MakeAvailable
#include <keylight/httplib_transport.hpp>
keylight::HttplibTransport transport;

Bring your own transport by subclassing keylight::Transport — useful when you already have a network layer (e.g. FHttpModule in Unreal, juce::URL in JUCE).

Fetch the tenant’s trusted keyset once so leases verify offline, then construct the client:

#include <keylight/keylight.hpp>
#include <keylight/httplib_transport.hpp>
// 1. Fetch the trusted Ed25519 keyset (or pin keys in Config directly).
keylight::HttplibTransport transport;
auto keys = keylight::fetchKeyset(transport, "https://api.keylight.dev", "your-tenant");
// 2. Configure the client.
keylight::Config cfg;
cfg.tenantId = "your-tenant";
cfg.productId = "your-product";
cfg.sdkKey = "sdk_live_…"; // from your Keylight dashboard
cfg.trustedKeys = keys.value_or(std::map<std::string, std::string>{});
cfg.maxOfflineDays = 7; // optional offline grace window
// 3. Resolve the default lease store path and construct.
auto storePath = keylight::default_store_path(cfg);
keylight::FileStore store(storePath);
keylight::Client kl(cfg, transport, store);
// 4. Run the on-launch check (validates online if needed, falls back to cached lease).
kl.checkOnLaunch();
// Activate a license key (online). The returned lease is Ed25519-verified
// before anything is persisted.
auto res = kl.activate("USER-LICENSE-KEY");
if (!res.activated) {
// res.error contains the reason
}
// Gate features on entitlements — works offline from the cached lease.
if (kl.hasEntitlement("pro")) {
// unlock pro features
}
// Check the current license state.
switch (kl.state()) {
case keylight::State::Licensed: break;
case keylight::State::Trial: break;
case keylight::State::Expired: break;
case keylight::State::Invalid: break;
}
// Release the seat when uninstalling / switching machines.
kl.deactivate();

The SDK ships a first-party Unreal adapter built on FHttpModule. Add keylight-cpp as a third-party module and use UKeylightSubsystem:

// In your GameInstance / GameMode (game thread):
if (auto* KL = GetGameInstance()->GetSubsystem<UKeylightSubsystem>())
{
TMap<FString, FString> TrustedKeys;
TrustedKeys.Add(TEXT("k1"), TEXT("<base64-public-key>"));
KL->Configure(TEXT("your-tenant"), TEXT("your-game"), TrustedKeys);
// OnActivateComplete fires on the game thread: (bSuccess, State, Message).
KL->OnActivateComplete.AddDynamic(this, &AYourClass::OnActivated);
KL->Activate(LicenseKey);
}
// Mark UFUNCTION() in your header so AddDynamic can bind it:
void AYourClass::OnActivated(bool bSuccess, EKeylightState State, const FString& Message)
{
if (State == EKeylightState::Licensed) { /* unlock pro content */ }
}

UKeylightSubsystem routes all HTTP through FHttpModule — no third-party HTTP dependency needed. Activation runs on a background thread and the completion delegate is marshaled back to the game thread.

The SDK ships a JUCE adapter over juce::URL. The adapter is audio-thread-safe: network calls happen on a background thread; entitlement reads are lock-free via a std::atomic-backed cache.

// In your PluginProcessor (message thread):
keylight::juce_integration::Licensing licensing(cfg); // uses juce::URL transport
licensing.checkOnLaunch(); // async, non-blocking
// On the audio thread (safe — lock-free atomic read):
bool isPro = licensing.hasFeature("pro");

Use the single-header drop-in to keep your plugin build self-contained: no CMake, no extra submodule, one #include.