C++ SDK

A header-only C++17 SDK for integrating Moonbase license activation into your native applications. It runs on Windows, macOS, and Linux, and ships with two JUCE integration paths for plug-in developers.

The SDK is open-source under the MIT license and lives at Moonbase-sh/moonbase-cpp. It exposes the activation primitives directly, so you can use it from any C++ application: CLI tools, Qt apps, custom plug-in formats, or JUCE plug-ins through the JUCE module that ships in the same repository.

It supports:

  • Browser-based activations with polling
  • Local RS256 JWT validation against an embedded public key
  • Online re-validation with configurable cadence and grace period
  • Cross-SDK device identity implementing the Moonbase device fingerprint spec, so an activation made from a web or Electron app validates in your native app
  • Overridable license storage (in-memory by default, file-backed store included)
  • Server-side activation revocation
  • Two JUCE paths: the drop-in moonbase_licensing module with a built-in activation UI, and a copy-paste bridge wrapping juce::OnlineUnlockStatus

Requirements

  • CMake 3.20 or newer
  • A C++17 compiler
  • Windows, macOS, or Linux
  • CURL::libcurl and OpenSSL (OpenSSL::SSL, OpenSSL::Crypto)
  • nlohmann_json 3.11+ — fetched automatically if not found on the system

Installation

The fastest way to consume the SDK is through CMake's FetchContent. For a system-wide install, build and install from source and pick it up with find_package. For vendored copies in your repo, add_subdirectory works the same way as FetchContent.

include(FetchContent)
FetchContent_Declare(moonbase_cpp
    GIT_REPOSITORY https://github.com/Moonbase-sh/moonbase-cpp.git
    GIT_TAG v4.0.0)
set(MOONBASE_BUILD_TESTS OFF)
set(MOONBASE_BUILD_EXAMPLES OFF)
FetchContent_MakeAvailable(moonbase_cpp)

target_link_libraries(your_app PRIVATE moonbase::licensing)

The exported moonbase::licensing target propagates the include directory and transitive dependencies on libcurl, OpenSSL, and nlohmann_json, so your project doesn't need to repeat find_package for any of them.

Building the SDK as a top-level project also produces moonbase_device_id, a diagnostic that prints the current machine's device ID and how it was derived. Enable it in a subproject build with -DMOONBASE_BUILD_DEVICE_ID_TOOL=ON.

Basic usage

Configure the SDK with your account endpoint, product ID, and embedded RS256 public key, then request an activation and poll until it's fulfilled in the browser:

#include <moonbase/moonbase.hpp>

moonbase::licensing_options options;
options.endpoint = "https://your-account.moonbase.sh";
options.product_id = "your-product";
options.public_key = embedded_public_key_pem;
options.account_id = "account-id"; // optional issuer check

moonbase::licensing licensing(options);

auto request = licensing.request_activation();
std::cout << "Open: " << request.browser_url << "\n";

std::optional<moonbase::license> license;
while (!license) {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    license = licensing.get_requested_activation(request);
}

licensing.store().store_local_license(*license);

Startup validation

On every launch, run validate_token_online against the stored token. It performs the local checks first (signature, device fingerprint, expiry) and then re-validates against the Moonbase API as needed:

if (auto local = licensing.store().load_local_license()) {
    auto validated = licensing.validate_token_online(local->token);
    licensing.store().store_local_license(validated); // persist refreshed token
}

Two licensing_options knobs control API cadence and offline tolerance:

  • online_validation_min_interval (default 5 minutes) — skip the API call when the local validated_at is newer than this. Keeps the method cheap to call frequently.
  • online_validation_grace_period (default 7 days) — maximum age the local token may reach without a successful online check. Within grace, transient transport failures fall back to the cached local result; beyond grace, the failure propagates.

Definitive server rejections (license_invalid_error, license_expired_error) always propagate regardless of grace. Offline-activated tokens (activation_method::offline) are validated locally even from validate_token_online — the SDK never contacts the API for them. Use validate_token_local directly when you only want the local check.

Revoking activations

Wire revoke_activation to a Deactivate or Sign out button so users can free up the activation seat for the current device:

if (auto local = licensing.store().load_local_license()) {
    licensing.revoke_activation(local->token);
}

On success the SDK tells the server to release the seat and clears the matching license from the local store. Revoke is only meaningful for online-activated paid licenses; calling it for offline or trial tokens raises operation_not_supported_error without contacting the API.

Custom device IDs and storage

The default device ID resolver, moonbase::moonbase_device_id_resolver, implements the cross-SDK Moonbase device fingerprint spec, reading SMBIOS on Windows, IOPlatformUUID on macOS, and machine-id plus world-readable DMI fields on Linux. Because every Moonbase SDK computes the same ID, an activation made from a web or Electron app validates in your native app and the other way round. Override it when you need an exact legacy fingerprint or any other application-specific device ID:

class my_resolver final : public moonbase::device_id_resolver {
public:
    std::string device_name() const override { return "Studio Mac"; }
    std::string device_id() const override { return "stable-device-id"; }
};

auto store = std::make_shared<moonbase::file_license_store>("licenses/license.mb");
auto resolver = std::make_shared<my_resolver>();
moonbase::licensing licensing(options, store, resolver);

A custom resolver's ID is compared literally, so it does not need to follow the spec's mbd2_ stamp format, and it gives up cross-SDK compatibility by definition.

file_license_store persists a JSON representation of the validated license at the path you give it. For per-user storage, point it at your platform's app-data directory (e.g. ~/Library/Application Support/YourApp/license.mb on macOS).

JUCE integration

There are two JUCE paths, both built on the same moonbase::licensing core SDK.

The moonbase_licensing module is the recommended choice for new projects. It's a drop-in JUCE 8 module with a built-in, themeable activation UI and no third-party dependencies, and it ships in this same repository under modules/moonbase_licensing/. See the JUCE Module page for the full guide.

The OnlineUnlockStatus bridge below is the alternative for products already built on juce::OnlineUnlockStatus, teams still on JUCE 7, or anyone who would rather build their own activation UI against the core SDK.

OnlineUnlockStatus bridge

The SDK includes a copy-paste bridge under examples/juce/MoonbaseJuceBridge.h. It provides:

  • moonbase::juce_bridge::MoonbaseUnlockStatus — subclass of juce::OnlineUnlockStatus driven by Moonbase's JWT flow.
  • MoonbaseJuceDeviceIdResolver: sources the device ID from juce::SystemStats::getUniqueDeviceID() instead of the spec fingerprint, for products that already bound licenses to it.
  • applyJuceMetadata(options) — populates activation metadata from JUCE's system and host helpers (DAW, plug-in format, OS, CPU, JUCE version).
  • tryLoadStoredLicenseAsync(callback) — non-blocking startup validation. State mutation and the callback are always marshalled to the JUCE message thread, so it's safe to call from AudioProcessor's constructor.
  • revokeActivationAsync(callback): releases the device's activation seat server-side, for a Deactivate or Sign out control.

A runnable standalone example ships with the SDK and is opt-in to avoid pulling JUCE into your build by default:

cmake -B build -DMOONBASE_BUILD_JUCE_EXAMPLE=ON
cmake --build build --target MoonbaseJuceExample

Reference implementation: HALO by Corino

For a complete, end-to-end example of the JUCE bridge wired into a real application, see the open-source HALO reference app. It's a JUCE 8 standalone built to look like a harmonic saturator plug-in — transfer curve, drive knob, animated I/O meters — but it doesn't process audio. The whole point of the project is the surrounding license-gate workflow: synchronous local JWT check on launch so the UI unlocks immediately, async online re-validation against the Corino demo account on a background thread, and a cog-menu Sign out that calls revokeActivationAsync to release the seat server-side.

Screenshot of the HALO activation screen

HALO demonstrates the full set of bridge features in a shippable shape — tryLoadStoredLicenseAsync on startup, the browser activation handshake, file-backed license storage under ~/Library/Application Support/Corino/HALO/, and revokeActivationAsync wired to a UI control. It also includes the macOS + Windows CI and release pipeline that publishes signed builds straight to the Moonbase account as product downloads.

A JUCE 8 standalone reference app demonstrating moonbase-cpp end-to-end against the public Corino demo account.

For the equivalent reference on the module path, see DRIFT on the JUCE Module page.

Releases and source

The SDK is MIT-licensed and follows Conventional Commits — every push to main is released automatically by semantic-release, with versioned GitHub Releases and source tarballs at https://github.com/Moonbase-sh/moonbase-cpp/archive/refs/tags/v<version>.tar.gz.

Header-only C++17 SDK for Moonbase license activation, plus the moonbase_licensing JUCE module.

Was this page helpful?