# outbound _(no submodules)_ ## Purpose Builds the outbound-socket allowlist from `policy.fetch[]` and wraps the host's `connect` adapter so denied destinations are refused BEFORE any OS socket is opened. Denial raises `SAND_WASM_OUTBOUND_DENIED` and fires the injected `onDeny` hook so the sandbox host can increment `plugin.permission_denied_total{capability="fetch"}` without coupling this module to the OTel API. The matcher supports exact hosts, `*.suffix` wildcards (RFC 6125 semantics — the apex `suaveplan.io` does NOT match `*.suaveplan.io`), and optional port suffixes. Bare-host rules match any port; port-pinned rules match only the named port. ## Features - Exact-host and `*.suffix` rule patterns. - Port-pinned rules (`api.stripe.com:443`). - Default destination port 443 when none is supplied. - O(rules) matching — no regex compilation, suitable for the typical small allowlists. - `gateOutboundConnect` wraps a host `connect` adapter; denied calls never invoke the inner adapter. ## Usage: Basic ```ts import { buildWasiOutboundAllowlist } from "@suaveplan/api-sandbox-wasm"; const allowlist = buildWasiOutboundAllowlist(policy); if (!allowlist.isAllowed("api.stripe.com")) { /* deny */ } ``` ## Usage: Advanced ```ts import { gateOutboundConnect } from "@suaveplan/api-sandbox-wasm/outbound"; const gated = gateOutboundConnect( allowlist, async (destination) => net.connect(destination), (destination) => instruments.recordPermissionDenied("fetch", destination) ); const socket = await gated("api.stripe.com"); // throws SAND_WASM_OUTBOUND_DENIED for non-listed hosts. ``` ## API Reference - `buildWasiOutboundAllowlist(policy): OutboundAllowlist` - `gateOutboundConnect(allowlist, connect, onDeny)` - `OutboundAllowlist.{isAllowed(destination), rules}` ## Implementation Notes Wildcards live inside a single `*.suffix` form rather than full glob matching because RFC 6125 wildcard semantics are well-defined and predictable. Adding nested wildcards (e.g. `**.example.com`) would require careful escape handling that this module does not attempt; if operators need deeper granularity they should enumerate sub-domains explicitly. Out-of-range port suffixes (>65535) on RULES are treated as host-only — the parser falls back to the bare-host path rather than throwing, because a parse failure here would be operator-visible during host boot and add no security value. Out-of-range ports on DESTINATIONS are treated as the default 443 for the same reason.