You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Proposal] Feature flag distribution layer & observability #390
OpenFeature standardizes how a single flag is evaluated and how that one evaluation is reported to telemetry (Appendix D → OpenTelemetry feature_flag.*). It says nothing about the distribution layer: the set of flags actually available to a service — which flags exist, their default values, whether they're enabled or disabled, whether they're simple or targeted, their variants, flag-set membership, and version.
That gap is exactly where vendors fill the vacuum with proprietary dashboards and exports. This proposal is to define a provider-neutral data model for the available-flag inventory and an emitter-agnostic way to observe it, so the OpenFeature thesis extends past evaluation: any flag management tool × any observability tool, as long as both speak the standard. You should be able to see the full picture of your flag system from open standards rather than a per-vendor approach, and swap either side without losing that picture.
Background / problem
What the spec covers today is strictly per-evaluation:
Appendix D maps that one evaluation onto OpenTelemetry feature-flag event records (feature_flag.key, feature_flag.result.variant, feature_flag.result.reason, feature_flag.set.id, feature_flag.version, feature_flag.provider.name, …).
To analyze a flag system — audit what exists, spot stale or always-default flags, see which flags carry targeting, correlate config across services — you need the inventory, not a stream of individual lookups. That inventory exists today only implicitly, and never as something you can observe through a standard:
The in-memory provider's flag-set schema already describes flags as variants + defaultVariant + optional contextEvaluator (targeting) + disabled + flagMetadata (see appendix-a-included-utilities.md and assets/gherkin/test-flags.json). This is effectively a distribution-layer schema that we use for testing but never standardized as a thing to expose or observe.
OFREP defines bulk evaluation — a natural inventory source.
flagSetId and version already live in flag metadata.
PROVIDER_CONFIGURATION_CHANGED already signals that the inventory changed.
So the building blocks exist; what's missing is a standard that ties them into a describable, observable distribution layer.
Why "who emits it" is the hard part
The obvious instinct — "the SDK should emit it" or "the vendor should emit it" — breaks down:
The SDK/provider only reliably knows the flags it has touched. In the dynamic-context paradigm it resolves flags lazily and may never see the full set; in the static-context paradigm it gets a bulk-evaluated set for one context, not the definitions or targeting.
The management tool / source knows everything — but in a GitOps / declarative setup the "source" is a YAML file in a git repo with no runtime to emit anything. The component that knows the inventory isn't always a running process.
The conclusion that makes this tractable: define the distribution layer as a neutral data model / semantic convention, decoupled from who emits it. One schema; multiple possible producers. That's also what keeps it vendor-neutral — the same shape whether it comes from a SaaS backend, a sync service, an SDK, or a build step.
Proposed distribution data model
The model is two-level: a flag set carries its own identity/metadata and contains the per-flag entries. This avoids repeating set-wide values on every flag, and matches how the rest of the spec already treats flagSetId/version as describing a flag set rather than an individual flag.
Flag-set metadata (describes the set as a whole):
Field
Meaning
Existing basis
flagSetId
logical identifier for the flag set
Appendix D flag metadata
version
version string for the flag set
Appendix D flag metadata
provider/source identity
which system this set came from
feature_flag.provider.name
set metadata
arbitrary additional set-level properties
—
Per-flag (one entry per flag in the set):
Field
Meaning
Existing basis
key
flag identifier
flag key
type
boolean / string / number / structure
flag type
defaultVariant
the variant key served when no targeting matches
in-memory provider schema
default value
value of the default variant (privacy caveat below)
in-memory provider schema
variant keys
the set of variant names (not values) — optional
in-memory provider schema
disabled
enabled vs disabled
in-memory provider schema (disabled: true)
complexity
simple vs targeted — derivable from presence of targeting/contextEvaluator
in-memory provider schema
flagMetadata
arbitrary additional per-flag properties
flag metadata
Two intentional reductions from the in-memory schema:
Variant values are excluded; only variant keys are kept (and even those are optional). The default value is enough to detect "the default was served" (compare it against the feature_flag.result.variant already in evaluation telemetry). Variant keys are cheap and non-sensitive and let you spot dead variants / multivariate cardinality. Variant values would inherit the same volume and privacy problems Appendix D already flags for feature_flag.result.value, for little analytical gain.
"Complexity" need not be a stored field — it's derivable from whether targeting rules are present (how the in-memory provider expresses it via contextEvaluator).
The intent throughout is the same vocabulary as evaluation telemetry, the in-memory schema, and OFREP — not a third, incompatible shape.
Emission responsibility — the heart of this proposal
One neutral schema, several legitimate producers. Rather than mandate one, recommend by proximity to source-of-truth:
Flag management backend / SaaS — emits the authoritative inventory directly. Best fidelity.
Sync service (e.g. flagd gRPC sync) — already holds the full flag set in transit; a natural emitter.
Provider / SDK — emits what it can observe; the fallback when nothing upstream emits. Covers the inventory the app actually touches even if it's partial.
GitOps / build-time emitter — for declarative sources, a CI/build step reads the flag definitions from the repo and emits the inventory, since there's no runtime owner.
Recommendation: the component nearest the source-of-truth SHOULD emit; the SDK MAY emit as a fallback; no single emitter is mandated. Because the schema is identical regardless of producer, an observability tool consumes it the same way no matter where it originated — which is the whole point.
Telemetry representation (open question — for discussion)
Deliberately not deciding the signal type yet; enumerating options and trade-offs for the thread:
Logs / events per flag definition — mirrors how OTel models evaluations today; rich, good for "describe each flag." Volume on large flag sets.
Metrics (gauges per flag) — e.g. a gauge for enabled/disabled, a dimension for complexity. Great for dashboards/alerting; weak for rich definition detail.
Resource-level attributes — flag-set / provider identity as resource attributes that decorate the above.
Combinations of the above.
Whatever we pick, it should be proposed upstream to OpenTelemetry semantic conventions, the same path the evaluation conventions took (see Appendix D history). This is the key decision I want maintainer input on.
Relationship to existing work
Appendix D — this is the inventory counterpart to its per-evaluation conventions; likely lands as an Appendix D section and/or a new spec section.
OFREP — bulk evaluation is a concrete inventory source the model can build on.
PROVIDER_CONFIGURATION_CHANGED — the natural trigger to (re-)emit inventory when it changes.
Hey @aepfli, I'm having trouble understanding what problem this proposal is solving. The example talks about the in-memory provider, and the proposed per-flag metadata is either redundant or difficult to extract from a "real" provider. I think it could be useful to be able to trace flag updates from the source to the SDKs. I'm just not sure this proposal addresses that.
Fair — the in-memory provider framing muddied it, and I buried the actual problem.
The only observable view of a flag system today is evaluation telemetry. It shows flags that were hit, one value at a time, for whatever contexts happened to occur. It can't tell you a flag exists but is never evaluated, or that a flag is globally disabled rather than just returning its default for this context. That's the gap: there's no standard way to see the configured inventory — what exists, what's enabled, what carries targeting — independent of traffic. That's what auditing, drift detection across services, and dead-flag cleanup need.
You're right that default and variant overlap with evaluation telemetry. I'd drop them. The fields that aren't recoverable from evaluations are the structural ones: existence, enabled/disabled, and simple-vs-targeted.
You're also right that the SDK can't extract this from most real providers, and that's the part I under-sold. The SDK isn't meant to synthesize it. The emitters are the components that already hold the full set — the management plane, the source, the sync service. flagd's sync already carries it; plenty of vendors expose list-flags APIs. The point is to standardize the shape so the analysis story isn't per-vendor.
Tracing updates from source to SDK is a different axis — that's the propagation lineage work in #313, "how did this change flow." This is "what is configured right now." They're complementary, and I'm happy to keep them apart.
So the question back to the TSC: is describing the inventory worth standardizing on its own, or do you only see value in the propagation side right now?
Okay, if I understand correctly, you're interested in a new API that would expose the flag configurations outside of a flag evaluation. What's the use case you're trying to solve? From my perspective, I think it should be the vendor's responsibility. Questions about how, where, when, ect can be answered with evaluation information. Supporting use cases like stale flag detection should live in the management tool.
I'd also be concerned about supporting this on clients. We'd have to expose too much information about the flag configuration to end users.
No new API. I want to standardize the observable signal for the inventory — most likely an improvement to the OpenTelemetry semantic conventions, if we agree there's a need.
The situation today: the management tool has the inventory, but evaluation telemetry lands in a separate observability system (unless your observability vendor also sells flag management). To catch dead or stale flags you either ingest evaluation data into the management tool, or export the inventory into the observability tool. Both directions work — but everyone does it in their own proprietary shape. A convention fixes the correlation problem: switching providers doesn't mean reworking dashboards to get the same data.
On responsibility: I agree it's the vendor's job to emit it. That's the point. The proposal doesn't ask the SDK or the spec to build the feature — it defines the shape the emission takes, the same way Appendix D didn't change how anyone evaluates flags, only how evaluations look in telemetry. We even have the natural trigger already: PROVIDER_CONFIGURATION_CHANGED signals exactly when the inventory changed and a re-emission makes sense. Stale-flag detection can live in the management tool, but plenty of teams run this analysis in their observability stack, and today that's per-vendor glue.
On clients: agreed, and that shapes the design. The emitter sits server-side, near the source of truth — management plane, sync service, build step. Nothing requires exposing configuration to end users, and the reduced field set (existence, enabled/disabled, targeted-or-not) deliberately leaves out values.
So the question isn't the mechanism — it's whether we want to normalize this inventory signal at all.
One thing I didn't account for when writing this: AI agents.
Agents need the inventory to act well. Not after the fact — before. Knowing which flags exist, which variants are available, and what context variables are in play is how an agent decides what it can and can't do. Without that, it's either guessing or getting the information injected in some vendor-specific way.
The good news is we don't need a new interface for this. If the inventory is observable — emitted through standard observability tooling — agents can consume it the same way any other system does. That's actually a stronger argument for getting the distribution layer and its telemetry representation right: it's not just for humans reading dashboards.
@aepfli how about handling this in PROVIDER_READY and PROVIDER_CONFIGURATION_CHANGED? Those are the natural moments a provider "has" its flags; feels like the right trigger for a corresponding OTel event carrying the inventory shape you describe.
This works cleanly for providers that actually hold the full set (static-context, in-process flagd, OFREP-backed). For lazy-resolving dynamic-context providers it becomes best-effort or no-op; not a blocker IMO but worth being upfront about and thinking about how to accomplish the same thing in such providers.
This might also settle the signal-type question you flagged; if we go OTel-event, we get the "describe each flag" richness of your logs option without inventing a new signal shape.
API-wise it stays mostly invisible to application authors. Provider authors would need a way to attach the inventory to those event emissions (extending ProviderEventDetails or similar); non-trivial but non-breaking. WDYT?
The only question, if we should also add some kind of optional standardization to the flag metadata. Currently the flag information is limited, without a description or intent of the flag it will get hard for eg. Agents to assume anything about possible flags.
Should we also try to standardize this via semantic convention, as a standardized signal?
The only question, if we should also add some kind of optional standardization to the flag metadata. Currently the flag information is limited, without a description or intent of the flag it will get hard for eg. Agents to assume anything about possible flags.
Should we also try to standardize this via semantic convention, as a standardized signal?
Yes, sorry I wasn't being explicit about that. What I propose would only be practically useful with the standardization you describe. I was only talking about where/when to emit and what signal to use, but you are right that we'd need to specify the shape or we don't really gain much in terms of interop and general compatibilty.
Wanna share our "solution" related to this discussion, as a eCommerce team using OpenFeature JS SDK - hopefully it's valuable.
We defined our own thin abstraction over OpenFeature with the API to define a feature flag keeping flag key, fallback and metadata together. Example would be this:
import{flag}from'@repo/feature-flags/core';exportconstQuickCheckoutFlag=flag.boolean({key: 'ecommerce.checkout-service.quick-checkout',fallback: false,description: 'Enables quick checkout flow',removalCondition: 'Remove after full rollout to production',category: 'rollout',experiment: {reference: 'quick-checkout-abtest',},// also considering to add variants(variations) to this definition but don't have a use case yet});// server side usageconstquickCheckoutEnabled=featureFlags.decide(QuickCheckoutFlag,{/* evaluationContext */});// client side usageconstquickCheckoutEnabled=useDecide(QuickCheckoutFlag);
This allows us to:
have better developer's ergonomics via a unified decide API(since flag definition encapsulates the value type)
have a basic validation of flag definition(e.g. flag key following a conventional pattern or management tool requirements)
allows flag definition(metadata) to be used by hooks kinda as a static evaluation context
descriptive metadata simplifies code review and maintenance
I feel like this could be a great foundation for more use cases like:
linting plugins for detecting stale flags or incorrect usage patterns
setup flags in the management tool as a part of CI/CD pipeline instead of developers creating them manually
... probably much more which I just have not discovered yet
Activity
aepfli commentedon Jun 10, 2026
@open-feature/technical-steering-committee wdyt about this, is this something worth to dig deeper?
beeme1mr commentedon Jun 10, 2026
Hey @aepfli, I'm having trouble understanding what problem this proposal is solving. The example talks about the in-memory provider, and the proposed per-flag metadata is either redundant or difficult to extract from a "real" provider. I think it could be useful to be able to trace flag updates from the source to the SDKs. I'm just not sure this proposal addresses that.
aepfli commentedon Jun 10, 2026
Fair — the in-memory provider framing muddied it, and I buried the actual problem.
The only observable view of a flag system today is evaluation telemetry. It shows flags that were hit, one value at a time, for whatever contexts happened to occur. It can't tell you a flag exists but is never evaluated, or that a flag is globally disabled rather than just returning its default for this context. That's the gap: there's no standard way to see the configured inventory — what exists, what's enabled, what carries targeting — independent of traffic. That's what auditing, drift detection across services, and dead-flag cleanup need.
You're right that default and variant overlap with evaluation telemetry. I'd drop them. The fields that aren't recoverable from evaluations are the structural ones: existence, enabled/disabled, and simple-vs-targeted.
You're also right that the SDK can't extract this from most real providers, and that's the part I under-sold. The SDK isn't meant to synthesize it. The emitters are the components that already hold the full set — the management plane, the source, the sync service. flagd's sync already carries it; plenty of vendors expose list-flags APIs. The point is to standardize the shape so the analysis story isn't per-vendor.
Tracing updates from source to SDK is a different axis — that's the propagation lineage work in #313, "how did this change flow." This is "what is configured right now." They're complementary, and I'm happy to keep them apart.
So the question back to the TSC: is describing the inventory worth standardizing on its own, or do you only see value in the propagation side right now?
beeme1mr commentedon Jun 10, 2026
Okay, if I understand correctly, you're interested in a new API that would expose the flag configurations outside of a flag evaluation. What's the use case you're trying to solve? From my perspective, I think it should be the vendor's responsibility. Questions about how, where, when, ect can be answered with evaluation information. Supporting use cases like stale flag detection should live in the management tool.
I'd also be concerned about supporting this on clients. We'd have to expose too much information about the flag configuration to end users.
aepfli commentedon Jun 11, 2026
No new API. I want to standardize the observable signal for the inventory — most likely an improvement to the OpenTelemetry semantic conventions, if we agree there's a need.
The situation today: the management tool has the inventory, but evaluation telemetry lands in a separate observability system (unless your observability vendor also sells flag management). To catch dead or stale flags you either ingest evaluation data into the management tool, or export the inventory into the observability tool. Both directions work — but everyone does it in their own proprietary shape. A convention fixes the correlation problem: switching providers doesn't mean reworking dashboards to get the same data.
On responsibility: I agree it's the vendor's job to emit it. That's the point. The proposal doesn't ask the SDK or the spec to build the feature — it defines the shape the emission takes, the same way Appendix D didn't change how anyone evaluates flags, only how evaluations look in telemetry. We even have the natural trigger already:
PROVIDER_CONFIGURATION_CHANGEDsignals exactly when the inventory changed and a re-emission makes sense. Stale-flag detection can live in the management tool, but plenty of teams run this analysis in their observability stack, and today that's per-vendor glue.On clients: agreed, and that shapes the design. The emitter sits server-side, near the source of truth — management plane, sync service, build step. Nothing requires exposing configuration to end users, and the reduced field set (existence, enabled/disabled, targeted-or-not) deliberately leaves out values.
So the question isn't the mechanism — it's whether we want to normalize this inventory signal at all.
aepfli commentedon Jun 18, 2026
One thing I didn't account for when writing this: AI agents.
Agents need the inventory to act well. Not after the fact — before. Knowing which flags exist, which variants are available, and what context variables are in play is how an agent decides what it can and can't do. Without that, it's either guessing or getting the information injected in some vendor-specific way.
The good news is we don't need a new interface for this. If the inventory is observable — emitted through standard observability tooling — agents can consume it the same way any other system does. That's actually a stronger argument for getting the distribution layer and its telemetry representation right: it's not just for humans reading dashboards.
toddbaert commentedon Jul 2, 2026
@aepfli how about handling this in
PROVIDER_READYandPROVIDER_CONFIGURATION_CHANGED? Those are the natural moments a provider "has" its flags; feels like the right trigger for a corresponding OTel event carrying the inventory shape you describe.This works cleanly for providers that actually hold the full set (static-context, in-process flagd, OFREP-backed). For lazy-resolving dynamic-context providers it becomes best-effort or no-op; not a blocker IMO but worth being upfront about and thinking about how to accomplish the same thing in such providers.
This might also settle the signal-type question you flagged; if we go OTel-event, we get the "describe each flag" richness of your logs option without inventing a new signal shape.
API-wise it stays mostly invisible to application authors. Provider authors would need a way to attach the inventory to those event emissions (extending ProviderEventDetails or similar); non-trivial but non-breaking. WDYT?
aepfli commentedon Jul 2, 2026
I like the idea.
The only question, if we should also add some kind of optional standardization to the flag metadata. Currently the flag information is limited, without a description or intent of the flag it will get hard for eg. Agents to assume anything about possible flags.
Should we also try to standardize this via semantic convention, as a standardized signal?
toddbaert commentedon Jul 2, 2026
Yes, sorry I wasn't being explicit about that. What I propose would only be practically useful with the standardization you describe. I was only talking about where/when to emit and what signal to use, but you are right that we'd need to specify the shape or we don't really gain much in terms of interop and general compatibilty.
AndrewBoklashko commentedon Aug 14, 2026
Wanna share our "solution" related to this discussion, as a eCommerce team using OpenFeature JS SDK - hopefully it's valuable.
We defined our own thin abstraction over OpenFeature with the API to define a feature flag keeping flag key, fallback and metadata together. Example would be this:
This allows us to:
decideAPI(since flag definition encapsulates the value type)I feel like this could be a great foundation for more use cases like: