Skip to content

Add HttpClient tracer header filter - #6697

Merged
tim-smart merged 3 commits into
Effect-TS:mainfrom
schickling-assistant:schickling-assistant/2026-07-28-httpclient-header-filter
Jul 30, 2026
Merged

Add HttpClient tracer header filter#6697
tim-smart merged 3 commits into
Effect-TS:mainfrom
schickling-assistant:schickling-assistant/2026-07-28-httpclient-header-filter

Conversation

@schickling-assistant

@schickling-assistant schickling-assistant commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Why

HttpClient currently emits every request and response header as a span attribute. Value redaction is configurable independently, but there is no way to control which header attributes are included.

Closes #6363.

What

Add HttpClient.TracerHeaderFilter, a context reference that filters request and response header span attributes by normalized header name.

How

The predicate defaults to constTrue, preserving current behavior. Both header attribute loops read the same predicate without mutating transmitted or received headers.

Tests cover unchanged default inclusion and selective suppression on both request and response paths.

Validation

  • pnpm lint-fix
  • pnpm --filter effect test --run test/unstable/http/HttpClient.test.ts
  • pnpm test-types packages/effect/typetest/unstable/http/HttpClient.tst.ts
  • pnpm check
Posted on behalf of @schickling
field value
agent_name co2-flame
agent_session_id 133b5d2a-3d5d-4f52-8f55-f12a834f6b13
agent_tool Codex CLI
agent_tool_version 0.145.0
agent_runtime Codex CLI 0.145.0
agent_model unknown
runtime_profile /nix/store/ph8rlhdj25mg71v81jsfzy6dq4xpcs9m-coding-agent-runtime-profile/share/coding-agents/profile.json
skills_manifest /nix/store/lsykz8x5481xrpbgk280xh3pypk1c5jy-agent-skills-corpus/share/agent-skills/manifest.json
worktree effect/schickling-assistant/2026-07-28-httpclient-header-filter
machine dev3
tooling_profile dotfiles@3649b53

Summary by CodeRabbit

  • New Features
    • Added configurable filtering for HTTP client request and response headers recorded as tracing span attributes.
    • Headers can now be selectively included while sensitive or unwanted headers remain excluded.
    • Existing behavior continues to record all headers by default.

@github-project-automation github-project-automation Bot moved this to Discussion Ongoing in PR Backlog Jul 28, 2026
@changeset-bot

changeset-bot Bot commented Jul 28, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: dd259e8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 30 packages
Name Type
effect Patch
@effect/opentelemetry Patch
@effect/platform-browser Patch
@effect/platform-bun Patch
@effect/platform-deno Patch
@effect/platform-node-shared Patch
@effect/platform-node Patch
@effect/vitest Patch
@effect/ai-anthropic Patch
@effect/ai-openai-compat Patch
@effect/ai-openai Patch
@effect/ai-openrouter Patch
@effect/atom-react Patch
@effect/atom-solid Patch
@effect/atom-vue Patch
@effect/sql-clickhouse Patch
@effect/sql-d1 Patch
@effect/sql-libsql Patch
@effect/sql-mssql Patch
@effect/sql-mysql2 Patch
@effect/sql-pg Patch
@effect/sql-pglite Patch
@effect/sql-sqlite-bun Patch
@effect/sql-sqlite-do Patch
@effect/sql-sqlite-node Patch
@effect/sql-sqlite-react-native Patch
@effect/sql-sqlite-wasm Patch
@effect/docgen Patch
@effect/doctest Patch
@effect/openapi-generator Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 586d4f42-7ae3-4514-8a5d-8696e9fee7d5

📥 Commits

Reviewing files that changed from the base of the PR and between a149f89 and 3f4de5a.

📒 Files selected for processing (3)
  • .changeset/add-http-client-tracer-header-filter.md
  • packages/effect/src/unstable/http/HttpClient.ts
  • packages/effect/test/unstable/http/HttpClient.test.ts

📝 Walkthrough

Walkthrough

HttpClient tracing now exposes a context-configurable header predicate. Request and response span attributes are emitted only for matching headers, while the default predicate preserves existing behavior. Tests cover default recording and suffix-based filtering.

Changes

HTTP client tracing

Layer / File(s) Summary
Header filter contract and instrumentation
packages/effect/src/unstable/http/HttpClient.ts
Adds the exported TracerHeaderFilter reference and applies it when recording request and response header span attributes.
Tracing behavior validation and release metadata
packages/effect/test/unstable/http/HttpClient.test.ts, .changeset/add-http-client-tracer-header-filter.md
Tests default and filtered header attributes and records the patch release change.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant FiberContext
  participant HttpClient
  participant Tracer
  FiberContext->>HttpClient: Provide TracerHeaderFilter
  HttpClient->>Tracer: Record matching request headers
  HttpClient->>Tracer: Record matching response headers
Loading

Suggested reviewers: tim-smart

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Adds a configurable TracerHeaderFilter predicate, defaults to constTrue, and filters both request and response header span attributes as requested.
Out of Scope Changes check ✅ Passed Changes stay focused on header-span filtering and tests, with only a release changeset added; no unrelated code paths appear introduced.

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

@effect-slopcop effect-slopcop Bot added 4.0 enhancement New feature or request labels Jul 28, 2026
@schickling-assistant

Copy link
Copy Markdown
Contributor Author

Local verification is green:

  • pnpm lint-fix
  • pnpm --filter effect test --run test/unstable/http/HttpClient.test.ts — 19/19 passed
  • pnpm test-types packages/effect/typetest/unstable/http/HttpClient.tst.ts — passed on TypeScript 5.9.3 and 6.0.3
  • pnpm check

Remote CI is pending Effect-TS maintainer workflow approval for this first-time fork contribution; it is not reporting a test failure.

This implements the Context.Reference predicate design proposed in #6363. The predicate defaults to constTrue, so existing behavior remains unchanged unless a consumer explicitly provides a filter.

Posted on behalf of @schickling
field value
agent_name co2-flame
agent_session_id 133b5d2a-3d5d-4f52-8f55-f12a834f6b13
agent_tool Codex CLI
agent_tool_version 0.145.0
agent_runtime Codex CLI 0.145.0
agent_model unknown
runtime_profile /nix/store/ph8rlhdj25mg71v81jsfzy6dq4xpcs9m-coding-agent-runtime-profile/share/coding-agents/profile.json
skills_manifest /nix/store/lsykz8x5481xrpbgk280xh3pypk1c5jy-agent-skills-corpus/share/agent-skills/manifest.json
worktree effect/schickling-assistant/2026-07-28-httpclient-header-filter
machine dev3
tooling_profile dotfiles@3649b53

@schickling-assistant
schickling-assistant marked this pull request as ready for review July 28, 2026 09:29
schickling-assistant added a commit to overengineeringstudio/effect-utils that referenced this pull request Jul 28, 2026
The @effect/platform patch allowlists seven HTTP headers before they become
span attributes. That package is merged into core in Effect 4, so the patch
cannot survive as-is, and dropping it would silently change what we emit to
telemetry.

The obvious lead - beta.102's new HttpClientRequest.updateHeaders/removeHeader
- was probed and refuted on two counts: those combinators mutate the request
actually sent to transport, so filtering telemetry with them drops real headers
from the wire, and they cannot reach response span attributes at all.

Precise beta.102 behaviour, verified in source: every header name becomes a
span attribute on both request and response paths. Values matching
Headers.CurrentRedactedNames render as <redacted>, and that list is
configurable, so value protection is already solved. What has no hook is
suppressing the attribute entirely - a telemetry-shape and cardinality concern
rather than a credential-leak one.

The upstream fix already existed as our own issue #6363 (its v3 predecessor
#6198 was closed only because v3 was frozen). That issue proposed a predicate
and offered a PR that nobody built, so we built it: Effect-TS/effect#6697 adds
a TracerHeaderFilter reference defaulting to constTrue and applies it to both
attribute loops.

Until that lands, a minimal core patch is the interim bridge, deliberately
scoped to be deleted rather than maintained.

Refs #925
@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Generated from PR build output; treat the content below as untrusted.

File Name Current Size Previous Size Difference
basic.ts 6.63 KB 6.63 KB 0.00 KB (0.00%)
batching.ts 9.42 KB 9.42 KB 0.00 KB (0.00%)
brand.ts 6.31 KB 6.31 KB 0.00 KB (0.00%)
cache.ts 10.12 KB 10.12 KB 0.00 KB (0.00%)
config.ts 20.04 KB 20.04 KB 0.00 KB (0.00%)
differ.ts 19.93 KB 19.93 KB 0.00 KB (0.00%)
http-client.ts 20.97 KB 20.94 KB +0.03 KB (+0.16%)
logger.ts 10.28 KB 10.28 KB 0.00 KB (0.00%)
metric.ts 8.55 KB 8.55 KB 0.00 KB (0.00%)
optic.ts 7.33 KB 7.33 KB 0.00 KB (0.00%)
pubsub.ts 14.26 KB 14.26 KB 0.00 KB (0.00%)
queue.ts 11.09 KB 11.09 KB 0.00 KB (0.00%)
schedule.ts 10.27 KB 10.27 KB 0.00 KB (0.00%)
schema-class.ts 18.86 KB 18.86 KB 0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts 28.67 KB 28.67 KB 0.00 KB (0.00%)
schema-representation-roundtrip.ts 24.99 KB 24.99 KB 0.00 KB (0.00%)
schema-string-transformation.ts 12.95 KB 12.95 KB 0.00 KB (0.00%)
schema-string.ts 10.65 KB 10.65 KB 0.00 KB (0.00%)
schema-template-literal.ts 14.85 KB 14.85 KB 0.00 KB (0.00%)
schema-toArbitraryLazy.ts 21.66 KB 21.66 KB 0.00 KB (0.00%)
schema-toCodeDocument.ts 24.08 KB 24.08 KB 0.00 KB (0.00%)
schema-toCodecJson.ts 18.91 KB 18.91 KB 0.00 KB (0.00%)
schema-toEquivalence.ts 18.73 KB 18.73 KB 0.00 KB (0.00%)
schema-toFormatter.ts 18.59 KB 18.59 KB 0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts 22.33 KB 22.33 KB 0.00 KB (0.00%)
schema-toRepresentation.ts 19.25 KB 19.25 KB 0.00 KB (0.00%)
schema.ts 18.12 KB 18.12 KB 0.00 KB (0.00%)
stm.ts 12.05 KB 12.05 KB 0.00 KB (0.00%)
stream.ts 9.37 KB 9.37 KB 0.00 KB (0.00%)

schickling-assistant added a commit to overengineeringstudio/effect-utils that referenced this pull request Jul 28, 2026
The @effect/platform patch allowlists seven HTTP headers before they become
span attributes. That package is merged into core in Effect 4, so the patch
cannot survive as-is, and dropping it would silently change what we emit to
telemetry.

The obvious lead - beta.102's new HttpClientRequest.updateHeaders/removeHeader
- was probed and refuted on two counts: those combinators mutate the request
actually sent to transport, so filtering telemetry with them drops real headers
from the wire, and they cannot reach response span attributes at all.

Precise beta.102 behaviour, verified in source: every header name becomes a
span attribute on both request and response paths. Values matching
Headers.CurrentRedactedNames render as <redacted>, and that list is
configurable, so value protection is already solved. What has no hook is
suppressing the attribute entirely - a telemetry-shape and cardinality concern
rather than a credential-leak one.

The upstream fix already existed as our own issue #6363 (its v3 predecessor
#6198 was closed only because v3 was frozen). That issue proposed a predicate
and offered a PR that nobody built, so we built it: Effect-TS/effect#6697 adds
a TracerHeaderFilter reference defaulting to constTrue and applies it to both
attribute loops.

Until that lands, a minimal core patch is the interim bridge, deliberately
scoped to be deleted rather than maintained.

Refs #925
schickling-assistant added a commit to overengineeringstudio/effect-utils that referenced this pull request Jul 28, 2026
* Add Effect 4 migration pattern catalog and verification

Phase 0 of the Effect 3 to 4 migration (#925) proved each migration pattern in
isolation before touching real packages, using a differential harness that runs
both majors as separate processes and diffs normalized behaviour traces.

These are the durable outputs:

- recipes/ - 17 per-pattern v3 to v4 recipes with equivalence evidence,
  gotchas, and codemod rules where the rewrite is mechanical
- alignment-register.md - every intended v3/v4 behaviour difference with a
  proposed decision and blast radius; the sign-off artifact
- register-verification.md - an adversarial pass over the register that tried
  to refute each accepted difference; 10 upheld, 2 refuted
- idiom-catalog.md - decision-ready refactor patterns with adopt/defer/skip
  dispositions, evidence drawn from LiveStore's completed migration

The harness itself stays in tmp/ as throwaway; only the findings are durable.

Notable results: span shape is identical under both recording and
non-recording tracers, so otel-contract's published contract is unaffected.
Fork defaults are identical, so the compatibility options LiveStore
cargo-culted are unnecessary. Two register entries were refuted and need
reclassification before migration starts - the RPC failure envelope changes
shape across a versionless browser/server protocol, and Restate embeds parser
text verbatim in an HTTP 400 payload.

The CLI work also surfaced an upstream bug, filed as Effect-TS/effect#6690
with a fix PR.

Refs #925, #981

* Record tsgo compatibility with Effect 4 beta.102

The pinned effect-tsgo (8d34c0a, locked 2026-06-17) was the last unanswered
blocker for the Effect 4 migration: whether a six-week-old compiler pin could
typecheck v4's module layout, and whether the Effect diagnostics gate would
survive the flip.

It can, and it does. The probe resolves every v4 subpath we use and proves all
three diagnostic categories still affect the build exit code - warning-only and
suggestion-only probes each exited 2, so the gate is not silently degraded to
errors-only.

Consequences: no tsgo bump is on the migration's critical path, so the flip and
any future compiler bump stay independent changes; and the errors-only waiver
stays an unused contingency rather than a planned step.

Also records the exact waiver mechanism should it ever be needed, and corrects
an earlier misreading - the comment at genie/external.ts:718-735 is accurate,
since the gate fields are the inverse of the generated ignore flags, as
documented at :768-771.

Refs #925

* Record the platform header-patch analysis and its upstream path

The @effect/platform patch allowlists seven HTTP headers before they become
span attributes. That package is merged into core in Effect 4, so the patch
cannot survive as-is, and dropping it would silently change what we emit to
telemetry.

The obvious lead - beta.102's new HttpClientRequest.updateHeaders/removeHeader
- was probed and refuted on two counts: those combinators mutate the request
actually sent to transport, so filtering telemetry with them drops real headers
from the wire, and they cannot reach response span attributes at all.

Precise beta.102 behaviour, verified in source: every header name becomes a
span attribute on both request and response paths. Values matching
Headers.CurrentRedactedNames render as <redacted>, and that list is
configurable, so value protection is already solved. What has no hook is
suppressing the attribute entirely - a telemetry-shape and cardinality concern
rather than a credential-leak one.

The upstream fix already existed as our own issue #6363 (its v3 predecessor
#6198 was closed only because v3 was frozen). That issue proposed a predicate
and offered a PR that nobody built, so we built it: Effect-TS/effect#6697 adds
a TracerHeaderFilter reference defaulting to constTrue and applies it to both
attribute loops.

Until that lands, a minimal core patch is the interim bridge, deliberately
scoped to be deleted rather than maintained.

Refs #925

* Close the three Phase 0 coverage gaps and record the wave-0 constraint

Phase 0 left three boundaries uncovered - filesystem watch ordering, Terminal
and Prompt PTY behaviour, and child-process backpressure. Each maps to a
package where a silent break was plausible, so they are closed before wave
repair reaches them.

Two of the three turned out to be non-deterministic within a single major:
filesystem watch varies in ordering, coalescing and even delete-tag
classification, and child-process traces vary in chunk boundaries and
cross-stream interleaving. Their gates are therefore invariant-based rather
than exact-trace: path membership and final state for watch, whole-stream byte
counts and lossless completion under a slow consumer for child processes.

That distinction is now a documented harness design rule. Pure patterns can be
exact-ordered-trace gated; anything crossing the OS must be invariant-gated,
and each recipe states which properties are deliberately not gated so a later
reader does not mistake the gap for an oversight and pin something flaky.

One real cross-major difference emerged: beta.102 removes recursive control
from FileSystem.watch and forces recursive Node and Bun watches, which silently
widens watch scope. Filed upstream as Effect-TS/effect#6698.

The watch work also prompted an audit of our own tag-dependent consumers, which
found one fragile optimisation guarded by a polling fallback - filed as #986,
independent of this migration.

Also records why wave 0 could not proceed naively: every candidate needs a
still-separate @effect/* package, and strict peer resolution rejects a partial
cohort. The flip must therefore move core effect and every still-separate
@effect/* package in one commit.

Refs #925, #986

* Record operational notes for writing Phase 1 baselines

Capturing the Effect 3 baselines that gate the Effect 4 flip surfaced a set of
traps that are invisible until they bite, and several of them look exactly like
success: a test task that does not exist, a test file that is never collected,
and a validation command that does not run tests all present as green.

Written down so the remaining baselines and the downstream migration do not
rediscover them:

- check:quick is TypeScript and lint only; for a test-only PR it proves almost
  nothing. Validate with the package test task on a committed worktree and
  confirm the test count via the otel-scrape summary, since exit 0 is
  compatible with collecting zero tests.
- devenv generates a test task only for packages listed in packagesWithTests,
  and there is no root vitest run in the task graph, so an unlisted package is
  never tested by CI. Includes the verification-worktree technique for
  validating a baseline whose task does not yet exist on main.
- Vitest include patterns are package-relative and differ per package, so a
  root-path invocation can silently collect nothing.
- A lockfile change cascades across eight fixed-output derivations, with the
  exact evergreen invocations, because chase-fod-closure cannot help when
  consumers lack fodGraph metadata.
- The lint gate covers only three paths, and the instrumented task reports the
  offending file by hash rather than path, so a single-line error can present
  as an opaque failure.

Also records which artifacts carry migration markers: baselines are permanent
gates and get inline TODOs at most, never bridge blocks.

Refs #925

* test: check Effect 4 baseline migration markers

* style: format Effect 4 migration findings with oxfmt

The findings live under context/, which the lint gate covers, and they were
written by hand without running the formatter. CI caught it at
lint:check:format.
Comment thread packages/effect/src/unstable/http/HttpClient.ts Outdated
schickling-assistant and others added 3 commits July 31, 2026 11:26
agent-session-id: 133b5d2a-3d5d-4f52-8f55-f12a834f6b13
agent-tool: Codex CLI
agent-tool-version: 0.145.0
agent-model: unknown
agent-runtime-profile: /nix/store/ph8rlhdj25mg71v81jsfzy6dq4xpcs9m-coding-agent-runtime-profile/share/coding-agents/profile.json
agent-skills-manifest: /nix/store/lsykz8x5481xrpbgk280xh3pypk1c5jy-agent-skills-corpus/share/agent-skills/manifest.json
tooling-profile: dotfiles@3649b53
@tim-smart
tim-smart force-pushed the schickling-assistant/2026-07-28-httpclient-header-filter branch from 5fa1c49 to dd259e8 Compare July 30, 2026 23:28
@tim-smart
tim-smart enabled auto-merge (squash) July 30, 2026 23:29
@tim-smart
tim-smart merged commit e56cd8f into Effect-TS:main Jul 30, 2026
14 of 15 checks passed
@github-project-automation github-project-automation Bot moved this from Discussion Ongoing to Done in PR Backlog Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4.0 enhancement New feature or request

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

HttpClient: allow filtering HTTP header span attributes (allowlist / predicate)

2 participants