-
Notifications
You must be signed in to change notification settings - Fork 0
push ingress
Added in v3.3.0 — Phase 3 of the subscription/listener RFC (noetl/ai-meta#90).
The gateway terminates untrusted inbound push traffic — generic
webhooks and Google Pub/Sub push deliveries — at
POST /ingress/{listener}. It is a verify-and-forward gatekeeper:
it authenticates each delivery, and only after verification passes
applies the subscription's header directives and forwards one
POST /api/execute per delivery to the server on the subscription's
dedicated pool. The gateway never reads domain data or holds a DB
connection on the ingress path
(Data Access Boundary).
This is the push activation mode (Mode C) of a
kind: Subscription — the pull
mode (Mode B) is the worker's continuous runtime.
POST /ingress/{listener} (no session auth — a source, not a user)
1. fetch ingress config (cached) — verify scheme + Wallet secret + dispatch + directives
2. verify the delivery — HMAC / bearer / Pub-Sub OIDC
fail → 401/403, noetl_ingress_rejected_total, NO forward, NO directives
3. ON SUCCESS ONLY: parse + resolve header directives (allowlist, RFC §7)
4. forward POST {server}/api/execute — one execution per delivery, dedicated pool
5. emit subscription.message.directives_applied (audit, RFC §7.6)
6. return 202 so the source acks
The ordering is the security property: a request that fails
verification is rejected before any directive is parsed, so an
unauthenticated caller can never drive routing (redirect / pool /
priority). In the code this is the verify_then_plan invariant — a
failed verification yields no dispatch plan at all
(src/ingress/mod.rs).
A push kind: Subscription declares its verify scheme + the Wallet
alias of its secret in the catalog spec.ingress.verify block. The
gateway resolves the config (including the decrypted secret for
HMAC/bearer) from the server's GET /api/internal/ingress/{listener}
endpoint — never from a gateway env var.
verify.type |
Check the gateway performs | Secret source |
|---|---|---|
hmac_sha256 |
Recompute HMAC-SHA256(secret, raw_body), constant-time compare against the header value (optional sha256= prefix). |
Wallet alias |
bearer |
Constant-time compare the Authorization: Bearer token against the expected token. |
Wallet alias |
pubsub_oidc |
Validate the Google-signed OIDC JWT: RS256 vs Google JWKS, aud == audience, email == service_account, email_verified, not expired. |
Config (JWKS public) |
none |
Rejected at catalog registration — push ingress always verifies. | — |
The verifiers live in
src/ingress/verify.rs.
Negative paths are unit-tested: bad signature, expired token, wrong
audience, wrong service-account, unknown kid, tampered body.
The pubsub_oidc positive signature path is validated live against
Google's real signing keys (noetl/ai-meta#91) — not just the
self-minted RSA key the unit tests use. The #[ignore]d
oidc_live_google_token_against_real_jwks test fetches the live JWKS
(https://www.googleapis.com/oauth2/v3/certs) via the gateway's own
fetch_google_jwks and validates a genuinely Google-signed OIDC token:
valid (correct aud + SA) → verified; wrong audience →
oidc_wrong_audience; wrong SA → oidc_wrong_sa; tampered signature →
oidc_bad_signature. The token is minted out-of-band by impersonating a
Google service account with a custom audience + --include-email, so it
carries the exact claims a Pub/Sub push subscription sends. Reproduce
with noetl/e2e scripts/live_validate_oidc_verify.sh. A full HTTP run
(gateway → kind server, real token to /ingress/{listener}) confirmed
one execution dispatched on a valid token and zero on the tampered /
wrong-aud / missing-token deliveries.
After verification the gateway runs the same allowlist-based directive
engine the worker's pull runtime uses (RFC §7), vendored serde-only into
src/ingress/directives.rs
so the edge stays free of the worker's heavy tool dependencies. Only
keys in the subscription's spec.headers.directives allowlist act as
instructions; a value allowlist (allowed: / map:) constrains the
target. Directives can redirect the target playbook
(dispatch.playbook), route to a different pool
(dispatch.execution_pool / priority), supply an idempotency_key,
hint content_type, and carry a W3C trace context into the execution.
For a webhook source the directive channel is the HTTP headers (auth
headers are stripped before forwarding); for a Pub/Sub push source
the gateway unwraps the message envelope and the directive channel is
the message attributes (RFC §7.1).
apiVersion: noetl.io/v1
kind: Subscription
metadata: { name: stripe-webhook, path: subscriptions/stripe-webhook }
spec:
source: webhook
mode: push
ingress:
gateway_path: /ingress/stripe # trailing segment = the {listener}
verify:
type: hmac_sha256
header: "Stripe-Signature"
secret: "stripe_webhook_secret" # Wallet alias — NOT an env var
dispatch:
playbook: domain/handle_stripe_event
payload_from: message.body
execution_pool: subscription
headers:
directives:
- header: x-noetl-route
controls: dispatch.playbook
allowed: [domain/handle_stripe_event, domain/handle_fraud]| Env var | Purpose |
|---|---|
NOETL_INTERNAL_API_TOKEN |
Service-account bearer the gateway presents to GET /api/internal/ingress/{listener}. Must match the server's value. Unset → /ingress/{listener} returns 503.
|
NOETL_BASE_URL |
The server the gateway forwards POST /api/execute to. |
The gateway's first /metrics surface ships with this feature:
-
noetl_ingress_received_total{subscription}— deliveries received. -
noetl_ingress_rejected_total{subscription,reason}— rejected at verification (no execution).reasonis low-cardinality (bad_signature,oidc_expired,oidc_wrong_audience, …). -
noetl_ingress_dispatched_total{subscription}— verified deliveries forwarded as one execution.
Each forward carries the execution_id and (best-effort) emits the
subscription.message.directives_applied audit event.
- Architecture — the gatekeeper charter.
- Configuration · Deployment
-
noetl/server wiki — the
kind: Subscriptioncatalog type + the/api/internal/ingress/{listener}config endpoint. - Subscription/Listener RFC
Gateway
Surfaces
Operations
See also
- noetl wiki
- ops wiki
- travel wiki (consumer)
- Ephemeral Blueprints