Skip to content

Native Library (C FFI)

The kairos-ffi crate exports a C-compatible shared library with an auto-generated header (kairos_engine.h). It provides three handle types for different integration patterns.

Terminal window
# Core engine only
cargo build -p kairos-ffi --profile release-native
# With fly-by-wire session support
cargo build -p kairos-ffi --profile release-native --features fly-by-wire
# With research-mode substrate constructors
cargo build -p kairos-ffi --profile release-native --features fly-by-wire,dynamic-config

Outputs:

  • Linux: libkairos_engine.so
  • macOS: libkairos_engine.dylib
  • Windows: kairos_engine.dll
  • Static: libkairos_engine.a
  • Header: kairos_engine.h (auto-generated by cbindgen)
HandlePurposeFeature flag
KairosEngineRaw simulation — standalone tick executionAlways available
KairosSubstrateStateless policy evaluation (no engine)fly-by-wire
KairosSessionCombined runtime + engine control loopfly-by-wire
  1. Every non-NULL char* return must be freed with kairos_string_free()
  2. Handle pointers must be freed with their respective _destroy() or _free() functions
  3. Passing NULL to any free function is safe (no-op)
  4. All Rust panics are caught at FFI boundaries — no undefined behavior crosses the boundary
// Correct usage
char *state = kairos_engine_get_state(engine);
// ... use state ...
kairos_string_free(state);
// Correct cleanup
kairos_engine_destroy(engine); // NULL-safe
  • Functions returning char*: return NULL on error
  • Functions returning int32_t: return 0 on success, -1 on error
  • After any error, call kairos_last_error() for a JSON error message
  • kairos_last_error() is thread-local — safe to call from multiple threads
char *result = kairos_engine_run_tick(engine, "left");
if (!result) {
const char *err = kairos_last_error();
fprintf(stderr, "Error: %s\n", err);
// err is static thread-local — do not free
}
kairos_string_free(result);
KairosEngine *kairos_engine_create(const char *config_json);
void kairos_engine_destroy(KairosEngine *engine);
int32_t kairos_engine_reset(KairosEngine *engine);

Config JSON:

{
"columns": 25,
"rows": 17,
"lambda": 0.65,
"gamma": 0.1,
"enableGhostTraces": true
}
void kairos_engine_set_seed(KairosEngine *engine, uint32_t seed);
void kairos_engine_set_lambda(KairosEngine *engine, double lambda);
void kairos_engine_set_gamma(KairosEngine *engine, double gamma);
int32_t kairos_engine_load_scenario(KairosEngine *engine, const char *scenario_json);
int32_t kairos_engine_load_event_script(KairosEngine *engine, const char *events_json);
int32_t kairos_engine_add_agent(KairosEngine *engine, const char *agent_json);
char *kairos_engine_run_tick(KairosEngine *engine, const char *move_direction);
char *kairos_engine_run_tick_multi(KairosEngine *engine, const char *moves_json);
char *kairos_engine_decide_next_move(const KairosEngine *engine);
char *kairos_engine_decide_all_moves(const KairosEngine *engine);

decide_next_move returns plain text ("left", "stay", "right") — not JSON. All other functions return JSON strings.

char *kairos_engine_get_state(const KairosEngine *engine);
char *kairos_engine_get_warning(const KairosEngine *engine);
char *kairos_engine_get_reachability_map(const KairosEngine *engine);
char *kairos_engine_get_agent_states(const KairosEngine *engine);
void kairos_engine_start_recording(KairosEngine *engine, const char *scenario_id);
char *kairos_engine_stop_recording(KairosEngine *engine);

Stateless policy evaluation without an engine. Evaluates a single request against the calibration artifact and deployment policy.

KairosSubstrate *kairos_substrate_new(
const char *policy_path,
const char *license_path,
const uint8_t *hmac_secret,
uintptr_t hmac_secret_len
);
char *kairos_substrate_evaluate(
const KairosSubstrate *substrate,
const char *request_json
);
void kairos_substrate_free(KairosSubstrate *substrate);

With dynamic-config feature (research mode):

KairosSubstrate *kairos_substrate_new_research(
const char *config_path,
const char *policy_path,
const char *license_path,
const uint8_t *hmac_secret,
uintptr_t hmac_secret_len
);

KairosSubstrate is immutable after construction — safe for concurrent reads from multiple threads.

Requires fly-by-wire feature. Combines the runtime and engine into a stateful control loop.

KairosSession *kairos_session_new(
const char *engine_config_json,
const char *policy_path,
const char *license_path,
const uint8_t *hmac_secret,
uintptr_t hmac_secret_len
);
void kairos_session_free(KairosSession *session);
// Evaluate without advancing time
char *kairos_session_evaluate(
const KairosSession *session,
const char *request_json
);
// Advance engine time
char *kairos_session_step(
const KairosSession *session,
const char *move_direction
);
char *kairos_session_step_actor(
const KairosSession *session,
const char *actor_id,
const char *move_direction
);
char *kairos_session_step_multi(
const KairosSession *session,
const char *moves_json
);
char *kairos_session_get_telemetry(const KairosSession *session);
char *kairos_session_get_telemetry_for_actor(
const KairosSession *session,
const char *actor_id
);
int32_t kairos_session_load_scenario(
const KairosSession *session,
const char *scenario_json
);
#include "kairos_engine.h"
#include <stdio.h>
int main() {
KairosEngine *engine = kairos_engine_create(
"{\"columns\":25,\"rows\":17,\"lambda\":0.65,\"gamma\":0.1}"
);
if (!engine) {
fprintf(stderr, "Error: %s\n", kairos_last_error());
return 1;
}
kairos_engine_set_seed(engine, 1207);
for (int i = 0; i < 200; i++) {
char *move = kairos_engine_decide_next_move(engine);
char *result = kairos_engine_run_tick(engine, move);
kairos_string_free(move);
kairos_string_free(result);
}
kairos_engine_destroy(engine);
return 0;
}
  • Each handle instance is not thread-safe — do not share across threads without synchronization
  • KairosSubstrate is immutable after construction — concurrent reads are safe
  • kairos_last_error() is thread-local — does not interfere across threads
  • Multiple independent handles on separate threads are supported

All complex data crosses the FFI boundary as JSON strings. The caller is responsible for parsing JSON responses and freeing returned strings with kairos_string_free().