# 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.