Skip to main content

plecto_host/
lib.rs

1//! plecto-host — embeds wasmtime to run `plecto:filter` components (ADR 000001 / 000002).
2//!
3//! ADR 000004 slice: the filter **runtime model**. The host branches on trust at load
4//! (ADR 000011's knot, made concrete):
5//!   - **trusted** filters get a fixed-capacity **pool** of reusable instances on a
6//!     **pooling-allocator** engine, checked out per request (ADR 000012). `init` runs once
7//!     *per instance* — Tenet 4 pays off (init-derived state stays resident). The pool is
8//!     lazily filled: a single thread only ever needs one instance, so init stays once; under
9//!     concurrency the pool builds more (up to its cap), which is where the pooling allocator
10//!     finally earns its keep. Saturation (every instance checked out) waits a bounded time
11//!     then fails **closed** (`Unavailable`), and an instance is recycled after serving a
12//!     configured number of requests to bound linear-memory state accumulation (§6.6).
13//!     Binding the pool to the tokio/quinn fast path (blocking pool vs fiber) is M2's job.
14//!   - **untrusted** filters get a **fresh instance per request** on an on-demand engine,
15//!     so linear memory is zeroized **by construction** (no slot reuse → CVE-2022-39393
16//!     surface absent, ADR 000006). The cost is `init` every request — the deliberate
17//!     trade of isolation (ADR 000011).
18//!
19//! State lives behind a `KvBackend` (in-memory or redb) — filters are stateless (Fork 4),
20//! keys are host-namespaced per filter identity + primitive (ADR 000011). The `Linker`
21//! stays **deny-by-default**: it lends ONLY the plecto host-API (log / clock / kv /
22//! counter / ratelimit). No WASI, network, filesystem, or sockets (ADR 000006).
23
24// Hot-path discipline (bp-rust): no unwrap/expect/panic/indexing on the data plane. Exempted
25// under `cfg(test)` — this crate's own `#[cfg(test)] mod tests` blocks legitimately use them;
26// `tests/*.rs` integration tests are separate crates and are never subject to this attribute.
27#![cfg_attr(
28    not(test),
29    warn(
30        clippy::unwrap_used,
31        clippy::expect_used,
32        clippy::panic,
33        clippy::indexing_slicing
34    )
35)]
36
37mod backend;
38mod observe;
39pub mod otlp;
40// Experimental streaming body filter (direction_0003 gates 1+2), OFF by default. A descendant of the
41// crate root, so it reuses the private `EpochTicker` metering; the shipped path is untouched.
42#[cfg(feature = "streaming-body")]
43mod streaming;
44#[cfg(feature = "streaming-body")]
45pub use streaming::{
46    StreamingDecision, StreamingFilterRuntime, StreamingLimits, run_streaming_body,
47};
48// Outbound capabilities for filters (ADR 000036 HTTP / ADR 000060 TCP), each OFF by default.
49// `outbound` is the pure allowlist + SSRF policy both share (one `classify` = one floor); the
50// wasmtime wirings that enforce it are `outbound_http` / `outbound_tcp` (per-feature gates), and
51// `resolver` is the host-side DNS seam they share.
52#[cfg(any(feature = "outbound-http", feature = "outbound-tcp"))]
53mod outbound;
54#[cfg(any(feature = "outbound-http", feature = "outbound-tcp"))]
55pub use outbound::AddrVerdict;
56#[cfg(feature = "outbound-http")]
57pub use outbound::{AllowEntry, OutboundPolicy, Scheme};
58#[cfg(feature = "outbound-tcp")]
59pub use outbound::{OutboundTcpPolicy, TcpAllowEntry};
60#[cfg(feature = "outbound-http")]
61mod outbound_http;
62#[cfg(feature = "outbound-tcp")]
63mod outbound_tcp;
64#[cfg(any(feature = "outbound-http", feature = "outbound-tcp"))]
65mod resolver;
66// Fat guest (ADR 000063): the minimal-WASI stdio bridge, OFF by default.
67#[cfg(feature = "fat-guest")]
68mod stdio;
69
70// Generic conformance battery (ADR 000065): the `plecto conformance` CLI surface. Production
71// code, distinct from `tests/polyglot.rs`'s fixture-specific internal regression suite.
72mod conformance;
73mod contract;
74// DevSigner (ADR 000065): the persistent, project-local signing key for `plecto dev` /
75// `plecto conformance`. Production code (unlike `test_support`) — it links into a plain
76// `plecto-server` build, not just behind `test-support`.
77mod dev_signer;
78mod engine;
79mod errors;
80mod filter;
81mod host;
82mod options;
83mod pool;
84mod quota;
85mod runtime;
86mod state;
87mod trust;
88mod util;
89// Test / dev signing support — **NOT production provenance**; see `test_support.rs`. The
90// `clippy::expect_used` allow was on the module itself (not a single item), so it moves here
91// with the module declaration.
92#[doc(hidden)]
93#[cfg(feature = "test-support")]
94#[allow(clippy::expect_used)]
95// dev/test-only fixture loader (test-support feature); not data-plane code
96pub mod test_support;
97
98pub use backend::{Acquire, Bucket, KvBackend, MemoryBackend, RedbBackend, apply_bucket};
99pub use conformance::{ConformanceCheck, ConformanceReport, check as run_conformance};
100pub use contract::{ContractVersion, FILTER_WIT, header};
101pub use dev_signer::{DEV_KEY_MARKER, DevKeyError, DevSigner, bound_sbom, public_key_path_for};
102pub use observe::{
103    FanOutSink, FilterSpan, Hook, InMemorySink, MetricsSink, MetricsSnapshot, NoopSink,
104    RequestTrace, SpanOutcome, TelemetrySink,
105};
106
107mod bindings {
108    // `wit/` here is a vendored copy of the repo-root `plecto/wit/` (the contract's source of
109    // truth) — cargo-package can only include files under this crate's own root, so publishing
110    // to crates.io needs the WIT available in-tree, not via a `../../` sibling path.
111    // `scripts/check_wit_vendoring.py` (run in CI) keeps this copy from drifting.
112    wasmtime::component::bindgen!({
113        path: "wit",
114        world: "filter",
115        // M3 Stage 1 (ADR 000021): the guest's exported hooks (init / on-request / on-response) run
116        // via call_async on wasmtime fibers — the prerequisite for future WASI async host calls. The
117        // trivial plecto host-API IMPORTS stay sync (they never block, so they don't need to be
118        // async). Body / stream<u8> contract stays frozen until Stage 2.
119        exports: { default: async },
120    });
121}
122
123// One canonical set of contract types for callers and tests.
124pub use bindings::plecto::filter::host_log::Level as LogLevel;
125pub use bindings::plecto::filter::types::{
126    Header, HttpRequest, HttpResponse, RequestBodyDecision, RequestDecision, RequestEdit,
127    ResponseDecision, ResponseEdit,
128};
129
130pub use errors::{LoadError, RunError};
131pub use filter::LoadedFilter;
132pub use host::Host;
133pub use options::{Isolation, LoadOptions};
134// `HostState` stays crate-internal (DECREE §2: minimal pub surface) — it has no public
135// constructor or methods and no external users; only `LogLine` crosses the crate boundary
136// (returned by `on_request`).
137pub use state::LogLine;
138pub use trust::{SignedArtifact, TrustPolicy};