Nacelle is an experimental Tokio-based Rust library for building typed streaming services across TCP, Unix sockets, HTTP/1, and TLS-enabled listeners.
context.respond(transport_response).awaitEach transport owns its request, response, and completion types. Handlers receive
a typed context with a streaming NacelleBody, connection metadata, and concrete
connection state, so services can process chunks without forcing full buffering.
Nacelle is currently 0.3.x. It is ready for experiments and prototype
integrations, but the public API is still allowed to change before 1.0.
The typed pipeline contracts, runtime limits, host/app builders, and telemetry observer contract are the most stable parts of the API. Transport metadata, listener options, stress-tool configuration, optional OpenSSL TLS detection, and metrics exporter integration are still moving.
Authentication and compression are not implemented in Nacelle. Keep those in your application, protocol layer, or edge proxy.
Until you pin a released crate version, depend on this repository directly:
[dependencies]
nacelle = { git = "https://github.com/microsoft/nacelle" }
nacelle-reference-protocol = { git = "https://github.com/microsoft/nacelle" }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }The unpublished nacelle-reference-protocol package is an example fixture from
this repository, not part of Nacelle's library API. Minimal TCP service using
that fixture:
use nacelle::core::pipeline::handler_fn;
use nacelle::core::{NacelleError, NacelleTelemetry};
use nacelle::tcp::{TcpRequestContext, TcpResponse, TcpServer};
use nacelle::NacelleApp;
use nacelle_reference_protocol::LengthDelimitedProtocol;
#[tokio::main(flavor = "multi_thread")]
async fn main() -> Result<(), NacelleError> {
let handler = handler_fn(
|mut context: TcpRequestContext<LengthDelimitedProtocol>| async move {
while let Some(chunk) = context.request_mut().body.next_chunk().await {
let _ = chunk?;
}
context.respond(TcpResponse::bytes("ok")).await
});
let server = TcpServer::<LengthDelimitedProtocol>::builder()
.protocol(LengthDelimitedProtocol)
.handler(handler)
.build()?;
let addr = "127.0.0.1:8080".parse().map_err(NacelleError::protocol)?;
NacelleApp::with_telemetry(NacelleTelemetry::default())
.with_ctrl_c_shutdown()
.tcp("echo", addr, server)
.run()
.await
}Run the checked-in examples from a local checkout:
# TCP echo with the reference protocol
cargo run -p nacelle-examples --bin echo -- 127.0.0.1:8080
# One app core served through two TCP protocol adapters
cargo run -p nacelle-examples --bin app_core -- 127.0.0.1:8080 127.0.0.1:8081
# HTTP echo
cargo run -p nacelle-examples --no-default-features --features http --bin http_echo -- 127.0.0.1:8080
# HTTP memory budget guard demo
cargo run -p nacelle-examples --no-default-features --features http --bin memory_guard
# TCP memory budget guard demo with the reference protocol
cargo run -p nacelle-examples --bin tcp_memory_guard
# HTTPS echo with an ephemeral self-signed certificate
cargo run -p nacelle-examples --no-default-features --features http,tls-self-signed --bin tls_http_echo -- 127.0.0.1:8443
# TCP echo with an ephemeral self-signed certificate
cargo run -p nacelle-examples --features tls-self-signed --bin tls_echo -- 127.0.0.1:8443
# TCP and HTTP listeners sharing app state and one host
cargo run -p nacelle-examples --features http --bin dual_echo -- 127.0.0.1:8080 127.0.0.1:8081- Transport-owned typed request, response, and completion contracts.
- Static handler and middleware dispatch without boxed hot-path futures.
- App-core serving with swappable protocol adapters.
- Streaming request and response bodies.
- Custom TCP protocol support over TCP and Unix domain sockets.
- Serial plain TCP, OpenSSL, optional OpenSSL, and Unix socket handlers with exclusive mutable connection state and no async mutex on the connection path.
- Explicit bounded TCP response coalescing for already-buffered request bursts; immediate delivery remains the default.
- HTTP/1 serving through Hyper.
- Rustls TLS for HTTP and TCP.
- OpenSSL TLS for TCP, including optional plain/TLS detection on one listener.
- Shared runtime limits, backpressure, graceful shutdown, and telemetry hooks.
- A stress server and stress client for local performance validation.
Choose the smallest feature set that matches the transports you actually run:
# TCP with a custom protocol (enabled by default)
nacelle = { version = "0.3" }
# HTTP only
nacelle = { version = "0.3", default-features = false, features = ["http"] }
# TCP + HTTP; backend-neutral metrics are always available
nacelle = { version = "0.3", features = ["http"] }
# TCP diagnostic phase histograms; still requires runtime activation
nacelle = { version = "0.3", features = ["phase-timing"] }
# Include setup hints in NacelleError Display output
nacelle = { version = "0.3", features = ["error-hints"] }
# Local self-signed TLS for tests
nacelle = { version = "0.3", features = ["tls-self-signed"] }
# TCP with OpenSSL, without Rustls
nacelle = { version = "0.3", default-features = false, features = ["tcp", "openssl"] }| Feature | Purpose |
|---|---|
tcp |
Custom TCP protocol transport over TCP and Unix sockets. Enabled by default. |
error-hints |
Include actionable setup hints in NacelleError display output. |
http |
Hyper HTTP/1 server transport. |
tls |
Provider-neutral TLS capability. |
rustls |
Rustls-backed TLS for HTTP and TCP. |
openssl |
OpenSSL-backed TLS for TCP. |
openssl-vendored |
Build OpenSSL from source when native OpenSSL is unavailable. |
tls-self-signed |
Generate ephemeral Rustls self-signed certificates for local tests. |
phase-timing |
Compile TCP read, decode, handler, encode, and write phase timers. Disabled by default. |
Nacelle emits metrics through the metrics
facade and does not select an exporter. Install the recorder chosen by your
application before constructing Nacelle runtime state, telemetry, or servers so
their cached metric handles bind to that recorder. Without a recorder, those
handles are inexpensive no-ops. Request-duration and TCP phase histograms remain
runtime opt-ins because they add timers to request and transport paths.
OpenSSL builds need native OpenSSL development files unless you enable
openssl-vendored. Vendored OpenSSL also needs Perl on Windows.
nacelle-corecontains shared typed pipeline, body, resource limit, lifecycle, telemetry, and provider-neutral TLS metadata.nacelle-opensslcontains reloadable OpenSSL configuration and negotiated connection metadata extraction.nacelle-rustlscontains reloadable Rustls configuration, certificate parsing, SNI policy, self-signed test support, and connection metadata extraction.nacelle-tcpcontains the TCP transport, protocol runtime, and TCP limits.nacelle-httpcontains the Hyper HTTP/1 transport, HTTP limits, and HTTP edge policy.nacelleis the convenience crate withcore,codec,tcp,http,openssl,rustls, andruntimecapability namespaces.examples/nacelle-examplesowns unpublished runnable examples and benchmarks.examples/nacelle-reference-protocolis an unpublished protocol fixture used by examples, tests, benchmarks, and stress tools.examples/nacelle-stress-*contains the stress harness.
Use explicit NacelleLimits plus transport-specific NacelleTcpLimits or
NacelleHttpLimits for production services. For internet-facing deployments,
the recommended shape is:
client -> proxy/load balancer/TLS -> Nacelle service
The proxy should own public TLS automation, coarse traffic filtering, and external idle timeouts. Nacelle should own application limits, protocol handling, body limits, telemetry, and graceful shutdown.
For direct HTTP exposure, configure NacelleHttpPolicy deliberately: trusted
proxy IPs, Host/method/URI/header limits, access logging, and per-peer caps. For
high connection counts, tune TCP read and response buffer capacities before
raising max_connections.
Nacelle does not enforce a runtime memory cap by default. Set
NacelleLimits::with_max_memory_bytes(...) only when you want to opt into
Nacelle's memory allocation budget for a measured deployment or test profile.
Self-signed certificates are intended for local tests and auto-deploy flows, not as a public-edge certificate strategy.
Run a short plain TCP smoke profile:
cargo run --release --package nacelle-stress-server -- \
--config examples/nacelle-stress-server/configs/tcp.toml
# In another shell:
cargo run --release --package nacelle-stress-test -- \
--connections 32 \
--pipeline 16 \
--duration-secs 15The root config.toml is loaded automatically when the stress server
is run from the repository root. It enables self-signed TCP TLS for local runs,
so the stress client needs --tls-insecure with that default config.
For repeatable local profiles, use the helper scripts:
./examples/run-stress-test.sh --config examples/nacelle-stress-server/configs/tcp.toml.\examples\run-stress-test.ps1 -Config examples/nacelle-stress-server/configs/tcp.tomlVerify the workspace before submitting changes:
cargo fmt --all
cargo clippy --workspace --all-features --all-targets -- -D warnings
cargo test --workspace --all-featuresBuild release binaries:
cargo build --releaseBuild just the stress binaries and copy them to ./artifacts/:
./build-all.sh- Getting started
- Architecture
- Runtime limits and backpressure
- Operations
- Production configuration
- HTTP hardening
- Stress testing
- Performance tuning
- Security scanning
- Reference protocol
- API stability
- Rust API reference
Build the mdBook site:
mdbook buildGenerate Rust API docs:
cargo doc --workspace --all-features --no-depsOpen issues and pull requests in the Nacelle repository. Follow the Code of Conduct when participating.
This project is licensed under the MIT License.
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow Microsoft's Trademark & Brand Guidelines. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies.