Summary
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:
- The evaluation API resolves one flag at a time:
resolve*Value(key, default, ctx) → evaluation details + flag metadata.
- 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
Out of scope / future
- An SDK API surface to expose the inventory to application code (a possible follow-up spec section).
- Governance / access-control concerns around exposing the full inventory.
- Concrete spec requirements — those come as a separate PR against
open-feature/spec once the model, emission approach, and signal type are agreed here.
Summary
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:
resolve*Value(key, default, ctx)→ evaluation details + flag metadata.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:
variants+defaultVariant+ optionalcontextEvaluator(targeting) +disabled+flagMetadata(seeappendix-a-included-utilities.mdandassets/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.flagSetIdandversionalready live in flag metadata.PROVIDER_CONFIGURATION_CHANGEDalready 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 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/versionas describing a flag set rather than an individual flag.Flag-set metadata (describes the set as a whole):
flagSetIdversionfeature_flag.provider.namePer-flag (one entry per flag in the set):
keytypedefaultVariantdisableddisabled: true)contextEvaluatorflagMetadataTwo intentional reductions from the in-memory schema:
feature_flag.result.variantalready 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 forfeature_flag.result.value, for little analytical gain.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:
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:
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
PROVIDER_CONFIGURATION_CHANGED— the natural trigger to (re-)emit inventory when it changes.provider-metadata-capability-discovery.md(closest analog: discovering provider capabilities),extend-provider-metadata.md,flag-metadata.md,metric-hooks.md,flagd-grpc-sync.md,inline-evaluation.md.Out of scope / future
open-feature/speconce the model, emission approach, and signal type are agreed here.