Skip to content

rfc(feature): Capture SDK options - #162

Draft
mydea wants to merge 51 commits into
mainfrom
rfc/capture-sdk-options
Draft

mydea wants to merge 51 commits into
mainfrom
rfc/capture-sdk-options

Conversation

@mydea

@mydea mydea commented Sep 21, 2026 •

Copy link
Copy Markdown
Member

WIP — draft RFC, still in progress.

Today we have no visibility into which options a given Sentry SDK instance was configured with. This RFC proposes a mechanism for SDKs to report the configuration they were initialized with (the arguments passed to Sentry.init(), plus relevant derived/effective values) to Sentry via a new, dedicated sdk_config envelope item, so it can be stored, surfaced, and acted upon — decoupled from whether the instance ever sends an event.

Knowing the configured options unlocks a range of use cases:

  • Self-healing / support — when data is missing or filtered, distinguish "never enabled" from "enabled but filtered" (was it a disabled feature, a sample rate, or a failure?).
  • Analytics & product decisions — learn which options are actually used in the wild, to inform what to highlight in docs, what to deprecate or remove in majors, and where to invest.
  • Configuration-aware querying — explain shifts in data over time by surfacing configuration changes (e.g. a change to filtering or sampling rules).
  • Setup audits & warnings — audit init() setups to suggest improvements or warn about potentially confusing behavior given the settings.
  • Discoverability of data sources — let users see all the Sentry.init()s producing data into a project, understand their sources, and see the filtering/sampling/config that affects them, including changes over time.

The RFC focuses on the capture and transport mechanism — the envelope shape (which options we capture, how non-serializable values and integrations are normalized, cross-SDK naming, Relay-side normalization), when SDKs send it, and how it is deduplicated and stored server-side. The downstream use cases above are motivation and future work.

Linear project: https://linear.app/getsentry/project/capture-sdk-options-js-08e8a89c71c9/overview

Rendered RFC

mydea and others added 8 commits September 21, 2026 13:50
Add initial boilerplate draft for capturing Sentry SDK init options.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Describe that we do not capture SDK config today, and the adjacent
signals (event SDK metadata, client reports). Remove the empty
Supporting Data section for now.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Option A (preferred): a dedicated, first-class envelope item for SDK
options. Option B: expand error events to carry the config.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Propose a language-agnostic, primitives-only payload: sdk (identity +
opt-in integration status via `active`), meta, options, integration_options,
and a free-form `_other` bucket. Cover callback normalization, generic
integration options, reflecting which integrations are actually used, and
cross-SDK naming.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Sending: server SDKs (debounced send after init, configurable delay,
  flush on shutdown), client SDKs (send-on-every-init vs. sampling),
  and a note that serverless shares client constraints.
- Storing: store-every-record vs. dedup-by-release (recommended), with
  an open question on whether release is a sufficient dedup key.
- Add top-level `timestamp` to the envelope; note options are scrubbed
  server-side (no client-side scrubbing).
- Add Drawbacks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename XXXX -> 0162 now that the PR is open, fill in the RFC PR link,
and add the index entry to README.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add an "Envelope item type" subsection recommending `sdk_config`, with
`sdk_options` and `client_config` as considered alternatives.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add "Not in scope / Follow-up work" (product usage; removing SDK
  metadata from events, which needs a backfill from sdk_config).
- Cross-SDK naming: recommend leaving option names unspecced (native
  SDK names as-is) rather than a canonical catalog.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread text/0162-capture-sdk-options.md Outdated
Comment thread text/0162-capture-sdk-options.md Outdated
Comment thread text/0162-capture-sdk-options.md Outdated
Comment thread text/0162-capture-sdk-options.md Outdated
### Option 2 (recommended): Deduplicate and store a single record

Store only **one** record per configuration and discard the rest. Because configuration is
essentially constant per release, we need a key to deduplicate by, and we suggest **release**:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we go for release we could still end up with many different init constellations because of options passed in by ENV vars

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated to propose deduping by release+env+dist+hash!

Comment thread text/0162-capture-sdk-options.md Outdated
Comment thread text/0162-capture-sdk-options.md Outdated
Comment thread text/0162-capture-sdk-options.md Outdated
Comment thread text/0162-capture-sdk-options.md Outdated
Comment thread text/0162-capture-sdk-options.md Outdated
Comment thread text/0162-capture-sdk-options.md Outdated
mydea and others added 9 commits September 23, 2026 11:33
…n Relay

Rework the envelope shape so SDKs send their final/effective `options` as-is
plus a flat `options_set_by_user` provenance array, and Relay derives
`normalized_options` (a canonical-keyed subset) at ingestion. Add explicit
serialization rules (functions -> [Function], integrations -> name), the
normalization catalog, and rationale for both decisions (Relay-side
normalization; effective options + flat set-by-user array).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…iming; add Open Questions

- Rename the per-integration runtime signal `active` -> `applied` (clearer:
  "took effect at runtime" rather than enabled/disabled).
- Reframe server-SDK send timing: debounce is one example default; any
  settling hook/mechanism is fine as long as final options are captured.
- Note the new `sdk_config` envelope item degrades gracefully — older
  Relay/self-hosted discards the unknown item and processes the rest.
- Add an Open Questions section (new-envelope-type infra: rate limits,
  payload/size limits, data category/quota/billing, outcomes) and reference
  the release dedup-key question.
- Clarify scrubbing: primarily server-side; SDKs MAY scrub known-sensitive
  values as best-effort, without guaranteeing fully-scrubbed data.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the two parallel name-keyed maps (`sdk.integrations` for status,
top-level `integration_options` for options) with a single top-level
`integrations` map. Each entry holds the integration's runtime `applied`
status plus its configured options nested under an `options` key, so
arbitrary user options can never collide with well-known status keys.

Also document a known limitation of sending effective options: the
user-provided *form* is lost when the SDK coerces a value (notably
`integrations` passed as a function -> resolved array). Note this is rare
(essentially `integrations` and `stackParser` in JS) and can be layered on
later via a sparse `options_user_provided` block without reworking the shape.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ns hash

Replace the two-strategy + hybrid dedup framing with a single rule:
deduplicate by `release` + `environment` + `dist` plus an optional,
SDK-provided options hash that is stable across instances sharing a config.
When present, the server folds the hash into the dedup key (capturing
within-release variation); when absent, dedup falls back to the composite key.

The hash is SDK-side by design (a robust server-side canonical hash isn't
feasible) and, if used, MUST be stamped on every event — as an attribute on
spans/logs and a context field on error/transaction events — so events
correlate exactly to their config. Correlation now falls out of the key:
bucket-level without a hash, exact with one. Update Open Questions and the
Drawbacks bullets to match.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mydea mydea changed the title rfc(feature): Capture SDK options [WIP] rfc(feature): Capture SDK options Sep 24, 2026
Comment thread text/0162-capture-sdk-options.md Outdated
Comment thread text/0162-capture-sdk-options.md Outdated
@szokeasaurusrex

szokeasaurusrex commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

@mydea I think we can greatly reduce the length of this RFC and make it a lot easier for folks to read over and quickly understand. I opened a concrete proposal against this PR branch in #163.

The shortened version should preserve the existing RFC as written without applying any code review suggestions from myself or others

@mydea

mydea commented Sep 25, 2026

Copy link
Copy Markdown
Member Author

I'm a bit concerned that the benefits of implementing this RFC may not outweigh the cost in all SDKs. The implementation and maintenance cost may vary significantly among SDKs, as will user demand and team resources.

If we decide to adopt this RFC, I would propose we make it explicitly "OPTIONAL" to implement at the SDK level, so that SDKs can chose to adopt it where the benefits outweigh the costs.

I do not see the need to make this officially optional. But SDKs can implement this in a fidelity that makes sense for them. E.g. if certain information/data is not reasonably accessible, we can omit things as it makes sense. At this stage, we should mostly get a feeling for which things specifically may not be feasible in certain SDKs and then make those parts of the spec optional/extensible.

@szokeasaurusrex

Copy link
Copy Markdown
Member

I do not see the need to make this officially optional. But SDKs can implement this in a fidelity that makes sense for them. E.g. if certain information/data is not reasonably accessible, we can omit things as it makes sense. At this stage, we should mostly get a feeling for which things specifically may not be feasible in certain SDKs and then make those parts of the spec optional/extensible.

As long as the spec does not end up forcing us to redesign SDKs to be able to send this data, beyond just adding the additional envelope types and collecting the data that is easily collected, I am satisfied with it 👍

Comment thread text/0162-capture-sdk-options.md Outdated
Comment thread text/0162-capture-sdk-options.md Outdated

| Option | Pros | Cons |
| ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| **I: Send on every `init()`** | Simple, same as server SDKs; every instance is represented | Much more data to ingest; an extra request per `init()` |

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With regards to the: "Much more data"

Do we have any idea about the distribution of "telemetry per sdk instance" (or simpler how much envelopes does an sdk send on avg)? Are we assuming that there is a long tail of SDKs that are initiated but never actually emit anything afterwards?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is just about client sdks - where way more instances are spawned, thus way more Sentry.init() is called than on a server which may only be started once with the given settings :)

Comment thread text/0162-capture-sdk-options.md Outdated
| Option | Pros | Cons |
| ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| **I: Send on every `init()`** | Simple, same as server SDKs; every instance is represented | Much more data to ingest; an extra request per `init()` |
| **II: Sample** a fraction of `init()` calls (e.g. 1%), assuming configuration is essentially constant per release | Much less overhead | Low-traffic releases and rare configurations may be missed; more configuration surface; harder to reason about and test |

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we sample on the client side based on the hash calculated? So the SDK would only send the config if the hash is different than the last sent item.

I realize this means we will need to make use of storage like localStorage which has no precedent, and probably something we never do, but this is just an idea.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah we'd need to store this in local storage which we can't by default, and why would you opt-in to that 😅 after talking to ingest folks etc. it seems that it's not really a concern to send this all the time, so this is the simplest for now IMHO.

Comment on lines +207 to +208
- **Overhead:** a new, potentially high-volume item (every `init()` for client and serverless
traffic), a new storage model, and new server-side processing.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a client-side precedent for high volume events in the browserSessionIntegration but the envelope is a few bytes compared to the sdk config.

Comment thread text/0162-capture-sdk-options.md Outdated

| Option | Pros | Cons |
| ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| **I: Send on every `init()`** | Simple, same as server SDKs; every instance is represented | Much more data to ingest; an extra request per `init()` |

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably an implementation detail but wanted to bring it up.

For SDKs with performance concerns like the browser, the sending should be deferred until the timing is appropriate, for browsers we could send on whenIdleOrHidden like with session envelopes.

On the browser, sending immediately on init could affect LCP and other web metrics.

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1139b7e. Configure here.

Comment thread text/0162-capture-sdk-options.md
Comment thread text/0162-capture-sdk-options.md Outdated
"sentry.sdk_config.version": { "type": "integer", "value": 1 },
"sentry.sdk_config.hash": { "type": "string", "value": "9f2c1a7e" },

"sentry.sdk_config.option.dsn": { "type": "string", "value": "https://<public-key>@o0.ingest.sentry.io/0" },

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mh these prefixes make sense but it's quite redundant in the scope of a sdk config.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, good point. I guess it depends on how we want to approach this, should these be "regular conventions" or type-specific conventions 🤔

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is easiest to still make the proper, global-defined semantic attributes. it's a bit verbose here but at least clear, we can document in conventions that these are internal and only to be used for this envelope type.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It also fits in better with other attributes like process.runtime.name if everything is from/in the conventions namespace.

Comment thread text/0162-capture-sdk-options.md
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.