Skip to content

feat(broker): add per-service extra_passthrough_headers (closes #104)#107

Closed
MackDing wants to merge 1 commit intoInfisical:mainfrom
Claws-ZH:feat-per-service-passthrough-headers
Closed

feat(broker): add per-service extra_passthrough_headers (closes #104)#107
MackDing wants to merge 1 commit intoInfisical:mainfrom
Claws-ZH:feat-per-service-passthrough-headers

Conversation

@MackDing
Copy link
Copy Markdown

Closes #104

Summary

Adds a per-service extra_passthrough_headers field that extends the core PassthroughHeaders allowlist with provider-specific headers that must be forwarded unchanged. Without it, credentialed requests to providers like Anthropic (which requires anthropic-version on every /v1/messages call) fail through agent-vault even though credential injection works — the header is silently dropped in the agent → upstream hop.

Implements the suggested fix from the issue, with validation guardrails so the new surface can't reopen the exact paths PassthroughHeaders was designed to close (Authorization, broker-scoped headers, hop-by-hop).

Example

services:
  - host: api.anthropic.com
    auth:
      type: api-key
      key: ANTHROPIC_API_KEY
      header: x-api-key
    extra_passthrough_headers:
      - anthropic-version
      - anthropic-beta

The built-in catalog now pre-fills these for the providers whose public API docs require a non-standard header as part of the protocol:

Template Default extra_passthrough_headers Source
anthropic anthropic-version, anthropic-beta https://docs.anthropic.com/en/api/versioning
openai OpenAI-Organization, OpenAI-Project, OpenAI-Beta https://platform.openai.com/docs/api-reference/authentication
stripe Stripe-Version https://docs.stripe.com/api/versioning
github X-GitHub-Api-Version https://docs.github.com/en/rest/overview/api-versions
notion Notion-Version (required on every request) https://developers.notion.com/reference/versioning
datadog DD-APPLICATION-KEY https://docs.datadoghq.com/api/latest/authentication/
supabase apikey, Prefer https://supabase.com/docs/guides/api

Others in the catalog (Slack, Twilio, SendGrid, Cloudflare, PagerDuty, Linear, Jira, Vercel, Resend, Postmark, Sentry, Shopify, AWS S3) were left untouched — their APIs work with just the auth header.

Context: I track 200+ AI/API gateways at awesome-ai-api and this header-stripping behavior came up as the single blocker for routing Anthropic traffic through a credential broker cleanly. Grateful for how sharply the issue was written — it made the patch straightforward.

Design

Change Why
broker.Service.ExtraPassthroughHeaders []string (new field, omitempty) Opt-in, preserves byte-for-byte JSON compatibility with existing stored services
broker.Validate denylist + RFC 7230 token check + dedup + passthrough-auth rejection Config-time validation so the runtime hot path can trust the data without re-checking
brokercore.InjectResult.ExtraPassthroughHeaders Surfaces the list through the credential-resolution boundary without changing any function signature
brokercore.ApplyInjection copies extras before injection overlay Injection still wins on collision — same invariant PassthroughHeaders provides for Authorization
Both ingresses (/proxy + MITM) use shared ApplyInjection Behaviour stays in lockstep by construction
Catalog presets Documented provider requirements made defaults an obvious win

No public function signatures change. Zero breaking changes for existing deployments.

Validation rules (enforced at config time)

Rejected with a descriptive error:

  • Authorization, Proxy-Authorization, X-Vault — credential / broker-scoped
  • Connection, Keep-Alive, TE, Trailer, Transfer-Encoding, Upgrade, Proxy-Authenticate — hop-by-hop per RFC 7230 §6.1
  • Names outside the RFC 7230 token character set (spaces, colons, control chars, non-ASCII)
  • Duplicate header names (case-insensitive)
  • Any extra_passthrough_headers on a passthrough-auth service (denylist already forwards everything, so the allowlist extension is a misconfiguration)

Matching against http.Header is case-insensitive (uses http.CanonicalHeaderKey), so anthropic-version in config correctly matches Anthropic-Version in a client request.

Tests

All new tests green; existing tests unchanged.

internal/broker

  • TestValidateExtraPassthroughHeadersAccepted — happy path
  • TestValidateExtraPassthroughHeadersRejectsDenylist (8 sub-cases) — each forbidden name
  • TestValidateExtraPassthroughHeadersRejectsInvalidName (5 sub-cases) — empty / space / colon / control / non-ASCII
  • TestValidateExtraPassthroughHeadersRejectsDuplicate — case-insensitive dedup
  • TestValidateExtraPassthroughHeadersRejectedOnPassthroughAuth — misconfiguration guard
  • TestServiceJSONRoundTripPreservesExtraPassthroughHeaders — end-to-end store path
  • TestServiceJSONOmitsEmptyExtraPassthroughHeaders — back-compat (omitempty)

internal/brokercore

  • TestApplyInjection_ForwardsExtraPassthroughHeaders — headers reach upstream, Content-Type still works, client Authorization doesn't leak
  • TestApplyInjection_ExtraPassthroughHeaders_AreCaseInsensitive — config anthropic-version matches client Anthropic-Version
  • TestApplyInjection_ExtraPassthrough_IgnoredForPassthroughService — defense in depth
  • TestApplyInjection_ExtraPassthrough_InjectionStillWins — auth config wins over client header

Docs

Added an Extra passthrough headers section to docs/learn/services.mdx explaining the problem, the YAML syntax, the validation guardrails, and when not to use it.

Repro check

Before:

{"type":"error","error":{"type":"invalid_request_error","message":"anthropic-version: header is required"}}

After (with the config above):

{"id":"msg_...","type":"message","role":"assistant", ...}

Happy to tighten naming, split into smaller commits, or adjust the catalog defaults based on how conservative you want to be about pre-filling them. Thanks for the clean issue!

Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

…ical#104)

Adds an opt-in per-service field that extends the core PassthroughHeaders
allowlist with provider-specific headers that must be forwarded
unchanged. Previously these were silently dropped by brokercore's
allowlist, causing requests that worked via direct provider access to
fail through agent-vault — most notably Anthropic's mandatory
`anthropic-version` header on every /v1/messages call.

## Design

- `broker.Service.ExtraPassthroughHeaders []string` — opt-in extension,
  omitempty so existing persisted services round-trip unchanged.
- `broker.Validate` enforces a denylist at config time: no Authorization,
  Proxy-Authorization, X-Vault, or hop-by-hop names, plus RFC 7230 token
  validation and duplicate detection. Rejected on `passthrough` auth
  since the denylist model already forwards every header.
- `brokercore.InjectResult.ExtraPassthroughHeaders` surfaces the list
  through the credential-resolution path without new parameters.
- `brokercore.ApplyInjection` copies the extra headers between the
  core allowlist and the injected credential overlay, so injection
  still wins on collision (same guarantee PassthroughHeaders provides
  for Authorization).
- Both ingresses (explicit /proxy + HTTPS_PROXY MITM) share the same
  code path, so behaviour stays in lockstep by construction.
- Catalog presets pre-fill recommended defaults for Anthropic, OpenAI,
  Stripe, GitHub, Notion, Datadog, and Supabase based on each
  provider's public API docs.

## Testing

- `broker`: validate-accepts, validate-rejects-denylist (8 names),
  invalid-name charset (5 cases), duplicate case-insensitive,
  rejected-on-passthrough-auth, plus JSON round-trip preservation and
  omitempty confirmation for back-compat.
- `brokercore`: ApplyInjection forwards the extra headers, case-
  insensitive matching, injection-still-wins on collision, and
  no-op-on-passthrough-service as defense in depth alongside
  validation.

Data-driven: the SuggestedCredentialKey list in the catalog was
cross-checked against public API documentation for each of the 20
providers. The 7 provider entries that gained defaults are the ones
whose documentation explicitly requires a non-standard request header
as part of the protocol (not an optional one). Others were left alone.

Closes Infisical#104
@dangtony98
Copy link
Copy Markdown
Contributor

Closing as decided to go with a different direction closer to traditional MITM architecture with #133

@dangtony98 dangtony98 closed this Apr 26, 2026
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.

Add per-service custom-header passthrough for provider APIs that require non-standard headers (e.g., anthropic-version)

2 participants