Skip to content

C / C++ SDK

The C/C++ SDK wraps the Rust SDK via a C ABI and ships as libratify_c.a (static) and libratify_c.so / libratify_c.dylib (shared). The header (include/ratify.h) is auto-generated by cbindgen and includes extern "C" guards so it works in both C and C++.

Byte-for-byte interoperable with Go, TypeScript, Python, and Rust. Apache-2.0.

Stability: every primitive on this page is stable in 1.0.0-alpha.8.

You’re writingUse
A Go service or CLIGo SDK
A Node.js or browser appTypeScript SDK
A Python script or ML pipelinePython SDK
A Rust service or high-performance binaryRust SDK — use directly, no FFI overhead
C or C++ codeThis SDK
Firmware, RTOS, hardware driverThis SDK (static library, libratify_c.a)
A language that FFIs to C (Swift, Zig, Julia, Lua, etc.)This SDK

Prerequisites: Rust toolchain 1.70+.

Terminal window
git clone https://github.com/identities-ai/ratify-protocol
cd ratify-protocol/sdks/c
cargo build --release

Outputs:

target/release/libratify_c.a — static library (embed in firmware / link statically)
target/release/libratify_c.so — shared library (Linux)
target/release/libratify_c.dylib — shared library (macOS)
include/ratify.h — C/C++ header (auto-generated, committed)

You can also download pre-built libraries for common targets from the GitHub Releases page. The committed include/ratify.h lets you use pre-built libraries without installing Rust.

Terminal window
cargo install cross --git https://github.com/cross-rs/cross
# ARM64 — Raspberry Pi 4, embedded Linux SBCs
cross build --release --target aarch64-unknown-linux-gnu
# ARM32 — Raspberry Pi 2/3, older embedded Linux
cross build --release --target armv7-unknown-linux-gnueabihf
# ARM Cortex-M4/M7 — FreeRTOS / Zephyr
rustup target add thumbv7em-none-eabihf
cargo build --release --target thumbv7em-none-eabihf
# RISC-V 64
cross build --release --target riscv64gc-unknown-linux-gnu

Quick start — Delegate → Present → Verify

Section titled “Quick start — Delegate → Present → Verify”

The complete agent authorization flow in C. All API entry/exit conditions are explicitly handled.

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "ratify.h"
static int fail(const char *step, char *err) {
fprintf(stderr, "FAIL: %s%s\n", step, err ? err : "unknown error");
ratify_error_free(err);
return 1;
}
int main(void) {
printf("Ratify %s — C SDK example\n\n", ratify_version());
char *err = NULL;
/* 1. Generate identities */
RatifyHumanRoot *root = NULL;
if (ratify_human_root_generate(&root) != RatifyOk)
return fail("ratify_human_root_generate", NULL);
RatifyAgent *agent = NULL;
if (ratify_agent_generate("MyDroneBot", "drone", &agent) != RatifyOk) {
ratify_human_root_free(root);
return fail("ratify_agent_generate", NULL);
}
/* 2. Issue a DelegationCert */
int64_t now = (int64_t)time(NULL);
RatifyDelegationCert *cert = NULL;
if (ratify_delegation_issue(root, agent,
"[\"physical:enter\"]",
now, now + 3600LL,
&cert, &err) != RatifyOk)
return fail("ratify_delegation_issue", err);
char *cert_json = ratify_delegation_cert_to_json(cert, &err);
if (!cert_json)
return fail("ratify_delegation_cert_to_json", err);
/* 3. Generate challenge and build ProofBundle */
uint8_t challenge[32];
if (ratify_challenge_generate(challenge, 32) != RatifyOk)
return fail("ratify_challenge_generate", NULL);
RatifyProofBundle *bundle = NULL;
if (ratify_proof_bundle_create(agent, cert_json,
challenge, 32,
now,
&bundle, &err) != RatifyOk)
return fail("ratify_proof_bundle_create", err);
ratify_string_free(cert_json);
char *bundle_json = ratify_proof_bundle_to_json(bundle, &err);
if (!bundle_json)
return fail("ratify_proof_bundle_to_json", err);
/* 4. Verify */
RatifyVerifyResult *result = NULL;
ratify_verify_bundle(bundle_json, "physical:enter", now, &result, &err);
if (ratify_verify_result_is_valid(result)) {
char *agent_id = ratify_verify_result_agent_id(result);
printf("authorized agent: %s\n", agent_id);
ratify_string_free(agent_id);
} else {
char *status = ratify_verify_result_identity_status(result);
printf("rejected: %s\n", status);
ratify_string_free(status);
}
/* 5. Cleanup */
ratify_verify_result_free(result);
ratify_string_free(bundle_json);
ratify_proof_bundle_free(bundle);
ratify_delegation_cert_free(cert);
ratify_agent_free(agent);
ratify_human_root_free(root);
return 0;
}

Build and run:

Terminal window
# macOS
cc example.c -I include -L target/release \
-lratify_c -lpthread -framework Security -framework CoreFoundation \
-o example && ./example
# Linux
cc example.c -I include -L target/release \
-lratify_c -lpthread -ldl -lm -o example && ./example
cmake_minimum_required(VERSION 3.20)
project(my_agent C)
set(RATIFY_SDK_DIR "${CMAKE_SOURCE_DIR}/vendor/ratify-c")
add_library(ratify STATIC IMPORTED)
set_target_properties(ratify PROPERTIES
IMPORTED_LOCATION "${RATIFY_SDK_DIR}/lib/libratify_c.a"
INTERFACE_INCLUDE_DIRECTORIES "${RATIFY_SDK_DIR}/include"
)
add_executable(my_agent main.c)
target_link_libraries(my_agent PRIVATE ratify pthread dl m)

Embedded / RTOS — FreeRTOS custom entropy

Section titled “Embedded / RTOS — FreeRTOS custom entropy”

On standard OS targets (Linux, macOS, Raspberry Pi) entropy is automatic. On RTOS targets without /dev/urandom, enable the custom-entropy Cargo feature and register your hardware TRNG:

Cargo.toml:

ratify-c = { path = "…", features = ["custom-entropy"] }

Application startup (STM32 example):

#include "ratify.h"
static int my_entropy(uint8_t *buf, size_t len) {
for (size_t i = 0; i < len; i += 4) {
uint32_t rnd;
if (HAL_RNG_GenerateRandomNumber(&hrng, &rnd) != HAL_OK)
return -1; /* -1 = entropy unavailable → library halts */
size_t copy = (len - i < 4) ? (len - i) : 4;
memcpy(buf + i, &rnd, copy);
}
return 0;
}
int main(void) {
/* Must be called before ratify_challenge_generate() or ratify_delegation_issue() */
ratify_set_entropy_source(my_entropy);
/* … rest of your RTOS application */
}

The library halts if ratify_set_entropy_source() was never called when custom-entropy is enabled — generating certs with weak randomness is worse than not running at all.

The C SDK is compatible with RTOS environments that provide a heap (alloc) but not the full Rust std. Bare-metal Cortex-M targets with no heap at all should use the Rust SDK directly with #[no_std] + alloc.

ArchitectureTarget tripleExample hardware
x86-64x86_64-unknown-linux-gnuIntel/AMD server, Linux PC
ARM64aarch64-unknown-linux-gnuRaspberry Pi 4, embedded Linux, Apple Silicon
ARM32armv7-unknown-linux-gnueabihfRaspberry Pi 2/3, older embedded Linux
ARM Cortex-M4/M7thumbv7em-none-eabihfSTM32, NXP — FreeRTOS, Zephyr
x86-32i686-unknown-linux-gnuLegacy industrial, 32-bit Linux
RISC-V 64riscv64gc-unknown-linux-gnuSiFive, emerging IoT
macOS ARM64aarch64-apple-darwinApple Silicon Mac
macOS x86-64x86_64-apple-darwinIntel Mac
Windows x86-64x86_64-pc-windows-msvcNative Windows

A Raspberry Pi test script is available at sdks/c/scripts/test-raspberry-pi.sh.

The C SDK passes 42 conformance fixtures against the canonical cross-language test vectors (the same set used by Go, TypeScript, Python, and Rust). Run them with:

Terminal window
cargo test --test conformance

Additional unit tests (null pointers, malformed JSON, round-trips, bad-argument detection):

Terminal window
cargo test --test api

Total: 100 tests.

Every function that returns a heap-allocated value documents which _free function to call. NULL is always safe to pass to _free functions.

ratify_human_root_free(root);
ratify_agent_free(agent);
ratify_delegation_cert_free(cert);
ratify_proof_bundle_free(bundle);
ratify_verify_result_free(result);
ratify_string_free(any_string); /* for *_to_json, *_id, *_status, *_reason */
ratify_error_free(err); /* for err_out parameters */