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 (jne → jmp) 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.