pub struct ListenerConfig {Show 19 fields
pub bind: Option<SocketAddr>,
pub component: String,
pub project: String,
pub site: String,
pub imports: Vec<String>,
pub env: BTreeMap<String, String>,
pub egress_allowlist: Vec<String>,
pub tenancy: Option<Tenancy>,
pub tls: Option<ListenerTlsConfig>,
pub max_conns: Option<usize>,
pub idle_timeout_ms: Option<u64>,
pub max_conn_duration_ms: Option<u64>,
pub max_conns_per_ip_per_sec: Option<f64>,
pub conn_burst_per_ip: Option<u32>,
pub conn_rate_ipv6_prefix: Option<u8>,
pub proxy_protocol: bool,
pub proxy_protocol_trust_any: bool,
pub proxy_protocol_trusted_cidrs: Vec<String>,
pub mode: ListenerMode,
}Expand description
[serve.listeners.<name>] — a host-owned inbound-TCP listener (PLAN-handler-tcp-inbound). The
node binds + listens + accepts on bind and dispatches each accepted connection to the guest
component’s tcp-connection-handler export, with tenancy OPERATOR-PINNED here (a raw socket
carries no Host/path/principal, so there is nothing per-connection to resolve). The guest is handed
only the connection’s wasi:io byte streams — it holds NO raw wasi:sockets authority (bind stays
host-side). Modeled on boatramp_core::function::FunctionConfig / the session route config.
Host-side per-listener TLS/STARTTLS termination (cap-req 06) and a host-mediated tcp-egress
egress_allowlist (cap-req 19 — the host dials + terminates TLS, the guest still holds no raw
socket) are both supported. See [validate_listeners].
Fields§
§bind: Option<SocketAddr>The address the node binds + listens + accepts on (e.g. 0.0.0.0:9000). Required (a blank
bind is refused at config load).
component: StringPath to the guest .wasm within the pinned site’s deployment (exports tcp-connection-handler).
Required.
project: StringThe project this listener’s component is deployed under (host-resolved, never guest input). The
active deployment of project/site is what the accept loop serves. Required.
site: StringThe site within project whose active deployment holds component. Required.
imports: Vec<String>Requested capabilities (interface names; see KNOWN_IMPORTS). A listener component declares
whatever sql/orm/kv/… it uses per connection — NOT wasi:sockets (the host owns the
socket; a tcp-connection-host guest imports only wasi:io, handed the accepted streams).
env: BTreeMap<String, String>Static environment variables injected into the component (never secrets).
egress_allowlist: Vec<String>Per-listener outbound-TCP allowlist (cap-req 19) for the listener guest’s HOST-MEDIATED
tcp-egress dials: the host:port / ip:port / CIDR:port targets the host may dial on the
guest’s behalf (the host dials + terminates TLS — the guest NEVER holds raw wasi:sockets). It
takes effect only when the node posture allow_guest_tcp_egress is on AND the component imports
tcp-egress; the dial is connect-only + SSRF-floored (the SAME gate as a handler/function
egress_allowlist). Empty ⇒ no egress. Enables e.g. an imap-proxy listener dialing the IMAPS
upstream.
tenancy: Option<Tenancy>Operator-pinned Dimension-0 tenancy for this listener’s sql/orm (resolved once from this
config, carried on every connection — nothing per-connection to resolve from). Absent ⇒ plain
(project = database).
tls: Option<ListenerTlsConfig>DEFERRED (Phase 1): per-listener host-TLS. Phase 1 ships plaintext-only; a declared tls block
is REFUSED at config load (fail-closed) so an operator is never misled into thinking a listener
terminates TLS when it does not. Terminate TLS in front of boatramp, or wait for the fast-follow.
max_conns: Option<usize>Cap on simultaneously-live connections for THIS listener (a per-listener DoS bound on top of the
engine’s conn-lane ceiling). 0 is refused; absent ⇒ DEFAULT_LISTENER_MAX_CONNS.
idle_timeout_ms: Option<u64>Tear a connection down after this many milliseconds with NO bytes in either direction (idle
timeout — the conn lane replaces the per-invocation epoch deadline with this). 0 is refused;
absent ⇒ DEFAULT_LISTENER_IDLE_TIMEOUT_MS.
max_conn_duration_ms: Option<u64>Absolute cap (milliseconds) on a single connection’s lifetime regardless of activity. 0 is
refused; absent ⇒ DEFAULT_LISTENER_MAX_CONN_DURATION_MS.
max_conns_per_ip_per_sec: Option<f64>Per-source-IP connection-rate limit (cap-req 18): the sustained rate (connections/sec) one
peer IP may open on THIS listener, atop a conn_burst_per_ip immediate allowance. A FaaS
listener guest can’t see the peer IP, so the HOST enforces this at the accept loop to keep
native-daemon brute-force protection (a client otherwise resets its auth-backoff by
reconnecting). Absent ⇒ ON at DEFAULT_LISTENER_CONN_RATE_PER_IP (protection is NOT
opt-in); 0 ⇒ DISABLED (no per-IP limit; the max_conns live-cap still applies).
conn_burst_per_ip: Option<u32>Immediate per-source-IP connection allowance (the token-bucket burst depth) before the
sustained max_conns_per_ip_per_sec throttles. Absent ⇒ DEFAULT_LISTENER_CONN_BURST_PER_IP.
Ignored when the rate is 0 (limit disabled).
conn_rate_ipv6_prefix: Option<u8>IPv6 bucket prefix (bits) for the per-source-IP connection-rate limit (senior-architect
review). An IPv6 peer is keyed for rate-limiting by this prefix, not its full /128: a single
site is routinely handed a whole /64, so keying on the full address would let one owner rotate
through addresses to get a fresh bucket per connection (and spray the tracked map). Masking to
the prefix collapses a site to one bucket. IPv4 is always keyed in full (/32). Valid 1..=128;
absent ⇒ DEFAULT_LISTENER_CONN_RATE_IPV6_PREFIX (/64). Ignored when the rate limit is off.
proxy_protocol: boolTrust a PROXY-protocol (v1/v2) header on each accepted connection (cap-req 18 completion):
when this listener sits behind a trusted L4 proxy/load-balancer, the kernel peer is the
proxy’s, so the host reads the prepended header to recover the REAL client address and uses it
for the per-source-IP rate limit AND the peer handed to the guest. Opt-in + fail-closed:
false (default) never parses a header (so a direct client can’t forge one to spoof its IP);
true asserts the listener is reachable ONLY via the trusted proxy, and a connection that does
not present a well-formed header is dropped. Set it ONLY when a proxy actually fronts the port.
proxy_protocol_trust_any: boolExplicitly trust a PROXY-protocol header from ANY source (senior-architect review — OWNER
DECISION: require trusted CIDRs by default). proxy_protocol = true now REQUIRES a non-empty
proxy_protocol_trusted_cidrs UNLESS this is set true (the firewall-only mode, where the
operator asserts the port is unreachable except via the proxy). Default false ⇒ a
proxy_protocol listener with no trusted CIDRs is REFUSED at config load (fail-closed,
boot-loud), so a header is never silently trusted from any source by omission. Set this ONLY
when you genuinely cannot enumerate the proxy’s source CIDRs but the port is firewalled to it.
proxy_protocol_trusted_cidrs: Vec<String>Trusted-proxy source CIDRs for proxy_protocol (cap-req 18 / security-review MEDIUM-1): the
defense-in-depth backstop to the network-isolation assumption. When NON-EMPTY, a PROXY-protocol
header is trusted ONLY when the kernel peer (the direct TCP source) is inside one of these
ip/prefix CIDRs (e.g. 10.0.0.0/8, 2001:db8::/32); a connection from anywhere else is
dropped — so a firewall gap can’t become source-IP spoofing. EMPTY (default) trusts a header
from any source (the firewall-only mode), which is sound only if the port is truly unreachable
except via the proxy; set this whenever you can. Ignored unless proxy_protocol is true.
mode: ListenerModeHow the accepted connection is driven (cap-req 08). ListenerMode::Connection (default) is the
long-lived model: the guest’s tcp-connection-handler holds the connection — and ONE wasm
instance — for the connection’s whole life. ListenerMode::Session drives the connection
through the re-entrant session model instead: the host owns the socket, re-enters the guest’s
session-handler per inbound batch carrying a host-held checkpoint, and holds NO instance between
batches — so thousands of mostly-idle connections (IMAP IDLE, long-poll) cost ~a socket each, not
an instance each, and a protocol-idle-but-alive connection is never torn down for inactivity
(only a dead peer or max_conn_duration reaps it). The component must export session-handler,
not tcp-connection-handler. Additive + back-compat: absent ⇒ Connection (unchanged).
Trait Implementations§
Source§impl Clone for ListenerConfig
impl Clone for ListenerConfig
Source§impl Debug for ListenerConfig
impl Debug for ListenerConfig
Source§impl Default for ListenerConfig
impl Default for ListenerConfig
Source§impl<'de> Deserialize<'de> for ListenerConfigwhere
ListenerConfig: Default,
impl<'de> Deserialize<'de> for ListenerConfigwhere
ListenerConfig: Default,
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Auto Trait Implementations§
impl Freeze for ListenerConfig
impl RefUnwindSafe for ListenerConfig
impl Send for ListenerConfig
impl Sync for ListenerConfig
impl Unpin for ListenerConfig
impl UnsafeUnpin for ListenerConfig
impl UnwindSafe for ListenerConfig
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more