Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
b6c2e81
feat(core): Add managed tunnel helpers that derive the tunnel path fr…
logaretm Oct 8, 2026
e0baa84
ref(core): Pass the whole request to the tunnel matcher and expose `g…
logaretm Oct 15, 2026
bdecbfc
perf(core): Cache the tunnel path per DSN
logaretm Oct 15, 2026
d238df3
feat(core): Serve a fixed managed tunnel path from a relative `tunnel…
logaretm Oct 15, 2026
a2ee935
feat(core): Drop the server spans of managed tunnel requests
logaretm Oct 15, 2026
a7cb393
fix(core): Skip fetch instrumentation while tracing is suppressed
logaretm Oct 15, 2026
1ee4846
feat(core): Check the tunnel DSN before reading the envelope body
logaretm Oct 15, 2026
a3eddb6
fix(core): Cancel the body of a rejected tunnel request
logaretm Oct 15, 2026
aa97143
feat(core): Forward the client IP of tunnel requests to Sentry
logaretm Oct 15, 2026
d53b981
fix(core): Match a fixed tunnel path that ends with a slash
logaretm Oct 16, 2026
c664a5a
fix(core): Only answer the managed tunnel under its base path
logaretm Oct 16, 2026
3d72f88
feat(core): Always accept the server's own DSN in the managed tunnel
logaretm Oct 16, 2026
cc5f9c6
feat(core): Add `applyTunnelDefaults` so framework SDKs fill in the b…
logaretm Oct 16, 2026
c3bdc87
feat(core): Add `setManagedTunnelBasePath` for servers that learn the…
logaretm Oct 16, 2026
f4c72bd
feat(core): Mark the tunnel handler's responses so the browser can te…
logaretm Oct 16, 2026
612037f
fix(core): Normalize tunnel base paths with one util that doesn't bac…
logaretm Oct 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/core/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import type { ResolvedDataCollection } from './types/datacollection';
import { createClientReportEnvelope } from './utils/clientreport';
import { consoleSandbox, debug } from './utils/debug-logger';
import { dsnToString, makeDsn } from './utils/dsn';
import { getTunnelIgnoreSpanFilter } from './utils/tunnel';
import { addItemToEnvelope, createAttachmentEnvelopeItem, getDataCategoryByType } from './utils/envelope';
import { getPossibleEventMessages, isInternalException } from './utils/eventUtils';
import { isObjectLike, isParameterizedString, isPlainObject, isPrimitive } from './utils/is';
Expand Down Expand Up @@ -271,6 +272,11 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
DEBUG_BUILD && debug.warn('No DSN provided, client will not send events.');
}

const tunnelSpanFilter = options._managedTunnel && getTunnelIgnoreSpanFilter(options._managedTunnel, this._dsn);
if (tunnelSpanFilter) {
this._options.ignoreSpans = [...(this._options.ignoreSpans ?? []), tunnelSpanFilter];
}

const { beforeSendSpan, traceLifecycle } = this._options;
// A `beforeSendSpan` callback is only invoked for the span format matching the trace lifecycle,
// so a mismatch means it is silently never called.
Expand Down
17 changes: 14 additions & 3 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,19 @@ export { isSentryRequestUrl } from './utils/isSentryRequestUrl';
export { handleCallbackErrors } from './utils/handleCallbackErrors';
export { safeCallback } from './utils/safeCallback';
export { parameterize, fmt } from './utils/parameterize';
export type { HandleTunnelRequestOptions } from './utils/tunnel';
export { handleTunnelRequest } from './utils/tunnel';
export type { HandleTunnelRequestOptions, TunnelRequestLike } from './utils/tunnel';
export {
applyTunnelDefaults,
getTunnelPath,
getTunnelPaths,
handleTunnelRequest,
handleTunnelRequestIfMatched,
isTunnelRequest,
normalizeTunnelBasePath,
resolveServerTunnelOption,
setManagedTunnelBasePath,
TUNNEL_RESPONSE_HEADER,
} from './utils/tunnel';
export { addAutoIpAddressToSession } from './utils/ipAddress';
export {
convertSpanLinksForEnvelope,
Expand Down Expand Up @@ -415,7 +426,7 @@ export type {
HttpHeadersCollection,
ResolvedDataCollection,
} from './types/datacollection';
export type { ClientOptions, CoreOptions as Options } from './types/options';
export type { ClientOptions, CoreOptions as Options, TunnelOptions } from './types/options';
export type { Package } from './types/package';
export type { PolymorphicEvent, PolymorphicRequest } from './types/polymorphics';
export type {
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/integrations/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getClient } from '../currentScopes';
import { instrumentFetchRequest } from '../fetch';
import { defineIntegration } from '../integration';
import { addFetchInstrumentationHandler } from '../instrument/fetch';
import { isTracingSuppressed } from '../tracing/trace';
import type { FetchBreadcrumbData, FetchBreadcrumbHint } from '../types/breadcrumb';
import type { HandlerDataFetch } from '../types/instrument';
import type { Integration, IntegrationFn } from '../types/integration';
Expand Down Expand Up @@ -92,7 +93,8 @@ export function createFetchIntegration({
return;
}

if (isSentryRequestUrl(handlerData.fetchData.url, client)) {
// Matches Node's http and undici instrumentation, so SDK-internal requests (e.g. a tunnel forward) leave no breadcrumb.
if (isTracingSuppressed() || isSentryRequestUrl(handlerData.fetchData.url, client)) {
return;
}

Expand Down
28 changes: 27 additions & 1 deletion packages/core/src/types/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export type IgnoreSpanAttributeValue = string | boolean | number | string[] | bo
* At least one of the properties (`name`, `op`, or `attributes`) must be set.
* If multiple are set, all must match for the span to be ignored.
*/
type IgnoreSpanFilter =
export type IgnoreSpanFilter =
| {
/**
* Spans with a name matching this pattern will be ignored.
Expand Down Expand Up @@ -373,6 +373,14 @@ export interface ClientOptions<TO extends BaseTransportOptions = BaseTransportOp
*/
tunnel?: string;

/**
* Set by server SDKs when the managed tunnel is enabled. Server request handlers check it to decide whether to
* serve the managed tunnel.
*
* @internal This option is not part of the public API and is subject to change at any time.
*/
_managedTunnel?: TunnelOptions;

/**
* Controls what data the SDK collects and sends to Sentry.
* All fields are optional — omitted fields use the documented defaults.
Expand Down Expand Up @@ -645,6 +653,24 @@ export type BeforeSendStreamedSpanCallback = (span: StreamedSpanJSON) => Streame
*/
export type BeforeSendStaticSpanCallback = (span: SpanJSON) => SpanJSON;

/** Options for the managed tunnel, see `tunnel`. */
export interface TunnelOptions {
/**
* Server only. DSNs whose envelopes the tunnel forwards, in addition to the server's own DSN. List the browser DSN
* here when the browser uses a different one.
*/
allowedDsns?: string[];

/** A fixed tunnel path instead of the one derived from the DSN. A relative `tunnel` string sets this. */
path?: string;

/** Path prefix the app is served under, e.g. `/docs`. Set the same value in the browser and the server `init`. */
basePath?: string;

/** Browser only. Append a trailing slash to the tunnel path, for servers that redirect paths without one. */
trailingSlash?: boolean;
}

/** Base configuration options for every SDK. */
export interface CoreOptions<TO extends BaseTransportOptions = BaseTransportOptions> extends Omit<
Partial<ClientOptions<TO>>,
Expand Down
Loading
Loading