Release v0.1.0 — Stable API (upcoming)
Status: draft / unreleased. v0.1.0 has not been tagged yet. The workspace
Cargo.tomlreads0.1.0andmainis converging on the release, but nov0.1.0git tag exists and no GitHub Release has been published. This page describes what the release will contain once cut. To use the current state, trackmaindirectly or pullghcr.io/quarmire/ndn-fwd:latest(theedgeandlatestcontainer tags are rebuilt on every push). The release will be tagged after the remaining open items from yoursunny’s review issues are confirmed resolved.
Target tag: v0.1.0
Previous: v0.1.0-alpha
Summary
v0.1.0 will be the first stable release of ndn-rs. It locks in all public names and crate structure that will remain stable through the v0.x series (barring explicit semver breaking changes). The key themes are:
- Name finalization — binary
ndn-router→ndn-fwd;RouterClient→ForwarderClient;AppFace→InProcFace; four face crates → onendn-faces. - API completeness —
Responderpattern for producers, consumer convenience methods,BlockingForwarderClientfor FFI, PSync subscriber variant. - Security ergonomics —
KeyChainsigning,trust_only,build_validator. - Config robustness — env-var expansion, parse-time validation.
- WebSocket TLS — self-signed and user-supplied cert modes; ACME deferred to 0.2.0.
- Operations — Docker image and default config for
ndn-fwd.
Breaking Changes
Binary rename: ndn-router → ndn-fwd
The standalone forwarder binary is now ndn-fwd. Update any scripts, systemd
units, or Docker entrypoints.
ndn-packet default features
ndn-packet previously enabled std by default. From 0.1.0 onwards, the default
is empty — this is required for no_std embedded targets to work without
default-features = false. Any downstream crate that was relying on the implicit
std feature must now add it explicitly:
ndn-packet = { version = "0.1", features = ["std"] }
AppError typed variants
AppError::Engine(anyhow::Error) is removed. Use the new variants:
#![allow(unused)]
fn main() {
AppError::Connection(ForwarderError) // IPC transport errors
AppError::Closed // connection dropped
AppError::Protocol(String) // malformed response
}
Producer::serve handler signature
The handler now receives a Responder for replying:
#![allow(unused)]
fn main() {
// Before (alpha):
producer.serve(|interest| async move {
Some(bytes) // return None to ignore
}).await;
// After (stable):
producer.serve(|interest, responder| async move {
responder.respond_bytes(bytes).await.ok();
// or: responder.nack(NackReason::NoRoute).await.ok();
}).await;
}
Crate consolidation
| Removed | Replacement |
|---|---|
ndn-face-net | ndn-faces (feature net, websocket) |
ndn-face-local | ndn-faces (feature local, spsc-shm) |
ndn-face-serial | ndn-faces (feature serial) |
ndn-face-l2 | ndn-faces (feature l2, bluetooth, wfb) |
ndn-pipeline | ndn-engine::pipeline |
New Features
Responder pattern
Handlers can now send Nacks as well as Data, and the responder’s ownership ensures exactly-once replies:
#![allow(unused)]
fn main() {
producer.serve(|interest, responder| async move {
match handle(&interest) {
Ok(bytes) => { responder.respond_bytes(bytes).await.ok(); }
Err(_) => { responder.nack(NackReason::NoRoute).await.ok(); }
}
}).await;
}
Consumer convenience methods
#![allow(unused)]
fn main() {
// Fetch multiple names in parallel.
let results = consumer.fetch_all(&[name_a, name_b]).await;
// Retry up to 3 times.
let data = consumer.fetch_with_retry(&name, 3).await?;
// Fetch all segments and reassemble.
let bytes = consumer.fetch_segmented(&prefix).await?;
// Fetch and verify signature.
let safe = consumer.get_verified(&name).await?;
}
BlockingForwarderClient
For C FFI, Python bindings, or other non-async contexts:
#![allow(unused)]
fn main() {
use ndn_ipc::BlockingForwarderClient;
let mut client = BlockingForwarderClient::connect("/tmp/ndn.sock")?;
client.register_prefix(&"/app/prefix".parse()?)?;
client.send(interest_wire)?;
let reply = client.recv(); // blocks until Data or timeout
}
KeyChain signing helpers
#![allow(unused)]
fn main() {
let kc = KeyChain::ephemeral("/com/example/app")?;
// Sign a Data packet.
let wire = kc.sign_data(DataBuilder::new(name, content))?;
// Build a validator trusting only one anchor prefix.
let v = KeyChain::trust_only("/ndn/testbed")?;
}
WebSocket TLS
#![allow(unused)]
fn main() {
use ndn_faces::net::websocket::{WebSocketFace, TlsConfig};
let listener = WebSocketFace::listen_tls(
"0.0.0.0:9696".parse()?,
TlsConfig::SelfSigned, // or TlsConfig::UserSupplied { cert_pem, key_pem }
).await?;
loop {
let face = listener.accept(next_face_id()).await?;
engine.add_face(Box::new(face)).await;
}
}
Roadmap: v0.2.0
- WebSocket ACME certificate renewal with SVS-based fleet distribution
- BLE face full GATT server implementation (NDNts
@ndn/web-bluetooth-transport) - WfbFace 802.11 monitor-mode injection
- ASF forwarding strategy
- PSync network-layer face integration