Skip to content

feat: authenticate cast RPC calls with x-internal-service-secret header - #377

Merged
codyborn merged 2 commits into
mainfrom
add-rpc-header-secret
Aug 21, 2026
Merged

feat: authenticate cast RPC calls with x-internal-service-secret header#377
codyborn merged 2 commits into
mainfrom
add-rpc-header-secret

Conversation

@codyborn

@codyborn codyborn commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds an optional RPC_HEADER_SECRET env var to scripts/deploy-v3-multichain.sh and scripts/deploy-quoter-multichain.sh, sent as the x-internal-service-secret header on every cast RPC preflight call (chain-id, code, call, balance) via cast's native --rpc-headers flag. Omitted when unset, so public-RPC / local-dev usage is unchanged. Mirrors Uniswap/uniswapx-parameterization-api#442, adapted to this repo's Foundry tooling.
  • Fixes .github/workflows/test-integration.yml, which started failing with 401 Unauthorized: Missing required header x-internal-service-secret once the RPC gateway began enforcing the header on every request. forge test's forking (vm.createSelectFork) builds its RPC provider straight from the URL string with no way to attach custom headers — confirmed by reading the Foundry source (crates/evm/core/src/fork/multi.rs) — so cast's native --rpc-headers doesn't help here; forking never goes through cast.

The fix: a local header-injecting proxy

  • scripts/rpc-header-proxy.py — a small stdlib-only local HTTP proxy that adds x-internal-service-secret to every forwarded JSON-RPC request.
  • scripts/with-rpc-header-proxy.sh — starts the proxy and points FOUNDRY_RPC_URL at 127.0.0.1 for a wrapped command, so it works regardless of which Foundry code path makes the request. No-ops (runs the command against UPSTREAM_RPC_URL directly) when RPC_HEADER_SECRET is unset.
  • test-integration.yml now runs scripts/with-rpc-header-proxy.sh forge test -vvv with UPSTREAM_RPC_URL: secrets.RPC_URL and RPC_HEADER_SECRET: secrets.RPC_HEADER_SECRET.
  • The two deploy scripts target public per-chain RPCs for forge script broadcasts, which don't need the header, so they're left as cast-only; their comments point at with-rpc-header-proxy.sh for anyone who does need it there.

Testing

  • bash -n on all four scripts.
  • Verified the empty-vs-set RPC_HEADERS_ARGS array expansion under set -u.
  • End-to-end proof against a mock gateway that 401s without the header: cast chain-id and a real forge test with vm.createSelectFork both fail with a 401 when hitting the mock directly, and both succeed when routed through with-rpc-header-proxy.sh.

🤖 Generated with Claude Code

codyborn and others added 2 commits August 14, 2026 13:53
Adds an optional RPC_HEADER_SECRET env var to the multichain deploy scripts,
sent as the x-internal-service-secret header on cast preflight calls so
they can be authenticated against internal RPC providers. forge script
(simulation/broadcast) can't carry the header — Foundry builds its fork/
broadcast RPC provider directly from the URL with no header support.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
forge test's vm.createSelectFork builds its RPC provider straight from the
URL with no way to attach custom headers, so the integration suite started
401ing once the RPC gateway began requiring x-internal-service-secret on
every request (cast's native --rpc-headers doesn't help here since forking
never goes through cast).

Adds scripts/rpc-header-proxy.py, a small local HTTP proxy that injects the
header before forwarding to the real endpoint, and
scripts/with-rpc-header-proxy.sh, which starts it and points FOUNDRY_RPC_URL
at 127.0.0.1 for the wrapped command. test-integration.yml now runs forge
test through the wrapper. Verified end-to-end against a mock gateway that
401s without the header: `forge test` passes through the proxy, fails
without it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@codyborn
codyborn requested a review from alanhwu August 15, 2026 12:37
@codyborn
codyborn merged commit fd60225 into main Aug 21, 2026
5 checks passed
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.

2 participants