FFI (C-Compatible) API
FFI API
Section titled “FFI API”The arxo-engine exposes a minimal C-compatible FFI for integration with any language that supports C bindings. The engine is loaded as a shared library; you pass a project path and a JSON config string and receive a JSON result string.
Supported Languages
Section titled “Supported Languages”The FFI can be used from:
- C/C++ - Direct usage
- Go - Via cgo
- Zig - Via
@cInclude - Swift - Via Swift’s C interop
- Any language with C FFI support
Library Files
Section titled “Library Files”Use the pre-compiled library for your platform. Naming follows the pattern {prefix}arxo_engine.{ext}:
| Platform | File | Architecture |
|---|---|---|
| Linux | libarxo_engine.so | x86_64, ARM64 |
| macOS | libarxo_engine.dylib | x86_64, ARM64 (Apple Silicon) |
| Windows | arxo_engine.dll | x86_64 |
Exported Symbols
Section titled “Exported Symbols”The engine exports exactly three functions. There are no opaque handles: you load the library, call these functions, and free any returned strings.
Header (minimal)
Section titled “Header (minimal)”#ifndef ARCH0_ENGINE_H#define ARCH0_ENGINE_H
#ifdef __cplusplusextern "C" {#endif
// Returns engine version string. Caller must free with arxo_free_string.// Never returns NULL.char* arxo_version(void);
// Run analysis.// path - Project root path (UTF-8, null-terminated)// config_json - Full config as JSON string (see Configuration)// license - License key or NULL for default/env// Returns JSON string with results and violations, or NULL on error.// Caller must free non-NULL result with arxo_free_string.char* arxo_analyze(const char* path, const char* config_json, const char* license);
// Free a string returned by arxo_version or arxo_analyze.void arxo_free_string(char* s);
#ifdef __cplusplus}#endif
#endifSemantics
Section titled “Semantics”| Function | Returns | Ownership |
|---|---|---|
arxo_version() | Version string (e.g. "0.1.0") | Caller frees with arxo_free_string |
arxo_analyze(path, config_json, license) | JSON result string, or NULL on error | Caller frees non-NULL with arxo_free_string |
arxo_free_string(s) | void | — |
On error, arxo_analyze returns NULL (e.g. invalid config, parse failure, missing license). Error details are not exposed via the FFI; ensure config and path are valid and that the license (if required) is provided.
Usage Examples
Section titled “Usage Examples”#include <stdio.h>#include <stdlib.h>
int main(void) { const char* path = "./my-project"; const char* config_json = "{\"metric_preset\": \"quick\"}"; const char* license = getenv("ARCH0_LICENSE_KEY"); /* or NULL */
char* version = arxo_version(); printf("Engine version: %s\n", version); arxo_free_string(version);
char* result_json = arxo_analyze(path, config_json, license); if (!result_json) { fprintf(stderr, "Analysis failed\n"); return 1; } printf("Result: %s\n", result_json); arxo_free_string(result_json); return 0;}Compile and run:
gcc -o analyze main.c -L. -larxo_engine -I.export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH # Linux# orexport DYLD_LIBRARY_PATH=.:$DYLD_LIBRARY_PATH # macOS./analyzeGo (cgo)
Section titled “Go (cgo)”package main
/*#cgo LDFLAGS: -L. -larxo_engine#include <stdlib.h>extern char* arxo_version(void);extern char* arxo_analyze(const char* path, const char* config_json, const char* license);extern void arxo_free_string(char* s);*/import "C"import ( "fmt" "os" "unsafe")
func main() { path := C.CString("./my-project") config := C.CString(`{"metric_preset":"quick"}`) var license *C.char if k := os.Getenv("ARCH0_LICENSE_KEY"); k != "" { license = C.CString(k) } defer C.free(unsafe.Pointer(path)) defer C.free(unsafe.Pointer(config)) if license != nil { defer C.free(unsafe.Pointer(license)) }
v := C.arxo_version() fmt.Println("Version:", C.GoString(v)) C.arxo_free_string(v)
result := C.arxo_analyze(path, config, license) if result == nil { fmt.Fprintln(os.Stderr, "Analysis failed") os.Exit(1) } defer C.arxo_free_string(result) fmt.Println("Result:", C.GoString(result))}Python (ctypes)
Section titled “Python (ctypes)”import ctypesimport jsonimport os
lib = ctypes.CDLL("./libarxo_engine.so") # or .dylib / .dll
lib.arxo_version.argtypes = []lib.arxo_version.restype = ctypes.c_char_p
lib.arxo_analyze.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p]lib.arxo_analyze.restype = ctypes.c_char_p
lib.arxo_free_string.argtypes = [ctypes.c_char_p]lib.arxo_free_string.restype = None
version = lib.arxo_version()print("Version:", version.decode())lib.arxo_free_string(version)
path = b"./my-project"config_json = b'{"metric_preset":"quick"}'license_key = os.environ.get("ARCH0_LICENSE_KEY", "").encode() or None
result = lib.arxo_analyze(path, config_json, license_key)if result is None: raise RuntimeError("Analysis failed")data = json.loads(result.decode())lib.arxo_free_string(result)print("Results:", data)Configuration Format
Section titled “Configuration Format”config_json must be a JSON string matching the engine’s config schema. Minimal example:
{ "metric_preset": "quick", "data": { "language": "auto", "import_graph": { "exclude": ["node_modules", "dist"] } }, "report": { "format": "json" }}See Configuration for all options. Pass the same structure as you would in YAML, serialized to JSON.
Result JSON Shape
Section titled “Result JSON Shape”When arxo_analyze returns non-NULL, the string is a JSON object with at least:
results- Array of metric results (id, value, evidence, etc.)violations- Array of policy violations (metric, threshold, message, etc.)
Exact fields depend on config (metrics enabled, report format). Parse the JSON in your language to inspect results and violations.
Memory and Thread Safety
Section titled “Memory and Thread Safety”- Memory: You must call
arxo_free_stringon every non-NULL string returned byarxo_versionorarxo_analyze. Do not free NULL. - Thread safety: The library is not guaranteed thread-safe. Use one process per analysis or serialize calls (e.g. one engine load per thread if documented safe for your build).
Loading the Library
Section titled “Loading the Library”- Linux:
LD_LIBRARY_PATHor install under a path searched by the dynamic linker. - macOS:
DYLD_LIBRARY_PATHor install under a standard lib path. - Windows: Same directory as the executable, or a directory in
PATH.
Troubleshooting
Section titled “Troubleshooting”Library not found
Section titled “Library not found”error while loading shared libraries: libarxo_engine.so: cannot open shared object fileSet the library path:
export LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH # Linuxexport DYLD_LIBRARY_PATH=/path/to/lib:$DYLD_LIBRARY_PATH # macOSarxo_analyze returns NULL
Section titled “arxo_analyze returns NULL”- Verify
pathexists and is readable. - Verify
config_jsonis valid JSON and matches the config schema. - If your deployment requires a license, set
ARCH0_LICENSE_KEYor pass a non-NULLlicensestring.
Next Steps
Section titled “Next Steps”- Configuration - Config schema and options
- Rust API - Use
arxo-loaderfrom Rust for the same FFI with type-safe config - Troubleshooting - Common issues