Versioning & ABI policy
What we promise not to break, and what "not breaking it" actually means for firmware that ships once and runs for a decade.
The problem this policy exists for#
A camera is not a web app. Firmware is qualified, certified, and then deployed to units that may not be reachable again for years. An SDK that requires a recompile to keep working is an SDK that strands them.
So the ABI is versioned structurally rather than by convention.
Version numbers#
#define XCAICX_VERSION_MAJOR 1
#define XCAICX_VERSION_MINOR 0
#define XCAICX_VERSION_PATCH 0
const char *xcaicx_version_string(void);| Component | Changes when |
|---|---|
| Major | An incompatible ABI change. Requires a recompile. |
| Minor | New functions, new struct fields, new enum values. Binary compatible. |
| Patch | Fixes only. No surface change. |
What is guaranteed within a major version#
- Functions are never removed and never change signature.
- Enum values are never renumbered. New values are appended, so a binary compiled today keeps interpreting the values it knows.
- Struct fields are never removed, reordered, or repurposed. New fields are appended only.
- Behaviour is not silently changed. A fix that alters what your code observes ships in a minor version with a note, not in a patch.
How struct_size works#
Every configuration and result struct starts with size_t struct_size.
xcaicx_config cfg;
xcaicx_config_init(&cfg); /* stamps struct_size = sizeof(cfg) as YOU compiled it */The library compares the value you set against the sizes it knows and uses it to decide which fields are actually present in your struct.
That gives both directions of compatibility:
| Case | Result |
|---|---|
| Old binary, new library | Library sees a smaller struct_size, uses only the old fields, applies defaults for the rest |
| New binary, old library | Library sees a larger struct_size than it knows and returns XCAICX_ERR_ABI_MISMATCH rather than reading fields it does not understand |
Do not
This only works if you call the *_init function. A zeroed or memcpy'd struct has struct_size == 0, which the library cannot interpret — that is the most common cause of ABI_MISMATCH. Never set struct_size by hand, and never copy a config struct between SDK versions.
Result structs carry it too, so you can defensively check before reading a field your header knows about but the runtime may not have written:
if (res->struct_size >= offsetof(xcaicx_result, total_ms) + sizeof(float))
log_timing(res->total_ms);Most integrations do not need that. It matters when you deliberately ship one binary against several SDK versions.
Why C and not C++#
Camera vendors build with whatever toolchain their SoC BSP ships — GCC 7 on a Rockchip Yocto image, NDK clang on Android, MSVC on a Windows NVR. A C++ ABI breaks across all of them: name mangling, exception tables and the standard library's own ABI all differ. A C ABI with explicit struct versioning is the only boundary that survives that spread.
The core is C++17 internally. Only the boundary is C.
Symbol visibility#
Only XCAICX_API symbols are exported; everything else is hidden (-fvisibility=hidden). Camera firmware links many static libraries into one image, and leaking our internals — or OpenSSL's, when it is static-linked — causes duplicate-symbol grief that lands on the integrator's desk.
On Windows, define XCAICX_USING_SHARED before including the header when linking against the DLL.
The token format#
The licence token carries its own version in the header: XCAICX1.
A future format would be XCAICX2, and old SDKs would reject it as LICENSE_MALFORMED rather than misinterpreting it. Any such change would be staged the same way a key rotation is: new tokens issued only to fleets known to understand them.
New claims inside a payload are backward compatible — an older verifier ignores fields it does not know, because the signature covers the encoded bytes rather than a parsed structure.
Deprecation#
When something must go:
- It is documented as deprecated in a minor release, with the replacement named.
- It keeps working for the remainder of the major version.
- It is removed only at the next major version.
Nothing is deprecated today.
Upgrading#
Patch and minor#
Drop in the new shared library. No recompile needed. Re-run your own integration tests — minor versions may add behaviour, and new event kinds or backends can appear.
Major#
Recompile against the new header and re-qualify. A major version will state exactly what changed and why; if it is not worth a re-qualification cycle to you, the previous major version keeps working — it just stops receiving fixes eventually.
Server and SDK together#
The licensing authority and the SDK version independently. The compatibility surface between them is the token format and the REST endpoints, both versioned in their own path (XCAICX1, /v1/).
Careful
The one thing that must move together is the signing key. A server signing with a key whose public half is not in a deployed SDK build produces BAD_SIGNATURE on every camera. Rotation is staged through the engine's license_pubkey config override precisely so key changes and firmware releases do not have to be simultaneous — see Running the authority.
Reporting a compatibility break#
If a minor or patch upgrade breaks a working integration, that is a bug in this policy, not in your code. Send the SDK versions, the platform, and the failing call to [email protected].
Next#
- C ABI reference — the surface this policy covers
- Running the authority — key rotation
- Status codes —
ABI_MISMATCHand friends