RockyGuard

Documentation · Quick start

Quick start

End-to-end integration in five concrete steps. These show the simple-branch integration; for a high-value feature you can go further and make the licence load-bearing (Step 4b). The full version, including the per-platform CMake link line and the CRT-variant caveat for Windows Debug builds, is in the shipped Customer Documentation Sec 2.3.

Step 1 — Generate your keypair (one time)

Run the keypair generator on a secure machine. Keep private.pem SECRET; embed public.pem in your application.

tools/license_keygen --private private.pem --public public.pem

Step 2 — Create an end-user license

Requires the vendor_license.json file your library vendor sent you. Pass the end user's hardware fingerprint via --fingerprint-value (they get it by running rg_fingerprint on their own machine).

tools/license_create \
  --vendor-license vendor_license.json \
  --key private.pem \
  --id "LIC-001" \
  --licensee "Customer Name" \
  --product "YourApp" \
  --expires "2027-12-31T23:59:59Z" \
  --fingerprint-value "<end user's fingerprint>" \
  --feature "pro_feature"

Step 3 — Embed your public key in your application

Paste the contents of public.pem into a static C++ string constant. This is what the verifier uses to confirm the license signature is yours.

static constexpr char PUBLIC_KEY[] = R"(-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA...your public key bytes...
-----END PUBLIC KEY-----)";

Step 4 — Verify on the customer's machine

Three calls inside one try/catch. Load the license, check the hardware match, optionally check feature flags. Every uncertain state returns a documented LicenseStatus.

Wrap construction in try/catch. The constructor is the one call in the verifier path that throws, and it throws only when PUBLIC_KEY above is not parseable PEM — the usual cause being a key that was truncated or line-wrapped during the paste in step 3. An uncaught throw is std::terminate(), which reaches your end user as a hard crash with no message: a silent 0xC0000409 exit in a Release build, and the abort() has been called dialog in a Debug build. If the verifier has to outlive the try block, hold it in a std::optional and construct with emplace()LicenseVerifier is neither copyable nor movable.

#include <rockyguard/rockyguard.h>

int main() {
    try {
        rockyguard::LicenseVerifier verifier(PUBLIC_KEY);
        auto result = verifier.load("license.json");
        if (!result) { std::cerr << result.message << "\n"; return 1; }
        if (!verifier.check_node_locked()) { return 1; }

        if (verifier.check_feature("pro_feature")) {
            // gated functionality
        }
    } catch (const std::runtime_error& e) {
        // Only fires if PUBLIC_KEY above is not parseable PEM -- a
        // truncated or mis-pasted key. Never for a bad license file.
        std::cerr << "Invalid public key: " << e.what() << "\n";
        return 1;
    }
    // licensed and running
}

Step 4b — Load-bearing protection (optional)

Step 4 is the simple branch: verify, then branch on the result. A few lines, and enough for most integrations. Its honest limit: if (!verifier.check_node_locked()) return 1; is one boolean gating a branch, so a one-instruction patch (jnejmp) in your shipped binary can flip it — in your own code, where the library cannot reach.

For a feature worth more than that, make the licence load-bearing. Instead of branching on a verdict, the licence physically carries the key to the feature: seal a real asset under it at build time with rg_bind_asset (a CMake helper wires it in, and rg_bind_lint checks the binding), then open it at runtime through LicenseAsset. A forged or patched licence yields the wrong key and the feature just produces garbage — there is no verdict to patch. It is incremental: protect one high-value feature, leave the rest on the simple branch above.

#include <rockyguard/license_asset.h>

// The build step sealed the feature's data under the licence's data
// key (rg_bind_asset) and emitted it as sealed_premium[].
rockyguard::LicenseAsset asset(verifier, "premium");
auto data = asset.open(sealed_premium, sealed_premium_len);
// A wrong or forged licence yields empty/garbage 'data', not a bypass.
use_premium(data);

The full walkthrough — discovering bindable assets, the CMake helper, and the mandatory wrong-licence test — is in the AI Integration Guide (section 6.5) and on /security.

Step 5 — Link against the library

Static linking is recommended. The shipped librockyguard.a / rockyguard.lib contains the RockyGuard code only; OpenSSL is bundled in the deps/ directory and must be linked explicitly. Full per-platform link line:

# Windows static (CMake):
target_link_libraries(your_app PRIVATE
    ${ROCKYGUARD}/lib/static/rockyguard.lib
    ${ROCKYGUARD}/deps/lib/libssl.lib
    ${ROCKYGUARD}/deps/lib/libcrypto.lib
    ws2_32 crypt32 iphlpapi ole32 oleaut32 wbemuuid)

# Linux static (CMake):
target_link_libraries(your_app PRIVATE
    ${ROCKYGUARD}/lib/static/librockyguard.a
    ${ROCKYGUARD}/deps/lib/libssl.a
    ${ROCKYGUARD}/deps/lib/libcrypto.a
    pthread dl)

On Windows, link rockyguard.lib from a Release consumer or rockyguard_mdd.lib from a Debug-CRT consumer (both ship in v1.3.2). Full caveat in the shipped docs.


That is the core (simple-branch) integration story; load-bearing protection (Step 4b) is the further step for high-value features. Adversarial probes (wrong key, stripped signature, past-date license, wrong-machine license) and the floating-license setup are covered in Customer Documentation Sections 4 and 5.

Ready to start?

30-day Premium trial, no credit card.