Camera SDK · C ABI · Edge inference
Computer vision that runs on the camera, and licensing that runs with it
XCAICX is an on-device CV SDK for AI camera manufacturers and the factories that deploy them. Detection, defect inspection, PPE compliance and plate localisation execute on the unit with no cloud round trip — and per-unit licensing and metered API billing are part of the runtime rather than something bolted on afterwards.
The demo below is not a video. It is the SDK's reference backend ported to JavaScript — same algorithms, same constants — running on your machine.
// firmware links one stable C ABI xcaicx_config cfg; xcaicx_config_init(&cfg); cfg.license_path = "/etc/xcaicx/batch.token"; cfg.state_dir = "/var/lib/xcaicx"; cfg.modules = XCAICX_MODULE_DETECT | XCAICX_MODULE_PPE; // activation happens once, on this unit activated as : LIC-D3ACF708DB6048AB bound device : b2cd209a6826bd8b33c0fdaad6304106 batch now : 1/5000 activated // then it runs, network or no network event: LINE_CROSS zone=doorway track=1 event: PPE_VIOLATION no_helmet score=0.71 metered inferences: 40 (2 modules x 20 frames)
Live · runs on your machine
The reference backend, running in your browser
Every box, track and event below is produced by a direct port of
backend_reference.cpp: the same Sobel/Otsu/connected-components
detection, the same MAD-thresholded tile statistics for defects, the same HSV
band ratios for PPE, the same plate geometry for ANPR, the same greedy-IoU
tracker and even-odd zone test. Point it at a synthetic scene, or at your own
camera.
Modules
A module the selected SKU does not entitle is struck through and cannot be
switched on — the runtime refuses it, exactly as
xcaicx_engine_create does.
Controls
Event stream
This is the reference backend, and it is not production accuracy.
It is contrast, edge and colour heuristics with no learned model behind it. It exists
so an integrator can validate frame plumbing, licensing, events and metering before a
model ever reaches them, and so CI is deterministic. On a real factory floor it will
miss things and invent things. Production deployments build with
XCAICX_WITH_ORT=ON and ship a model bundle; the engine logs the
distinction at INFO on every start so nobody discovers it in the field.
Nothing leaves your machine. Frames from the synthetic scenes and from your camera are analysed in the page and never uploaded — which is the SDK's entire argument, so the website holds to it too.
Capability modules
Four modules, mapped one-to-one onto license entitlements
Each module is a bit in the license token. The engine refuses to start one whose bit is false, so what a customer bought and what their cameras can do cannot drift apart.
Detection & tracking
People, vehicles and objects, with a greedy-IoU tracker that coasts across skipped frames. Zones, directed tripwires and dwell timers turn tracks into events your VMS can act on.
Defect inspection
Tile statistics against the frame's own robust median and MAD, so a real defect cannot inflate the statistics used to find it. Deliberately conservative: a false reject stops a production line.
PPE compliance
Helmet and hi-vis checks on the head and torso bands of each detected person. It reports the absence, because the actionable event on a factory floor is the violation, not the compliance.
Plate localisation & OCR
Plates are dense horizontal-edge rectangles at a characteristic aspect ratio. Localisation runs on any build; reading the characters needs the text model in the production bundle.
The commercial loop
From a batch order to an invoice, without a spreadsheet in the middle
The same six steps run whether you ship five cameras or five hundred thousand. Every one of them is exercised by the test suite.
Sell a batch
An OEM buys N units. They receive one batch key — unbound, activation required — and nothing else. It is a licence to activate, not a licence to run.
$ xcaicxctl batch --customer CUST-CAMCO --sku oem-industrial --units 5000 batch : B-2026-1C4417D5 (5000 units) batch key : XCAICX1.eyJsaWMiOiJMSUMtQkFUQ0gt…
Flash every camera with the same key
One image, one key, the whole production line. No per-unit provisioning step and no serial-number spreadsheet to keep in sync.
Each unit activates once
On first boot the SDK exchanges the batch key for a licence bound to that camera's hardware fingerprint, and the server decrements the batch counter. That decrement is the billing event. Re-activating the same device returns the existing token and consumes nothing — a reflash or an SD-card swap must never be a billing event.
POST /v1/activate { "batch_token": "XCAICX1.…", "device_id": "b2cd209a…" } 200 { "license_id": "LIC-D3ACF708DB6048AB", "reactivated": false, "batch_units_remaining": 4999 }
Frames go in, events come out
Feed whatever your ISP already produces — NV12 and I420 are first-class, so you are not paying for an RGB conversion on the boards that can least afford it. Tracking, zones and tripwires run on top.
Usage spools, then ships
One inference per module per frame is counted on-device, checkpointed to disk and resumed across reboots. The spool is cleared only after the server acknowledges, so an outage delays revenue rather than losing it. Replays are deduped server-side.
The period rates itself
Activated units, metered inference over the included allowance, and annual maintenance on perpetual SKUs, rated into an invoice per customer.
$ xcaicxctl invoice CUST-CAMCO --days 30 XCAICX OEM Industrial - per unit 1,204 unit XCAICX OEM Industrial - annual maintenance 892 unit-year TOTAL — USD
Licensing · live
Poke at the enforcement yourself
A licence is a signed, self-contained string:
XCAICX1.<base64url(payload)>.<base64url(ed25519_sig)>.
The lab below generates a throwaway Ed25519 keypair in your browser, mints a real
token against it, and verifies it with the same rules the C++ verifier applies on
the camera. Try to break it.
Token
Verifier verdict
Decoded payload
In-page public key: — generated here, discarded when
you close the tab. The production root key never leaves the licensing server;
its public half is compiled into the SDK, so a camera verifies with no network
and nothing on the device can mint a licence.
5 units remaining on this batch.
Server responses
Offline licensing is deterrence, not DRM. An attacker with root on the camera and a disassembler can patch out the check — that is true of every licensing scheme running on hardware the attacker controls. What this design buys is that casual copying is dead, entitlements cannot be widened without forging Ed25519, seat accounting is server-side and cannot be inflated on-device, and weak fingerprint sources are recorded per activation so a cluster of trivially-clonable units on one account is visible to you.
| Attack | Result |
|---|---|
| Flip a byte in the payload | BadSignature |
Re-encode with anpr: true, keep the signature | BadSignature |
| Copy a licence to another camera | WrongDevice |
| Wind the clock back a year | ClockRollback |
| Use a token past its term | Expired, after the grace window |
| Use a revoked licence | Revoked |
| Forge a batch key with your own key | 403 at activation |
| Present a validly-signed key we never sold | 403 — no seat behind it |
| Replay a usage record to avoid billing | Deduped server-side |
| Reboot to reset the metered counter | Counters resume from disk |
Integration
One header is the entire supported surface
Deliberately a C ABI, not a C++ one. 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 — and a C++ ABI breaks across every one of them. Structs carry a
leading struct_size, so a binary compiled today keeps working against
a future SDK.
#include "xcaicx/xcaicx.h" xcaicx_config cfg; xcaicx_config_init(&cfg); /* always first */ cfg.license_path = "/etc/xcaicx/unit.token"; cfg.state_dir = "/var/lib/xcaicx"; cfg.modules = XCAICX_MODULE_DETECT | XCAICX_MODULE_PPE; xcaicx_engine *engine; if (xcaicx_engine_create(&cfg, &engine) != XCAICX_OK) { fprintf(stderr, "%s\n", xcaicx_last_error()); return 1; } /* in your ISP callback — borrowed pixels, never copied */ xcaicx_frame f = { .struct_size = sizeof f, .data = nv12, .data_len = len, .width = 1920, .height = 1080, .format = XCAICX_PIX_NV12 }; const xcaicx_result *res; if (xcaicx_stream_process(stream, &f, &res) == XCAICX_OK) for (size_t i = 0; i < res->detection_count; ++i) printf("%s %.2f\n", res->detections[i].class_name, res->detections[i].score);
import xcaicx with xcaicx.Engine(license_path="unit.token", state_dir="/var/lib/xcaicx") as eng: print(eng.license.sku, eng.license.entitlements) with eng.stream("line-3", modules=xcaicx.Module.DETECT | xcaicx.Module.PPE) as s: s.add_zone("packing-bay", [(0.55, 0), (1, 0), (1, 1), (0.55, 1)], dwell_seconds=30) s.add_line("doorway", (0.5, 0), (0.5, 1)) result = s.process(frame_bgr, width=1920, height=1080, fmt=xcaicx.PixelFormat.BGR8) for ev in result.events: print(ev.kind.name, ev.zone_name, ev.detail)
Builds where your BSP builds
CMake and C++17, with OpenSSL the only hard dependency — and it is already in
every BSP. Cross-compiling is a standard toolchain file. Yocto and Buildroot
recipes need openssl in DEPENDS and nothing else.
Backends are one interface
Reference and ONNX Runtime ship in the box. TensorRT, RKNN, OpenVINO and Hailo
mean implementing a single header — backend.hpp is the whole extension
surface.
Air-gapped is a first-class path
Factories that will never let a camera reach the internet are a large share of the industrial market. Pre-provision device-bound tokens offline and the camera never touches a network; usage still accrues locally for reconciliation.
Degrade, do not die
A lapsed licence stops analytics, never the video path. A camera that stops recording because a licence expired is a warranty claim, so the SDK is built so that cannot happen.
Questions
The things integrators actually ask first
Does anything leave the camera?
Inference does not. The whole pipeline runs on the unit, and a camera with
offline = 1 never opens a socket. What optionally leaves is licensing
and metering: an activation call on first boot, and a periodic heartbeat carrying
counts — inferences, frames, stream-seconds, events. No frames, no crops, no
imagery of any kind.
What accuracy should we expect?
From the reference backend that ships in the box: enough to bring up your frame plumbing and event handling, and not enough for production. It is contrast, edge and colour heuristics. Production accuracy comes from building with ONNX Runtime and shipping a model bundle, and the numbers that matter are the ones measured on your cameras in your lighting — which is a conversation, not a datasheet row.
What happens when a camera is offline for a month?
It keeps working. Usage spools to disk and is only cleared after the server acknowledges it, so a long outage delays billing rather than losing it. A licence that expires during the outage enters its grace window; past that, analytics stop and video keeps recording.
Can a customer clone one paid image onto fifty cameras?
Not casually. Licences are bound to a hardware fingerprint, so the copy fails with
WRONG_DEVICE on every unit but the original. Bind to fused silicon or a
secure element rather than a MAC address and it gets considerably harder — the SDK
supports that today through XCAICX_DEVICE_ID. Against an attacker with
root and a disassembler, no software scheme wins; see the note in the licensing
section.
How is a reflash or an RMA handled?
Re-activating a device already on file returns its existing token and consumes no additional unit, so reflashes and SD-card swaps are free. A genuine hardware replacement is a new fingerprint and therefore a new unit; revoke the old licence if the board is being scrapped.
What does it cost?
Per programme. There are two revenue lines — a per-unit royalty or per-camera subscription, and metered inference over an included allowance — and the shape that fits a 500-unit pilot is not the shape that fits a 100,000-unit run. Tell us the volume, the modules and whether the fleet is online, and you will get real numbers rather than a list price to negotiate down from.
Which SoCs has this run on?
The core is portable C++17 with OpenSSL and no accelerator assumptions, so the reference backend runs anywhere your BSP does. Accelerated inference depends on the backend: ONNX Runtime is in-tree, and TensorRT, RKNN, OpenVINO and Hailo are a single interface each. Bring the board you are shipping and we will talk about what the bring-up actually looks like.
Talk to us
Start with the board you are shipping
The useful first conversation is about your SoC, your volume, your modules and whether the fleet will be online. Evaluation licences are available for the full module set.