From 5963907eb162042662fa0b2b581275a0dadf1660 Mon Sep 17 00:00:00 2001 From: Jonathan Norris Date: Thu, 18 Jun 2026 16:24:33 -0400 Subject: [PATCH 01/11] feat: supply the bound domain to provider initialization Signed-off-by: Jonathan Norris --- specification.json | 14 ++++++++++++++ specification/sections/01-flag-evaluation.md | 8 ++++++++ specification/sections/02-providers.md | 20 ++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/specification.json b/specification.json index 35ce7efc2..65def6e1c 100644 --- a/specification.json +++ b/specification.json @@ -35,6 +35,13 @@ "RFC 2119 keyword": "SHOULD", "children": [] }, + { + "id": "Requirement 1.1.2.5", + "machine_id": "requirement_1_1_2_5", + "content": "When the `provider mutator` invokes a provider's `initialize` function, the `API` MUST supply the `domain` the provider is being registered under, if any.", + "RFC 2119 keyword": "MUST", + "children": [] + }, { "id": "Requirement 1.1.3", "machine_id": "requirement_1_1_3", @@ -529,6 +536,13 @@ } ] }, + { + "id": "Requirement 2.4.3", + "machine_id": "requirement_2_4_3", + "content": "The `provider` MAY define an initialization function which additionally accepts the `domain` it is registered under, if any.", + "RFC 2119 keyword": "MAY", + "children": [] + }, { "id": "Requirement 2.5.1", "machine_id": "requirement_2_5_1", diff --git a/specification/sections/01-flag-evaluation.md b/specification/sections/01-flag-evaluation.md index cb47dffe4..b88b23db0 100644 --- a/specification/sections/01-flag-evaluation.md +++ b/specification/sections/01-flag-evaluation.md @@ -76,6 +76,14 @@ Client client = OpenFeatureAPI.getInstance().getClient('domain-1'); Though it's possible to use [events](./05-events.md) to await provider readiness, such functions can make things simpler for `application authors` and `integrators`. Implementations indicate an error in a manner idiomatic to the language in use (returning an error, throwing an exception, etc). +#### Requirement 1.1.2.5 + +> When the `provider mutator` invokes a provider's `initialize` function, the `API` **MUST** supply the `domain` the provider is being registered under, if any. + +This allows providers to scope domain-specific behavior, such as partitioning a persistent cache, to the `domain` they are bound to. + +See [provider initialization](./02-providers.md#24-initialization), [domain](../glossary.md#domain) for details. + #### Requirement 1.1.3 > The `API` **MUST** provide a function to bind a given `provider` to one or more clients using a `domain`. If the domain already has a bound provider, it is overwritten with the new mapping. diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index baa7d87e3..08a0c39d4 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -207,6 +207,26 @@ If the error is irrecoverable (perhaps due to bad credentials or invalid configu see: [error codes](../types.md#error-code) +#### Requirement 2.4.3 + +> The `provider` **MAY** define an initialization function which additionally accepts the `domain` it is registered under, if any. + +When a provider is bound to a [domain](../glossary.md#domain), it may need to scope behavior to that domain, such as partitioning a persistent cache or labeling telemetry, so that multiple providers sharing the same storage do not collide. +Supplying the `domain` at initialization mirrors how the global `evaluation context` is supplied (see [Requirement 2.4.1](#requirement-241)). + +```java +class MyProvider implements Provider { + // the global context and the bound domain are passed to the initialization function + void initialize(EvaluationContext initialContext, String domain) { + this.cacheKeyPrefix = domain; // scope persisted state to this domain + this.flagCache = this.restClient.bulkEvaluate(initialContext); + } +} +``` + +A `provider` instance is initialized only once, even when bound to multiple `domains`; in that case the `domain` supplied is the one under which it was first registered. +The default provider, which is not bound to a domain, is initialized without one. + ### 2.5. Shutdown [![hardening](https://img.shields.io/static/v1?label=Status&message=hardening&color=yellow)](https://github.com/open-feature/spec/tree/main/specification#hardening) From 80aafb633366652c86892613887033a4ace9fee7 Mon Sep 17 00:00:00 2001 From: Jonathan Norris Date: Thu, 18 Jun 2026 16:28:20 -0400 Subject: [PATCH 02/11] docs: use typescript example for domain initialization Signed-off-by: Jonathan Norris --- specification/sections/02-providers.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index 08a0c39d4..e3abf8429 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -214,12 +214,11 @@ see: [error codes](../types.md#error-code) When a provider is bound to a [domain](../glossary.md#domain), it may need to scope behavior to that domain, such as partitioning a persistent cache or labeling telemetry, so that multiple providers sharing the same storage do not collide. Supplying the `domain` at initialization mirrors how the global `evaluation context` is supplied (see [Requirement 2.4.1](#requirement-241)). -```java +```typescript class MyProvider implements Provider { // the global context and the bound domain are passed to the initialization function - void initialize(EvaluationContext initialContext, String domain) { - this.cacheKeyPrefix = domain; // scope persisted state to this domain - this.flagCache = this.restClient.bulkEvaluate(initialContext); + async initialize(initialContext: EvaluationContext, domain?: string): Promise { + this.domain = domain; } } ``` From c2ed7356eca0dc11333c16dc7ac42122dcbeefe2 Mon Sep 17 00:00:00 2001 From: Jonathan Norris Date: Thu, 18 Jun 2026 17:06:13 -0400 Subject: [PATCH 03/11] refactor: fold domain supply into requirements 2.4.1 and 1.1.2.2 Signed-off-by: Jonathan Norris --- specification.json | 18 ++-------- specification/sections/01-flag-evaluation.md | 10 +----- specification/sections/02-providers.md | 38 ++++++-------------- 3 files changed, 14 insertions(+), 52 deletions(-) diff --git a/specification.json b/specification.json index 65def6e1c..fd6762bd7 100644 --- a/specification.json +++ b/specification.json @@ -17,7 +17,7 @@ { "id": "Requirement 1.1.2.2", "machine_id": "requirement_1_1_2_2", - "content": "The `provider mutator` function MUST invoke the `initialize` function on the newly registered provider before using it to resolve flag values.", + "content": "The `provider mutator` function MUST invoke the `initialize` function on the newly registered provider before using it to resolve flag values, supplying the global `evaluation context` and the `domain` the provider is being registered under, if any.", "RFC 2119 keyword": "MUST", "children": [] }, @@ -35,13 +35,6 @@ "RFC 2119 keyword": "SHOULD", "children": [] }, - { - "id": "Requirement 1.1.2.5", - "machine_id": "requirement_1_1_2_5", - "content": "When the `provider mutator` invokes a provider's `initialize` function, the `API` MUST supply the `domain` the provider is being registered under, if any.", - "RFC 2119 keyword": "MUST", - "children": [] - }, { "id": "Requirement 1.1.3", "machine_id": "requirement_1_1_3", @@ -517,7 +510,7 @@ { "id": "Requirement 2.4.1", "machine_id": "requirement_2_4_1", - "content": "The `provider` MAY define an initialization function which accepts the global `evaluation context` as an argument and performs initialization logic relevant to the provider.", + "content": "The `provider` MAY define an initialization function which accepts the global `evaluation context` and the bound `domain` (if any) as arguments and performs initialization logic relevant to the provider.", "RFC 2119 keyword": "MAY", "children": [] }, @@ -536,13 +529,6 @@ } ] }, - { - "id": "Requirement 2.4.3", - "machine_id": "requirement_2_4_3", - "content": "The `provider` MAY define an initialization function which additionally accepts the `domain` it is registered under, if any.", - "RFC 2119 keyword": "MAY", - "children": [] - }, { "id": "Requirement 2.5.1", "machine_id": "requirement_2_5_1", diff --git a/specification/sections/01-flag-evaluation.md b/specification/sections/01-flag-evaluation.md index b88b23db0..a8273add5 100644 --- a/specification/sections/01-flag-evaluation.md +++ b/specification/sections/01-flag-evaluation.md @@ -38,7 +38,7 @@ See [provider](./02-providers.md), [creating clients](#creating-clients) for det #### Requirement 1.1.2.2 -> The `provider mutator` function **MUST** invoke the `initialize` function on the newly registered provider before using it to resolve flag values. +> The `provider mutator` function **MUST** invoke the `initialize` function on the newly registered provider before using it to resolve flag values, supplying the global `evaluation context` and the `domain` the provider is being registered under, if any. Application authors can await the newly set `provider's` readiness using the `PROVIDER_READY` event. Provider instances which are already active (because they have been bound to another `domain` or otherwise) need not be initialized again. @@ -76,14 +76,6 @@ Client client = OpenFeatureAPI.getInstance().getClient('domain-1'); Though it's possible to use [events](./05-events.md) to await provider readiness, such functions can make things simpler for `application authors` and `integrators`. Implementations indicate an error in a manner idiomatic to the language in use (returning an error, throwing an exception, etc). -#### Requirement 1.1.2.5 - -> When the `provider mutator` invokes a provider's `initialize` function, the `API` **MUST** supply the `domain` the provider is being registered under, if any. - -This allows providers to scope domain-specific behavior, such as partitioning a persistent cache, to the `domain` they are bound to. - -See [provider initialization](./02-providers.md#24-initialization), [domain](../glossary.md#domain) for details. - #### Requirement 1.1.3 > The `API` **MUST** provide a function to bind a given `provider` to one or more clients using a `domain`. If the domain already has a bound provider, it is overwritten with the new mapping. diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index e3abf8429..dc083ab24 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -170,23 +170,26 @@ class MyProvider implements Provider { #### Requirement 2.4.1 -> The `provider` **MAY** define an initialization function which accepts the global `evaluation context` as an argument and performs initialization logic relevant to the provider. +> The `provider` **MAY** define an initialization function which accepts the global `evaluation context` and the bound `domain` (if any) as arguments and performs initialization logic relevant to the provider. Many feature flag frameworks or SDKs require some initialization before they can be used. They might require the completion of an HTTP request, establishing persistent connections, or starting timers or worker threads. The initialization function is an ideal place for such logic. -```java +The `domain` the provider is registered under is also supplied, allowing the provider to scope domain-specific behavior, such as partitioning a persistent cache, so that multiple providers sharing the same storage do not collide. +A `provider` instance is initialized only once, even when bound to multiple `domains`; in that case the `domain` supplied is the one under which it was first registered. +The default provider, which is not bound to a domain, is initialized without one. + +```typescript // MyProvider implementation of the initialize function defined in Provider class MyProvider implements Provider { //... - // the global context is passed to the initialization function - void initialize(EvaluationContext initialContext) { - /* - A hypothetical initialization function: make an initial call doing some bulk initial evaluation, start a worker to do periodic updates - */ - this.flagCache = this.restClient.bulkEvaluate(initialContext); + // the global context and the bound domain are passed to the initialization function + async initialize(initialContext: EvaluationContext, domain?: string): Promise { + this.domain = domain; + // A hypothetical initialization function: make an initial call doing some bulk initial evaluation, start a worker to do periodic updates + this.flagCache = await this.restClient.bulkEvaluate(initialContext); this.startPolling(); } @@ -207,25 +210,6 @@ If the error is irrecoverable (perhaps due to bad credentials or invalid configu see: [error codes](../types.md#error-code) -#### Requirement 2.4.3 - -> The `provider` **MAY** define an initialization function which additionally accepts the `domain` it is registered under, if any. - -When a provider is bound to a [domain](../glossary.md#domain), it may need to scope behavior to that domain, such as partitioning a persistent cache or labeling telemetry, so that multiple providers sharing the same storage do not collide. -Supplying the `domain` at initialization mirrors how the global `evaluation context` is supplied (see [Requirement 2.4.1](#requirement-241)). - -```typescript -class MyProvider implements Provider { - // the global context and the bound domain are passed to the initialization function - async initialize(initialContext: EvaluationContext, domain?: string): Promise { - this.domain = domain; - } -} -``` - -A `provider` instance is initialized only once, even when bound to multiple `domains`; in that case the `domain` supplied is the one under which it was first registered. -The default provider, which is not bound to a domain, is initialized without one. - ### 2.5. Shutdown [![hardening](https://img.shields.io/static/v1?label=Status&message=hardening&color=yellow)](https://github.com/open-feature/spec/tree/main/specification#hardening) From 216c6599ef2abba57c3788b633a7bcfb1b91a1f8 Mon Sep 17 00:00:00 2001 From: Jonathan Norris Date: Fri, 19 Jun 2026 10:09:54 -0400 Subject: [PATCH 04/11] docs: simplify domain wording in requirement 1.1.2.2 Signed-off-by: Jonathan Norris --- specification.json | 2 +- specification/sections/01-flag-evaluation.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/specification.json b/specification.json index fd6762bd7..e64c3cac7 100644 --- a/specification.json +++ b/specification.json @@ -17,7 +17,7 @@ { "id": "Requirement 1.1.2.2", "machine_id": "requirement_1_1_2_2", - "content": "The `provider mutator` function MUST invoke the `initialize` function on the newly registered provider before using it to resolve flag values, supplying the global `evaluation context` and the `domain` the provider is being registered under, if any.", + "content": "The `provider mutator` function MUST invoke the `initialize` function on the newly registered provider before using it to resolve flag values, supplying the bound `domain`, if any.", "RFC 2119 keyword": "MUST", "children": [] }, diff --git a/specification/sections/01-flag-evaluation.md b/specification/sections/01-flag-evaluation.md index a8273add5..d9bef9b1e 100644 --- a/specification/sections/01-flag-evaluation.md +++ b/specification/sections/01-flag-evaluation.md @@ -38,7 +38,7 @@ See [provider](./02-providers.md), [creating clients](#creating-clients) for det #### Requirement 1.1.2.2 -> The `provider mutator` function **MUST** invoke the `initialize` function on the newly registered provider before using it to resolve flag values, supplying the global `evaluation context` and the `domain` the provider is being registered under, if any. +> The `provider mutator` function **MUST** invoke the `initialize` function on the newly registered provider before using it to resolve flag values, supplying the bound `domain`, if any. Application authors can await the newly set `provider's` readiness using the `PROVIDER_READY` event. Provider instances which are already active (because they have been bound to another `domain` or otherwise) need not be initialized again. From 5a538ebde49c7caf43ec634693633802ff523e6d Mon Sep 17 00:00:00 2001 From: Jonathan Norris Date: Fri, 19 Jun 2026 10:14:31 -0400 Subject: [PATCH 05/11] docs: keep java example for requirement 2.4.1 Signed-off-by: Jonathan Norris --- specification/sections/02-providers.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index dc083ab24..9e7a6e98d 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -180,16 +180,18 @@ The `domain` the provider is registered under is also supplied, allowing the pro A `provider` instance is initialized only once, even when bound to multiple `domains`; in that case the `domain` supplied is the one under which it was first registered. The default provider, which is not bound to a domain, is initialized without one. -```typescript +```java // MyProvider implementation of the initialize function defined in Provider class MyProvider implements Provider { //... // the global context and the bound domain are passed to the initialization function - async initialize(initialContext: EvaluationContext, domain?: string): Promise { + void initialize(EvaluationContext initialContext, String domain) { this.domain = domain; - // A hypothetical initialization function: make an initial call doing some bulk initial evaluation, start a worker to do periodic updates - this.flagCache = await this.restClient.bulkEvaluate(initialContext); + /* + A hypothetical initialization function: make an initial call doing some bulk initial evaluation, start a worker to do periodic updates + */ + this.flagCache = this.restClient.bulkEvaluate(initialContext); this.startPolling(); } From c3eef4d102b3e41db8c0ef9ba00474b7167bed38 Mon Sep 17 00:00:00 2001 From: Jonathan Norris Date: Fri, 19 Jun 2026 10:14:54 -0400 Subject: [PATCH 06/11] docs: mark domain parameter nullable in 2.4.1 example Signed-off-by: Jonathan Norris --- specification/sections/02-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index 9e7a6e98d..f99019e5b 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -186,7 +186,7 @@ class MyProvider implements Provider { //... // the global context and the bound domain are passed to the initialization function - void initialize(EvaluationContext initialContext, String domain) { + void initialize(EvaluationContext initialContext, @Nullable String domain) { this.domain = domain; /* A hypothetical initialization function: make an initial call doing some bulk initial evaluation, start a worker to do periodic updates From db8210ea09726e35693b65b38f1d7747ffe39692 Mon Sep 17 00:00:00 2001 From: Jonathan Norris Date: Fri, 19 Jun 2026 13:53:51 -0400 Subject: [PATCH 07/11] feat: add domain-scoped provider declaration and single-domain binding enforcement Signed-off-by: Jonathan Norris --- specification.json | 22 ++++++++++++++++++++ specification/sections/01-flag-evaluation.md | 16 +++++++++++++- specification/sections/02-providers.md | 9 ++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/specification.json b/specification.json index e64c3cac7..e7972273b 100644 --- a/specification.json +++ b/specification.json @@ -70,6 +70,21 @@ "RFC 2119 keyword": "MUST NOT", "children": [] }, + { + "id": "Condition 1.1.8", + "machine_id": "condition_1_1_8", + "content": "The `provider` declares that it is `domain-scoped`.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 1.1.8.1", + "machine_id": "conditional_requirement_1_1_8_1", + "content": "The `provider mutator` MUST NOT bind a `domain-scoped` provider instance to more than one `domain`, rejecting any attempt to bind an already-bound instance to an additional `domain`.", + "RFC 2119 keyword": "MUST NOT", + "children": [] + } + ] + }, { "id": "Requirement 1.2.1", "machine_id": "requirement_1_2_1", @@ -529,6 +544,13 @@ } ] }, + { + "id": "Requirement 2.4.3", + "machine_id": "requirement_2_4_3", + "content": "The `provider` MAY declare that it is `domain-scoped`, indicating that it maintains state specific to a single `domain`, such as a persistent cache, that cannot be shared across `domains`.", + "RFC 2119 keyword": "MAY", + "children": [] + }, { "id": "Requirement 2.5.1", "machine_id": "requirement_2_5_1", diff --git a/specification/sections/01-flag-evaluation.md b/specification/sections/01-flag-evaluation.md index d9bef9b1e..fb968db4c 100644 --- a/specification/sections/01-flag-evaluation.md +++ b/specification/sections/01-flag-evaluation.md @@ -147,6 +147,20 @@ See [setting a provider](#setting-a-provider), [domain](../glossary.md#domain) f Clients may be created in critical code paths, and even per-request in server-side HTTP contexts. Therefore, in keeping with the principle that OpenFeature should never cause abnormal execution of the first party application, this function should never throw. Abnormal execution in initialization should instead occur during provider registration. +#### Condition 1.1.8 + +> The `provider` declares that it is `domain-scoped`. + +see: [Requirement 2.4.3](./02-providers.md#requirement-243) + +##### Conditional Requirement 1.1.8.1 + +> The `provider mutator` **MUST NOT** bind a `domain-scoped` provider instance to more than one `domain`, rejecting any attempt to bind an already-bound instance to an additional `domain`. + +A `domain-scoped` provider keys per-`domain` state on the single `domain` supplied to its `initialize` function. +Allowing the same instance to back a second `domain` would reintroduce the collision the declaration exists to prevent, so the `API` rejects the binding rather than sharing the instance. +Rejection should occur in a manner idiomatic to the implementation language (throwing, returning an error, etc.), and leaves any existing binding intact. + ### 1.2. Client Usage #### Requirement 1.2.1 @@ -577,7 +591,7 @@ import { createIsolatedOpenFeatureAPI } from '@openfeature/web-sdk/isolated'; > A `provider` instance **SHOULD NOT** be registered with more than one `API` instance simultaneously. Because the `API` instance manages the [lifecycle](./02-providers.md) of its associated providers (including initialization, shutdown, and event handling), binding a `provider` to more than one `API` instance could result in undefined behavior. -A `provider` instance can be registered with multiple `domains` within a single `API` instance. +A `provider` instance can be registered with multiple `domains` within a single `API` instance, unless it declares itself `domain-scoped` (see [Requirement 2.4.3](./02-providers.md#requirement-243)), in which case it can be bound to at most one `domain`. When a provider is no longer associated with an `API` instance, it can be registered to another. See [setting a provider](#setting-a-provider), [domain](../glossary.md#domain) for details. diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index f99019e5b..90434db8b 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -178,6 +178,7 @@ The initialization function is an ideal place for such logic. The `domain` the provider is registered under is also supplied, allowing the provider to scope domain-specific behavior, such as partitioning a persistent cache, so that multiple providers sharing the same storage do not collide. A `provider` instance is initialized only once, even when bound to multiple `domains`; in that case the `domain` supplied is the one under which it was first registered. +A provider that maintains `domain`-specific state can instead declare itself `domain-scoped` (see [Requirement 2.4.3](#requirement-243)), in which case it is restricted to a single `domain` and this ambiguity does not arise. The default provider, which is not bound to a domain, is initialized without one. ```java @@ -212,6 +213,14 @@ If the error is irrecoverable (perhaps due to bad credentials or invalid configu see: [error codes](../types.md#error-code) +#### Requirement 2.4.3 + +> The `provider` **MAY** declare that it is `domain-scoped`, indicating that it maintains state specific to a single `domain`, such as a persistent cache, that cannot be shared across `domains`. + +Most providers are stateless with respect to their `domain` and can safely back multiple `domains` from a single instance. +Providers that persist or cache `domain`-specific data need a stable, unambiguous `domain` to key that state on. +By declaring itself `domain-scoped`, such a provider signals that the `API` must bind it to at most one `domain` (see [Requirement 1.1.8](./01-flag-evaluation.md#condition-118)), guaranteeing the `domain` supplied to `initialize` is the only one the instance will ever serve. + ### 2.5. Shutdown [![hardening](https://img.shields.io/static/v1?label=Status&message=hardening&color=yellow)](https://github.com/open-feature/spec/tree/main/specification#hardening) From 3171b57f985574dba11e65cb8e56df036bc2f083 Mon Sep 17 00:00:00 2001 From: Jonathan Norris Date: Fri, 19 Jun 2026 14:15:07 -0400 Subject: [PATCH 08/11] docs: trim rationale from conditional requirement 1.1.8.1 Signed-off-by: Jonathan Norris --- specification/sections/01-flag-evaluation.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specification/sections/01-flag-evaluation.md b/specification/sections/01-flag-evaluation.md index fb968db4c..88ff29a20 100644 --- a/specification/sections/01-flag-evaluation.md +++ b/specification/sections/01-flag-evaluation.md @@ -158,7 +158,6 @@ see: [Requirement 2.4.3](./02-providers.md#requirement-243) > The `provider mutator` **MUST NOT** bind a `domain-scoped` provider instance to more than one `domain`, rejecting any attempt to bind an already-bound instance to an additional `domain`. A `domain-scoped` provider keys per-`domain` state on the single `domain` supplied to its `initialize` function. -Allowing the same instance to back a second `domain` would reintroduce the collision the declaration exists to prevent, so the `API` rejects the binding rather than sharing the instance. Rejection should occur in a manner idiomatic to the implementation language (throwing, returning an error, etc.), and leaves any existing binding intact. ### 1.2. Client Usage From 502c325ad62ee34d93024ce3b4ab71993b9d7ba5 Mon Sep 17 00:00:00 2001 From: Jonathan Norris Date: Fri, 19 Jun 2026 14:34:19 -0400 Subject: [PATCH 09/11] docs: clarify the domain argument is optional in requirement 2.4.1 Signed-off-by: Jonathan Norris --- specification.json | 2 +- specification/sections/02-providers.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/specification.json b/specification.json index e7972273b..ea9950d9a 100644 --- a/specification.json +++ b/specification.json @@ -525,7 +525,7 @@ { "id": "Requirement 2.4.1", "machine_id": "requirement_2_4_1", - "content": "The `provider` MAY define an initialization function which accepts the global `evaluation context` and the bound `domain` (if any) as arguments and performs initialization logic relevant to the provider.", + "content": "The `provider` MAY define an initialization function which accepts the global `evaluation context` and, optionally, the bound `domain` (if any), and performs initialization logic relevant to the provider.", "RFC 2119 keyword": "MAY", "children": [] }, diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index 90434db8b..21ce4f634 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -170,7 +170,7 @@ class MyProvider implements Provider { #### Requirement 2.4.1 -> The `provider` **MAY** define an initialization function which accepts the global `evaluation context` and the bound `domain` (if any) as arguments and performs initialization logic relevant to the provider. +> The `provider` **MAY** define an initialization function which accepts the global `evaluation context` and, optionally, the bound `domain` (if any), and performs initialization logic relevant to the provider. Many feature flag frameworks or SDKs require some initialization before they can be used. They might require the completion of an HTTP request, establishing persistent connections, or starting timers or worker threads. From 17279506740ce9da1f5f67a06b070f1deb48d27d Mon Sep 17 00:00:00 2001 From: Jonathan Norris Date: Fri, 19 Jun 2026 14:45:10 -0400 Subject: [PATCH 10/11] docs: require domain-scoped providers to accept the domain at initialization Signed-off-by: Jonathan Norris --- specification.json | 7 +++++++ specification/sections/02-providers.md | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/specification.json b/specification.json index ea9950d9a..5747700bf 100644 --- a/specification.json +++ b/specification.json @@ -551,6 +551,13 @@ "RFC 2119 keyword": "MAY", "children": [] }, + { + "id": "Requirement 2.4.4", + "machine_id": "requirement_2_4_4", + "content": "A `provider` that declares itself `domain-scoped` MUST accept the bound `domain` during initialization.", + "RFC 2119 keyword": "MUST", + "children": [] + }, { "id": "Requirement 2.5.1", "machine_id": "requirement_2_5_1", diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index 21ce4f634..275ff4920 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -221,6 +221,13 @@ Most providers are stateless with respect to their `domain` and can safely back Providers that persist or cache `domain`-specific data need a stable, unambiguous `domain` to key that state on. By declaring itself `domain-scoped`, such a provider signals that the `API` must bind it to at most one `domain` (see [Requirement 1.1.8](./01-flag-evaluation.md#condition-118)), guaranteeing the `domain` supplied to `initialize` is the only one the instance will ever serve. +#### Requirement 2.4.4 + +> A `provider` that declares itself `domain-scoped` **MUST** accept the bound `domain` during initialization. + +A `domain-scoped` declaration is only meaningful if the provider consumes the `domain` it is given to scope its state. +This is a contract on the provider; implementations may not be able to detect or reject a violation automatically, so it is not guaranteed to surface as a runtime error. + ### 2.5. Shutdown [![hardening](https://img.shields.io/static/v1?label=Status&message=hardening&color=yellow)](https://github.com/open-feature/spec/tree/main/specification#hardening) From e1a1c35f7ceff8d2a6aa83e9ac2bd42d9f3eb9c2 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Thu, 25 Jun 2026 15:30:07 -0400 Subject: [PATCH 11/11] fixup: redundant wording Signed-off-by: Todd Baert --- specification.json | 2 +- specification/sections/02-providers.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/specification.json b/specification.json index 5747700bf..8362a3a82 100644 --- a/specification.json +++ b/specification.json @@ -525,7 +525,7 @@ { "id": "Requirement 2.4.1", "machine_id": "requirement_2_4_1", - "content": "The `provider` MAY define an initialization function which accepts the global `evaluation context` and, optionally, the bound `domain` (if any), and performs initialization logic relevant to the provider.", + "content": "The `provider` MAY define an initialization function which accepts the global `evaluation context` and an optional bound `domain`, which performs initialization logic relevant to the provider.", "RFC 2119 keyword": "MAY", "children": [] }, diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index 275ff4920..6a5e2ac1f 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -170,7 +170,7 @@ class MyProvider implements Provider { #### Requirement 2.4.1 -> The `provider` **MAY** define an initialization function which accepts the global `evaluation context` and, optionally, the bound `domain` (if any), and performs initialization logic relevant to the provider. +> The `provider` **MAY** define an initialization function which accepts the global `evaluation context` and an optional bound `domain`, which performs initialization logic relevant to the provider. Many feature flag frameworks or SDKs require some initialization before they can be used. They might require the completion of an HTTP request, establishing persistent connections, or starting timers or worker threads.