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): fail a wasm build of this
64// crate fast, with a clear message, before tokio/reqwest/redis are even
65// attempted. Modules see ports, never runtimes, so no wasm graph can
66// reach this crate through a module — the gate exists for the venture
67// that wires the wrong runtime into a Worker.
68#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
69compile_error!(
70    "cratefield-runtime-native is native-only: tokio, reqwest and redis do not \
71     compile to wasm (issue #19, ADR 0001). A wasm target must not depend on \
72     this crate — use cratefield-runtime-cloudflare on Workers, and check the \
73     wasm dependency graph of the venture."
74);
75
76mod config;
77mod cron;
78mod ports;
79mod runtime;
80mod server;
81mod tracing_setup;
82
83pub use config::EnvConfig;
84pub use cron::{CronError, fan_out, spawn_cron_scheduler};
85pub use ports::{
86    CLIENT_IP_HEADER, DirBlob, RedisBundle, RedisKv, RedisPortError, RedisRateLimiter,
87    ReqwestClient, SpawnDefer, TokioClock, client_ip, redis_from_env, trusted_proxy_headers,
88};
89pub use runtime::Native;
90pub use server::{ServeError, serve, serve_on, shutdown_signal};
91pub use tracing_setup::install_tracing;