Skip to content
Back to Guides

SDK 13 with an existing tracer and restricted capture

·

OpenTelemetry

If your 12.x setup uses buildInterfereOtelKit with a private BasicTracerProvider to export only request-error spans, do not replace it with application mode and assume equivalent collection. The kit is removed in v13. The host-provider composition below uses the public v13 API without it. Independent delivery of errors under a recording host span is planned for rc.3; rc.1 and rc.2 do not preserve the request-error-only contract. Do not deploy this migration until the exact candidate includes and passes that delivery fix.

The 12.x shape being replaced

This restricted setup used the kit’s resource attributes and span processors on a private provider, an inert propagator, a root span around onRequestError, an exception-only forwarding processor, query stripping, empty request headers and a waitUntil flush. A separate application registerOTel exported to Datadog/Sentry. In the browser, DD_RUM and Sentry kept their existing configuration while Interfere denied analytics/replay consent and disabled device, logs and header propagation.

The private provider was a delivery boundary: ordinary host spans never went to Interfere. Removing that provider requires a replacement for that boundary, not just removal of the kit import.

Host-provider composition in v13

Call this helper from your existing Node instrumentation.ts registration with the same host configuration you already use. Keep your service name, exporters, sampler, context manager, propagators and instrumentation settings. Install the correlation processor while constructing the provider, not after it has registered.

TypeScript
1import { registerOTel } from "@vercel/otel";2import {3  correlationSpanProcessor,4  register as registerInterfere,5} from "@interfere/next/instrumentation";67export async function registerApplicationTracing(8  host: Parameters<typeof registerOTel>[0] & { serviceName: string },9) {10  registerOTel({11    ...host,12    spanProcessors: [13      correlationSpanProcessor(),14      ...(host.spanProcessors ?? ["auto"]),15    ],16  });17  await registerInterfere({18    tracing: "application",19    serviceName: host.serviceName,20    consoleBridge: false,21    captureUncaughtException: false,22    captureUnhandledRejection: false,23    privacy: { urls: true },24  });25}
  • The host configuration spread preserves the application’s existing tracing configuration; retaining its processor list preserves existing export paths. "auto" preserves Vercel’s default processors only when the application did not specify a list.
  • correlationSpanProcessor() copies permitted Interfere baggage onto host spans. It installs no provider, propagator or exporter. With no incoming Interfere baggage, it cannot manufacture browser-session linkage.
  • tracing: "application" leaves OpenTelemetry globals to the host. It does not mean errors-only export or no header propagation in the browser.
  • consoleBridge: false preserves disabled Interfere console capture. The two global-handler options avoid adding process-wide error listeners when the application already owns them.
  • privacy.urls: true redacts query values and removes fragments in Interfere URL telemetry. It does not remove query parameter names or sanitize unrelated attributes already on the host’s spans. Keep the application’s own sanitization.

Inventory existing Interfere Trace Drains and collector fan-out too: leaving one that forwards all host spans enabled cannot satisfy a request-error-only policy. Any change to those customer integrations needs its existing scoped authorization; do not silently remove a drain or broaden collection.

Keep the existing Datadog/Sentry exporter settings. Do not repoint OTEL_EXPORTER_OTLP_* to Interfere or add an Interfere Trace Drain as an errors-only workaround: either can change which ordinary host spans are delivered.

Preserve request-header and query restrictions

Wrap the Next hook before passing request context to Interfere. This adapter accepts your existing lifetime extension function, such as Vercel’s waitUntil; use its returned function as the existing onRequestError hook or compose it with the application’s other error handlers.

TypeScript
1import type { Instrumentation } from "next";2import { onRequestError as captureRequestError } from "@interfere/next/instrumentation";34export function restrictedRequestErrorHook(5  waitUntil: (promise: Promise<void>) => void,6): Instrumentation.onRequestError {7  return (error, request, errorContext) => {8    waitUntil(9      captureRequestError(10        error,11        {12          method: request.method,13          path: request.path.replace(/[?#].*$/u, ""),14          headers: {},15        },16        {17          ...errorContext,18          routePath: errorContext.routePath.replace(/[?#].*$/u, ""),19        },20      ),21    );22  };23}

An empty header object prevents the hook’s normal allowed-header capture. Stripping the query and fragment preserves the former request-path restriction; passing the request unchanged would not. Error messages/stacks and host-created span attributes still need the customer’s existing privacy review. onRequestError flushes Interfere-owned telemetry before its promise settles; it does not flush the host provider, whose lifecycle stays application-owned.

Preserve browser restrictions

In instrumentation-client.ts, keep the existing service name and initialize before mounting the provider. The headers option requires the rc.2 API or a later candidate containing it; do not use this snippet with rc.1.

TypeScript
1import { init } from "@interfere/next/instrumentation-client";23void init({4  serviceName: "existing-service-name",5  consent: false,6  tracing: {7    mode: "application",8    headers: false,9    captureFailedRequests: false,10  },11});

consent: false denies analytics and replay from startup, including device fingerprint enrichment. It does not disable errors, logs, tracing or first-party session storage. headers: false stops Interfere’s trace and baggage writes, including to the page origin; an inert host propagator or application mode alone does not. captureFailedRequests: false prevents failed HTTP responses from being promoted into exception events; requests remain observed. Existing DD_RUM and Sentry configuration remains application-owned and may still write its own headers.

Retain disabled plugin settings in both the build-injected/remote configuration and provider overrides. In particular, consent does not gate logs, and a provider override applied after initialization is not proof that nothing was captured before mounting. Keep the existing startup configuration disabling logs; do not enable it while migrating. The provider can retain these explicit restrictions:

TSX
1import { InterfereProvider } from "@interfere/next/provider";2import type { PropsWithChildren } from "react";34export function RestrictedCaptureProvider({ children }: PropsWithChildren) {5  return (6    <InterfereProvider7      consent={false}8      plugins={{ device: false, logs: false, replay: false }}9    >10      {children}11    </InterfereProvider>12  );13}

Version gate: independent error delivery in rc.3

In rc.1 and rc.2 application mode, onRequestError adds the exception to an active recording host span and returns. That error reaches Interfere only if the host span is exported there. Omitting Interfere’s OTEL_EXPORTER_OTLP_* settings does not produce an independent errors-only copy. With no recording active span, Interfere instead creates an isolated exception span through its private exporter; that different branch is not proof that errors under the host provider are delivered.

A Vercel Trace Drain forwards host spans, so it also does not preserve the former exception-only filter. correlationSpanProcessor() only enriches. Do not restore the removed kit, import internal processors, switch to managed mode, or clear the active context merely to force a different capture branch.

The rc.3 delivery change is intended to make register({ tracing: "application" }) send an isolated exception span to Interfere even when a recording host span is active, link its host trace/span IDs, and still record the host event. It introduces errors: "interfere" | "host", with "interfere" as the default and "host" for applications that deliberately rely on their own export path. Ordinary host spans need not be exported to Interfere. Where both paths reach Interfere, ingestion must deduplicate the exception. This is the target contract, not a claim about an already qualified package.

The recipe omits errors so it uses that intended default once available; adding errors: "host" would defeat this errors-only migration unless the host export path also satisfies the restriction. Keep the restricted 12.x integration until the rc.3 fix has been accepted for the exact candidate. Qualification must demonstrate active recording, unsampled and absent host spans; one allowed error delivered once; no ordinary host spans delivered to Interfere; unchanged vendor export; and preserved header/query/consent restrictions. A TypeScript compile proves API compatibility only, not delivery or privacy equivalence.