Skip to content

feat(engine): optional egress proxy for http_push delivery (use_proxy)#227

Merged
khaliqgant merged 3 commits into
mainfrom
feat/http-push-use-proxy
Jul 1, 2026
Merged

feat(engine): optional egress proxy for http_push delivery (use_proxy)#227
khaliqgant merged 3 commits into
mainfrom
feat/http-push-use-proxy

Conversation

@khaliqgant

@khaliqgant khaliqgant commented Jul 1, 2026

Copy link
Copy Markdown
Member

What

Adds a per-node opt-in — delivery.use_proxy: true — that routes an http_push webhook POST through a deployment-configured forwarder instead of hitting the destination directly.

Why

Some webhook receivers sit behind Cloudflare bot protection that blocks the engine's own network origin — a Cloudflare Worker gets a zone-level 403 on every request (verified exhaustively: bare GET to the receiver's homepage from a Worker → 403; identical request from curl / AWS / GCP → normal). So for those receivers the webhook can never be delivered directly from the Worker. Routing through a non-Cloudflare proxy gets through.

This generalizes the one-off proxy we stood up for a specific agent into a first-class, reusable flag any http_push node can opt into.

How

  • httpDeliverySchema gains use_proxy?: boolean (stored in the node's deliveryConfig; url stays the real destination).

  • EngineConfig.httpPushProxy?: { url, secret } — the deployment's single forwarder, wired by the host adapter from env/secrets.

  • dispatchHttpPush: when use_proxy is set, POST to the proxy URL with:

    • X-Forward-To: <real destination>
    • X-Proxy-Auth: <shared secret>

    The real url is still SSRF-checked; the proxy URL is operator-configured and trusted. If use_proxy is set but no proxy is configured, the delivery fails with a clear error (no http_push proxy configured) rather than silently going direct (which would just be blocked).

The forwarder (non-CF host) validates X-Proxy-Auth, reads X-Forward-To, and forwards the body + webhook auth headers.

Tests (19/19 conformance pass)

  • Routes through the proxy with the right control headers and preserves the node's own webhook auth header.
  • Fails cleanly when use_proxy is set without a configured proxy (delivery stays queued, error recorded, no webhook POST).

Deploy to activate (host side)

  1. Publish this as the next @relaycast/engine.
  2. In relaycast-cloud: bump the engine, set HTTP_PUSH_PROXY_URL / HTTP_PUSH_PROXY_SECRET, wire them into EngineConfig.httpPushProxy (adapter change staged), redeploy.
  3. Register the target node with delivery.use_proxy: true and the real url.

🤖 Generated with Claude Code

Review in cubic

Adds a per-node opt-in that routes an http_push webhook POST through a
deployment-configured forwarder instead of hitting the destination directly.
Motivation: some receivers sit behind Cloudflare bot protection that blocks the
engine's own network origin (Cloudflare Workers get a zone-level 403), so the
webhook can never be delivered from the Worker directly. Routing through a
non-Cloudflare proxy gets through.

- `httpDeliverySchema` gains `use_proxy?: boolean` (stored in the node's
  deliveryConfig; `url` stays the real destination).
- `EngineConfig.httpPushProxy?: { url, secret }` — the deployment's single
  forwarder, wired by the host adapter (env/secrets).
- `dispatchHttpPush`: when `use_proxy` is set, POST to the proxy URL with the
  real target in `X-Forward-To` and the shared secret in `X-Proxy-Auth`; the
  real `url` is still SSRF-checked. If `use_proxy` is set but no proxy is
  configured, the delivery fails with a clear error rather than silently going
  direct (which would just be blocked).

Tests: routes through the proxy with the right control headers + preserves the
node's own webhook auth header; and fails cleanly when use_proxy is set without
a configured proxy. 19/19 conformance tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@khaliqgant, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 58 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7a88d89e-9d52-4c8e-b299-1c2dd73883d1

📥 Commits

Reviewing files that changed from the base of the PR and between 2a59766 and 6399c6c.

📒 Files selected for processing (15)
  • README.md
  • openapi.yaml
  • packages/engine/src/__tests__/conformance/nodeDeliveryContracts.test.ts
  • packages/engine/src/adapters/node/index.ts
  • packages/engine/src/engine/httpPushDispatch.ts
  • packages/engine/src/engine/invocationCompletion.ts
  • packages/engine/src/engine/nodeContext.ts
  • packages/engine/src/engine/nodeDeliver.ts
  • packages/engine/src/routes/action.ts
  • packages/engine/src/routes/agent.ts
  • packages/engine/src/routes/deliveryRouting.ts
  • packages/engine/src/routes/fanout.ts
  • packages/engine/src/routes/reaction.ts
  • packages/engine/src/routes/receipt.ts
  • packages/sdk-typescript/src/types.ts
📝 Walkthrough

Walkthrough

Adds optional HTTP push egress proxy support: EngineConfig.httpPushProxy (url/secret), a use_proxy flag on http_push delivery config, proxy routing logic in dispatchHttpPush (forwarding headers, missing-proxy failure handling), test harness wiring, and conformance tests validating proxied and unconfigured-proxy behavior.

Changes

HTTP push egress proxy

Layer / File(s) Summary
Config and schema for proxy support
packages/engine/src/ports/index.ts, packages/engine/src/routes/node.ts
EngineConfig gains optional httpPushProxy: { url?, secret? }; httpDeliverySchema gains optional use_proxy boolean.
Proxy routing in dispatchHttpPush
packages/engine/src/routes/deliveryRouting.ts
When use_proxy is set, request targets the configured proxy URL with X-Forward-To/X-Proxy-Auth headers merged into buildHttpPushHeaders output; missing proxy config records a retryable failure.
Harness wiring and conformance tests
packages/engine/src/__tests__/conformance/harness.ts, packages/engine/src/__tests__/conformance/nodeDeliveryContracts.test.ts
makeNodeStack forwards httpPushProxy into runtime config; new tests verify proxied delivery headers and queued/failed state when no proxy is configured.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Node
  participant dispatchHttpPush
  participant HttpPushProxy
  participant Destination

  Node->>dispatchHttpPush: delivery with use_proxy=true
  alt httpPushProxy configured
    dispatchHttpPush->>HttpPushProxy: POST requestUrl with X-Forward-To, X-Proxy-Auth
    HttpPushProxy->>Destination: forward request
  else no proxy configured
    dispatchHttpPush->>dispatchHttpPush: record retryable failure, delivery stays queued
  end
Loading

Possibly related PRs

  • AgentWorkforce/relaycast#225: Both PRs modify dispatchHttpPush's header construction via buildHttpPushHeaders(...), with this PR extending it with proxy routing/auth headers.

Poem

A rabbit hops through headers new,
"X-Forward-To" and secrets too,
Proxying webhooks, safe and neat,
No leaking URLs down the street.
Hop, hop, dispatch — the tunnel's set,
My burrow's snug behind the net! 🐇🔀

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: optional egress proxy support for http_push deliveries.
Description check ✅ Passed The description is directly about the same proxy-routing change and its tests, so it matches the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/http-push-use-proxy

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for routing http_push node deliveries through an optional egress proxy (httpPushProxy) when use_proxy is enabled, allowing webhook requests to bypass network origin blocks. Feedback highlights a critical security vulnerability where case-insensitive header injection (e.g., using lowercase x-forward-to) could bypass SSRF checks. It is recommended to strip any user-supplied proxy control headers case-insensitively before merging.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

// recorded as a retryable dispatch error rather than rejecting uncaught.
const headers = await buildHttpPushHeaders(config, args.eventType, args.delivery.id, body, timestamp);
const response = await globalThis.fetch(url, {
const headers = { ...(await buildHttpPushHeaders(config, args.eventType, args.delivery.id, body, timestamp)), ...proxyHeaders };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

Security Vulnerability: Potential SSRF / Header Injection Bypass\n\nBecause JavaScript object keys are case-sensitive, if a user registers a node with static_headers containing lowercase versions of the proxy control headers (e.g., x-forward-to or x-proxy-auth), they will not be overwritten by the uppercase X-Forward-To and X-Proxy-Auth keys in proxyHeaders.\n\nWhen globalThis.fetch sends the request, both headers will be sent. Many proxy implementations (especially those in Node.js/Express/Hono) automatically lowercase incoming headers and may concatenate duplicates or select the first occurrence. This could allow an attacker to spoof the destination URL and bypass the engine's SSRF checks, routing requests to internal services.\n\nTo prevent this, we should case-insensitively remove any user-supplied proxy control headers from the base headers before merging proxyHeaders.

    const baseHeaders = await buildHttpPushHeaders(config, args.eventType, args.delivery.id, body, timestamp);\n    for (const key of Object.keys(baseHeaders)) {\n      const lower = key.toLowerCase();\n      if (lower === 'x-forward-to' || lower === 'x-proxy-auth') {\n        delete baseHeaders[key];\n      }\n    }\n    const headers = { ...baseHeaders, ...proxyHeaders };

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fa319c2832

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// proxy URL is operator-configured and trusted.
let requestUrl = url;
const proxyHeaders: Record<string, string> = {};
if (config.use_proxy === true) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor proxy for all http_push POST paths

When a node sets use_proxy because its receiver blocks the engine origin, this branch only changes durable message dispatch. The existing http_push ephemeral path still calls postEphemeralEventToHttpPushNode from nodeDeliver/nodeContext and fetches config.url directly, so reactions, read receipts, presence, and context updates for the same proxied node continue to bypass the proxy and fail in exactly the environment this option is for. Please pass the configured proxy through that shared path or otherwise make use_proxy apply to all http_push POSTs.

Useful? React with 👍 / 👎.

// Opt this node's webhook delivery into the deployment-configured egress proxy
// (EngineConfig.httpPushProxy). `url` stays the real destination; the engine
// forwards through the proxy at dispatch time. See dispatchHttpPush.
use_proxy: z.boolean().optional(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Document the new use_proxy wire field

This adds a new HTTP JSON field, but the public contract was not updated: openapi.yaml's HttpPushNodeDelivery schema and the TypeScript SDK HttpPushNodeDelivery type still describe only url/ack_mode/auth, and README examples omit the option. The repo's AGENTS.md explicitly requires updating README.md and openapi.yaml together when API behavior changes; without those updates, generated docs/clients and SDK users will not know how to opt into the proxy.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/engine/src/routes/deliveryRouting.ts`:
- Around line 257-265: In deliveryRouting.ts, the use_proxy path in the delivery
routing logic only validates proxyCfg.url and then conditionally skips
X-Proxy-Auth when proxyCfg.secret is missing. Update the proxy handling in the
same block that sets requestUrl and proxyHeaders so it fails fast when
httpPushProxy.secret is absent, records the retry/failure, and returns failed
instead of dispatching unauthenticated through the proxy. Reference the existing
use_proxy branch, proxyCfg, and proxyHeaders setup in the delivery routing flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 47b1f4b4-8408-4169-83e0-98a0b7e4dfca

📥 Commits

Reviewing files that changed from the base of the PR and between bd91867 and fa319c2.

📒 Files selected for processing (5)
  • packages/engine/src/__tests__/conformance/harness.ts
  • packages/engine/src/__tests__/conformance/nodeDeliveryContracts.test.ts
  • packages/engine/src/ports/index.ts
  • packages/engine/src/routes/deliveryRouting.ts
  • packages/engine/src/routes/node.ts

Comment thread packages/engine/src/routes/deliveryRouting.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 issues found across 5 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/engine/src/ports/index.ts">

<violation number="1" location="packages/engine/src/ports/index.ts:106">
P2: `httpPushProxy.secret` should be required whenever a proxy is configured. Current type allows `{ url }`, causing proxied deliveries to be sent without `X-Proxy-Auth` despite the proxy contract requiring authenticated forwarding.</violation>
</file>

<file name="packages/engine/src/routes/node.ts">

<violation number="1" location="packages/engine/src/routes/node.ts:52">
P2: The new `use_proxy` wire field is accepted by the API but the public contract (`openapi.yaml`) and SDK types have not been updated. Generated clients and documentation consumers won't know this option exists.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/engine/src/routes/deliveryRouting.ts
* rejects Cloudflare Workers). Hosted adapters wire this from their secrets.
*/
httpPushProxy?: {
url?: string;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: httpPushProxy.secret should be required whenever a proxy is configured. Current type allows { url }, causing proxied deliveries to be sent without X-Proxy-Auth despite the proxy contract requiring authenticated forwarding.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/engine/src/ports/index.ts, line 106:

<comment>`httpPushProxy.secret` should be required whenever a proxy is configured. Current type allows `{ url }`, causing proxied deliveries to be sent without `X-Proxy-Auth` despite the proxy contract requiring authenticated forwarding.</comment>

<file context>
@@ -93,6 +93,19 @@ export interface EngineConfig {
+   * rejects Cloudflare Workers). Hosted adapters wire this from their secrets.
+   */
+  httpPushProxy?: {
+    url?: string;
+    secret?: string;
+  };
</file context>

Comment thread packages/engine/src/routes/deliveryRouting.ts Outdated
Comment thread packages/engine/src/routes/deliveryRouting.ts Outdated
// Opt this node's webhook delivery into the deployment-configured egress proxy
// (EngineConfig.httpPushProxy). `url` stays the real destination; the engine
// forwards through the proxy at dispatch time. See dispatchHttpPush.
use_proxy: z.boolean().optional(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The new use_proxy wire field is accepted by the API but the public contract (openapi.yaml) and SDK types have not been updated. Generated clients and documentation consumers won't know this option exists.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/engine/src/routes/node.ts, line 52:

<comment>The new `use_proxy` wire field is accepted by the API but the public contract (`openapi.yaml`) and SDK types have not been updated. Generated clients and documentation consumers won't know this option exists.</comment>

<file context>
@@ -46,6 +46,10 @@ const httpDeliverySchema = z.object({
+  // Opt this node's webhook delivery into the deployment-configured egress proxy
+  // (EngineConfig.httpPushProxy). `url` stays the real destination; the engine
+  // forwards through the proxy at dispatch time. See dispatchHttpPush.
+  use_proxy: z.boolean().optional(),
 });
 
</file context>

@agent-relay-code

Copy link
Copy Markdown
Contributor

PR #227 Review — feat(engine): optional egress proxy for http_push delivery (use_proxy)

Summary

The PR adds an opt-in egress proxy for http_push node webhook delivery. A node registers with delivery.use_proxy: true; at dispatch time, if the deployment sets EngineConfig.httpPushProxy, the POST goes to the proxy url with the real target in X-Forward-To and the proxy secret in X-Proxy-Auth. If use_proxy is set but no proxy is configured, the delivery fails-closed (stays queued, records an error, does not POST the direct URL).

I traced the change across all five changed files plus callers, config, storage, and docs. The change is well-scoped and safe.

Verification (ran the way CI does)

  • npx turbo build --filter=@relaycast/enginepass (tsc typecheck of src green; note engine tsconfig excludes tests, so tsc build isn't affected by the test files' implicit-any authed params).
  • npx turbo test --filter=@relaycast/engine207 passed / 207, including both new tests (routes http_push through the configured egress proxy… and fails an http_push use_proxy delivery when no proxy is configured).
  • npx turbo lint --filter=@relaycast/enginepass.
  • No package depends on @relaycast/engine (verified), so this diff cannot break a downstream package.
  • Full-repo npx turbo test/lint is what CI runs; @relaycast/mcp:build was OOM-Killed (exit 137) in this sandbox — a memory limit, not a code fault. mcp does not depend on engine and does not reference use_proxy, so it is unrelated to this PR.

Findings

  • Fail-closed behavior is correct. When use_proxy is set but no proxy is configured, dispatchHttpPush calls recordHttpPushRetry (leaves status queued, records use_proxy set but no http_push proxy configured, schedules a retry) and returns 'failed'. It does not silently fall back to POSTing the direct url. Good — no fail-open regression.
  • SSRF posture preserved. The real url is still isSafeExternalUrl-checked before the proxy branch; only the operator-configured, trusted proxy URL bypasses that check (documented in the code comment). No safety default was weakened.
  • Header merge order is intentional. proxyHeaders are spread after buildHttpPushHeaders(...), so X-Forward-To/X-Proxy-Auth are additive control headers and the node's own webhook auth headers are preserved for the proxy to forward — matching the happy-path test's assertions.
  • Config/storage flow is sound. use_proxy is added to httpDeliverySchema in node.ts, flows through normalizeDelivery into stored deliveryConfig, and is read as config.use_proxy === true in dispatch.

Auto-applied (mechanical, non-semantic)

  • openapi.yaml — added the use_proxy boolean property (with description) to the HttpPushNodeDelivery schema (openapi.yaml:652). Purely additive documentation to keep the spec aligned with behavior per repo rules; validated that the YAML still parses and the field is present. No runtime code changed.

Addressed comments

  • No bot or human review comments were present in .workforce/context.json (no reviews/threads supplied), so there are none to reconcile against the current checkout.

Advisory Notes

  • Unbounded retry (pre-existing, not introduced here): the use_proxy-without-proxy failure joins the existing http_push sweep, which retries on a 30s backoff with no max-attempts cap — identical to how existing http_push failures (bad URL, HTTP 5xx) already behave. This is not a regression introduced by this PR, so I left it unchanged. If the team wants a dead-letter/attempt cap for chronically misconfigured use_proxy nodes, that's a separate human-decided change (schema + sweep logic) outside this PR's scope.
  • Test typing nit (no change made): the two new tests use inline authed = (path, key, body) => … with implicit any params, unlike the strictly-typed helpers elsewhere in the file. It's harmless (engine tsconfig excludes tests; vitest transpiles without type errors; eslint passes), so I did not touch test code — adding/altering tests is a human decision.

The PR builds, lints, and tests green for the only package it affects, is correctly fail-closed, and introduces no safety regressions. The one non-passing item in the full run (mcp build OOM) is a sandbox resource limit unrelated to this diff, not a real CI failure I can attribute to the change — I cannot assert the full CI matrix is green from here, so I'm not declaring the PR ready.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
openapi.yaml (1)

652-658: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider adding default: false for consistency.

Other optional fields in this schema (e.g. ack_mode) declare an explicit default. Adding default: false here would make the contract clearer for API consumers, even though the backend already treats undefined as falsy.

📝 Optional refactor
         use_proxy:
           type: boolean
+          default: false
           description: >
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openapi.yaml` around lines 652 - 658, The `use_proxy` schema field is missing
an explicit default, unlike nearby optional fields such as `ack_mode`. Update
the `use_proxy` definition in the OpenAPI schema to declare `default: false` so
the API contract is consistent and clearer for consumers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@openapi.yaml`:
- Around line 652-658: The node delivery contract in the README is missing the
new delivery.use_proxy behavior described in openapi.yaml. Update the README.md
section that documents node delivery to include delivery.use_proxy, explain that
it routes webhook delivery through the deployment-configured egress proxy
instead of posting directly to url, and note the queued/error behavior when no
proxy is configured. Keep the wording aligned with the existing delivery
contract docs and match the semantics defined around delivery.use_proxy in the
schema.

---

Nitpick comments:
In `@openapi.yaml`:
- Around line 652-658: The `use_proxy` schema field is missing an explicit
default, unlike nearby optional fields such as `ack_mode`. Update the
`use_proxy` definition in the OpenAPI schema to declare `default: false` so the
API contract is consistent and clearer for consumers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ec9d8c6b-e443-4560-b53c-655a8d0d7b0e

📥 Commits

Reviewing files that changed from the base of the PR and between fa319c2 and 2a59766.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (1)
  • openapi.yaml

Comment thread openapi.yaml Outdated
…ephemeral path, docs

Addresses PR review (Gemini/cubic/CodeRabbit/Codex):

- SSRF/header-injection (HIGH): operator static_headers could inject case-variant
  `x-forward-to`/`x-proxy-auth` and repoint the proxy at an internal address. Add
  both to RELAYCAST_CONTROLLED_HEADERS so they can never be set via static_headers
  (case-insensitive), for durable and ephemeral paths. Test asserts a malicious
  `x-forward-to` static header is dropped.
- Require proxy secret: `use_proxy` now fails unless BOTH proxy url and secret are
  configured (no unauthenticated proxy POST). Extracted a shared
  `resolveHttpPushProxy` helper; dispatchHttpPush uses it. Test covers the
  url-without-secret case.
- Ephemeral path: `postEphemeralEventToHttpPushNode` now honors `use_proxy` (via the
  shared helper) and also fixes its own `redirect:'error'` → `'manual'` (same Workers
  bug). `httpPushProxy` threaded through NodeDeliverDeps/NodeContextDeps and all
  ephemeral deps construction sites.
- Docs: document `use_proxy` in openapi.yaml (HttpPushNodeDelivery), the TS SDK type
  (`useProxy`), and README, per AGENTS.md.

20/20 conformance tests pass; engine + SDK tsc clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@khaliqgant

Copy link
Copy Markdown
Member Author

Thanks all — addressed the review in 6399c6c (rebased on top of the reviewer-bot fixes in 2a59766). Summary:

SSRF / header-injection (Gemini HIGH, cubic P1) — Fixed at the root: added x-forward-to and x-proxy-auth to RELAYCAST_CONTROLLED_HEADERS, so operator static_headers can never set them (the check is case-insensitive via name.toLowerCase()). This covers both the durable and ephemeral paths, so a case-variant x-forward-to can't survive into buildHttpPushHeaders output and can't override the engine-set control header. Added a test that registers a malicious x-forward-to: http://169.254.169.254/ static header and asserts it's dropped and never sent.

Require proxy secret (CodeRabbit Major, cubic P2)use_proxy now fails unless both proxy url and secret are configured; X-Proxy-Auth is always sent (never silently omitted). Centralized in a shared resolveHttpPushProxy helper. Added a test for the url-without-secret case.

Honor proxy for all http_push POSTs (Codex P2)postEphemeralEventToHttpPushNode now honors use_proxy via the same helper, so reactions / read receipts / presence / context updates for a proxied node also route through the proxy. Threaded httpPushProxy through NodeDeliverDeps/NodeContextDeps and every ephemeral deps-construction site. Also fixed that path's own redirect: 'error''manual' (it had the same Workers bug as the durable path).

Document the wire field (Codex P1) — Added use_proxy to openapi.yaml (HttpPushNodeDelivery), the TS SDK type (useProxy), and README, per AGENTS.md. (Deduped with the bot's openapi addition.)

20/20 conformance tests pass; engine + SDK tsc clean.

@khaliqgant khaliqgant merged commit 30ebfa9 into main Jul 1, 2026
5 checks passed
@khaliqgant khaliqgant deleted the feat/http-push-use-proxy branch July 1, 2026 06:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant