# Embedda Node SDK (`nexus-node-sdk`)

Bring your own node — a camera box, a sensor gateway, a whole device network —
into an Embedda swarm, in Rust. Public package name (documentation only, never
published): `embedda-node-sdk`.

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

## From zero to a publishing node

```rust
use nexus_node_sdk::*;

// 1. Identity — Ed25519, SAME key-file format as the production node
//    (shared single source: libs/platform/nexus-node-identity).
let id = NodeIdentity::load_or_create(std::path::Path::new("/var/lib/mynode"))?;

// 2. Honest capabilities — the node-api types re-used, frozen as
//    contracts/schemas/node-capabilities.v1.schema.json. A bare node claims
//    NOTHING it doesn't have.
let caps = bare_virtual_node_capabilities("my-node-1", "My Node 1");

// 3. Signed node-package descriptor (marketplace enablement, EXPERIMENTAL).
let pkg = SignedNodePackage::sign(NodePackageDescriptor { /* … */ }, &id)?;
pkg.verify()?;

// 4. Publish on the real bus (frozen wire v1: nexus-swarm-wire).
//    Env: NEXUS_SWARM_SEMANTIC=1 + transport config — see module docs.
let swarm = Swarm::connect_from_env("my-node-1")?;
swarm.publish_detection(&detection, "cam-0", None)?;   // msgpack, world/detection/{cam}
swarm.publish_world_snapshot(&snapshot);               // JSON,   world/snapshot/{node}
```

Runnable versions: `cargo run -p nexus-node-sdk --example virtual_node` and the
e2e `cargo test -p nexus-node-sdk --test e2e_virtual_node` (real loopback Zenoh
transport, HMAC-verified delivery, zero hardware).

## Honesty rules you inherit (binding)

- **Synthetic data is marked.** Anything you emit that is not a real sensor
  observation carries `OriginTag::Simulated`
  (`WorldDetection::with_origin`). The virtual-node example does; copy it.
  There is deliberately no "real" tag — reality is the unmarked default so it
  can never be claimed explicitly and lied about.
- **Declare only what you have.** `bare_virtual_node_capabilities` claims no
  cameras, no accelerator, no world model. Fill `NodeCapabilities` in only for
  surfaces your node actually serves.
- **Errors are actionable.** "Not configured" is an `Err` with recovery
  instructions, never a silent default.

## Status (per surface)

| Surface | Label | Notes |
|---|---|---|
| Identity (`NodeIdentity`) | SHIPPED | shared file format with node-api, parity-tested |
| Capabilities (`NodeCapabilities`) | SHIPPED | node-api's own types re-used; example + schema frozen |
| Bus publish (`Swarm`) | SHIPPED | thin wrapper over `nexus-swarm-egress`; wire = `nexus-swarm-wire` v1 (golden fixtures frozen) |
| Enrollment (`enrollment::checkin`) | SHIPPED | client is mock-tested; the real Node API implements durable create/check-in/approve/revoke/rotate with one-time secrets and Ed25519 identity binding; `sdk/node/e2e.sh` covers pending → approved → enrolled. |
| Node package descriptor (`SignedNodePackage`) | EXPERIMENTAL | descriptor + Ed25519 sign/verify real and tested; schema frozen (`contracts/schemas/node-package.v1.schema.json`) |
| Marketplace (listing/distribution/payments) | ROADMAP | does not exist; see [PACKAGING.md](PACKAGING.md) |
| CBBA fabric participation | ROADMAP | fabric-bus is example-only; publishing ≠ bidding |

## What this SDK does not do

- It does not reimplement transport or signing — it delegates to the exact
  crates the production nodes ship (`nexus-swarm-egress`, `nexus-comms`).
- It does not auto-approve or grant trust: enrollment stays pending until an authorized operator approves the claim.
- It does not publish packages anywhere (npm/PyPI/crates.io are a hard STOP).
- It does not give your node any trust: an unapproved claim is untrusted, and
  a signed package descriptor proves authorship, not endorsement.

## Tests

```bash
cargo test -p nexus-node-sdk                 # unit + enrollment mock + e2e
cargo test -p nexus-swarm-wire               # frozen wire fixtures (golden)
cargo test -p nexus-node-identity            # shared key-file format
```
