# node-app-sdk-rust

> **Status: `1.0.0-experimental`** — API surface is frozen pending validation by at least two first-party packages. Breaking changes between `1.0.0-experimental.*` releases are possible. Pin to an exact version in production.

Rust SDK for building **Node-App** native plugins as shared libraries (`.so` / `.dylib` / `.dll`). Node-Apps run inside the [Node](https://github.com/econ-v1/node-app-distribution) Lightning-powered marketplace daemon and communicate with the host through a stable C ABI.

## What it gives you

- A `NodeApp` trait — implement it on your plugin type to get HTTP, event, and capability handling.
- The `declare_node_app!` macro — generates all FFI boilerplate (vtable, panic guards, JSON serialization) from your trait impl.
- Helpers for talking to the host: `log_info!`, `invoke_capability`, `publish_event`, `get_config`, `get_storage`, `set_storage`.
- Distributed-trace propagation across capability invocations (thread-local span context).
- Hard limits matching the host: `MAX_CAPABILITY_RESPONSE_SIZE` (16 MiB), `MAX_EVENT_NAME_LEN` (256 B), `MAX_EVENT_DATA_LEN` (64 KiB).

## Quick start

Add to your `Cargo.toml`:

```toml
[package]
name = "my-node-app"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]      # required: host loads via libloading

[dependencies]
node-app-sdk-rust = "1.0.0-experimental"
serde_json = "1.0"
```

`src/lib.rs`:

```rust
use node_app_sdk_rust::*;

#[derive(Default)]
pub struct MyApp;

impl NodeApp for MyApp {
    fn metadata() -> NodeAppInfo {
        NodeAppInfo {
            name: "my-app".into(),
            version: "0.1.0".into(),
            author: "Me".into(),
            description: "Hello-world Node-App".into(),
            capabilities: vec!["http_handler".into()],
        }
    }

    fn handle_request(&self, _req: AppRequest) -> Result<AppResponse, NodeAppError> {
        Ok(AppResponse {
            status: 200,
            headers: Default::default(),
            body: serde_json::json!({ "hello": "world" }),
        })
    }
}

declare_node_app!(MyApp);
```

Build:

```sh
cargo build --release
# Output: target/release/libmy_node_app.so (Linux), .dylib (macOS), .dll (Windows)
```

Drop the resulting shared library into the host's app directory alongside a `manifest.json`. The host discovers it on startup or via `app.discover`.

## Calling host capabilities

```rust
use node_app_sdk_rust::{invoke_capability, CapabilityRequest};

let response = invoke_capability(&CapabilityRequest {
    id: uuid::Uuid::new_v4().to_string(),
    capability: "core.storage.get".into(),
    payload: serde_json::json!({ "key": "user_pref" }),
    caller_node_id: None,
    trace_id: None,
    span_id: None,
    parent_span_id: None,
    trace_depth: None,
})?;
```

Trace context (`trace_id` / `span_id` / `trace_depth`) is propagated automatically when invoking from inside `handle_capability` — you don't need to thread it manually.

## Publishing events

```rust
use node_app_sdk_rust::publish_event;

publish_event(
    "my-app.something_happened",
    &serde_json::json!({ "user_id": 42 }),
)?;
```

Event names **must** be namespaced with your app name (`my-app.*`); the host rejects un-namespaced events.

## ABI stability

This SDK targets **Node Host API v1**. The C ABI is defined by `node-app-api` (re-exported here) and the canonical C header lives at `core/host-abi-v1/include/node-host-api-v1.h` in the host repo. Apps declare their API version in `NodeAppMetadata.api_version`; the host refuses to load apps with mismatched versions.

## License

Licensed under either of

- Apache License, Version 2.0
- MIT License

at your option.
