Skip to main content

cratefield_runtime_native/
lib.rs

1//! `cratefield-runtime-native` runs a Factory Zero `Harness` as a single
2//! binary on tokio (ADR 0001, issue #19): axum on a `TcpListener`,
3//! `reqwest` (rustls) behind the `HttpClient` port, Redis behind
4//! `RateLimiter` and `KeyValue`, `tokio::spawn` behind `Defer`, the
5//! system clock behind `Clock`, and an in-process cron scheduler fanning
6//! out to every module's `scheduled(ctx, cron)`.
7//!
8//! The move for a venture (architecture section 10): keep the modules and
9//! adapters, switch `.runtime(Cloudflare::new().db("DB"))` for
10//! `.runtime(Native::new().db(Postgres::connect(url).await?))`, ship the
11//! Dockerfile. No module code changes.
12//!
13//! # Native-only gate
14//!
15//! tokio, reqwest, redis and the JSON log subscriber do not compile to
16//! wasm, so this crate is impossible to build for a wasm target:
17//! `src/lib.rs` fails compilation with a clear message on `wasm32`/
18//! `wasm64` (the same gate `cratefield-adapter-postgres` uses), and the
19//! native dependencies are target-gated in `Cargo.toml`. The wasm graph
20//! of `examples/venture` must stay free of this crate; CI asserts it
21//! with `cargo tree --target wasm32-unknown-unknown`.
22//!
23//! # Client IP (the one behavioral difference from Workers)
24//!
25//! On Workers the edge sets `cf-connecting-ip` and forwarding headers are
26//! client-forgeable. A native deployment sits behind its **own** proxy,
27//! so this runtime owns IP resolution: [`trusted_proxy_headers`] lists
28//! the header names the deployment is willing to believe (default
29//! **empty** — an unconfigured deployment trusts no forwarding header),
30//! [`client_ip`] resolves the address from those headers or the socket
31//! peer, and the server middleware sanitizes every forwarding header
32//! before the router sees the request. See [`client_ip`] for how the
33//! resolved address is carried to the modules.
34//!
35//! # Redis is not the Workers bindings
36//!
37//! [`RedisRateLimiter`] and [`RedisKv`] implement the ports with
38//! semantics as close to the Workers Rate Limiting binding and KV as
39//! Redis allows; every difference is documented on the type, not papered
40//! over. The headline ones: the limiter is a true sliding window that
41//! can report `retry_after` (the binding is fixed-window and cannot),
42//! and Redis is strongly consistent and single-node where KV is
43//! eventually consistent and replicated per-colo.
44//!
45//! ```ignore
46//! # async fn demo() -> Result<(), Box<dyn std::error::Error>> {
47//! use std::sync::Arc;
48//! use cratefield_core::Harness;
49//! use cratefield_runtime_native::{Native, serve};
50//!
51//! let db = cratefield_adapter_postgres::Postgres::connect("postgres://user:pass@host:5432/venture").await?;
52//! let harness = Arc::new(harness()); // your composition, as on Workers
53//! let runtime = Native::new().db_arc(Arc::new(db));
54//! serve(harness, runtime).await?;
55//! # Ok(())
56//! # }
57//! # fn harness() -> Harness { unimplemented!() }
58//! ```
59
60#![doc = include_str!("../README.md")]
61#![forbid(unsafe_code)]
62
63// The whole crate is native-only (issue #19). Modules see ports, never
64// runtimes, so no wasm graph reaches this crate through a module; the
65// hazard is a venture that wires the wrong runtime into a Worker.
66//
67// **This message has never been printed, and cannot be.** Cargo builds a
68// crate's dependencies before the crate itself, so a wasm build of this
69// one dies in mio — "This wasm target is unsupported by mio" — long
70// before this line is evaluated. A `build.rs` does not rescue it either;
71// cargo aborts on the dependency failure first. The guard stays because
72// it documents the rule at the place the rule is about, but the check
73// that actually runs is `no_wasm_package_can_reach_a_native_only_crate`
74// in `crates/cli-acceptance/tests/native_only_off_wasm.rs`, which reads
75// the dependency graph instead of compiling it — and which finds this
76// crate *by* the guard below, so deleting it turns that check off.
77#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
78compile_error!(
79    "cratefield-runtime-native is native-only: tokio, reqwest and redis do not \
80     compile to wasm (issue #19, ADR 0001). A wasm target must not depend on \
81     this crate — use cratefield-runtime-cloudflare on Workers, and check the \
82     wasm dependency graph of the venture."
83);
84
85mod config;
86mod cron;
87mod ports;
88mod runtime;
89mod server;
90mod tracing_setup;
91
92pub use config::EnvConfig;
93pub use cron::{CronError, fan_out, spawn_cron_scheduler};
94pub use ports::{
95    CLIENT_IP_HEADER, Connection, DirBlob, InProcessRealtime, OutboundOptions, RedisBundle,
96    RedisKv, RedisPortError, RedisRateLimiter, ReqwestClient, SpawnDefer, TokioClock, client_ip,
97    redis_from_env, trusted_proxy_headers, vet_ip_ok,
98};
99pub use runtime::Native;
100pub use server::{ServeError, serve, serve_on, shutdown_signal};
101pub use tracing_setup::install_tracing;