Temp commit before restructure

This commit is contained in:
mfahampshire
2026-04-29 10:11:51 +01:00
parent 7783da7f9c
commit 2011aa9018
13 changed files with 1051 additions and 83 deletions
Generated
+877 -8
View File
File diff suppressed because it is too large Load Diff
-3
View File
@@ -30,7 +30,6 @@ thiserror.workspace = true
[dev-dependencies]
smolmix-dns = { workspace = true }
futures = { workspace = true }
tokio = { workspace = true, features = ["io-util", "macros", "rt-multi-thread", "time", "net"] }
tokio-tungstenite.workspace = true
webpki-roots.workspace = true
@@ -46,8 +45,6 @@ reqwest = { workspace = true, features = ["rustls"] }
chrono = { workspace = true }
libp2p = { version = "0.56", features = ["tokio", "noise", "yamux", "ping", "macros"] }
tokio-util = { workspace = true, features = ["compat"] }
tokio-smoltcp = "0.5"
tracing = { workspace = true }
[[example]]
name = "websocket"
-59
View File
@@ -1,59 +0,0 @@
# smolmix
A TCP/UDP tunnel over the Nym mixnet. Uses smoltcp as a userspace network stack and connects to an Exit Gateway's IP Packet Router, so the exit IP is the gateway's — not yours.
`Tunnel` gives you standard `TcpStream` and `UdpSocket` types (from tokio-smoltcp) that work transparently with the async Rust ecosystem: tokio-rustls for TLS, hyper for HTTP, tokio-tungstenite for WebSockets, etc.
## Quick start
```rust
use smolmix::Tunnel;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let tunnel = Tunnel::new().await?;
// Raw TCP — works with any protocol
let mut tcp = tunnel.tcp_connect("1.1.1.1:80".parse()?).await?;
tcp.write_all(b"GET / HTTP/1.1\r\nHost: 1.1.1.1\r\nConnection: close\r\n\r\n").await?;
// Raw UDP — datagrams over the mixnet
let udp = tunnel.udp_socket().await?;
udp.send_to(&packet, "1.1.1.1:53".parse()?).await?;
```
## Companion crates
For higher-level protocols, add the companion crates you need:
| Crate | What it provides |
|-------|-----------------|
| **smolmix** | `Tunnel`, `TcpStream`, `UdpSocket` (this crate) |
| [smolmix-dns](../dns/) | `Resolver` newtype wrapping hickory-resolver |
| [smolmix-tls](../tls/) | Shared `TlsConnector` and `connect()` with webpki roots |
| [smolmix-hyper](../hyper/) | `Client` newtype wrapping hyper-util |
| [smolmix-tungstenite](../tungstenite/) | `connect()` for WebSocket over TLS |
| [smolmix-libp2p](../libp2p/) | `SmolmixTransport` implementing libp2p `Transport` |
```toml
[dependencies]
smolmix = { workspace = true }
smolmix-dns = { workspace = true } # DNS resolution
smolmix-tls = { workspace = true } # TLS setup (used by hyper + tungstenite)
smolmix-hyper = { workspace = true } # HTTP client
smolmix-tungstenite = { workspace = true } # WebSocket client
smolmix-libp2p = { workspace = true } # libp2p transport
```
## Examples
All examples include a clearnet-vs-mixnet comparison with timing and accept `--ipr <ADDRESS>` for targeting a specific exit node.
```sh
cargo run -p smolmix --example tcp # raw TCP connection
cargo run -p smolmix --example udp # raw UDP datagram
cargo run -p smolmix --example websocket # WebSocket over TLS (raw TcpStream composability)
```
## Architecture
See [`src/ARCHITECTURE.md`](src/ARCHITECTURE.md).
+1 -1
View File
@@ -64,7 +64,7 @@ async fn https_get(
#[tokio::main]
async fn main() -> Result<(), BoxError> {
smolmix::init_logging();
nym_bin_common::logging::setup_tracing_logger();
rustls::crypto::ring::default_provider()
.install_default()
.expect("Failed to install rustls crypto provider");
+1 -1
View File
@@ -148,7 +148,7 @@ impl Transport for SmolmixTransport {
#[tokio::main]
async fn main() -> Result<(), BoxError> {
smolmix::init_logging();
nym_bin_common::logging::setup_tracing_logger();
let args: Vec<String> = std::env::args().collect();
+13 -2
View File
@@ -1,17 +1,28 @@
[package]
name = "smolmix-dns"
description = "DNS resolution through the Nym mixnet via hickory-resolver"
version = "0.0.1"
authors.workspace = true
edition = "2021"
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme = "README.md"
publish = true
[dependencies]
smolmix = { workspace = true }
hickory-proto = { workspace = true, features = ["tokio"] }
hickory-resolver = { workspace = true, features = ["tokio"] }
tokio-smoltcp = "0.5"
tokio-smoltcp = { workspace = true }
[dev-dependencies]
# Clearnet baseline in example
hickory-resolver = { workspace = true, features = ["tokio", "system-config"] }
tokio = { workspace = true, features = ["macros", "rt-multi-thread", "time"] }
tracing = { workspace = true }
nym-bin-common = { workspace = true, features = ["basic_tracing"] }
[[example]]
name = "resolve"
+1 -1
View File
@@ -18,7 +18,7 @@ type BoxError = Box<dyn std::error::Error + Send + Sync>;
#[tokio::main]
async fn main() -> Result<(), BoxError> {
smolmix::init_logging();
nym_bin_common::logging::setup_tracing_logger();
let domain = "example.com";
+17 -3
View File
@@ -1,25 +1,39 @@
[package]
name = "smolmix-hyper"
description = "HTTP client routing all traffic through the Nym mixnet via hyper"
version = "0.0.1"
authors.workspace = true
edition = "2021"
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme = "README.md"
publish = true
[dependencies]
smolmix = { workspace = true }
smolmix-dns = { workspace = true }
smolmix-tls = { workspace = true }
bytes = { workspace = true }
hyper = { workspace = true, features = ["client", "http1"] }
hyper-util = { workspace = true, features = ["tokio", "client-legacy"] }
http-body-util = { workspace = true }
smolmix-tls = { workspace = true }
tokio-smoltcp = "0.5"
tokio-smoltcp = { workspace = true }
pin-project-lite = "0.2"
tower = { workspace = true }
tokio = { workspace = true }
[dev-dependencies]
# Clearnet baseline in example
reqwest = { workspace = true }
rustls = { workspace = true }
tokio = { workspace = true, features = ["macros", "rt-multi-thread", "time"] }
tracing = { workspace = true }
nym-bin-common = { workspace = true, features = ["basic_tracing"] }
[[example]]
name = "get"
[[example]]
name = "post"
+1 -1
View File
@@ -15,7 +15,7 @@ type BoxError = Box<dyn std::error::Error + Send + Sync>;
#[tokio::main]
async fn main() -> Result<(), BoxError> {
smolmix::init_logging();
nym_bin_common::logging::setup_tracing_logger();
rustls::crypto::ring::default_provider()
.install_default()
.expect("Failed to install rustls crypto provider");
+1 -1
View File
@@ -27,7 +27,7 @@ const JSON_BODY: &str = r#"{"message": "hello from the Nym mixnet!"}"#;
#[tokio::main]
async fn main() -> Result<(), BoxError> {
smolmix::init_logging();
nym_bin_common::logging::setup_tracing_logger();
rustls::crypto::ring::default_provider()
.install_default()
.expect("Failed to install rustls crypto provider");
+19 -2
View File
@@ -1,11 +1,28 @@
[package]
name = "smolmix-tls"
description = "TLS connector with webpki roots for smolmix tunneled connections"
version = "0.0.1"
authors.workspace = true
edition = "2021"
license.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
readme = "README.md"
publish = true
[dependencies]
tokio-smoltcp = "0.5"
tokio-rustls = "0.26"
tokio-smoltcp = { workspace = true }
tokio-rustls = { workspace = true }
rustls = { workspace = true }
webpki-roots = { workspace = true }
[dev-dependencies]
smolmix = { workspace = true }
tokio = { workspace = true, features = ["macros", "rt-multi-thread", "time", "net", "io-util"] }
tracing = { workspace = true }
nym-bin-common = { workspace = true, features = ["basic_tracing"] }
[[example]]
name = "connect"
+1 -1
View File
@@ -1,6 +1,6 @@
# smolmix-tls
Shared TLS setup for smolmix tunneled connections. Provides a pre-configured `TlsConnector` with webpki root certificates and convenience functions for TLS over `TcpStream`, used internally by `smolmix-hyper` and `smolmix-tungstenite`.
Shared TLS setup for smolmix tunneled connections. Provides a pre-configured `TlsConnector` with webpki root certificates and convenience functions for TLS over `TcpStream`, used internally by `smolmix-hyper`.
## Quick start
+119
View File
@@ -0,0 +1,119 @@
//! TLS connection: clearnet vs Nym mixnet comparison.
//!
//! Performs a TLS handshake and HTTPS GET request via both clearnet (tokio-rustls
//! over a system TCP socket) and the mixnet (smolmix-tls over a tunnel), then
//! compares timing and verifies both see the same content.
//!
//! Run with:
//! cargo run -p smolmix-tls --example connect
//! cargo run -p smolmix-tls --example connect -- --ipr <IPR_ADDRESS>
use std::sync::Arc;
use rustls::ClientConfig;
use smolmix::{Recipient, Tunnel};
use smolmix_tls::{connect_with, connector};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tracing::info;
type BoxError = Box<dyn std::error::Error + Send + Sync>;
#[tokio::main]
async fn main() -> Result<(), BoxError> {
nym_bin_common::logging::setup_tracing_logger();
rustls::crypto::ring::default_provider()
.install_default()
.expect("Failed to install rustls crypto provider");
let host = "example.com";
let addr: std::net::SocketAddr = "93.184.216.34:443".parse()?;
// --- Clearnet baseline via tokio + tokio-rustls ---
info!("Clearnet TLS connection to {host}...");
let clearnet_start = tokio::time::Instant::now();
let mut root_store = rustls::RootCertStore::empty();
root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
let config = ClientConfig::builder()
.with_root_certificates(root_store)
.with_no_client_auth();
let tls_connector = tokio_rustls::TlsConnector::from(Arc::new(config));
let tcp = tokio::net::TcpStream::connect(addr).await?;
let server_name = rustls::pki_types::ServerName::try_from(host.to_string())?;
let mut tls = tls_connector.connect(server_name, tcp).await?;
tls.write_all(
format!("GET / HTTP/1.1\r\nHost: {host}\r\nConnection: close\r\n\r\n").as_bytes(),
)
.await?;
let mut clearnet_buf = Vec::new();
tls.read_to_end(&mut clearnet_buf).await?;
let clearnet_duration = clearnet_start.elapsed();
let clearnet_status = String::from_utf8_lossy(&clearnet_buf[..40.min(clearnet_buf.len())]);
info!(
"Clearnet: {} bytes, status: {:?} ({:?})",
clearnet_buf.len(),
clearnet_status.lines().next().unwrap_or(""),
clearnet_duration
);
// --- Mixnet via smolmix-tls ---
let args: Vec<String> = std::env::args().collect();
let ipr_addr = args
.iter()
.position(|a| a == "--ipr")
.and_then(|i| args.get(i + 1));
let tunnel = if let Some(addr_str) = ipr_addr {
let recipient: Recipient = addr_str.parse().expect("invalid IPR address");
Tunnel::new_with_ipr(recipient).await?
} else {
Tunnel::new().await?
};
let tls_conn = connector();
info!("Mixnet TLS connection to {host}...");
let mixnet_start = tokio::time::Instant::now();
let tcp = tunnel.tcp_connect(addr).await?;
let mut tls = connect_with(&tls_conn, tcp, host).await?;
tls.write_all(
format!("GET / HTTP/1.1\r\nHost: {host}\r\nConnection: close\r\n\r\n").as_bytes(),
)
.await?;
let mut mixnet_buf = Vec::new();
tls.read_to_end(&mut mixnet_buf).await?;
let mixnet_duration = mixnet_start.elapsed();
let mixnet_status = String::from_utf8_lossy(&mixnet_buf[..40.min(mixnet_buf.len())]);
info!(
"Mixnet: {} bytes, status: {:?} ({:?})",
mixnet_buf.len(),
mixnet_status.lines().next().unwrap_or(""),
mixnet_duration
);
// --- Compare ---
info!("=== Results ===");
info!(
"Clearnet: {} bytes in {:?}",
clearnet_buf.len(),
clearnet_duration
);
info!(
"Mixnet: {} bytes in {:?}",
mixnet_buf.len(),
mixnet_duration
);
let slowdown = mixnet_duration.as_millis() as f64 / clearnet_duration.as_millis().max(1) as f64;
info!("Slowdown: {slowdown:.1}x");
tunnel.shutdown().await;
Ok(())
}