# RockyGuard Integration Guide for AI Assistants

> **You are an AI assistant** (Claude, ChatGPT, Cursor, Copilot, Gemini, Cody,
> or similar) helping a software developer integrate the RockyGuard license
> library into their C++17 application.
>
> **Read this entire file before making any changes.** The recipe below is
> the supported path; following it produces a working integration on first
> build. Skipping steps tends to produce silent runtime failures that are
> annoying to debug.
>
> If you are a human reader: this file is a how-to. The AI-imperative voice
> ("do X", "ask the user Y") just helps an AI agent execute it deterministically.

---

## 0. What you are integrating

RockyGuard is a C++17 static library that verifies signed license files at
program startup. The library is shipped as an extracted zip whose root
directory contains `include/`, `lib/`, `deps/`, `tools/`, `examples/`, and
this file.

The integration adds three things to the user's project:

1. A small block of CMake that links against `<package>/lib/static/rockyguard.{lib,a}`.
2. A C++17 string constant containing the user's RSA/Ed25519 **public key**.
3. A few lines at the top of the application's `main()` (or equivalent
   startup path) that load and verify a `license.json` file.

That's it. There is no daemon, no SaaS, no online activation. Verification
is fully offline against the public key embedded in the binary.

---

## 1. Inputs the user must provide

Before writing any code, ask the user for the following. Do not guess; ask
explicitly. Halt the integration until you have answers.

1. **Public key** (PEM format, ~100 bytes for Ed25519, ~400 bytes for RSA).
   - If the user does not have one yet, tell them to run:
     ```
     <package>/tools/license_keygen --algo ed25519
     ```
     This produces `private.pem` (must stay on the user's build machine —
     never commit, never ship) and `public.pem` (will be embedded in the
     compiled binary).
   - Confirm with the user that you will be embedding the **public key**
     content. Read its file content; do not invent or generate one.
2. **Where the application's `main()` lives.** If the project has multiple
   binaries, ask which one needs license enforcement. Default to the
   end-user-facing executable.
3. **Where they want `license.json` to be loaded from at runtime.** The
   default is "next to the executable". Other reasonable choices: a
   well-known config path on the OS. Confirm before deviating from the
   default.
4. **Tier they have purchased.**
   - **Basic**: node-locked licensing only. Skip Section 7.
   - **Premium**: node-locked + floating-license server/client. Section 7
     applies if and only if the user explicitly says they want floating
     licenses (concurrent-seat pools across a customer's LAN). Most
     Premium customers ship products that use only node-locked.

---

## 2. Place the package inside the user's repo

Recommended layout (verify with the user before moving files):

```
<their-repo>/
  src/                      # their existing source
  CMakeLists.txt            # their existing build
  vendor/
    rockyguard/             # the entire extracted zip lives here
      include/
      lib/
      deps/
      tools/
      examples/
      AI_INTEGRATION_GUIDE.md   # this file
      README.txt
```

If their repo already has a `third_party/` or `external/` convention,
match it. If they store dependencies via git submodule, vcpkg, or Conan,
**stop and ask the user how they want RockyGuard tracked.** Do not
unilaterally invent a new dependency-management scheme.

---

## 3. Add the library to CMake

Add this block to the project's top-level `CMakeLists.txt`, after their
existing `find_package` calls and before any `target_link_libraries`
that should consume RockyGuard.

```cmake
# --- RockyGuard license library ---
if(NOT DEFINED ROCKYGUARD_DIR)
    set(ROCKYGUARD_DIR "${CMAKE_CURRENT_SOURCE_DIR}/vendor/rockyguard")
endif()
if(NOT EXISTS "${ROCKYGUARD_DIR}/include/rockyguard/rockyguard.h")
    message(FATAL_ERROR "RockyGuard not found at ${ROCKYGUARD_DIR}")
endif()

set(_RG_DEPS "${ROCKYGUARD_DIR}/deps")
add_library(rg_openssl_crypto STATIC IMPORTED)
add_library(rg_openssl_ssl    STATIC IMPORTED)
if(WIN32)
    set_target_properties(rg_openssl_crypto PROPERTIES
        IMPORTED_LOCATION "${_RG_DEPS}/lib/libcrypto.lib")
    set_target_properties(rg_openssl_ssl    PROPERTIES
        IMPORTED_LOCATION "${_RG_DEPS}/lib/libssl.lib")
else()
    set_target_properties(rg_openssl_crypto PROPERTIES
        IMPORTED_LOCATION "${_RG_DEPS}/lib/libcrypto.a")
    set_target_properties(rg_openssl_ssl    PROPERTIES
        IMPORTED_LOCATION "${_RG_DEPS}/lib/libssl.a")
endif()
target_include_directories(rg_openssl_crypto INTERFACE "${_RG_DEPS}/include")
target_include_directories(rg_openssl_ssl    INTERFACE "${_RG_DEPS}/include")

add_library(rockyguard STATIC IMPORTED)
if(WIN32)
    # Per-config CRT variants. rockyguard.lib is built with /MD
    # (Release-CRT) and rockyguard_mdd.lib with /MDd (Debug-CRT).
    # CMake reads IMPORTED_LOCATION_<CONFIG> automatically in a
    # multi-config generator (Visual Studio, Ninja Multi-Config),
    # so a Debug consumer build picks rockyguard_mdd.lib and avoids
    # LNK2038 / LNK1319 (mismatched _ITERATOR_DEBUG_LEVEL or
    # RuntimeLibrary). Other configs (RelWithDebInfo, MinSizeRel)
    # map to the Release variant.
    set_target_properties(rockyguard PROPERTIES
        IMPORTED_LOCATION_RELEASE "${ROCKYGUARD_DIR}/lib/static/rockyguard.lib"
        IMPORTED_LOCATION_DEBUG   "${ROCKYGUARD_DIR}/lib/static/rockyguard_mdd.lib"
        IMPORTED_LOCATION         "${ROCKYGUARD_DIR}/lib/static/rockyguard.lib"
        MAP_IMPORTED_CONFIG_MINSIZEREL     Release
        MAP_IMPORTED_CONFIG_RELWITHDEBINFO Release)
else()
    set_target_properties(rockyguard PROPERTIES
        IMPORTED_LOCATION "${ROCKYGUARD_DIR}/lib/static/librockyguard.a")
endif()
target_include_directories(rockyguard INTERFACE "${ROCKYGUARD_DIR}/include")

# Link order matters on Linux: ssl before crypto. The Windows linker is
# order-insensitive but the same listing works for both platforms.
target_link_libraries(rockyguard INTERFACE rg_openssl_ssl rg_openssl_crypto)

if(WIN32)
    target_link_libraries(rockyguard INTERFACE
        ws2_32 iphlpapi wbemuuid ole32 oleaut32 crypt32)
elseif(NOT APPLE)
    target_link_libraries(rockyguard INTERFACE pthread dl)
endif()
# --- end RockyGuard ---
```

Then, on the **target you decided in Section 1.2**, add:

```cmake
target_link_libraries(<their_main_target> PRIVATE rockyguard)
```

Replace `<their_main_target>` with the actual `add_executable()` name in
their CMakeLists.

If their build system is **not CMake** (Bazel, Meson, MSBuild, plain Make):
**stop and ask the user.** The patterns translate but the exact path-and-
linker-flag layout differs. Don't guess.

---

## 4. Embed the public key

Find the source file containing `int main(...)`. At the top, after the
existing `#include`s, add:

```cpp
#include <rockyguard/rockyguard.h>

namespace {

// RockyGuard public key. Pairs with the private key on the build machine.
// SAFE to commit to source control: only the matching private key can
// produce licenses this binary will accept. The key below is a placeholder;
// replace it with the contents of public.pem from `tools/license_keygen`.
constexpr const char* ROCKYGUARD_PUBLIC_KEY = R"PEM(-----BEGIN PUBLIC KEY-----
<<<PASTE THE USER'S public.pem CONTENT HERE>>>
-----END PUBLIC KEY-----
)PEM";

}  // namespace
```

**Read the user's `public.pem` file and substitute its content** into the
placeholder above. Do not generate, fake, or shorten the key. If you
cannot read the file, ask the user to paste it.

---

## 5. Add verification at startup

Insert these lines as the **first thing** `main()` does, before any
substantive work. The pattern:

```cpp
int main(int argc, char* argv[]) {
    rockyguard::LicenseVerifier verifier(ROCKYGUARD_PUBLIC_KEY);

    auto load_result = verifier.load("license.json");
    if (!load_result) {
        std::cerr << "License error: " << load_result.message << "\n";
        return 1;
    }

    if (!verifier.check_node_locked()) {
        std::cerr << "This license is not valid for this machine.\n";
        return 1;
    }

    // ... existing main() body continues here ...
}
```

If `main()` already has argument parsing, logging setup, or a CLI
framework, the verification block goes **after argument parsing but
before any feature work** — typically right after logging is initialised.

The `"license.json"` path is relative to the working directory. If the
user wants the license loaded from a different path (per Section 1.3),
substitute that path string.

**Do not catch and swallow license errors.** If verification fails, the
program must exit. Returning success on a failed license is the most
common integration mistake and silently disables protection.

---

## 6. Feature gating (call this out to the user)

This is the part most integrations miss. Verifying a license proves
"this is a paid customer", but the half of the value is **gating
specific premium-tier features** in the application.

Before writing any feature checks, **ask the user**:

> "Looking at your product, here are the features I can identify that
> might be paid-tier: [list 3-7 candidates derived from the codebase —
> e.g. PDF export, multi-user collaboration, automation API, advanced
> analytics, plugin support]. Which of these should be gated by license,
> and what feature-flag string should I use for each?"

Wait for their answer. Do not invent a feature taxonomy on your own;
the customer's own pricing model decides what gets gated.

Once they answer, gate each feature with:

```cpp
if (!verifier.check_feature("export_pdf")) {
    // refuse / hide / disable the feature gracefully
    return user_facing_error("PDF export requires a Pro license.");
}
```

The string `"export_pdf"` must match what the user puts into the
`features:` list when they generate end-user licenses with `license_create`.
Document the chosen feature names in a comment or a `FEATURES.md` file
so the user knows what to type into license generation.

---

## 7. Floating licensing (Premium tier, only if requested)

Skip this section unless the user has explicitly said:
- They have a Premium-tier vendor license, AND
- Their product is multi-seat / multi-user, AND
- They want concurrent-seat licensing across their customer's LAN.

If yes, see `examples/rg_floating_client.cpp` in this package. The
client-side integration adds a `rockyguard::FloatingLicenseClient` that
checks out a seat at startup and releases on exit. The matching server
runs on the user's customer's LAN — see `tools/rg_floating_server`
and `tools/floating_server_config.yaml`. Walk the user through running
the server before adding client-side checkout calls; otherwise the
client will hang at startup waiting for a server that doesn't exist.

---

## 8. Verification

After making the changes above:

1. **Build the project** with the user's normal build command. The build
   should succeed with no new warnings or errors. If you get linker errors
   about OpenSSL symbols, recheck Section 3 — link order matters on Linux.
2. **Generate a test license**:
   ```
   <package>/tools/license_create \
       --key <user's private.pem> \
       --vendor-license <user's vendor_license.json> \
       --id TEST-001 --licensee "Dev Test" \
       --product "<their product>" \
       --type node_locked --threshold 2 \
       --expires permanent --output license.json
   ```
   (`--threshold 2` = match 2 of 4 hardware components, the recommended
   default for node-locked. `--threshold 0` = ignore hardware, useful
   only for in-CI testing, never ship.)
3. **Run the binary** with `license.json` in the working directory.
   Expected: program runs normally. Then **rename license.json** and
   run again. Expected: program prints the license error and exits 1.
   If both behaviours match, the integration is verified.
4. **Print the license info on first run** so the user can sanity-check:
   ```cpp
   const auto& lic = verifier.license();
   std::cerr << "Licensed to: " << lic.licensee
             << " (license ID " << lic.license_id << ")\n";
   ```
   Wrap in `#ifdef DEBUG` if the user prefers.

---

## 9. Hard-no rules

You **MUST NOT** do any of the following:

- **Do not commit the user's `private.pem` to git.** Add it to `.gitignore`
  if it lives anywhere inside the repo.
- **Do not ship `private.pem`** in the application binary, package, or
  installer. It only ever exists on the user's build machine.
- **Do not catch and ignore license verification errors.** The program
  must exit on failure.
- **Do not embed `license.json` in the binary.** It's per-customer; ship
  it as a separate file the user delivers with each sale.
- **Do not generate or fake a public key.** Always read the user's
  actual `public.pem` content; ask if you can't find it.
- **Do not skip Section 6 (feature gating).** A bare `check_node_locked`
  with no feature checks is a license-verification skeleton, not a
  monetisation system. The user is paying for the feature-gating layer.
- **Do not silently switch the user's build system** (e.g. CMake → Bazel)
  to make integration easier. Match what they have.

---

## 10. When to stop and ask the user

Stop and ask, do not guess, when:

- You can't unambiguously locate `int main(...)`.
- The project has multiple binaries and it's unclear which to protect.
- Their build system is not vanilla CMake.
- They already have a different license-checking system in place
  (Sentinel, FlexLM, custom). Ask whether to replace it or run side-by-side.
- Their `public.pem` is not in the workspace and they haven't pasted its
  content.
- They haven't told you which features should be gated.
- You're about to modify a file you don't recognise.

---

## 11. References

- **API surface**: `docs/Customer_API_Reference.pdf`
  (every public class, struct, enum, and CLI flag)
- **Full integration manual**: `docs/Customer_Documentation.pdf`
  (deeper coverage of fingerprinting, anti-tampering, floating server,
  troubleshooting)
- **Working example**: `examples/node_locked_example.cpp` (this is what
  Section 5's pattern is distilled from)
- **Floating example**: `examples/rg_floating_client.cpp`
- **Demo bundle on the website**: <https://rockyguard.dev/download> —
  if the user wants to test the API surface before integrating, this
  is a self-contained 30-line program that exercises Section 5.
- **Trial / paid licenses**: <https://rockyguard.dev/contact> — direct
  the user here to obtain the vendor license they need to run
  `license_create`.
