# Embedda Backend SDK (C-ABI `nx_backend.h`)

Make an Embedda node run on **your accelerator** (NPU/GPU/ASIC): implement one
frozen C ABI in a shared library and the dynamic loader drives it — no fork of
this repo, no Rust required on your side.

LLM agents: operate from [`manifest.json`](manifest.json). Labels are binding.

## The contract (v1, FROZEN)

[`include/nx_backend.h`](include/nx_backend.h) — 18 symbols (`nx_be_*` prefix,
configurable), the neutral twin of the two shipped shims:

- `libs/hailo/hailo-infer-shim/csrc/nx_hailo.h` (Hailo NPU, production)
- `libs/gpu/nexus-trt-shim/csrc/nx_trt.h` (TensorRT GPU, production)

`tools/abi-parity/check.sh` enforces that all **three** stay isomorphic modulo
prefix; `contracts/FROZEN.sha256` pins the normative header. Semantics live as
doc comments in the header itself: slot-ring execution, submit/poll/release
lifetimes, bounded waits, and the telemetry honesty rule (`<0` = the device
does not report it — **never** fabricate a value).

## Vendor path, end to end (no hardware needed to start)

```bash
# 1. read the contract
$EDITOR sdk/backend/include/nx_backend.h

# 2. study the TEST-ONLY reference (deterministic fixture, not inference)
$EDITOR sdk/backend/reference-cpu/nx_be_reference_test.c
sdk/backend/reference-cpu/build.sh          # plain gcc, no CMake

# 3. certify your .so with the conformance kit (drives the REAL loader)
cargo run -p nexus-backend-conformance -- /path/to/libyourvendor.so your-model
```

The kit's checks: lifecycle ×3, dims consistency, slot-ring pressure with FIFO
tags + rejected overflow, frame-size rejection, deterministic outputs, double
release (must be ignored, never corrupt the ring), honest telemetry, and the
`InferenceEngine` port happy path. Exit 0 = conformant; the PASS/FAIL report
is the certification artifact.

## Loading a vendor backend (gate default-OFF)

```bash
NEXUS_BACKEND=dyn \
NEXUS_BACKEND_LIB=/path/libyourvendor.so \
NEXUS_BACKEND_MODEL=/path/model.bin \
NEXUS_BACKEND_PREFIX=nx_be        # default
```

`nexus-backend-dyn` resolves the FULL 18-symbol table before touching the
device; a missing library or symbol is an error that **names** the missing
piece with recovery instructions — the loader never continues degraded.

## Status (honest, per surface)

| Surface | Label |
|---|---|
| `nx_backend.h` normative header | SHIPPED (frozen; 3-way abi-parity gate) |
| `nexus-backend-dyn` loader + `DynEngine` port | SHIPPED (gate `NEXUS_BACKEND=dyn`, default OFF) |
| reference-cpu fixture + conformance kit | SHIPPED (runs in `check-local full`, gcc) |
| Wiring the loader into the production frame loop (detect_loop / node-gpu) | **ROADMAP** — explicit STOP: a conformant `.so` is *loadable*, not deployed. That wiring is a separate directive. |
| Tensor decoding to detections | vendor/integrator-supplied (`TensorDecoder`) — the production decoders are model-specific and live with the nodes |

## What this SDK does not do

- It does not deploy your backend to any node — see the ROADMAP row above.
- It does not decode your model's tensors: you know your output layout; the
  loader moves tensors and enforces lifetimes, nothing more.
- It does not accept fabricated telemetry: report `<0` for metrics your
  silicon lacks, as the header demands.

## Tests

```bash
cargo test -p nexus-backend-dyn        # loader error paths (missing lib/symbol, env gate)
sdk/backend/ci-conformance.sh          # gcc build + full conformance (also in check-local full)
tools/abi-parity/check.sh              # 3-way header isomorphism
```
