ryu-webhook-ingress
The swappable public-reachability seam (unified-tool-gateway epic #479, P6a) that lets a loopback-bound Ryu Core receive third-party webhooks — Composio triggers (which are webhook-delivered, with no event-pull API) and per-workflow webhooks — by giving Core a publicly-reachable URL pointed at its existing handler.
Role in the decomposition
An extracted Core capability crate, compiled into apps/core as the in-process
default (no IPC on any hot path). Exposing a tunnel + deciding which backend runs is
what runs → Core; there is no policy here. Public webhook routes stay
kernel-ingress in Core and forward inbound deliveries into this engine.
Zero dependency on apps/core. The kernel couplings (Composio verify/run,
workflow-secret lookup, mesh funnel, auth token, data dir) invert through the
WebhookIngressHost trait, installed by Core at boot via set_global_host.
Nothing hardcoded — the backend swap seam
The backend is a swappable Ingress enum selected by the webhook.ingress.backend
pref (IngressKind, kebab-case wire form), default RyuRelay:
- RyuRelay — the managed default: outbound SSE push (no inbound port).
- TailscaleFunnel — via
ryu-mesh'sensure_funnel. - Cloudflared — a quick tunnel subprocess.
- OwnRelay — BYO public base URL (
RYU_WEBHOOK_INGRESS_URLenv override, else thewebhook.ingress.urlpref).
Backend dispatch uses native async fn trait methods + a closed enum match — no
async-trait/dyn (the host seam is the one dyn boundary).
Key surface
Ingress/IngressKind+ backend sources (RyuRelaySource,TailscaleFunnelSource,CloudflaredSource,OwnRelaySource).deliver_inbound/deliver_workflow_webhook— the path-routed inbound dispatcher with fail-closed HMAC re-verification, a replay window, and delivery dedup.record_delivery/last_delivery/timestamp_fresh/workflow_webhook_path.
Consumed as
Compiled-into-Core crate; Core's public webhook routes forward into it.
Deps: tokio, reqwest, futures-util, serde/serde_json, anyhow, async-trait.