Skip to main content

Crate ryu_webhook_ingress

Crate ryu_webhook_ingress 

Source
Expand description

Webhook ingress — the swappable public-reachability seam (P6a of the unified-tool-gateway epic, #479). Extracted from apps/core into its own crate (program §3/§4 W3); the kernel couplings are inverted through WebhookIngressHost, so this crate has ZERO dependency on apps/core.

Composio triggers are webhook-delivered: there is no event-pull API, so a local Core bound to 127.0.0.1 never receives them. This crate is the swappable seam that gives Core a publicly-reachable URL pointed at its existing handler (POST /api/composio/webhook, reached via the host), so a trigger fires unchanged. Public webhook routes stay kernel-ingress in Core (program §5) and forward into this engine.

Core vs Gateway (CLAUDE.md §1): exposing a tunnel + deciding which backend runs is what runsCore. There is no policy here.

“Nothing hardcoded” (CLAUDE.md §1): the backend is a swappable Ingress enum selected by the webhook.ingress.backend pref, with an RYU_WEBHOOK_INGRESS_URL env override for the BYO (OwnRelay) case. The default is the managed IngressKind::RyuRelay.

Backend dispatch uses native async fn trait methods (not object-safe) + a closed Ingress enum match-dispatched — no async-trait, no dyn. See [tunnels]. (The host seam is the one dyn/async-trait boundary.)

Structs§

CloudflaredSource
Cloudflared — adopt-or-spawn a cloudflared quick tunnel pointed at Core’s local port. start() spawns cloudflared tunnel --url http://localhost:<port>, parses the assigned https://<sub>.trycloudflare.com base from its output, and holds the child alive for the process lifetime (dropping the child tears the tunnel down). public_url() returns that base joined with WEBHOOK_PATH. No account/login is needed — quick tunnels are anonymous and ephemeral, which is exactly the BYO-public-URL contract this seam needs. Requires the cloudflared binary on PATH; spawn failure errors clearly.
OwnRelaySource
OwnRelay — the BYO ingress: the user already exposes Core (or a reverse proxy) at a public URL and configures it here. The base comes from the env RYU_WEBHOOK_INGRESS_URL (preferred) or the value handed at construction (e.g. a pref). public_url() appends the webhook path.
RyuRelaySource
RyuRelay — the managed push relay (the default). Core opens an outbound SSE subscription to apps/server; Composio POSTs to a public ingress URL and the server fans the payload out over that stream, which Core dispatches in-process. The register + SSE-client loop live in [super::ryu_relay]; this source delegates to them.
TailscaleFunnelSource
TailscaleFunnel — exposes Core’s bind port to the public internet via the P5 mesh’s Tailscale Funnel. Consumes the host’s ensure_funnel / funnel_url (Core forwards to crate::mesh). When the mesh is not enabled/available — or no host is installed — it stub-errors with a clear “Phase 5” message so this runs standalone.

Enums§

InboundOutcome
The outcome of routing one inbound webhook by path.
Ingress
The closed set of ingress backends, match-dispatched (no async-trait/dyn).
IngressKind
The four ingress backends. Serializes kebab-case so the wire form and the pref value round-trip (ryu-relay / tailscale-funnel / cloudflared / own-relay).
WorkflowWebhookOutcome
The outcome of delivering a per-workflow webhook. Rich enough that both the axum workflow_webhook handler (→ HTTP status) and the relay dispatcher (→ log line) map from the same decision, so their auth can never drift.
WorkflowWebhookSecret
The raw result of looking up a workflow’s webhook trigger secret. The crate (not the host) owns the empty-secret → NoSecret decision, so this returns the trigger’s secret field verbatim (Secret(None) when the trigger exists but carries no secret at all).

Constants§

INGRESS_BACKEND_PREF
The pref key selecting the active ingress backend (webhook.ingress.backend).
INGRESS_URL_PREF
The pref key holding the BYO public base URL (the OwnRelay fallback when the RYU_WEBHOOK_INGRESS_URL env override is absent).
OWN_RELAY_URL_ENV
The env var a BYO operator sets to declare Core’s public base URL.
WEBHOOK_PATH
The path Composio is pointed at. Every tunnel/relay appends this to its public base so an inbound webhook lands on Core’s existing handler.

Traits§

WebhookIngress
The webhook-ingress trait every backend implements. Native async fn (not object-safe) → stored via the closed Ingress enum, never dyn.
WebhookIngressHost
Every kernel coupling the webhook-ingress engine needs, inverted. dyn-stored (→ async_trait), installed once at boot. Implemented by Core; the crate’s own tests install a mock.

Functions§

configured_kind
The configured backend kind, resolved from (1) the RYU_WEBHOOK_INGRESS_URL env override ⇒ IngressKind::OwnRelay, else (2) the backend_pref (webhook.ingress.backend), else (3) the IngressKind::DEFAULT (RyuRelay). Shared by from_prefs, the backend selector, and the status handler so they never disagree.
deliver_inbound
Route an inbound webhook to the correct in-process handler by path.
deliver_workflow_webhook
Verify and (on success) fire a per-workflow webhook trigger. This is the single source of truth for the workflow-webhook auth + run path — the HTTP handler and the relay dispatcher both call it, guaranteeing identical fail-closed semantics.
ensure_relay_started
Idempotently ensure the RyuRelay subscription is live (register + SSE loop).
first_http_delivery
Process-global dedup set for DIRECT-HTTP deliveries. The relay transport keeps its own per-subscription set (ryu_relay.rs); this one covers the public HTTP handlers, which face the same at-least-once retry semantics. Returns true when id is new (dispatch) — false when already seen (skip). An empty id is always “new”: deliveries without a delivery-id header are not dedupable and pass through unchanged.
from_prefs
Build the configured Ingress from the resolved pref values + the local server URL.
last_delivery
The unix-seconds timestamp of the last accepted delivery for path, if any.
public_base_url
The resolved public origin base URL (no webhook path) — but ONLY when the active ingress is a true reverse-proxy origin that forwards every path to Core. None otherwise.
public_url
The current public ingress URL, if one has been resolved.
record_delivery
Record that path just received (and accepted) a delivery, stamping “now”. Called from both the relay dispatcher and the direct HTTP handlers so the registry reflects every source.
relay_inbound_url
The reachable public URL a third party POSTs to so an inbound webhook at path reaches THIS node over the RyuRelay ingress: <relay_base>/api/composio-relay/inbound/<relay_token>/<path>. None until the node has registered with the relay (no persisted token yet). This is how a per-workflow webhook becomes discoverable under the default (RyuRelay) ingress, where there is no path-forwarding origin base.
set_global_host
Install the host implementation. Called once from apps/core at startup (unconditionally — Core consumes this crate as a non-optional dependency and the public webhook routes reach it in every build). Idempotent: a second call is ignored.
set_public_url
Publish the resolved public ingress URL for GET /api/webhook-ingress/status.
timestamp_fresh
Whether an inbound delivery is fresh enough to accept, given the value of a timestamp header (e.g. Svix/Composio webhook-timestamp) if present.
workflow_webhook_path
Build the canonical per-workflow webhook path for id. The registry (GET /api/webhooks) and the delivery recorder use this so the stored last-delivery key and the advertised URL never drift.