Skip to main content

tachyon_web/
lib.rs

1//! # Tachyon-Web
2//!
3//! A clean, highly optimized, multi-protocol web framework for Rust.
4//!
5//! `tachyon-web` follows the philosophy of an **Axum-compatible API**, but takes a simpler,
6//! more unified approach to high-performance transport protocols. Where other frameworks require
7//! complex configurations and separate crates to handle modern protocols, `tachyon-web` provides
8//! a seamless, unified experience out-of-the-box for **HTTP/1.1, HTTP/2, and HTTP/3**, as well
9//! as **automatic Let's Encrypt TLS certificate management**.
10//!
11//! ## Design philosophy
12//!
13//! 1. **Axum-like simplicity**: Build a `Router`, chain `.route()` calls, and use type-safe
14//!    extractors (`Path`, `Query`, `Json`, `State`) in handler functions — the same ergonomic
15//!    patterns you already know.
16//!
17//! 2. **Drop-in replacement for Axum workloads**: Most `axum` handlers compile against
18//!    `tachyon-web` without modification. The main incompatibility is Tower middleware layers,
19//!    which Tachyon does not use — by design, to eliminate the overhead Tower introduces.
20//!
21//! 3. **Effortless TLS, HTTP/2, and HTTP/3**: Starting a TLS server is a single call.
22//!    Let's Encrypt integration is built-in — no CLI tools, no shell scripts, no cron jobs.
23//!    Certificates are automatically issued, cached to disk, and hot-reloaded on renewal.
24//!
25//! 4. **High Performance**: Built natively on `hyper` and `s2n-quic`, `tachyon-web` prioritizes
26//!    minimal allocations, lock-free hot paths, and direct socket handling.
27//!
28//! ## Quick start: Plain HTTP
29//!
30//! ```rust,no_run
31//! use tachyon_web::{Router, Server, get};
32//! use tachyon_web::http::response::Html;
33//! use tokio::net::TcpListener;
34//!
35//! async fn hello_world() -> Html<&'static str> {
36//!     Html("<h1>Hello from Tachyon-Web!</h1>")
37//! }
38//!
39//! #[tokio::main]
40//! async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
41//!     let app = Router::new()
42//!         .route("/", get(hello_world));
43//!
44//!     let listener = TcpListener::bind("0.0.0.0:8080").await?;
45//!     Server::new(app).serve_http(listener).await?;
46//!     Ok(())
47//! }
48//! ```
49//!
50//! ## HTTPS with automatic Let's Encrypt certificates
51//!
52//! Call [`Server::serve_all_acme`] to get fully automatic certificate management:
53//! - Issues a certificate from Let's Encrypt on first startup.
54//! - Serves ACME HTTP-01 challenges in-process (no separate server or Certbot required).
55//! - Saves credentials and the certificate to disk — safe across restarts.
56//! - Renews automatically 30 days before expiry with exponential-backoff retries.
57//! - Hot-swaps the certificate in the TLS stack with **zero downtime**.
58//!
59//! ```rust,no_run
60//! use tachyon_web::{Router, Server, get};
61//!
62//! async fn hello() -> &'static str { "Hello, secure world!" }
63//!
64//! #[tokio::main]
65//! async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
66//!     #[cfg(feature = "lets-encrypt")]
67//!     {
68//!         let app = Router::new().route("/", get(hello));
69//!
70//!         Server::new(app)
71//!             .serve_all_acme(
72//!                 "0.0.0.0:443",                   // HTTPS / HTTP/2 / HTTP/3
73//!                 "0.0.0.0:80",                    // HTTP redirect + ACME challenges
74//!                 vec!["example.com".to_string()], // domains (must resolve to this server)
75//!                 "admin@example.com".to_string(), // Let's Encrypt contact email
76//!                 "/var/cache/tachyon/certs",      // persistent cert cache (survives restarts)
77//!                 false,                           // false = production LE, true = staging
78//!             )
79//!             .await?;
80//!     }
81//!     Ok(())
82//! }
83//! ```
84//!
85//! ## HTTPS with a pre-loaded certificate (self-signed or CA-issued)
86//!
87//! For development or when you manage certificates externally:
88//!
89//! ```rust,no_run
90//! use tachyon_web::{Router, Server, get};
91//!
92//! async fn hello() -> &'static str { "secure hello" }
93//!
94//! #[tokio::main]
95//! async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
96//!     #[cfg(feature = "cert-gen")]
97//!     {
98//!         use tachyon_web::tls;
99//!
100//!         let app = Router::new().route("/", get(hello));
101//!
102//!         // Generate an ephemeral self-signed cert (for development only).
103//!         let cert = tls::generate_self_signed_cert(vec!["localhost".to_string()])?;
104//!
105//!         Server::new(app)
106//!             .start_all(
107//!                 "0.0.0.0:443",
108//!                 Some("0.0.0.0:80"),  // optional HTTP → HTTPS redirect
109//!                 cert.cert_pem,
110//!                 cert.key_pem,
111//!             )
112//!             .await?;
113//!     }
114//!     Ok(())
115//! }
116//! ```
117//!
118//! ## Native Tor `.onion` hidden services
119//!
120//! With the `tor` feature, [`Server::serve_tor`] publishes the app directly as a v3 Tor hidden
121//! service — via [`arti-client`](https://docs.rs/arti-client)/[`tor-hsservice`](https://docs.rs/tor-hsservice) —
122//! with no external `tor` daemon or reverse proxy required:
123//!
124//! ```rust,no_run
125//! use tachyon_web::{Router, Server, get};
126//!
127//! async fn hello() -> &'static str { "Hello from an onion service!" }
128//!
129//! #[tokio::main]
130//! async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
131//!     #[cfg(feature = "tor")]
132//!     {
133//!         let app = Router::new().route("/", get(hello));
134//!         Server::new(app).serve_tor("my-hidden-service").await?;
135//!     }
136//!     Ok(())
137//! }
138//! ```
139//!
140//! ## Native I2P `.b32.i2p` eepsites
141//!
142//! With the `i2p` feature, [`Server::serve_i2p`] publishes the app directly as an I2P eepsite —
143//! via the vendored, statically-linked [`libi2pd`](https://github.com/PurpleI2P/i2pd) router —
144//! with no external `i2pd`/Java-I2P process required:
145//!
146//! ```rust,no_run
147//! use tachyon_web::{Router, Server, get};
148//!
149//! async fn hello() -> &'static str { "Hello from an eepsite!" }
150//!
151//! #[tokio::main]
152//! async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
153//!     #[cfg(feature = "i2p")]
154//!     {
155//!         let app = Router::new().route("/", get(hello));
156//!         Server::new(app).serve_i2p("my-eepsite").await?;
157//!     }
158//!     Ok(())
159//! }
160//! ```
161//!
162//! **⚠️ Unlike every other feature in this crate, `i2p` pulls in a dependency that itself
163//! contains `unsafe` code — the `#![forbid(unsafe_code)]` below still holds for
164//! `tachyon-web`'s own source (it cannot be locally overridden by any feature), but it says
165//! nothing about the FFI boundary this feature links in.** `libi2pd` is a C++ library with no
166//! stable C ABI, so supporting it at all requires an `unsafe` FFI boundary — one written for
167//! this project ([`i2pd-sys`](https://docs.rs/i2pd-sys)/[`tachyon-i2p`](https://docs.rs/tachyon-i2p)),
168//! not a long-established independently-audited pure-Rust dependency the way `arti-client` is
169//! for `tor`. See [`server::i2p`] for the full disclosure before enabling this in anything
170//! security-sensitive.
171
172#![forbid(unsafe_code, elided_lifetimes_in_paths)]
173#![allow(clippy::multiple_crate_versions)]
174#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))]
175
176#[cfg(not(any(feature = "http1", feature = "http2")))]
177compile_error!(
178    "tachyon-web requires at least one of the \"http1\" or \"http2\" features to serve anything"
179);
180
181#[cfg(any(feature = "tor", feature = "i2p"))]
182pub mod anonymity;
183pub mod http;
184pub mod routing;
185pub mod server;
186#[cfg(feature = "tls")]
187pub mod tls;
188#[cfg(feature = "ws")]
189pub mod ws;
190
191// ─── Public re-exports ────────────────────────────────────────────────────────
192
193pub use http::error::{Error, Result};
194pub use http::response;
195pub use http::response::{
196    AppendHeaders, Html, IntoResponse, IntoResponseParts, Redirect, ResponseParts,
197};
198pub use routing::extract;
199#[cfg(feature = "cookies")]
200pub use routing::extract::Cookies;
201#[cfg(feature = "form")]
202pub use routing::extract::Form;
203#[cfg(feature = "json")]
204pub use routing::extract::Json;
205#[cfg(feature = "original-uri")]
206pub use routing::extract::OriginalUri;
207#[cfg(feature = "query")]
208pub use routing::extract::Query;
209pub use routing::extract::{
210    ConnectInfo, Extension, FromRef, FromRequest, FromRequestParts, Host, Path, RawQuery, State,
211};
212pub use routing::handler::{BoxedFuture, BoxedHandler, Handler};
213pub use routing::middleware::{MiddlewarePosition, Next};
214pub use routing::static_dir::ServeDir;
215pub use routing::{
216    MethodRouter, Router, RouterError, any, connect, delete, get, head, options, patch, post, put,
217    trace,
218};
219#[cfg(feature = "tls")]
220pub use server::{HttpsServer, RustlsConfig, bind_rustls};
221pub use server::{MultiServer, Server, serve};