Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Release v0.1.0 — Stable API (upcoming)

Status: draft / unreleased. v0.1.0 has not been tagged yet. The workspace Cargo.toml reads 0.1.0 and main is converging on the release, but no v0.1.0 git tag exists and no GitHub Release has been published. This page describes what the release will contain once cut. To use the current state, track main directly or pull ghcr.io/quarmire/ndn-fwd:latest (the edge and latest container 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-routerndn-fwd; RouterClientForwarderClient; AppFaceInProcFace; four face crates → one ndn-faces.
  • API completenessResponder pattern for producers, consumer convenience methods, BlockingForwarderClient for FFI, PSync subscriber variant.
  • Security ergonomicsKeyChain signing, 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-routerndn-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

RemovedReplacement
ndn-face-netndn-faces (feature net, websocket)
ndn-face-localndn-faces (feature local, spsc-shm)
ndn-face-serialndn-faces (feature serial)
ndn-face-l2ndn-faces (feature l2, bluetooth, wfb)
ndn-pipelinendn-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