[Proposal] Feature flag distribution layer & observability

Open
#390 10 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
25/100
Issue type
Feature
Clarity
Needs clarification
Activity status
Quiet

Research direction

Start with specification/appendix-d-observability.md, then compare the in-memory provider schema in specification/appendix-a-included-utilities.md and assets/gherkin/test-flags.json with the bulk-evaluation material in specification/appendix-c. Review related issues #372, #373, and #313 before proposing anything. Done requires maintainer agreement on the inventory model, emitter responsibility, and telemetry signal, followed by a separate spec PR.

Written by the indexing model from the issue text.

Description

enhancement specification

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

  • 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.
  • #372 / #373 — this belongs to the same "native OTel telemetry" track; filing here to join that conversation, not fork it.
  • #313 (propagation correlation) — kept separate. It correlates one async propagation with one evaluation; it's a sibling sub-case of distribution observability, referenced but not folded in.
  • Prior art: the (now largely dormant) OFEP repo still has useful precedent — 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

  • 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.
Dominant language
Python
Stars
1.3k
Forks
58
Avg merge
2d 18h
Merged PRs (30d)
2

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from open-feature/spec

All issues in open-feature/spec

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.