Skip to content

fix(passthrough): OTLP route/identity attribution, route on rejected events, header_key 401 names its header - #983

Merged
jarvis9443 merged 2 commits into
mainfrom
feat/passthrough-obs-followup
Aug 17, 2026
Merged

fix(passthrough): OTLP route/identity attribution, route on rejected events, header_key 401 names its header#983
jarvis9443 merged 2 commits into
mainfrom
feat/passthrough-obs-followup

Conversation

@jarvis9443

@jarvis9443 jarvis9443 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Observability and error-surface follow-ups to #982, found by running the forward-proxy acceptance harness (a real TLS-terminating-device simulation relaying live traffic through a standalone gateway with an OTLP exporter attached):

  • OTLP spans now carry passthrough attribution. The usage event has carried passthrough_route_name / client_identity since feat(passthrough): explicit PassthroughRoute resources replace the implicit provider tunnel #982, but the OTLP sink builds span attributes by hand and had no passthrough branch — a relayed request exported as a bare chat.completions span with the route and end-user identity recorded nowhere, and gen_ai.operation.name: chat misclassified relays as model inference. Passthrough events now export as passthrough <route> with aisix.passthrough.route_name; aisix.client_identity exports unconditionally when set, so any future handler that populates it inherits the attribute. The JSON-serializing sinks (Datadog, SLS, object store) already carried both fields and are unchanged.

  • Rejected requests keep their route. The passthrough error path emitted usage events through the shared emit_error_usage_event, which cannot set handler-specific fields — a 401/403/422 event lost passthrough_route_name even though the route had matched. The helper is split into build_error_usage_event + emit_prepared_usage_event (the 11 other call sites are untouched), and the passthrough arm attributes the route before emitting.

  • header_key 401s name the right header. A missing gateway key on a header_key route answered "missing or malformed Authorization header" — but on those routes Authorization deliberately carries the caller's own upstream credential, so the message pointed at exactly the wrong header. New MissingRouteAuthHeader variant names the route's configured header.

  • The strict schema rejects cross-mode auth companions. auth_header_name outside header_key and anonymous_key_id outside anonymous were accepted-and-ignored; two new coupling branches reject them (covering the absent-auth_mode default), with the CP enforcing the identical rule on its side. source_cidrs stays mode-agnostic — the gateway honors it as an extra IP allowlist on every mode.

Tests: two OTLP sink unit tests, a coupling test for the cross-mode rejections (passthrough span shape + protocol-ungated identity), and the header_key e2e scenario now asserts the 401 names the configured header and not Authorization (fails on the pre-fix message).

Verified live on the acceptance harness: the relayed span exports passthrough copilot-auxiliary + both attributes, and the 401 names x-aisix-api-key.

Refs api7/AISIX-Cloud#1312.

Summary by CodeRabbit

  • New Features

    • Added support for custom gateway-key authentication headers on passthrough routes.
    • Added route names and client identity details to exported telemetry.
    • Improved passthrough operation and span naming for clearer observability.
  • Bug Fixes

    • Missing route authentication now returns a clear 401 error identifying the required header.
    • Authentication settings are now validated according to the selected mode.
    • Error telemetry now includes the matched passthrough route.

…events, header_key 401 names its header

A passthrough relay exported as a bare chat.completions span: the OTLP
sink builds attributes by hand and had no passthrough branch, so the
route name and the identity_header-extracted end-user identity never
reached the trace backend (the serde-serializing sinks already carried
both). Spans now export as 'passthrough <route>' with
aisix.passthrough.route_name, and aisix.client_identity exports
whenever set, unconditioned on the protocol.

The error path lost the same attribution: emit_error_usage_event cannot
set handler-specific fields, so a 401/403 event dropped the matched
route. Split into build_error_usage_event + emit_prepared_usage_event
(other callers untouched) and attribute the route in the passthrough
arm.

A header_key route's missing-key 401 said 'missing or malformed
Authorization header' — the one header a BYO integrator must NOT touch,
since it carries the caller's own upstream credential. New
MissingRouteAuthHeader variant names the configured header instead.
@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e9790a80-8916-4480-bf1b-07e82effba07

📥 Commits

Reviewing files that changed from the base of the PR and between d8d2619 and 36d81e3.

📒 Files selected for processing (8)
  • crates/aisix-core/src/models/passthrough_route.rs
  • crates/aisix-obs/src/otlp_http_sink.rs
  • crates/aisix-proxy/src/attempt.rs
  • crates/aisix-proxy/src/error.rs
  • crates/aisix-proxy/src/passthrough_route.rs
  • crates/aisix-proxy/src/usage_attr.rs
  • schemas/resources/passthrough_route.schema.json
  • tests/e2e/src/cases/passthrough-route-e2e.test.ts

Included review availability: 0 reviews are currently available. Based on recent review activity, included reviews refill at 1 per hour.


📝 Walkthrough

Walkthrough

Passthrough routes now enforce authentication-field combinations, report missing configured headers with specific 401 errors, attach route names to error events, and export passthrough route and client identity data in OTLP spans.

Changes

Passthrough authentication and telemetry

Layer / File(s) Summary
Route authentication validation
schemas/resources/passthrough_route.schema.json, crates/aisix-core/src/models/passthrough_route.rs
Schema and model validation restrict auth_header_name and anonymous_key_id to their matching authentication modes. Omitted auth_mode uses gateway-key validation.
Route authentication errors and usage events
crates/aisix-proxy/src/error.rs, crates/aisix-proxy/src/attempt.rs, crates/aisix-proxy/src/passthrough_route.rs, crates/aisix-proxy/src/usage_attr.rs, tests/e2e/src/cases/passthrough-route-e2e.test.ts
Missing route headers return MissingRouteAuthHeader with HTTP 401 and invalid_api_key. Error usage events receive the matched route name before emission.
Passthrough OTLP observability
crates/aisix-obs/src/otlp_http_sink.rs
Passthrough spans use the passthrough operation, include route-based names and route attributes, and export capped client identity values.

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

Merge Risk: ⚪ Minimal · up to 36d81

The change improves passthrough attribution, rejected-request route reporting, header-specific 401 messages, and auth-schema validation without any identified current-head merge-blocking risk; it is merge-ready after normal checks and review.

Possibly related PRs

  • api7/aisix#982: Introduces the explicit PassthroughRoute functionality extended by this PR.
  • api7/aisix#953: Shares passthrough request handling and usage-event changes.
  • api7/aisix#931: Shares passthrough telemetry and usage-event handling changes.

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
E2e Test Quality Review ⚠️ Warning The new E2E test checks only the 401 body; OTLP span changes and rejected-event route attribution have only synthetic unit coverage, so the route-to-usage-event-to-exporter flow is unverified. Add a real passthrough E2E with a mock OTLP exporter. Assert successful and rejected requests produce the expected span name, route, client identity, and error-event attribution.
Security Check ❓ Inconclusive The checked commit diff contains only two schema-related files, unlike the supplied summary; the implementation changes described in the summary are absent. Provide the correct pull-request base and head diff, or confirm that only the two schema additions are in scope.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main observability and header-key authentication changes in the pull request.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/passthrough-obs-followup

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

@nic-6443
nic-6443 requested a lite review from Copilot August 17, 2026 17:38

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

A companion outside its auth mode (auth_header_name off header_key,
anonymous_key_id off anonymous) is never consulted at runtime — a row
carrying one was accepted-and-ignored, the configuration shape that
saves fine and never fires. Two coupling branches now reject the
leftovers (handling the absent-auth_mode = gateway_key default); the CP
enforces the identical rule on create and patch. source_cidrs stays
mode-agnostic on purpose: it is an extra IP allowlist on every mode.
@jarvis9443

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@jarvis9443
jarvis9443 merged commit 952ba5e into main Aug 17, 2026
14 checks passed
@jarvis9443
jarvis9443 deleted the feat/passthrough-obs-followup branch August 17, 2026 19:26
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