vs. NDNts (TypeScript) and mw-nfd (Multi-worker NFD)
📝 Note: NDNts and mw-nfd serve different audiences than ndn-rs. NDNts targets browser and Node.js applications where JavaScript is the only option. mw-nfd targets existing NFD deployments that need higher throughput without a full rewrite. This comparison explains each project’s niche and where ndn-rs overlaps or differs.
This page compares ndn-rs with two further implementations: NDNts, a full NDN stack written in TypeScript that runs in browsers and Node.js, and mw-nfd, a modified NFD that adds a multi-threaded worker model to the reference C++ forwarder. They occupy distinct niches — browser applications and high-throughput C++ deployments, respectively — but both are common points of comparison when evaluating NDN implementations.
NDNts (TypeScript / Browser)
| Aspect | NDNts | ndn-rs | Rationale |
|---|---|---|---|
| Runtime environment | Browser and Node.js. NDNts is the only NDN implementation that runs natively in a web browser via WebTransport and WebSocket faces. Bundle size is approximately 150 KB (gzip). | Native binary and WASM. ndn-rs compiles to native code for servers and embedded targets, and to WASM for in-browser simulation (ndn-sim). The WASM build is not a production forwarder — it is used for research and testing. | There is no substitute for NDNts when building a web application that needs to participate in an NDN network. ndn-rs does not compete here; it complements NDNts by acting as the router or producer that browser-based NDNts clients connect to. |
| Packet encoding | TypeScript with BigInt TLV encoding. NDNts encodes TLV lengths as JavaScript BigInt values to handle >32-bit length fields correctly. | bytes::Bytes zero-copy slicing. TLV is parsed directly over the wire buffer; no intermediate JavaScript objects are created per field. | JavaScript’s JIT compiler can optimise hot encoding/decoding paths, but the overhead of object allocation per TLV field is unavoidable in a GC’d runtime. ndn-rs’s lazy decode via OnceLock and zero-copy Bytes slicing avoids per-field allocation entirely. |
| Face types | WebTransport, WebSocket, Unix socket (Node.js only). The face set is constrained by what browsers expose — no raw UDP multicast, no Ethernet, no shared memory. | Full face set. UDP multicast, TCP unicast, Unix socket, shared-memory (ShmFace), in-process (InProcFace), and Ethernet (ndn-faces). | Browser security policy prevents raw socket access, so NDNts’s face options are inherently limited. ndn-rs runs outside the browser sandbox and can use any OS networking primitive. |
| Concurrency model | Single-threaded event loop. JavaScript is single-threaded; NDNts uses async/await over the event loop. True parallelism requires Web Workers with postMessage overhead for packet transfer. | Multi-core async. ndn-rs uses Tokio’s multi-threaded runtime; pipeline stages and face I/O run on a shared thread pool without message-passing overhead between threads. | JavaScript’s event loop model is excellent for I/O-bound tasks but cannot parallelize CPU-bound packet processing across cores. ndn-rs’s DashMap PIT and sharded locking are designed specifically to scale with core count. |
| Type safety | TypeScript (structural typing). NDNts is well-typed for a TypeScript library; type errors are caught at compile time. However, TypeScript types are erased at runtime, and any casts can silently bypass type checking. | Rust (nominal typing with ownership). The compiler enforces not just type correctness but ownership and lifetime invariants. PacketContext cannot be used after it has been forwarded — this is a type error, not a runtime panic. | Both projects prioritise correctness via type systems. Rust’s type system is strictly stronger: ownership eliminates a class of bugs (use-after-forward, data races) that TypeScript’s type system cannot express. |
| Signing and validation | Full signing suite. NDNts supports SHA-256, ECDSA, RSA, and HMAC signing. It has the most complete browser-compatible signing story of any NDN implementation, using the Web Crypto API when available. | Full signing suite via ndn-security. DigestSha256, EdDSA, ECDSA, BLAKE3, HMAC. Does not use Web Crypto (not in the browser). | NDNts’s Web Crypto integration is its signing advantage — hardware key storage via the browser’s credential manager is accessible to NDNts but not to ndn-rs. |
| Ecosystem fit | JavaScript/TypeScript ecosystem. NDNts is a set of npm packages; applications add it with npm install. Works with any JS build tool (Vite, webpack, esbuild). | Rust ecosystem. ndn-rs is a set of Cargo crates. Works with Rust’s standard toolchain and cross-compilation targets. | NDNts is the right choice whenever the application is already in JavaScript — a React SPA, an Electron app, a Next.js server. Rewriting in Rust to gain ndn-rs’s performance would rarely be justified for web applications. |
| Simulation | None built-in. Testing NDNts applications typically requires a real NFD or ndnd instance running locally. | In-process simulation. ndn-sim provides SimFace and SimLink for topology simulation without any external process. | The lack of a built-in simulation environment means NDNts integration tests depend on external infrastructure. ndn-rs integration tests are self-contained and run in CI without any setup. |
mw-nfd (Multi-worker NFD, C++)
| Aspect | mw-nfd | ndn-rs | Rationale |
|---|---|---|---|
| Architecture | Modified NFD with a worker thread pool. mw-nfd adds multiple worker threads to NFD’s pipeline, each owning a subset of faces, while sharing global PIT and FIB data structures with reader-writer locks. | Embeddable engine with DashMap PIT. ndn-rs is not a modification of an existing codebase; it was designed for concurrency from the start, with a sharded PIT and an async trait pipeline. | mw-nfd’s multi-threading is bolted onto a single-threaded codebase. Shared data structures still require coarse reader-writer locks in many places. ndn-rs’s DashMap is sharded at a fine granularity, designed so that independent PIT entries can be inserted and looked up simultaneously. |
| Deployment | Drop-in for existing NFD deployments. mw-nfd is intended to replace nfd in existing setups; it speaks the same management protocol and uses the same configuration format. | Independent binary (ndn-fwd) or embedded library. Compatible with NFD management protocol for monitoring tools; configuration is TOML-based rather than NFD-config. | mw-nfd’s drop-in story is a genuine advantage for organisations already running NFD. Migrating to ndn-rs requires re-expressing configuration in TOML and updating any scripts that invoke NFD-specific internals. The NFD management protocol compatibility means monitoring tools (nfd-status) work without modification. |
| Embeddability | Not embeddable. mw-nfd is a daemon; applications connect over a Unix socket. | Embeddable library. ndn-engine is a Rust crate that can be added as a dependency and run in-process. | Same argument as vs. NFD: eliminating the IPC boundary removes a round-trip on every packet and allows the application and forwarder to share data structures directly. |
| Memory safety | C++ (unsafe). mw-nfd inherits NFD’s C++ codebase. The multi-threading changes introduce new shared mutable state that is difficult to audit for data races. ThreadSanitizer can catch races at runtime, but not exhaustively. | Rust (memory-safe). The compiler rejects data races statically. Adding a new shared data structure forces the programmer to choose between Mutex, RwLock, or DashMap explicitly — the choice is visible in the type, not hidden in comments. | Introducing shared mutable state into a previously single-threaded C++ codebase is one of the highest-risk refactors in systems programming. Rust’s ownership model makes this safe by construction: if it compiles, there are no data races. |
| PIT concurrency | Global reader-writer lock per worker, or per-worker PIT partitions (implementation varies by version). Contention can occur when multiple workers process packets for the same name prefix. | DashMap: fine-grained sharding by name hash. Independent names in different shards never contend. Workers processing different name prefixes run in true parallel. | Fine-grained sharding is more work to implement correctly but scales better under diverse traffic. A global or coarse lock limits throughput gains from adding more workers. |
| Strategy system | Inherits NFD’s strategy system. Strategies are C++ classes; changing a strategy requires recompilation and daemon restart. mw-nfd adds no new strategy extensibility beyond NFD. | Trait + WASM. Built-in strategies compile in; external strategies load as WASM modules at runtime without a restart. | mw-nfd’s focus is throughput scaling, not strategy extensibility. ndn-rs adds runtime extensibility that mw-nfd does not attempt to address. |
| Forward compatibility | Tied to NFD’s release cadence. mw-nfd is a fork; divergence from upstream NFD grows over time and merging upstream changes requires effort. | Independent codebase. ndn-rs evolves on its own roadmap and tracks the NDN spec directly, not NFD’s implementation. | Maintaining a fork of a large C++ project accumulates technical debt. ndn-rs does not carry this burden, but also lacks a decade of NFD’s production hardening. |
Where NDNts and mw-nfd Have the Advantage
NDNts:
- Browser-native. No other NDN implementation runs in a web browser. If your application is a web app, NDNts is the answer.
- npm ecosystem. One
npm installand it works with any JavaScript build pipeline. No Rust toolchain required. - Web Crypto integration. Hardware-backed key storage via browser credentials is accessible only to browser-native code.
- Community. NDNts is actively maintained and has the widest adoption among JavaScript NDN developers.
mw-nfd:
- Drop-in for NFD. Existing NFD deployments gain multi-core throughput with minimal configuration changes.
- Spec authority. mw-nfd inherits NFD’s status as the reference implementation, so its forwarding behaviour is de facto correct by definition.
- Production history. mw-nfd runs in real testbed deployments; ndn-rs’s multi-core forwarding is newer and less battle-tested at scale.
Complementary Deployment Pattern
A common pattern that uses all three projects together: NDNts runs in a browser application (consumer and producer), connecting over WebSocket to an ndn-fwd instance (ndn-rs) acting as an edge forwarder, which peers upstream with mw-nfd or NDN-DPDK nodes on the testbed backbone. Each implementation operates in the tier where it has the clearest advantage.