Skip to content

feat(browser): support flag-key obfuscation - #405

Merged
leoromanovsky merged 5 commits into
mainfrom
browser-flag-key-obfuscation
Oct 7, 2026
Merged

leoromanovsky merged 5 commits into
mainfrom
browser-flag-key-obfuscation

Conversation

@leoromanovsky

@leoromanovsky leoromanovsky commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Tracking: FFLSDK-262

Motivation

Readable flag keys can reveal upcoming features. Support obfuscated assignments without changing application evaluation calls.
This reduces readable names in payloads; it does not hide values or prevent dictionary guessing. It is not encryption or response signing.

Changes and Decisions

Advertise support through this header instead of a JSON supported_capabilities field. Existing request fields stay unchanged.

X-DD-FEATURE-FLAGS-CAPABILITIES: assignment-encoding-flag-key-256-v1

The SDK adds this header automatically only for Datadog endpoints. Requests through flaggingProxy omit it unless the application supplies it in customHeaders. Cross-origin proxies must allow it in CORS before opting in.

Capabilities are sorted and comma-separated. They describe support, not a requirement. The edge still selects plaintext or obfuscated responses through its rollout.

For obfuscated: true, validate obfuscation: {scheme, salt} and hash the application key for lookup. The response scheme remains flag-key-sha256-v1:

lowercase_hex(SHA256(
  UTF8("datadog.feature-flags.flag-key.v1") || 0x00 ||
  decode_hex(salt) || UTF8(application_flag_key)
))

The public salt contains 16 bytes, encoded as 32 lowercase hexadecimal characters. Keys use exact UTF-8 bytes without normalization.

sequenceDiagram
    participant App
    participant SDK as Browser provider and shared core
    participant Edge as Fastly
    SDK->>Edge: Precompute request + capability header
    Edge-->>SDK: Plaintext, or encoded keys + scheme + salt
    SDK->>SDK: Validate and retain assignments with encoding
    App->>SDK: Evaluate original flag key
    SDK->>SDK: Hash lookup key only for encoded assignments
    SDK-->>App: Original value and evaluation details
    SDK->>SDK: Keep original flag key in telemetry
Loading
  • Keep evaluation calls, values, and original-key telemetry unchanged. Leave rules-based delivery unchanged.
  • Cache lookup hashes, not values, with a 1,024-key limit per encoding descriptor. A salt change invalidates the lookup cache.
  • Preserve existing exposure resets. The online provider clears deduplication when a previously loaded createdAt changes. The core provider uses configuration identity. createdAt is a configuration timestamp, not an experiment revision. Same-configuration deduplication and the online provider's first-fetch exception remain unchanged.
  • Limit exposure caches to 50,000 entries per scope, matching Node. Eviction permits another exposure. Keep the IndexedDB migration in #188 separate.
  • Write configurations under versioned IndexedDB keys. Read legacy plaintext entries on upgrade without overwriting them. Plaintext rollback replaces the new SDK's encoded entry.
  • Serialize encoded portable snapshots as version 2, so older readers reject them. Plaintext and rules-only snapshots remain version 1.
  • Accept new flag keys and unknown JSON fields. An unsupported flag type affects only that flag.
  • Route every response-level parser error through fallback, including malformed plaintext. With a matching cache, retain assignments and report STALE. Without usable cached assignments, reject initialization and report ERROR. Do not retry without the capability.

Failed context-switch behavior remains separate: FFLSDK-255.

Release only after ddoghq/dd-source#121201 is deployed to every site this SDK targets. Before releasing, verify that each site's OPTIONS response allows X-DD-FEATURE-FLAGS-CAPABILITIES. Browsers block assignment requests until that CORS change is live. This PR does not deploy backend changes.

Related: edge rollout, documentation.

Validation

Nine Chromium smoke tests passed against packed packages and local HTTP fixtures. They covered header negotiation, salt changes, offline restoration of the latest value, plaintext rollback, and operation without Web Crypto or native text encoders.

A compatibility check executed the previous reader from main at 9f198f1 against new cache entries and portable snapshots. It retained access to legacy plaintext, missed new encoded cache entries, and rejected encoded version-2 snapshots.

These checks did not use staging.

@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

OpenFeature Browser Provider Bundle Sizes

Measured from the Vite production output after installing packed @datadog/flagging-core and @datadog/openfeature-browser tarballs.

Both scenarios initialize an OpenFeature provider, evaluate a boolean flag, change context, and evaluate again. Telemetry is disabled for DatadogProvider; no tracking hooks are registered for DatadogCoreProvider. DatadogProvider fetches precomputed assignments for each context; DatadogCoreProvider receives rules from fetchRulesConfiguration once and evaluates locally.

Sizes include OpenFeature and the same small scenario harness. Configuration responses are supplied by Playwright and are not bundled. These are complete scenario JS sizes, not configuration payload sizes or isolated provider/Protobuf costs; the difference between rows is not a decoder-only delta.

Scenario HTML JS Assets Raw JS Gzip JS Protobuf Markers
DatadogProvider (precomputed fetching) provider.html 19 83.5 KiB 31.5 KiB no
DatadogCoreProvider + fetchRulesConfiguration core-provider.html 14 149.5 KiB 46.8 KiB yes

Dependency checks passed: no Protobuf markers in default/precomputed scenarios; markers present in rules-based scenarios.

OpenFeature Browser Tracking Hook Bundle Sizes

Synthetic entrypoints import and call tracking hook factories from the packed @datadog/openfeature-browser/rules-based ESM package. Deltas are measured against the no-hook baseline from the same Vite production build.

Scenario HTML JS Assets Raw JS Raw Δ Gzip JS Gzip Δ
tracking hook baseline tracking-baseline.html 6 2.6 KiB 0 B 1.6 KiB 0 B
exposure logging hook tracking-exposure.html 14 57.5 KiB +54.9 KiB 22.2 KiB +20.6 KiB
evaluation logging hook tracking-evaluation.html 14 53.2 KiB +50.7 KiB 20.8 KiB +19.2 KiB
RUM tracking hook tracking-rum.html 10 5.0 KiB +2.4 KiB 2.9 KiB +1.4 KiB
all tracking hooks tracking-all.html 17 61.7 KiB +59.1 KiB 24.1 KiB +22.5 KiB

This report shows current PR artifact sizes only; it does not compare against the base branch.

Comment thread packages/browser/src/openfeature/core-provider.ts Outdated
Comment thread packages/core/src/configuration/flag-key-obfuscation.ts
Comment thread test-app/tests/smoke.spec.ts Outdated
Comment thread packages/browser/src/transport/fetchConfiguration.ts
Comment thread packages/browser/README.md
Comment thread packages/core/src/evaluation/precomputed-evaluation.ts Outdated
@leoromanovsky
leoromanovsky requested a review from aarsilv October 6, 2026 22:08

@aarsilv aarsilv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for iterating! 🚀 Just a few nits left you can take or leave as you see fit!

Comment thread packages/browser/src/openfeature/core-provider.ts Outdated
Comment thread packages/core/src/configuration/flag-key-obfuscation.ts
Comment thread test-app/tests/smoke.spec.ts Outdated
Comment thread packages/browser/src/transport/fetchConfiguration.ts
Comment thread packages/browser/README.md
Comment thread packages/browser/src/transport/fetchConfiguration.ts Outdated
Comment thread packages/browser/src/openfeature/provider.ts Outdated
Comment thread packages/core/src/configuration/flag-key-obfuscation.ts
Comment thread packages/browser/src/openfeature/core-provider.ts Outdated
Comment thread packages/browser/README.md
@leoromanovsky
leoromanovsky merged commit fcd1955 into main Oct 7, 2026
5 checks passed
@leoromanovsky
leoromanovsky deleted the browser-flag-key-obfuscation branch October 7, 2026 14:28
@linear-code

linear-code Bot commented Oct 8, 2026

Copy link
Copy Markdown

FFLSDK-262

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