Skip to content

feat(core): Add managed tunnel helpers that derive the tunnel path from the DSN - #25199

Draft
logaretm wants to merge 16 commits into
developfrom
awad/tunnel-core-dsn-path
Draft

logaretm wants to merge 16 commits into
developfrom
awad/tunnel-core-dsn-path

Conversation

@logaretm

@logaretm logaretm commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

Overview

SDK Tunnel is a feature that allows client SDKs (e.g: browser) to send their requests through an endpoint that lives on the user's own application that forwards to Sentry. This bypasses adblockers and similar extentions.

Tunnels could be configured as a fixed endpoint defined by the user (defaulted to /monitoring) but that usually ends up in global adblocker lists. Alternatively, users can tell the SDK to use a random endpoint to bypass that.

Previously the tunnel logic mostly lived in client bundles and in server frameworks build time, this had two issues:

  • Both client and server bundles needed to share a single ENV during build, or a single process. So both can agree on the random URL generated.
  • CI/CD workflows means assets can drift, so older client bundles might be sending requests to older server bundles and vice versa, what happens then is during the rollout/phase-out, the random endpoints won't match and thus these envelopes will be getting 404'ed.

This stack of PRs re-works the idea by moving to more stable yet random URL generation based on the client's DSN and moves the tunnel handling into the runtime of the core SDK.

Meaning tunneling now is a core SDK capability rather than a meta framework feature.

This PR

This up the core for the new tunnel mechanism that will be implemented against, it does the following:

  • Sets up the tunnel generation and matching logic.
  • Sets up global span/breadcrumb ignore rules for tunneled requests.
  • Adds request checking, header forwarding, and the proxying to the Sentry SaaS DSN.

It also sets up a core key decisions that will carry over throughout the stack:

  • Base paths are respected always: if a user sets /app as a base path our tunnels handlers will expect that prefix.
  • Severs always allow their own DSNs: This avoids the footgun where a user misconfigures allowedDSNs on the server, this correction only applies if the client and server share the same DSN, if there is a different DSN involved then it is on the user to configure that correctly. Will work on documenting this.

@logaretm
logaretm added this pull request to stack #25201 October 8, 2026 15:45
@logaretm
logaretm force-pushed the awad/tunnel-core-dsn-path branch from af27973 to a2af1e1 Compare October 8, 2026 16:05
@logaretm logaretm changed the title feat(core): Derive the tunnel path from the DSN and match tunnel requests feat(core): Add managed tunnel helpers that derive the tunnel path from the DSN Oct 8, 2026
@logaretm
logaretm force-pushed the awad/tunnel-core-dsn-path branch 4 times, most recently from c544ac0 to 89a5b2c Compare October 15, 2026 15:36
@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 30.28 kB +1.36% +406 B 🔺
@sentry/browser - with treeshaking flags 28.44 kB +1.53% +427 B 🔺
@sentry/browser - with treeshaking flags tracing without tracing 28.33 kB +1.47% +409 B 🔺
@sentry/browser (incl. Tracing) 52.45 kB +0.74% +385 B 🔺
@sentry/browser (incl. Tracing + Span Streaming) 52.46 kB +0.75% +390 B 🔺
@sentry/browser (incl. Tracing, Profiling) 55.4 kB +0.71% +388 B 🔺
@sentry/browser (incl. Tracing, Replay) 92.22 kB +0.44% +396 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 81.09 kB +0.51% +411 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas) 96.95 kB +0.43% +406 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback) 109.9 kB +0.38% +409 B 🔺
@sentry/browser (incl. Feedback) 47.79 kB +0.84% +397 B 🔺
@sentry/browser (incl. sendFeedback) 35.36 kB +1.2% +417 B 🔺
@sentry/browser (incl. FeedbackAsync) 40.44 kB +0.99% +394 B 🔺
@sentry/browser (incl. Metrics) 31.28 kB +1.24% +382 B 🔺
@sentry/browser (incl. Logs) 31.57 kB +1.26% +392 B 🔺
@sentry/browser (incl. Metrics & Logs) 32.24 kB +1.35% +427 B 🔺
@sentry/react 32.09 kB +1.24% +392 B 🔺
@sentry/react (incl. Tracing) 54.77 kB +0.71% +386 B 🔺
@sentry/vue 38.44 kB +1.06% +402 B 🔺
@sentry/vue (incl. Tracing) 55.4 kB +0.72% +393 B 🔺
@sentry/svelte 30.31 kB +1.36% +406 B 🔺
@sentry/remix (Remix 3 client bundle) 57.49 kB +0.72% +409 B 🔺
CDN Bundle 31.99 kB +1.09% +344 B 🔺
CDN Bundle (incl. Tracing) 52.93 kB +0.67% +351 B 🔺
CDN Bundle (incl. Logs, Metrics) 34.2 kB +1.08% +363 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) 54.88 kB +0.65% +352 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) 75.12 kB +0.48% +355 B 🔺
CDN Bundle (incl. Tracing, Replay) 90.63 kB +0.42% +379 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 92.6 kB +0.46% +418 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) 96.74 kB +0.36% +346 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 98.78 kB +0.45% +437 B 🔺
CDN Bundle - uncompressed 94.11 kB +1% +927 B 🔺
CDN Bundle (incl. Tracing) - uncompressed 156.95 kB +0.59% +915 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed 100.65 kB +0.93% +927 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 162.91 kB +0.57% +915 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 230.69 kB +0.41% +929 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed 277.12 kB +0.34% +915 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 283.06 kB +0.33% +915 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 290.82 kB +0.32% +915 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 296.75 kB +0.31% +915 B 🔺
@sentry/nextjs (client) 57.18 kB +0.67% +377 B 🔺
@sentry/sveltekit (client) 52.83 kB +0.74% +384 B 🔺
@sentry/core/server 41.56 kB +1.07% +439 B 🔺
@sentry/core/browser 13.81 kB - -
@sentry/node 153.5 kB +0.28% +416 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 84.04 kB - -
@sentry/node - without tracing 95.25 kB +0.41% +381 B 🔺
@sentry/node - without channel injection 131.55 kB +0.31% +402 B 🔺
@sentry/aws-serverless 103.41 kB +0.37% +375 B 🔺
@sentry/cloudflare (withSentry) - minified 211.77 kB +0.56% +1.17 kB 🔺
@sentry/cloudflare (withSentry) 525.03 kB +0.48% +2.5 kB 🔺
@sentry/nextjs/cloudflare (withSentry) - minified 229.44 kB +0.52% +1.17 kB 🔺

View base workflow run

@logaretm
logaretm force-pushed the awad/tunnel-core-dsn-path branch from 3bba67e to 4eea4c9 Compare October 15, 2026 17:36
@logaretm

Copy link
Copy Markdown
Member Author

bugbot run

@cursor cursor 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.

Stale Bugbot comment from a previous run.

Comment thread packages/core/src/utils/tunnel.ts Outdated
Comment thread packages/core/test/lib/utils/tunnel.test.ts
@logaretm

Copy link
Copy Markdown
Member Author

bugbot run

1 similar comment
@logaretm

Copy link
Copy Markdown
Member Author

bugbot run

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

1 issue from previous review remains unresolved.

Fix All in Cursor

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit a9a04bc. Configure here.

…om the DSN

Browser and server SDKs can now derive the same opaque tunnel path from the DSN
with `getTunnelPath`, so they agree on it without a build step.
`isTunnelRequest` and `handleTunnelRequestIfMatched` let a server wrapper serve
the tunnel when its client has `_managedTunnel` set, which
`resolveServerTunnelOption` derives from the `tunnel` option. `allowedDsns`
defaults to the client's DSN and the forward to Sentry runs with tracing
suppressed.
…etTunnelPaths`

Callers now hand `isTunnelRequest` a request-like object, so the matching rule
can look at more of the request later without changing every framework.
`getTunnelPaths` covers frameworks that can only register routes.
Servers derive the tunnel paths on every POST, so each DSN is now parsed and
hashed once. An invalid DSN in `allowedDsns` also logs once instead of on every
request.
…` on the server

A relative `tunnel` can't be a transport URL on the server, so it now names the
path the server serves, and `TunnelOptions.path` overrides the DSN-derived path.
`tunnel: '/monitor'` on both sides then gives a fixed path the same way
`tunnel: true` gives the DSN-derived one.
Server SDKs start the incoming request span before a framework hook gets to
answer a tunnel request, so clients with the managed tunnel enabled now add the
tunnel paths to `ignoreSpans`.
@logaretm
logaretm force-pushed the awad/tunnel-core-dsn-path branch from a9a04bc to d53b981 Compare October 16, 2026 04:23
@linear-code

linear-code Bot commented Oct 16, 2026

Copy link
Copy Markdown

JS-3942

Comment thread packages/core/src/utils/tunnel.ts Fixed

This branch has not been deployed

No deployments
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