From c20e59b89b919a363f9e0f447d72261d57e6c8b1 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Thu, 27 Nov 2025 15:39:34 -0500 Subject: [PATCH 01/21] docs: add docs for selector, metadata Signed-off-by: Todd Baert --- docs/concepts/selectors.md | 188 ++++++++++++++++ docs/guides/migrating-to-flag-sets.md | 164 ++++++++++++++ docs/reference/selector-syntax.md | 243 +++++++++++++++++++++ docs/reference/specifications/providers.md | 94 +++++++- docs/reference/sync-configuration.md | 73 ++++++- docs/troubleshooting.md | 38 ++++ 6 files changed, 795 insertions(+), 5 deletions(-) create mode 100644 docs/concepts/selectors.md create mode 100644 docs/guides/migrating-to-flag-sets.md create mode 100644 docs/reference/selector-syntax.md diff --git a/docs/concepts/selectors.md b/docs/concepts/selectors.md new file mode 100644 index 000000000..79cd86d17 --- /dev/null +++ b/docs/concepts/selectors.md @@ -0,0 +1,188 @@ +# Selectors + +Selectors are query expressions that allow you to filter flag configurations from flagd's sync service. They enable providers to request only specific subsets of flags instead of receiving all flags, making flagd more efficient and flexible for complex deployments. + +## Overview + +In flagd, **selectors** provide a way to query flags based on different criteria. This is particularly powerful because flagd decouples **flag sources** from **flag sets**, allowing for more granular control over which flags are synchronized and evaluated. + +### Key Concepts + +- **Flag Source**: Where flag configuration data comes from (file, HTTP endpoint, gRPC service, etc.) +- **Flag Set**: A logical grouping of flags identified by a `flagSetId` +- **Selector**: A query expression that filters flags by source, flag set, or other criteria +- **Flag Set Metadata**: The selector information is "reflected" back in response metadata for transparency + +## Source vs Flag Set Decoupling + +### Before: Tight Coupling + +Historically, each source provided exactly one flag set, and providers had to target specific sources: + +```yaml +# Old approach - targeting a specific source +selector: "my-flag-source.json" +``` + +### After: Flexible Flag Sets + +Now, sources and flag sets are decoupled. A single source can contain multiple flag sets, and flag sets can span multiple sources: + +```yaml +# New approach - targeting a logical flag set +selector: "flagSetId=project-42" +``` + +## Flag Set Configuration + +Flag sets are typically configured at the top level of a flag configuration, with all flags in that configuration inheriting the same `flagSetId`. This is the recommended approach for most use cases. + +### Set-Level Configuration + +The most common pattern is to set the `flagSetId` at the configuration level, where all flags inherit it: + +```json +{ + "metadata": { + "flagSetId": "payment-service", + "version": "v1.2.0" + }, + "flags": { + "new-checkout-flow": { + "state": "ENABLED", + "variants": { + "on": true, + "off": false + }, + "defaultVariant": "on" + }, + "stripe-integration": { + "state": "DISABLED", + "variants": { "on": true, "off": false }, + "defaultVariant": "off" + } + } +} +``` + +In this example, both `new-checkout-flow` and `stripe-integration` flags belong to the `payment-service` flag set. + +### Metadata Inheritance and Override + +Flagd uses a hierarchical metadata system: + +1. **Set-Level Metadata**: Defined in the top-level `metadata` section, inherited by all flags +2. **Flag-Level Metadata**: Defined in individual flag `metadata`, overrides set-level values for that flag +3. **Merged Result**: Flag evaluations return merged metadata with flag-level taking precedence + +### Flag-Level Overrides (Advanced) + +For advanced use cases, individual flags can override the set-level `flagSetId`: + +```json +{ + "metadata": { + "flagSetId": "payment-service", + "team": "payments" + }, + "flags": { + "standard-feature": { + "state": "ENABLED", + "variants": { "on": true, "off": false }, + "defaultVariant": "on" + // Inherits flagSetId: "payment-service" + }, + "experimental-feature": { + "metadata": { + "flagSetId": "experiments", // Override: belongs to different set + "owner": "research-team" + }, + "state": "DISABLED", + "variants": { "on": true, "off": false }, + "defaultVariant": "off" + } + } +} +``` + +In this example: +- `standard-feature` inherits `flagSetId: "payment-service"` from set level +- `experimental-feature` overrides to `flagSetId: "experiments"` +- Both flags inherit `team: "payments"` (unless overridden at flag level) + +## Flag Set Metadata "Reflection" + +When you make a request with a selector, flagd "reflects" the selector information back in the response metadata. This provides transparency about what was actually queried and helps with debugging. + +### Example + +**Request with selector:** +``` +Selector: "flagSetId=project-42" +``` + +**Response includes reflected metadata:** +```json +{ + "flags": { /* ... */ }, + "metadata": { + "flagSetId": "project-42" + } +} +``` + +This helps you: +- Verify that your selector was parsed correctly +- Debug complex selector queries +- Understand exactly what flags were returned +- Audit flag access patterns + +## Use Cases + +### Multi-Tenant Applications + +```yaml +# Tenant A's flags +selector: "flagSetId=tenant-a" + +# Tenant B's flags +selector: "flagSetId=tenant-b" +``` + +### Environment Separation + +```yaml +# Development environment +selector: "flagSetId=dev-features" + +# Production environment +selector: "flagSetId=prod-features" +``` + +### Feature Team Isolation + +```yaml +# Payment team's flags +selector: "flagSetId=payments" + +# User interface team's flags +selector: "flagSetId=ui-components" +``` + +### Legacy Source-Based Selection + +```yaml +# Still supported for backward compatibility +selector: "source=legacy-config.json" +``` + +## Best Practices + +1. **Use Flag Sets for Logical Grouping**: Prefer `flagSetId` over `source` for new deployments +2. **Plan Your Flag Set Strategy**: Design flag sets around logical boundaries (teams, features, environments) +3. **Leverage Metadata**: Use metadata for debugging and auditing +5. **Document Your Schema**: Clearly document your flag set naming conventions for your team + +## Migration Considerations + +The selector enhancement maintains full backward compatibility. See the [migration guide](../guides/migrating-to-flag-sets.md) for detailed guidance on transitioning from source-based to flag-set-based selection patterns. diff --git a/docs/guides/migrating-to-flag-sets.md b/docs/guides/migrating-to-flag-sets.md new file mode 100644 index 000000000..57f172410 --- /dev/null +++ b/docs/guides/migrating-to-flag-sets.md @@ -0,0 +1,164 @@ +# Migrating to Flag Sets + +This guide helps you transition from source-based selector patterns to flag-set-based patterns, taking advantage of flagd's enhanced selector capabilities while maintaining backward compatibility. + +## Understanding the Change + +### Before: Source-Based Selection + +In the traditional approach, providers targeted specific sources: + +```yaml +# Provider configuration targeting a source file +selector: "config/my-flags.json" +``` + +This created tight coupling between providers and sources: +- Providers had to know which source contained their flags +- Moving flags between sources required provider reconfiguration +- One source could only serve one logical set of flags + +### After: Flag Set-Based Selection + +With flag sets, providers target logical groupings of flags: + +```yaml +# Provider configuration targeting a flag set +selector: "flagSetId=my-application" +``` + +This provides flexibility: +- Providers are decoupled from sources +- Sources can contain multiple flag sets +- Flag sets can span multiple sources +- No breaking changes - old selectors still work + +## Migration Process + +### Step 1: Add Flag Set IDs to Configurations + +Add `flagSetId` to your flag configurations at the set level: + +**Before:** +```json +{ + "flags": { + "feature-a": { + "state": "ENABLED", + "variants": {"on": true, "off": false}, + "defaultVariant": "on" + } + } +} +``` + +**After:** +```json +{ + "metadata": { + "flagSetId": "my-application" + }, + "flags": { + "feature-a": { + "state": "ENABLED", + "variants": {"on": true, "off": false}, + "defaultVariant": "on" + } + } +} +``` + +### Step 2: Update Provider Configurations + +Change provider selectors from source-based to flag set-based: + +```java +// Before +new FlagdProvider(FlagdOptions.builder() + .selector("config/app-flags.json").build()); + +// After +new FlagdProvider(FlagdOptions.builder() + .selector("flagSetId=my-application").build()); +``` + +### Step 3: Verify Migration + +Test that selectors work correctly and check metadata reflection: + +```bash +curl -H "Flagd-Selector: flagSetId=my-application" \ + http://localhost:8014/ofrep/v1/evaluate/flags +``` + +## Flag Set Organization Patterns + +**By Application/Service:** +```yaml +flagSetId: "user-service" # All user-related flags +flagSetId: "payment-service" # All payment-related flags +``` + +**By Environment:** +```yaml +flagSetId: "development" # Dev-specific flags +flagSetId: "production" # Production flags +``` + +**By Team:** +```yaml +flagSetId: "frontend-team" # Frontend features +flagSetId: "backend-team" # Backend features +``` + +Choose the pattern that best matches your deployment and organizational structure. + +## Testing and Rollback + +**Test Migration:** +```bash +# Verify new selector works +curl -H "Flagd-Selector: flagSetId=my-app" \ + http://localhost:8014/ofrep/v1/evaluate/flags + +# Check backward compatibility +curl -H "Flagd-Selector: config/legacy-flags.json" \ + http://localhost:8014/ofrep/v1/evaluate/flags +``` + +**Rollback if Needed:** +- Revert provider configurations to source-based selectors +- Keep `flagSetId` metadata in flag configurations for future attempts +- Use metadata reflection to debug issues + +## Common Issues + +**No flags returned**: Check that `flagSetId` in selector matches flag configuration exactly + +**Wrong flags returned**: Look for flag-level `flagSetId` overrides or header/body selector conflicts + +**Selector ignored**: Verify selector syntax is correct (`flagSetId=value`, not `flagSetId:value`) + +## Best Practices + +- **Group logically**: Organize flags by service, environment, or team +- **Name consistently**: Use clear, descriptive flag set names +- **Test first**: Validate migration in non-production environments +- **Use metadata reflection**: Check reflected metadata for debugging + +## FAQ + +**Q: Do I have to migrate?** +A: No, source-based selectors continue to work. Migration is optional but recommended. + +**Q: Can flag sets span multiple sources?** +A: Yes, multiple sources can contribute flags to the same flag set. + +**Q: Can I mix selector types?** +A: Yes, different providers can use different selector patterns. + +## Additional Resources + +- [Selector Concepts](../concepts/selectors.md) - Understanding selectors and flag sets +- [Selector Syntax Reference](../reference/selector-syntax.md) - Complete syntax documentation +- [ADR: Decouple Flag Source and Set](../architecture-decisions/decouple-flag-source-and-set.md) - Technical decision rationale \ No newline at end of file diff --git a/docs/reference/selector-syntax.md b/docs/reference/selector-syntax.md new file mode 100644 index 000000000..c4c3fca51 --- /dev/null +++ b/docs/reference/selector-syntax.md @@ -0,0 +1,243 @@ +# Selector Syntax Reference + +This document provides the complete technical specification for flagd selector syntax, including supported operators, precedence rules, and metadata reflection behavior. + +## Syntax Overview + +Selectors use a simple key-value syntax to filter flags. Currently, selectors support single key-value pairs with plans to expand to more complex queries in the future. + +### Basic Syntax + +``` += +``` + +### Backward Compatibility Syntax + +``` + +``` + +When no `=` is present, the value is treated as a source selector for backward compatibility. + +## Supported Keys + +### `flagSetId` + +Selects flags belonging to a specific flag set. + +**Syntax:** +``` +flagSetId= +``` + +**Examples:** +``` +flagSetId=project-42 +flagSetId=dev-environment +flagSetId=team-payments +``` + +**Special Case - Empty Flag Set:** +``` +flagSetId= +``` +Selects flags that don't belong to any named flag set (equivalent to the "null" flag set). + +### `source` + +Selects flags from a specific source. + +**Syntax:** +``` +source= +``` + +**Examples:** +``` +source=config/flags.json +source=http://flag-server/config +source=./local-flags.yaml +``` + +## Selector Precedence + +When selectors are provided in multiple locations, flagd uses the following precedence order (highest to lowest): + +1. **gRPC Header**: `Flagd-Selector` header in gRPC metadata +2. **HTTP Header**: `Flagd-Selector` header in HTTP requests +3. **Request Body**: `selector` field in protobuf/JSON request body + +### Example: Header Precedence + +```bash +# gRPC request with both header and body selector +# Header takes precedence +grpcurl -H "Flagd-Selector: flagSetId=production" \ + -d '{"selector": "flagSetId=development"}' \ + localhost:8013 flagd.sync.v1.FlagSyncService/FetchAllFlags + +# Result: Uses "flagSetId=production" from header +``` + +## Metadata "Reflection" + +Flagd reflects selector information back in response metadata, providing transparency about query execution. + +### Reflection Behavior + +**Input Selector:** +``` +flagSetId=project-42 +``` + +**Reflected in Response Metadata:** +```json +{ + "metadata": { + "flagSetId": "project-42" + } +} +``` + +### Multiple Metadata Sources + +Reflected metadata includes: +- **Selector Information**: The parsed selector key-value pairs +- **Set-Level Metadata**: Metadata from the flag configuration itself +- **Source Context**: Additional context from sync operations + +### Metadata Inheritance + +Flagd uses a hierarchical metadata system where flags inherit metadata from their flag set: + +**Set-Level Metadata (Inherited):** +```json +{ + "metadata": { + "flagSetId": "payment-service", + "team": "payments", + "version": "1.2.0" + }, + "flags": { + "checkout-flow": { + "state": "ENABLED" + // Inherits all set-level metadata + } + } +} +``` + +**Flag-Level Metadata (Override):** +```json +{ + "metadata": { + "flagSetId": "payment-service", + "team": "payments" + }, + "flags": { + "experimental-feature": { + "metadata": { + "flagSetId": "experiments", // Overrides set-level + "owner": "research-team" // Adds flag-specific metadata + // Still inherits "team": "payments" + }, + "state": "DISABLED" + } + } +} +``` + +## Examples + +### Flag Set Selection + +```bash +# Select flags from the "payments" flag set +curl -H "Flagd-Selector: flagSetId=payments" \ + http://localhost:8014/ofrep/v1/evaluate/flags +``` + +### Source Selection (Legacy) + +```bash +# Select flags from a specific source (backward compatibility) +curl -H "Flagd-Selector: source=config/prod-flags.json" \ + http://localhost:8014/ofrep/v1/evaluate/flags +``` + +### Empty Flag Set Selection + +```bash +# Select flags that don't belong to any named flag set +curl -H "Flagd-Selector: flagSetId=" \ + http://localhost:8014/ofrep/v1/evaluate/flags +``` + +### Provider SDK Usage + +#### Go Provider +```go +import "github.com/open-feature/go-sdk-contrib/providers/flagd" + +provider := flagd.NewProvider( + flagd.WithHost("localhost"), + flagd.WithPort(8013), + flagd.WithSelector("flagSetId=user-service"), +) +``` + +#### Java Provider +```java +FlagdProvider provider = new FlagdProvider( + FlagdOptions.builder() + .host("localhost") + .port(8013) + .selector("flagSetId=payment-service") + .build() +); +``` + +#### JavaScript Provider +```javascript +const provider = new FlagdProvider({ + host: 'localhost', + port: 8013, + selector: 'flagSetId=frontend-features' +}); +``` + +## Future Enhancements + +The selector syntax is designed to be extensible. Future versions may support: + +- **Multiple Criteria**: `flagSetId=app1,source=prod` +- **Complex Queries**: `flagSetId=app1 OR flagSetId=app2` +- **Filter Expressions**: `metadata.environment=production` +- **Kubernetes-Style Selectors**: `app=frontend,tier=web` + +> **Note**: The current implementation supports single key-value pairs only. Complex selectors are planned for future releases. + +## API Reference + +### gRPC Services + +**Sync Service:** +- `SyncFlags(SyncFlagsRequest)`: Supports selector in header and request body +- `FetchAllFlags(FetchAllFlagsRequest)`: Supports selector in header and request body + +**Evaluation Service:** +- `ResolveBoolean(ResolveBooleanRequest)`: Supports selector in header +- `ResolveString(ResolveStringRequest)`: Supports selector in header +- `ResolveInt(ResolveIntRequest)`: Supports selector in header +- `ResolveFloat(ResolveFloatRequest)`: Supports selector in header +- `ResolveObject(ResolveObjectRequest)`: Supports selector in header +- `ResolveAll(ResolveAllRequest)`: Supports selector in header + +### HTTP/OFREP Services + +**OFREP Endpoints:** +- `POST /ofrep/v1/evaluate/flags/{key}`: Supports selector in header +- `POST /ofrep/v1/evaluate/flags`: Supports selector in header + +All HTTP endpoints support the `Flagd-Selector` header for selector specification. diff --git a/docs/reference/specifications/providers.md b/docs/reference/specifications/providers.md index 101570ea7..a84625759 100644 --- a/docs/reference/specifications/providers.md +++ b/docs/reference/specifications/providers.md @@ -279,7 +279,7 @@ Below are the supported configuration parameters (note that not all apply to bot | keepAliveTime | FLAGD_KEEP_ALIVE_TIME_MS | http 2 keepalive | long | 0 | rpc & in-process | | cache | FLAGD_CACHE | enable cache of static flags | String - `lru`, `disabled` | lru | rpc | | maxCacheSize | FLAGD_MAX_CACHE_SIZE | max size of static flag cache | int | 1000 | rpc | -| selector | FLAGD_SOURCE_SELECTOR | selects a single sync source to retrieve flags from only that source | string | null | in-process | +| selector | FLAGD_SOURCE_SELECTOR | Selector expression to filter flags (e.g., `flagSetId=my-app`, `source=config.json`) | string | null | in-process | | providerId | FLAGD_PROVIDER_ID | A unique identifier for flagd(grpc client) initiating the request. | string | null | in-process | | offlineFlagSourcePath | FLAGD_OFFLINE_FLAG_SOURCE_PATH | offline, file-based flag definitions, overrides host/port/targetUri | string | null | file | | offlinePollIntervalMs | FLAGD_OFFLINE_POLL_MS | poll interval for reading offlineFlagSourcePath | int | 5000 | file | @@ -298,8 +298,98 @@ envoy://localhost:9211/flagd-sync.service The custom name resolver provider in this case will use the endpoint name i.e. `flagd-sync.service` as [authority](https://github.com/grpc/grpc-java/blob/master/examples/src/main/java/io/grpc/examples/nameresolve/ExampleNameResolver.java#L55-L61) and connect to `localhost:9211`. -### Metadata +### Selector Configuration + +Providers support selector configuration to filter which flags are synchronized or evaluated. This enables more granular control in multi-tenant or multi-environment deployments. + +#### Selector Syntax + +Providers accept selector expressions using the following syntax: + +- **Flag Set Selection**: `flagSetId=` - Target flags belonging to a specific flag set +- **Source Selection**: `source=` - Target flags from a specific source (legacy) +- **Backward Compatibility**: `` - Treated as source selection + +#### Selector Precedence + +When selectors are provided in multiple locations, the following precedence applies: + +1. **Request Header**: `Flagd-Selector` header (RPC and OFREP requests) +2. **Provider Configuration**: `selector` option in provider constructor +3. **Environment Variable**: `FLAGD_SOURCE_SELECTOR` environment variable + +#### Usage Examples + +**Flag Set-Based Selection (Recommended):** +```javascript +const provider = new FlagdProvider({ + host: 'localhost', + port: 8013, + selector: 'flagSetId=user-service' +}); +``` + +**Source-Based Selection (Legacy):** +```javascript +const provider = new FlagdProvider({ + host: 'localhost', + port: 8013, + selector: 'source=config/app-flags.json' +}); +``` + +**Header-Based Selection:** +```bash +# gRPC request with selector header +grpcurl -H "Flagd-Selector: flagSetId=payment-service" \ + localhost:8013 flagd.evaluation.v1.Service/ResolveBoolean + +# OFREP request with selector header +curl -H "Flagd-Selector: flagSetId=frontend-features" \ + http://localhost:8014/ofrep/v1/evaluate/flags/my-flag +``` + +### Metadata and Metadata Reflection + +#### Flag Metadata When a flag is resolved, the returned [metadata](./flag-definitions.md#metadata) is a merged representation of the metadata defined on the flag set, and on the flag, with the flag metadata taking priority. Flag metadata is returned on a "best effort" basis when flags are resolved: disabled, missing or erroneous flags return the metadata of the associated flag set whenever possible. This is particularly important for debugging purposes and error metrics. + +#### Selector Metadata Reflection + +Flagd "reflects" selector information back in response metadata, providing transparency about query execution. This helps with debugging selector expressions and understanding which flags were actually queried. + +**Example - gRPC Response:** +```protobuf +// Request with selector header: "Flagd-Selector: flagSetId=payment-service" +message ResolveBooleanResponse { + bool value = 1; + string reason = 2; + string variant = 3; + google.protobuf.Struct metadata = 4; // Contains reflected selector info +} +``` + +**Example - OFREP Response:** +```json +{ + "value": true, + "reason": "TARGETING_MATCH", + "variant": "on", + "metadata": { + "flagSetId": "payment-service", + "team": "payments", + "version": "1.2.0" + } +} +``` + +#### Debugging with Metadata Reflection + +Use reflected metadata to: +- **Verify Selector Parsing**: Confirm your selector was interpreted correctly +- **Debug Empty Results**: Check if selectors are filtering flags as expected +- **Audit Access Patterns**: Log selector metadata for compliance and monitoring +- **Troubleshoot Configuration**: Identify selector precedence issues diff --git a/docs/reference/sync-configuration.md b/docs/reference/sync-configuration.md index 8cf679efd..e60ec49aa 100644 --- a/docs/reference/sync-configuration.md +++ b/docs/reference/sync-configuration.md @@ -55,7 +55,7 @@ Alternatively, these configurations can be passed to flagd via config file, spec | interval | optional `uint32` | Used for http, gcs and azblob syncs; requests will be made at this interval. Defaults to 5 seconds. | | tls | optional `boolean` | Enable/Disable secure TLS connectivity. Currently used only by gRPC sync. Default (ex: if unset) is false, which will use an insecure connection | | providerID | optional `string` | Value binds to grpc connection's providerID field. gRPC server implementations may use this to identify connecting flagd instance | -| selector | optional `string` | Value binds to grpc connection's selector field. gRPC server implementations may use this to filter flag configurations | +| selector | optional `string` | Selector expression to filter flag configurations. Supports `source=` and `flagSetId=` syntax. See [selector syntax](selector-syntax.md) for details. | | certPath | optional `string` | Used for grpcs sync when TLS certificate is needed. If not provided, system certificates will be used for TLS connection | | maxMsgSize | optional `int` | Used for gRPC sync to set max receive message size (in bytes) e.g. 5242880 for 5MB. If not provided, the default is [4MB](https://pkg.go.dev/google.golang.org#grpc#MaxCallRecvMsgSize) | @@ -100,7 +100,7 @@ Startup command: {"uri":"grpc-source:8080","provider":"grpc"}, {"uri":"my-flag-source:8080","provider":"grpc", "maxMsgSize": 5242880}, {"uri":"envoy://localhost:9211/test.service", "provider":"grpc"}, - {"uri":"my-flag-source:8080","provider":"grpc", "certPath": "/certs/ca.cert", "tls": true, "providerID": "flagd-weatherapp-sidecar", "selector": "source=database,app=weatherapp"}, + {"uri":"my-flag-source:8080","provider":"grpc", "certPath": "/certs/ca.cert", "tls": true, "providerID": "flagd-weatherapp-sidecar", "selector": "flagSetId=weatherapp"}, {"uri":"gs://my-bucket/my-flag.json","provider":"gcs"}, {"uri":"azblob://my-container/my-flag.json","provider":"azblob"}]' ``` @@ -132,7 +132,7 @@ sources: certPath: /certs/ca.cert tls: true providerID: flagd-weatherapp-sidecar - selector: "source=database,app=weatherapp" + selector: "flagSetId=weatherapp" - uri: gs://my-bucket/my-flag.json provider: gcs - uri: azblob://my-container/my-flags.json @@ -186,3 +186,70 @@ the reload of the secrets from the filesystem every `ReloadDelayS` seconds. "tokenURL": "http://localhost:8180/sso/oauth2/token" }}]' ``` + +## Selector Configuration + +Selectors allow you to filter flag configurations from sync sources, enabling more granular control over which flags are synchronized. This is particularly useful in multi-tenant or multi-environment deployments. + +### Selector Syntax + +Selectors support two main patterns: + +- **Flag Set Selection**: `flagSetId=` - Select flags belonging to a specific flag set +- **Source Selection**: `source=` - Select flags from a specific source (legacy) +- **Backward Compatibility**: `` - Treated as source selection for compatibility + +### Usage Patterns + +#### Flag Set-Based Selection (Recommended) + +Target logical groupings of flags independent of their source: + +```yaml +sources: + - uri: grpc://flag-server:8080 + provider: grpc + selector: "flagSetId=payment-service" + - uri: grpc://flag-server:8080 + provider: grpc + selector: "flagSetId=user-service" +``` + +#### Source-Based Selection (Legacy) + +Target specific sources directly: + +```yaml +sources: + - uri: grpc://flag-server:8080 + provider: grpc + selector: "source=production-flags" +``` + +#### Empty Flag Set Selection + +Select flags that don't belong to any named flag set: + +```yaml +sources: + - uri: grpc://flag-server:8080 + provider: grpc + selector: "flagSetId=" +``` + +### Selector Precedence + +When selectors are provided in multiple locations, the following precedence applies: + +1. **gRPC Header**: `Flagd-Selector` header (highest priority) +2. **Request Body**: `selector` field in request +3. **Configuration**: `selector` field in source configuration (lowest priority) + +### Best Practices + +- **Use Flag Sets**: Prefer `flagSetId` over `source` for new deployments +- **Logical Grouping**: Group flags by application, environment, or team +- **Consistent Naming**: Use clear, consistent flag set naming conventions +- **Document Schema**: Maintain documentation of your flag set structure + +For detailed selector syntax and examples, see the [Selector Syntax Reference](selector-syntax.md). diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 6537d81f1..4584efb67 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -71,3 +71,41 @@ You may need to explicitly allow HTTP2 or gRPC in your platform if you're using !!! note HTTP2 _is not_ strictly for the flag [evaluation gRPC service](./reference/specifications/protos.md#schemav1schemaproto), which is exposed both as a gRPC service and a RESTful HTTP/1.1 service, thanks to the [connect protocol](https://connectrpc.com/docs/protocol/). + +--- + +## Selector Issues + +### No Flags Returned with Selector + +**Problem**: Provider returns no flags when using a selector. + +**Debugging Steps:** +- Verify `flagSetId` in selector matches flag configuration exactly +- Check selector syntax: `flagSetId=my-app` (not `flagSetId:my-app`) +- Test without selector to confirm flags exist + +### Wrong Flags Returned + +**Problem**: Selector returns unexpected flags. + +**Debugging Steps:** +- Check for flag-level `flagSetId` overrides in individual flags +- Verify header precedence: `Flagd-Selector` header overrides request body +- Use metadata reflection to see what selector was actually applied + +### Selector Ignored + +**Problem**: Selector appears to be ignored, all flags returned. + +**Debugging Steps:** +- Verify selector syntax is correct (`key=value` format) +- Check if provider configuration has a selector that overrides requests +- Ensure selector value is not empty (`flagSetId=` returns all flags without flagSetId) + +**Debug with metadata reflection:** +```bash +curl -H "Flagd-Selector: flagSetId=my-app" \ + http://localhost:8014/ofrep/v1/evaluate/flags +# Check response metadata to see parsed selector +``` From 67de77794268b3aad58f8c61ba194d68c36195c4 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Thu, 27 Nov 2025 15:46:02 -0500 Subject: [PATCH 02/21] fixup: markdown and ToC Signed-off-by: Todd Baert --- docs/concepts/selectors.md | 6 +++++- docs/guides/migrating-to-flag-sets.md | 11 ++++++++++- docs/reference/selector-syntax.md | 17 +++++++++++++++++ docs/reference/specifications/providers.md | 6 ++++++ docs/troubleshooting.md | 4 ++++ mkdocs.yml | 4 ++++ 6 files changed, 46 insertions(+), 2 deletions(-) diff --git a/docs/concepts/selectors.md b/docs/concepts/selectors.md index 79cd86d17..b93d31818 100644 --- a/docs/concepts/selectors.md +++ b/docs/concepts/selectors.md @@ -106,6 +106,7 @@ For advanced use cases, individual flags can override the set-level `flagSetId`: ``` In this example: + - `standard-feature` inherits `flagSetId: "payment-service"` from set level - `experimental-feature` overrides to `flagSetId: "experiments"` - Both flags inherit `team: "payments"` (unless overridden at flag level) @@ -117,11 +118,13 @@ When you make a request with a selector, flagd "reflects" the selector informati ### Example **Request with selector:** + ``` Selector: "flagSetId=project-42" ``` **Response includes reflected metadata:** + ```json { "flags": { /* ... */ }, @@ -132,6 +135,7 @@ Selector: "flagSetId=project-42" ``` This helps you: + - Verify that your selector was parsed correctly - Debug complex selector queries - Understand exactly what flags were returned @@ -181,7 +185,7 @@ selector: "source=legacy-config.json" 1. **Use Flag Sets for Logical Grouping**: Prefer `flagSetId` over `source` for new deployments 2. **Plan Your Flag Set Strategy**: Design flag sets around logical boundaries (teams, features, environments) 3. **Leverage Metadata**: Use metadata for debugging and auditing -5. **Document Your Schema**: Clearly document your flag set naming conventions for your team +4. **Document Your Schema**: Clearly document your flag set naming conventions for your team ## Migration Considerations diff --git a/docs/guides/migrating-to-flag-sets.md b/docs/guides/migrating-to-flag-sets.md index 57f172410..14b665407 100644 --- a/docs/guides/migrating-to-flag-sets.md +++ b/docs/guides/migrating-to-flag-sets.md @@ -14,6 +14,7 @@ selector: "config/my-flags.json" ``` This created tight coupling between providers and sources: + - Providers had to know which source contained their flags - Moving flags between sources required provider reconfiguration - One source could only serve one logical set of flags @@ -28,6 +29,7 @@ selector: "flagSetId=my-application" ``` This provides flexibility: + - Providers are decoupled from sources - Sources can contain multiple flag sets - Flag sets can span multiple sources @@ -40,6 +42,7 @@ This provides flexibility: Add `flagSetId` to your flag configurations at the set level: **Before:** + ```json { "flags": { @@ -53,6 +56,7 @@ Add `flagSetId` to your flag configurations at the set level: ``` **After:** + ```json { "metadata": { @@ -94,18 +98,21 @@ curl -H "Flagd-Selector: flagSetId=my-application" \ ## Flag Set Organization Patterns **By Application/Service:** + ```yaml flagSetId: "user-service" # All user-related flags flagSetId: "payment-service" # All payment-related flags ``` **By Environment:** + ```yaml flagSetId: "development" # Dev-specific flags flagSetId: "production" # Production flags ``` **By Team:** + ```yaml flagSetId: "frontend-team" # Frontend features flagSetId: "backend-team" # Backend features @@ -116,6 +123,7 @@ Choose the pattern that best matches your deployment and organizational structur ## Testing and Rollback **Test Migration:** + ```bash # Verify new selector works curl -H "Flagd-Selector: flagSetId=my-app" \ @@ -127,6 +135,7 @@ curl -H "Flagd-Selector: config/legacy-flags.json" \ ``` **Rollback if Needed:** + - Revert provider configurations to source-based selectors - Keep `flagSetId` metadata in flag configurations for future attempts - Use metadata reflection to debug issues @@ -161,4 +170,4 @@ A: Yes, different providers can use different selector patterns. - [Selector Concepts](../concepts/selectors.md) - Understanding selectors and flag sets - [Selector Syntax Reference](../reference/selector-syntax.md) - Complete syntax documentation -- [ADR: Decouple Flag Source and Set](../architecture-decisions/decouple-flag-source-and-set.md) - Technical decision rationale \ No newline at end of file +- [ADR: Decouple Flag Source and Set](../architecture-decisions/decouple-flag-source-and-set.md) - Technical decision rationale diff --git a/docs/reference/selector-syntax.md b/docs/reference/selector-syntax.md index c4c3fca51..371846885 100644 --- a/docs/reference/selector-syntax.md +++ b/docs/reference/selector-syntax.md @@ -27,11 +27,13 @@ When no `=` is present, the value is treated as a source selector for backward c Selects flags belonging to a specific flag set. **Syntax:** + ``` flagSetId= ``` **Examples:** + ``` flagSetId=project-42 flagSetId=dev-environment @@ -39,9 +41,11 @@ flagSetId=team-payments ``` **Special Case - Empty Flag Set:** + ``` flagSetId= ``` + Selects flags that don't belong to any named flag set (equivalent to the "null" flag set). ### `source` @@ -49,11 +53,13 @@ Selects flags that don't belong to any named flag set (equivalent to the "null" Selects flags from a specific source. **Syntax:** + ``` source= ``` **Examples:** + ``` source=config/flags.json source=http://flag-server/config @@ -87,11 +93,13 @@ Flagd reflects selector information back in response metadata, providing transpa ### Reflection Behavior **Input Selector:** + ``` flagSetId=project-42 ``` **Reflected in Response Metadata:** + ```json { "metadata": { @@ -103,6 +111,7 @@ flagSetId=project-42 ### Multiple Metadata Sources Reflected metadata includes: + - **Selector Information**: The parsed selector key-value pairs - **Set-Level Metadata**: Metadata from the flag configuration itself - **Source Context**: Additional context from sync operations @@ -112,6 +121,7 @@ Reflected metadata includes: Flagd uses a hierarchical metadata system where flags inherit metadata from their flag set: **Set-Level Metadata (Inherited):** + ```json { "metadata": { @@ -129,6 +139,7 @@ Flagd uses a hierarchical metadata system where flags inherit metadata from thei ``` **Flag-Level Metadata (Override):** + ```json { "metadata": { @@ -177,6 +188,7 @@ curl -H "Flagd-Selector: flagSetId=" \ ### Provider SDK Usage #### Go Provider + ```go import "github.com/open-feature/go-sdk-contrib/providers/flagd" @@ -188,6 +200,7 @@ provider := flagd.NewProvider( ``` #### Java Provider + ```java FlagdProvider provider = new FlagdProvider( FlagdOptions.builder() @@ -199,6 +212,7 @@ FlagdProvider provider = new FlagdProvider( ``` #### JavaScript Provider + ```javascript const provider = new FlagdProvider({ host: 'localhost', @@ -223,10 +237,12 @@ The selector syntax is designed to be extensible. Future versions may support: ### gRPC Services **Sync Service:** + - `SyncFlags(SyncFlagsRequest)`: Supports selector in header and request body - `FetchAllFlags(FetchAllFlagsRequest)`: Supports selector in header and request body **Evaluation Service:** + - `ResolveBoolean(ResolveBooleanRequest)`: Supports selector in header - `ResolveString(ResolveStringRequest)`: Supports selector in header - `ResolveInt(ResolveIntRequest)`: Supports selector in header @@ -237,6 +253,7 @@ The selector syntax is designed to be extensible. Future versions may support: ### HTTP/OFREP Services **OFREP Endpoints:** + - `POST /ofrep/v1/evaluate/flags/{key}`: Supports selector in header - `POST /ofrep/v1/evaluate/flags`: Supports selector in header diff --git a/docs/reference/specifications/providers.md b/docs/reference/specifications/providers.md index a84625759..c5f7451df 100644 --- a/docs/reference/specifications/providers.md +++ b/docs/reference/specifications/providers.md @@ -321,6 +321,7 @@ When selectors are provided in multiple locations, the following precedence appl #### Usage Examples **Flag Set-Based Selection (Recommended):** + ```javascript const provider = new FlagdProvider({ host: 'localhost', @@ -330,6 +331,7 @@ const provider = new FlagdProvider({ ``` **Source-Based Selection (Legacy):** + ```javascript const provider = new FlagdProvider({ host: 'localhost', @@ -339,6 +341,7 @@ const provider = new FlagdProvider({ ``` **Header-Based Selection:** + ```bash # gRPC request with selector header grpcurl -H "Flagd-Selector: flagSetId=payment-service" \ @@ -362,6 +365,7 @@ This is particularly important for debugging purposes and error metrics. Flagd "reflects" selector information back in response metadata, providing transparency about query execution. This helps with debugging selector expressions and understanding which flags were actually queried. **Example - gRPC Response:** + ```protobuf // Request with selector header: "Flagd-Selector: flagSetId=payment-service" message ResolveBooleanResponse { @@ -373,6 +377,7 @@ message ResolveBooleanResponse { ``` **Example - OFREP Response:** + ```json { "value": true, @@ -389,6 +394,7 @@ message ResolveBooleanResponse { #### Debugging with Metadata Reflection Use reflected metadata to: + - **Verify Selector Parsing**: Confirm your selector was interpreted correctly - **Debug Empty Results**: Check if selectors are filtering flags as expected - **Audit Access Patterns**: Log selector metadata for compliance and monitoring diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 4584efb67..4ab0cdd2c 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -81,6 +81,7 @@ You may need to explicitly allow HTTP2 or gRPC in your platform if you're using **Problem**: Provider returns no flags when using a selector. **Debugging Steps:** + - Verify `flagSetId` in selector matches flag configuration exactly - Check selector syntax: `flagSetId=my-app` (not `flagSetId:my-app`) - Test without selector to confirm flags exist @@ -90,6 +91,7 @@ You may need to explicitly allow HTTP2 or gRPC in your platform if you're using **Problem**: Selector returns unexpected flags. **Debugging Steps:** + - Check for flag-level `flagSetId` overrides in individual flags - Verify header precedence: `Flagd-Selector` header overrides request body - Use metadata reflection to see what selector was actually applied @@ -99,11 +101,13 @@ You may need to explicitly allow HTTP2 or gRPC in your platform if you're using **Problem**: Selector appears to be ignored, all flags returned. **Debugging Steps:** + - Verify selector syntax is correct (`key=value` format) - Check if provider configuration has a selector that overrides requests - Ensure selector value is not empty (`flagSetId=` returns all flags without flagSetId) **Debug with metadata reflection:** + ```bash curl -H "Flagd-Selector: flagSetId=my-app" \ http://localhost:8014/ofrep/v1/evaluate/flags diff --git a/mkdocs.yml b/mkdocs.yml index 641f133f8..9f2001c87 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -78,7 +78,10 @@ nav: - 'Concepts': - 'Feature Flagging': 'concepts/feature-flagging.md' - 'Syncs': 'concepts/syncs.md' + - 'Selectors': 'concepts/selectors.md' - 'Architecture': 'architecture.md' + - 'Guides': + - 'Migrating to Flag Sets': 'guides/migrating-to-flag-sets.md' - 'OpenFeature Providers': - 'providers/index.md' - 'Go': 'providers/go.md' @@ -95,6 +98,7 @@ nav: - 'Start': 'reference/flagd-cli/flagd_start.md' - 'Version': 'reference/flagd-cli/flagd_version.md' - 'Sync Configuration': 'reference/sync-configuration.md' + - 'Selector Syntax': 'reference/selector-syntax.md' - 'gRPC sync service': 'reference/grpc-sync-service.md' - 'OFREP service': 'reference/flagd-ofrep.md' - 'Flag Definitions': From 1edca5ca83459054e7a5912941544fd4c252f62b Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Thu, 27 Nov 2025 15:48:05 -0500 Subject: [PATCH 03/21] Update docs/concepts/selectors.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Todd Baert --- docs/concepts/selectors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/concepts/selectors.md b/docs/concepts/selectors.md index b93d31818..406abcef6 100644 --- a/docs/concepts/selectors.md +++ b/docs/concepts/selectors.md @@ -149,7 +149,7 @@ This helps you: # Tenant A's flags selector: "flagSetId=tenant-a" -# Tenant B's flags +# Tenant B's flags selector: "flagSetId=tenant-b" ``` From acf4e5649e4867ff5ecf6ef8ed7f1f6d9494fd7b Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Thu, 27 Nov 2025 15:48:15 -0500 Subject: [PATCH 04/21] Update docs/guides/migrating-to-flag-sets.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Todd Baert --- docs/guides/migrating-to-flag-sets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/migrating-to-flag-sets.md b/docs/guides/migrating-to-flag-sets.md index 14b665407..943ad943b 100644 --- a/docs/guides/migrating-to-flag-sets.md +++ b/docs/guides/migrating-to-flag-sets.md @@ -64,7 +64,7 @@ Add `flagSetId` to your flag configurations at the set level: }, "flags": { "feature-a": { - "state": "ENABLED", + "state": "ENABLED", "variants": {"on": true, "off": false}, "defaultVariant": "on" } From 4566230b3e2f3eaa18923885c19f1be074b7306c Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Thu, 27 Nov 2025 15:48:23 -0500 Subject: [PATCH 05/21] Update docs/reference/selector-syntax.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Todd Baert --- docs/reference/selector-syntax.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/selector-syntax.md b/docs/reference/selector-syntax.md index 371846885..715f688ce 100644 --- a/docs/reference/selector-syntax.md +++ b/docs/reference/selector-syntax.md @@ -241,7 +241,7 @@ The selector syntax is designed to be extensible. Future versions may support: - `SyncFlags(SyncFlagsRequest)`: Supports selector in header and request body - `FetchAllFlags(FetchAllFlagsRequest)`: Supports selector in header and request body -**Evaluation Service:** +**Evaluation Service:** - `ResolveBoolean(ResolveBooleanRequest)`: Supports selector in header - `ResolveString(ResolveStringRequest)`: Supports selector in header From 4d59bfba35952f62f5e0f0a6e2443ad1a6966276 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Thu, 27 Nov 2025 15:54:42 -0500 Subject: [PATCH 06/21] fixup: more lint Signed-off-by: Todd Baert --- docs/concepts/selectors.md | 2 +- docs/reference/selector-syntax.md | 16 ++++---- docs/reference/specifications/providers.md | 44 +++++++++++----------- docs/reference/sync-configuration.md | 22 +++++------ 4 files changed, 42 insertions(+), 42 deletions(-) diff --git a/docs/concepts/selectors.md b/docs/concepts/selectors.md index 406abcef6..6d3ffc6b3 100644 --- a/docs/concepts/selectors.md +++ b/docs/concepts/selectors.md @@ -119,7 +119,7 @@ When you make a request with a selector, flagd "reflects" the selector informati **Request with selector:** -``` +```text Selector: "flagSetId=project-42" ``` diff --git a/docs/reference/selector-syntax.md b/docs/reference/selector-syntax.md index 715f688ce..6cd3a3a40 100644 --- a/docs/reference/selector-syntax.md +++ b/docs/reference/selector-syntax.md @@ -8,13 +8,13 @@ Selectors use a simple key-value syntax to filter flags. Currently, selectors su ### Basic Syntax -``` +```text = ``` ### Backward Compatibility Syntax -``` +```text ``` @@ -28,13 +28,13 @@ Selects flags belonging to a specific flag set. **Syntax:** -``` +```text flagSetId= ``` **Examples:** -``` +```text flagSetId=project-42 flagSetId=dev-environment flagSetId=team-payments @@ -42,7 +42,7 @@ flagSetId=team-payments **Special Case - Empty Flag Set:** -``` +```text flagSetId= ``` @@ -54,13 +54,13 @@ Selects flags from a specific source. **Syntax:** -``` +```text source= ``` **Examples:** -``` +```text source=config/flags.json source=http://flag-server/config source=./local-flags.yaml @@ -94,7 +94,7 @@ Flagd reflects selector information back in response metadata, providing transpa **Input Selector:** -``` +```text flagSetId=project-42 ``` diff --git a/docs/reference/specifications/providers.md b/docs/reference/specifications/providers.md index c5f7451df..5148bef16 100644 --- a/docs/reference/specifications/providers.md +++ b/docs/reference/specifications/providers.md @@ -106,7 +106,7 @@ We always rely on the [integrated functionality of GRPC for reconnection](https: We are configuring the underlying reconnection mechanism whenever we can, based on our configuration. (not all GRPC implementations support this) | language/property | min connect timeout | max backoff | initial backoff | jitter | multiplier | -|-------------------|-----------------------------------|--------------------------|--------------------------|--------|------------| +| ----------------- | --------------------------------- | ------------------------ | ------------------------ | ------ | ---------- | | GRPC property | grpc.initial_reconnect_backoff_ms | max_reconnect_backoff_ms | min_reconnect_backoff_ms | 0.2 | 1.6 | | Flagd property | deadlineMs | retryBackoffMaxMs | retryBackoffMs | 0.2 | 1.6 | | --- | --- | --- | --- | --- | --- | @@ -262,28 +262,28 @@ precedence. Below are the supported configuration parameters (note that not all apply to both resolver modes): -| Option name | Environment variable name | Explanation | Type & Values | Default | Compatible resolver | -| --------------------- | ------------------------------ | ---------------------------------------------------------------------- | ---------------------------- | ----------------------------- | ----------------------- | -| resolver | FLAGD_RESOLVER | mode of operation | String - `rpc`, `in-process` | rpc | rpc & in-process | -| host | FLAGD_HOST | remote host | String | localhost | rpc & in-process | -| port | FLAGD_PORT | remote port | int | 8013 (rpc), 8015 (in-process) | rpc & in-process | -| targetUri | FLAGD_TARGET_URI | alternative to host/port, supporting custom name resolution | string | null | rpc & in-process | -| tls | FLAGD_TLS | connection encryption | boolean | false | rpc & in-process | -| socketPath | FLAGD_SOCKET_PATH | alternative to host port, unix socket | String | null | rpc & in-process | -| certPath | FLAGD_SERVER_CERT_PATH | tls cert path | String | null | rpc & in-process | -| deadlineMs | FLAGD_DEADLINE_MS | deadline for unary calls, and timeout for initialization | int | 500 | rpc & in-process & file | -| streamDeadlineMs | FLAGD_STREAM_DEADLINE_MS | deadline for streaming calls, useful as an application-layer keepalive | int | 600000 | rpc & in-process | -| retryBackoffMs | FLAGD_RETRY_BACKOFF_MS | initial backoff for stream retry | int | 1000 | rpc & in-process | -| retryBackoffMaxMs | FLAGD_RETRY_BACKOFF_MAX_MS | maximum backoff for stream retry | int | 120000 | rpc & in-process | -| retryGracePeriod | FLAGD_RETRY_GRACE_PERIOD | period in seconds before provider moves from STALE to ERROR state | int | 5 | rpc & in-process & file | -| keepAliveTime | FLAGD_KEEP_ALIVE_TIME_MS | http 2 keepalive | long | 0 | rpc & in-process | -| cache | FLAGD_CACHE | enable cache of static flags | String - `lru`, `disabled` | lru | rpc | -| maxCacheSize | FLAGD_MAX_CACHE_SIZE | max size of static flag cache | int | 1000 | rpc | +| Option name | Environment variable name | Explanation | Type & Values | Default | Compatible resolver | +| --------------------- | ------------------------------ | ------------------------------------------------------------------------------------ | ---------------------------- | ----------------------------- | ----------------------- | +| resolver | FLAGD_RESOLVER | mode of operation | String - `rpc`, `in-process` | rpc | rpc & in-process | +| host | FLAGD_HOST | remote host | String | localhost | rpc & in-process | +| port | FLAGD_PORT | remote port | int | 8013 (rpc), 8015 (in-process) | rpc & in-process | +| targetUri | FLAGD_TARGET_URI | alternative to host/port, supporting custom name resolution | string | null | rpc & in-process | +| tls | FLAGD_TLS | connection encryption | boolean | false | rpc & in-process | +| socketPath | FLAGD_SOCKET_PATH | alternative to host port, unix socket | String | null | rpc & in-process | +| certPath | FLAGD_SERVER_CERT_PATH | tls cert path | String | null | rpc & in-process | +| deadlineMs | FLAGD_DEADLINE_MS | deadline for unary calls, and timeout for initialization | int | 500 | rpc & in-process & file | +| streamDeadlineMs | FLAGD_STREAM_DEADLINE_MS | deadline for streaming calls, useful as an application-layer keepalive | int | 600000 | rpc & in-process | +| retryBackoffMs | FLAGD_RETRY_BACKOFF_MS | initial backoff for stream retry | int | 1000 | rpc & in-process | +| retryBackoffMaxMs | FLAGD_RETRY_BACKOFF_MAX_MS | maximum backoff for stream retry | int | 120000 | rpc & in-process | +| retryGracePeriod | FLAGD_RETRY_GRACE_PERIOD | period in seconds before provider moves from STALE to ERROR state | int | 5 | rpc & in-process & file | +| keepAliveTime | FLAGD_KEEP_ALIVE_TIME_MS | http 2 keepalive | long | 0 | rpc & in-process | +| cache | FLAGD_CACHE | enable cache of static flags | String - `lru`, `disabled` | lru | rpc | +| maxCacheSize | FLAGD_MAX_CACHE_SIZE | max size of static flag cache | int | 1000 | rpc | | selector | FLAGD_SOURCE_SELECTOR | Selector expression to filter flags (e.g., `flagSetId=my-app`, `source=config.json`) | string | null | in-process | -| providerId | FLAGD_PROVIDER_ID | A unique identifier for flagd(grpc client) initiating the request. | string | null | in-process | -| offlineFlagSourcePath | FLAGD_OFFLINE_FLAG_SOURCE_PATH | offline, file-based flag definitions, overrides host/port/targetUri | string | null | file | -| offlinePollIntervalMs | FLAGD_OFFLINE_POLL_MS | poll interval for reading offlineFlagSourcePath | int | 5000 | file | -| contextEnricher | - | sync-metadata to evaluation context mapping function | function | identity function | in-process | +| providerId | FLAGD_PROVIDER_ID | A unique identifier for flagd(grpc client) initiating the request. | string | null | in-process | +| offlineFlagSourcePath | FLAGD_OFFLINE_FLAG_SOURCE_PATH | offline, file-based flag definitions, overrides host/port/targetUri | string | null | file | +| offlinePollIntervalMs | FLAGD_OFFLINE_POLL_MS | poll interval for reading offlineFlagSourcePath | int | 5000 | file | +| contextEnricher | - | sync-metadata to evaluation context mapping function | function | identity function | in-process | ### Custom Name Resolution diff --git a/docs/reference/sync-configuration.md b/docs/reference/sync-configuration.md index e60ec49aa..349726fd5 100644 --- a/docs/reference/sync-configuration.md +++ b/docs/reference/sync-configuration.md @@ -47,17 +47,17 @@ The flagd accepts a string argument, which should be a JSON representation of an Alternatively, these configurations can be passed to flagd via config file, specified using the `--config` flag. -| Field | Type | Note | -| ----------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| uri | required `string` | Flag configuration source of the sync | -| provider | required `string` | Provider type - `file`, `fsnotify`, `fileinfo`, `kubernetes`, `http`, `grpc`, `gcs` or `azblob` | -| authHeader | optional `string` | Used for http sync; set this to include the complete `Authorization` header value for any authentication scheme (e.g., "Bearer token_here", "Basic base64_credentials", etc.). | -| interval | optional `uint32` | Used for http, gcs and azblob syncs; requests will be made at this interval. Defaults to 5 seconds. | -| tls | optional `boolean` | Enable/Disable secure TLS connectivity. Currently used only by gRPC sync. Default (ex: if unset) is false, which will use an insecure connection | -| providerID | optional `string` | Value binds to grpc connection's providerID field. gRPC server implementations may use this to identify connecting flagd instance | -| selector | optional `string` | Selector expression to filter flag configurations. Supports `source=` and `flagSetId=` syntax. See [selector syntax](selector-syntax.md) for details. | -| certPath | optional `string` | Used for grpcs sync when TLS certificate is needed. If not provided, system certificates will be used for TLS connection | -| maxMsgSize | optional `int` | Used for gRPC sync to set max receive message size (in bytes) e.g. 5242880 for 5MB. If not provided, the default is [4MB](https://pkg.go.dev/google.golang.org#grpc#MaxCallRecvMsgSize) | +| Field | Type | Note | +| ---------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| uri | required `string` | Flag configuration source of the sync | +| provider | required `string` | Provider type - `file`, `fsnotify`, `fileinfo`, `kubernetes`, `http`, `grpc`, `gcs` or `azblob` | +| authHeader | optional `string` | Used for http sync; set this to include the complete `Authorization` header value for any authentication scheme (e.g., "Bearer token_here", "Basic base64_credentials", etc.). | +| interval | optional `uint32` | Used for http, gcs and azblob syncs; requests will be made at this interval. Defaults to 5 seconds. | +| tls | optional `boolean` | Enable/Disable secure TLS connectivity. Currently used only by gRPC sync. Default (ex: if unset) is false, which will use an insecure connection | +| providerID | optional `string` | Value binds to grpc connection's providerID field. gRPC server implementations may use this to identify connecting flagd instance | +| selector | optional `string` | Selector expression to filter flag configurations. Supports `source=` and `flagSetId=` syntax. See [selector syntax](selector-syntax.md) for details. | +| certPath | optional `string` | Used for grpcs sync when TLS certificate is needed. If not provided, system certificates will be used for TLS connection | +| maxMsgSize | optional `int` | Used for gRPC sync to set max receive message size (in bytes) e.g. 5242880 for 5MB. If not provided, the default is [4MB](https://pkg.go.dev/google.golang.org#grpc#MaxCallRecvMsgSize) | The `uri` field values **do not** follow the [URI patterns](#uri-patterns). The provider type is instead derived from the `provider` field. Only exception is the remote provider where `http(s)://` is expected by default. Incorrect From 9da4e0763b66f4c280063ec4dd465fbd88568875 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 28 Nov 2025 12:34:10 -0500 Subject: [PATCH 07/21] Update docs/reference/specifications/providers.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Todd Baert --- docs/reference/specifications/providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/specifications/providers.md b/docs/reference/specifications/providers.md index 5148bef16..f63ac55c8 100644 --- a/docs/reference/specifications/providers.md +++ b/docs/reference/specifications/providers.md @@ -307,7 +307,7 @@ Providers support selector configuration to filter which flags are synchronized Providers accept selector expressions using the following syntax: - **Flag Set Selection**: `flagSetId=` - Target flags belonging to a specific flag set -- **Source Selection**: `source=` - Target flags from a specific source (legacy) +- **Source Selection**: `source=` - Target flags from a specific source (legacy) - **Backward Compatibility**: `` - Treated as source selection #### Selector Precedence From a785ae676f55fa2fd7ad3bf0b9978f156456ecd1 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 28 Nov 2025 12:34:35 -0500 Subject: [PATCH 08/21] Update docs/guides/migrating-to-flag-sets.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Todd Baert --- docs/guides/migrating-to-flag-sets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/migrating-to-flag-sets.md b/docs/guides/migrating-to-flag-sets.md index 943ad943b..f06ec47a2 100644 --- a/docs/guides/migrating-to-flag-sets.md +++ b/docs/guides/migrating-to-flag-sets.md @@ -81,7 +81,7 @@ Change provider selectors from source-based to flag set-based: new FlagdProvider(FlagdOptions.builder() .selector("config/app-flags.json").build()); -// After +// After new FlagdProvider(FlagdOptions.builder() .selector("flagSetId=my-application").build()); ``` From 1d1e70e963a498d0e2b60138c2982a0de7092792 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 28 Nov 2025 12:34:42 -0500 Subject: [PATCH 09/21] Update docs/reference/sync-configuration.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Todd Baert --- docs/reference/sync-configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/sync-configuration.md b/docs/reference/sync-configuration.md index 349726fd5..70a191d14 100644 --- a/docs/reference/sync-configuration.md +++ b/docs/reference/sync-configuration.md @@ -210,7 +210,7 @@ sources: - uri: grpc://flag-server:8080 provider: grpc selector: "flagSetId=payment-service" - - uri: grpc://flag-server:8080 + - uri: grpc://flag-server:8080 provider: grpc selector: "flagSetId=user-service" ``` From 2dfdb81dd2a4414c86db0ebba10c743dfc037805 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 28 Nov 2025 12:34:49 -0500 Subject: [PATCH 10/21] Update docs/reference/specifications/providers.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Todd Baert --- docs/reference/specifications/providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/specifications/providers.md b/docs/reference/specifications/providers.md index f63ac55c8..889bc464a 100644 --- a/docs/reference/specifications/providers.md +++ b/docs/reference/specifications/providers.md @@ -381,7 +381,7 @@ message ResolveBooleanResponse { ```json { "value": true, - "reason": "TARGETING_MATCH", + "reason": "TARGETING_MATCH", "variant": "on", "metadata": { "flagSetId": "payment-service", From 1a31adb8ad0320a0e1391bbac1cdcad327728290 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 28 Nov 2025 12:35:01 -0500 Subject: [PATCH 11/21] Update docs/reference/specifications/providers.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Todd Baert --- docs/reference/specifications/providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/specifications/providers.md b/docs/reference/specifications/providers.md index 889bc464a..94cde32e2 100644 --- a/docs/reference/specifications/providers.md +++ b/docs/reference/specifications/providers.md @@ -347,7 +347,7 @@ const provider = new FlagdProvider({ grpcurl -H "Flagd-Selector: flagSetId=payment-service" \ localhost:8013 flagd.evaluation.v1.Service/ResolveBoolean -# OFREP request with selector header +# OFREP request with selector header curl -H "Flagd-Selector: flagSetId=frontend-features" \ http://localhost:8014/ofrep/v1/evaluate/flags/my-flag ``` From 020c565422daebd62727675af0bb9bd36e5c1867 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 28 Nov 2025 12:35:22 -0500 Subject: [PATCH 12/21] Update docs/reference/selector-syntax.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Todd Baert --- docs/reference/selector-syntax.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/selector-syntax.md b/docs/reference/selector-syntax.md index 6cd3a3a40..2529d0ac9 100644 --- a/docs/reference/selector-syntax.md +++ b/docs/reference/selector-syntax.md @@ -226,7 +226,7 @@ const provider = new FlagdProvider({ The selector syntax is designed to be extensible. Future versions may support: - **Multiple Criteria**: `flagSetId=app1,source=prod` -- **Complex Queries**: `flagSetId=app1 OR flagSetId=app2` +- **Complex Queries**: `flagSetId=app1 OR flagSetId=app2` - **Filter Expressions**: `metadata.environment=production` - **Kubernetes-Style Selectors**: `app=frontend,tier=web` From 1267da7d199b52e3eeb2216cffa3b38312ffaf39 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 28 Nov 2025 12:49:24 -0500 Subject: [PATCH 13/21] Apply suggestion from @toddbaert Signed-off-by: Todd Baert --- docs/guides/migrating-to-flag-sets.md | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/docs/guides/migrating-to-flag-sets.md b/docs/guides/migrating-to-flag-sets.md index f06ec47a2..e870ef0df 100644 --- a/docs/guides/migrating-to-flag-sets.md +++ b/docs/guides/migrating-to-flag-sets.md @@ -120,26 +120,6 @@ flagSetId: "backend-team" # Backend features Choose the pattern that best matches your deployment and organizational structure. -## Testing and Rollback - -**Test Migration:** - -```bash -# Verify new selector works -curl -H "Flagd-Selector: flagSetId=my-app" \ - http://localhost:8014/ofrep/v1/evaluate/flags - -# Check backward compatibility -curl -H "Flagd-Selector: config/legacy-flags.json" \ - http://localhost:8014/ofrep/v1/evaluate/flags -``` - -**Rollback if Needed:** - -- Revert provider configurations to source-based selectors -- Keep `flagSetId` metadata in flag configurations for future attempts -- Use metadata reflection to debug issues - ## Common Issues **No flags returned**: Check that `flagSetId` in selector matches flag configuration exactly From 72880ef0bc389ed66436c05971dc48889a15ffe5 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 28 Nov 2025 12:50:11 -0500 Subject: [PATCH 14/21] Apply suggestion from @toddbaert Signed-off-by: Todd Baert --- docs/guides/migrating-to-flag-sets.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/guides/migrating-to-flag-sets.md b/docs/guides/migrating-to-flag-sets.md index e870ef0df..90ec10a67 100644 --- a/docs/guides/migrating-to-flag-sets.md +++ b/docs/guides/migrating-to-flag-sets.md @@ -143,9 +143,6 @@ A: No, source-based selectors continue to work. Migration is optional but recomm **Q: Can flag sets span multiple sources?** A: Yes, multiple sources can contribute flags to the same flag set. -**Q: Can I mix selector types?** -A: Yes, different providers can use different selector patterns. - ## Additional Resources - [Selector Concepts](../concepts/selectors.md) - Understanding selectors and flag sets From 73ec3bf12da5e900255800c8fba0f4ba8e53aff5 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 28 Nov 2025 12:51:26 -0500 Subject: [PATCH 15/21] Apply suggestion from @toddbaert Signed-off-by: Todd Baert --- docs/reference/specifications/providers.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/reference/specifications/providers.md b/docs/reference/specifications/providers.md index 94cde32e2..76d1b7bd1 100644 --- a/docs/reference/specifications/providers.md +++ b/docs/reference/specifications/providers.md @@ -316,7 +316,6 @@ When selectors are provided in multiple locations, the following precedence appl 1. **Request Header**: `Flagd-Selector` header (RPC and OFREP requests) 2. **Provider Configuration**: `selector` option in provider constructor -3. **Environment Variable**: `FLAGD_SOURCE_SELECTOR` environment variable #### Usage Examples From 33742ff6643cc5376ffeb3fab4b84be00979fd9f Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 28 Nov 2025 12:52:21 -0500 Subject: [PATCH 16/21] Apply suggestion from @toddbaert Signed-off-by: Todd Baert --- docs/reference/specifications/providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/specifications/providers.md b/docs/reference/specifications/providers.md index 76d1b7bd1..3b9fff1fd 100644 --- a/docs/reference/specifications/providers.md +++ b/docs/reference/specifications/providers.md @@ -390,7 +390,7 @@ message ResolveBooleanResponse { } ``` -#### Debugging with Metadata Reflection +#### Debugging with Metadata Use reflected metadata to: From cf5c7e2e8c7bf80e6118614f1ce86f34f7fa0986 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 28 Nov 2025 12:52:38 -0500 Subject: [PATCH 17/21] Apply suggestion from @toddbaert Signed-off-by: Todd Baert --- docs/reference/specifications/providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/specifications/providers.md b/docs/reference/specifications/providers.md index 3b9fff1fd..2d276bda4 100644 --- a/docs/reference/specifications/providers.md +++ b/docs/reference/specifications/providers.md @@ -359,7 +359,7 @@ When a flag is resolved, the returned [metadata](./flag-definitions.md#metadata) Flag metadata is returned on a "best effort" basis when flags are resolved: disabled, missing or erroneous flags return the metadata of the associated flag set whenever possible. This is particularly important for debugging purposes and error metrics. -#### Selector Metadata Reflection +#### Selector Metadata "Reflection" Flagd "reflects" selector information back in response metadata, providing transparency about query execution. This helps with debugging selector expressions and understanding which flags were actually queried. From f48d00f2cc46969193ee56fb002b2bd7362e7d6d Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 28 Nov 2025 13:12:21 -0500 Subject: [PATCH 18/21] fixup: separate metadata section Signed-off-by: Todd Baert --- docs/concepts/metadata.md | 174 +++++++++++++++++++++++++++ docs/concepts/selectors.md | 74 +----------- docs/reference/selector-syntax.md | 73 +---------- docs/reference/sync-configuration.md | 55 +-------- mkdocs.yml | 1 + 5 files changed, 186 insertions(+), 191 deletions(-) create mode 100644 docs/concepts/metadata.md diff --git a/docs/concepts/metadata.md b/docs/concepts/metadata.md new file mode 100644 index 000000000..8880269e9 --- /dev/null +++ b/docs/concepts/metadata.md @@ -0,0 +1,174 @@ +# Metadata + +Metadata in flagd provides contextual information about flags, flag sets, and evaluation results. It enables rich configuration, debugging capabilities, and transparent query execution through inheritance and reflection mechanisms. + +## Overview + +Flagd supports metadata at two levels: + +- **Flag Set-Level Metadata**: Applied to entire flag configurations +- **Flag-Level Metadata**: Applied to individual flags + +## Metadata Inheritance + +Flagd uses a hierarchical metadata system where flags inherit metadata from their containing flag set, with the ability to override specific values at the flag level. + +### Flag Set-Level Metadata + +The most common pattern is defining metadata at the configuration level, where all flags inherit it: + +```json +{ + "metadata": { + "flagSetId": "payment-service", + "team": "payments", + "version": "v1.2.0", + "environment": "production" + }, + "flags": { + "checkout-flow": { + "state": "ENABLED", + "variants": {"on": true, "off": false}, + "defaultVariant": "on" + // Inherits all set-level metadata + }, + "payment-gateway": { + "state": "DISABLED", + "variants": {"on": true, "off": false}, + "defaultVariant": "off" + // Also inherits all set-level metadata + } + } +} +``` + +### Flag-Level Overrides + +Individual flags can override inherited metadata or add flag-specific metadata: + +```json +{ + "metadata": { + "flagSetId": "payment-service", + "team": "payments", + "version": "v1.2.0" + }, + "flags": { + "standard-feature": { + "state": "ENABLED", + "variants": {"on": true, "off": false}, + "defaultVariant": "on" + // Inherits: flagSetId="payment-service", team="payments", version="v1.2.0" + }, + "experimental-feature": { + "metadata": { + "flagSetId": "experiments", // Override: different flag set + "owner": "research-team", // Addition: flag-specific metadata + "experimental": true // Addition: flag-specific metadata + // Still inherits: team="payments", version="v1.2.0" + }, + "state": "DISABLED", + "variants": {"on": true, "off": false}, + "defaultVariant": "off" + } + } +} +``` + +### Inheritance Behavior + +1. **Default Inheritance**: Flags inherit all set-level metadata +2. **Selective Override**: Flag-level metadata overrides specific inherited values +3. **Additive Enhancement**: Flag-level metadata can add new keys not present at set level +4. **Preserved Inheritance**: Non-overridden set-level metadata remains inherited + +## Metadata Reflection + +Metadata reflection provides transparency by echoing selector and configuration information back in API responses. This enables debugging, auditing, and verification of flag targeting. + +### Selector Reflection + +When making requests with selectors, flagd "reflects" the parsed selector information in the "top-level" `metadata` field: + +**Request:** +```bash +curl -H "Flagd-Selector: flagSetId=payment-service" \ + http://localhost:8014/ofrep/v1/evaluate/flags +``` + +**Response includes reflected metadata:** +```json +{ + "flags": { + "checkout-flow": { + "key": "checkout-flow", + "value": true, + "variant": "on", + "metadata": { + "flagSetId": "payment-service", + "team": "payments" + } + } + }, + "metadata": { + "flagSetId": "payment-service" // Reflected from selector + } +} +``` + +### Configuration Reflection + +Flag evaluation responses include the complete merged metadata for each flag: + +```json +{ + "key": "experimental-feature", + "value": false, + "variant": "off", + "metadata": { + "flagSetId": "experiments", // Overridden at flag level + "owner": "research-team", // Added at flag level + "experimental": true, // Added at flag level + "team": "payments", // Inherited from set level + "version": "v1.2.0" // Inherited from set level + } +} +``` + +## Common Metadata Fields + +### Standard Fields + +Some metadata fields are defined in the flag-definition schema for common use-cases: + +- **`flagSetId`**: Logical grouping identifier for selectors +- **`version`**: Configuration or flag version + +### Custom Fields + +You can define any custom metadata fields relevant to your use case: + +```json +{ + "metadata": { + "flagSetId": "user-service", + "version": "v34", + "costCenter": "engineering", + "compliance": "pci-dss", + "lastReviewed": "2024-01-15", + "approver": "team-lead" + } +} +``` + +## Use Cases + +**Debugging**: Metadata reflection shows which selectors were used and how inheritance resolved, making it easier to troubleshoot flag targeting issues. + +**Governance**: Track team ownership, compliance requirements, and approval workflows through custom metadata fields. + +**Environment Management**: Use metadata for version tracking, environment identification, and change management across deployments. + +**Multi-Tenancy**: Isolate tenants through flag sets and maintain tenant-specific configurations and governance. + +**Observability**: Metadata attributes can be used in telemetry spans and metrics, providing operational visibility into flag usage patterns and configuration context. \ No newline at end of file diff --git a/docs/concepts/selectors.md b/docs/concepts/selectors.md index 6d3ffc6b3..df79d9d43 100644 --- a/docs/concepts/selectors.md +++ b/docs/concepts/selectors.md @@ -67,79 +67,13 @@ The most common pattern is to set the `flagSetId` at the configuration level, wh In this example, both `new-checkout-flow` and `stripe-integration` flags belong to the `payment-service` flag set. -### Metadata Inheritance and Override +### Metadata Integration -Flagd uses a hierarchical metadata system: +Selectors work closely with flagd's metadata system. For advanced patterns like flag-level overrides of `flagSetId` or complex metadata inheritance, see the [Metadata concepts](metadata.md) section. -1. **Set-Level Metadata**: Defined in the top-level `metadata` section, inherited by all flags -2. **Flag-Level Metadata**: Defined in individual flag `metadata`, overrides set-level values for that flag -3. **Merged Result**: Flag evaluations return merged metadata with flag-level taking precedence +## Metadata Reflection -### Flag-Level Overrides (Advanced) - -For advanced use cases, individual flags can override the set-level `flagSetId`: - -```json -{ - "metadata": { - "flagSetId": "payment-service", - "team": "payments" - }, - "flags": { - "standard-feature": { - "state": "ENABLED", - "variants": { "on": true, "off": false }, - "defaultVariant": "on" - // Inherits flagSetId: "payment-service" - }, - "experimental-feature": { - "metadata": { - "flagSetId": "experiments", // Override: belongs to different set - "owner": "research-team" - }, - "state": "DISABLED", - "variants": { "on": true, "off": false }, - "defaultVariant": "off" - } - } -} -``` - -In this example: - -- `standard-feature` inherits `flagSetId: "payment-service"` from set level -- `experimental-feature` overrides to `flagSetId: "experiments"` -- Both flags inherit `team: "payments"` (unless overridden at flag level) - -## Flag Set Metadata "Reflection" - -When you make a request with a selector, flagd "reflects" the selector information back in the response metadata. This provides transparency about what was actually queried and helps with debugging. - -### Example - -**Request with selector:** - -```text -Selector: "flagSetId=project-42" -``` - -**Response includes reflected metadata:** - -```json -{ - "flags": { /* ... */ }, - "metadata": { - "flagSetId": "project-42" - } -} -``` - -This helps you: - -- Verify that your selector was parsed correctly -- Debug complex selector queries -- Understand exactly what flags were returned -- Audit flag access patterns +When you make a request with a selector, flagd "reflects" the selector information back in the response metadata for transparency and debugging. For complete details on metadata selector reflection, inheritance, and configuration patterns, see the [Metadata concepts](metadata.md) section. ## Use Cases diff --git a/docs/reference/selector-syntax.md b/docs/reference/selector-syntax.md index 2529d0ac9..82b18d3e9 100644 --- a/docs/reference/selector-syntax.md +++ b/docs/reference/selector-syntax.md @@ -86,78 +86,9 @@ grpcurl -H "Flagd-Selector: flagSetId=production" \ # Result: Uses "flagSetId=production" from header ``` -## Metadata "Reflection" +## Metadata Reflection -Flagd reflects selector information back in response metadata, providing transparency about query execution. - -### Reflection Behavior - -**Input Selector:** - -```text -flagSetId=project-42 -``` - -**Reflected in Response Metadata:** - -```json -{ - "metadata": { - "flagSetId": "project-42" - } -} -``` - -### Multiple Metadata Sources - -Reflected metadata includes: - -- **Selector Information**: The parsed selector key-value pairs -- **Set-Level Metadata**: Metadata from the flag configuration itself -- **Source Context**: Additional context from sync operations - -### Metadata Inheritance - -Flagd uses a hierarchical metadata system where flags inherit metadata from their flag set: - -**Set-Level Metadata (Inherited):** - -```json -{ - "metadata": { - "flagSetId": "payment-service", - "team": "payments", - "version": "1.2.0" - }, - "flags": { - "checkout-flow": { - "state": "ENABLED" - // Inherits all set-level metadata - } - } -} -``` - -**Flag-Level Metadata (Override):** - -```json -{ - "metadata": { - "flagSetId": "payment-service", - "team": "payments" - }, - "flags": { - "experimental-feature": { - "metadata": { - "flagSetId": "experiments", // Overrides set-level - "owner": "research-team" // Adds flag-specific metadata - // Still inherits "team": "payments" - }, - "state": "DISABLED" - } - } -} -``` +Flagd reflects selector information back in response metadata, providing transparency about query execution. For complete details on metadata selector reflection, inheritance patterns, and configuration examples, see the [Metadata concepts](../concepts/metadata.md) section. ## Examples diff --git a/docs/reference/sync-configuration.md b/docs/reference/sync-configuration.md index 70a191d14..1156f9446 100644 --- a/docs/reference/sync-configuration.md +++ b/docs/reference/sync-configuration.md @@ -189,67 +189,22 @@ the reload of the secrets from the filesystem every `ReloadDelayS` seconds. ## Selector Configuration -Selectors allow you to filter flag configurations from sync sources, enabling more granular control over which flags are synchronized. This is particularly useful in multi-tenant or multi-environment deployments. - -### Selector Syntax - -Selectors support two main patterns: - -- **Flag Set Selection**: `flagSetId=` - Select flags belonging to a specific flag set -- **Source Selection**: `source=` - Select flags from a specific source (legacy) -- **Backward Compatibility**: `` - Treated as source selection for compatibility - -### Usage Patterns - -#### Flag Set-Based Selection (Recommended) - -Target logical groupings of flags independent of their source: +Selectors allow you to filter flag configurations from sync sources. Add the `selector` field to source configurations: ```yaml sources: - uri: grpc://flag-server:8080 provider: grpc - selector: "flagSetId=payment-service" + selector: "flagSetId=payment-service" # Flag set selection - uri: grpc://flag-server:8080 provider: grpc - selector: "flagSetId=user-service" -``` - -#### Source-Based Selection (Legacy) - -Target specific sources directly: - -```yaml -sources: - - uri: grpc://flag-server:8080 - provider: grpc - selector: "source=production-flags" -``` - -#### Empty Flag Set Selection - -Select flags that don't belong to any named flag set: - -```yaml -sources: - - uri: grpc://flag-server:8080 - provider: grpc - selector: "flagSetId=" + selector: "source=legacy-flags" # Source selection (legacy) ``` ### Selector Precedence -When selectors are provided in multiple locations, the following precedence applies: - -1. **gRPC Header**: `Flagd-Selector` header (highest priority) +1. **Request Headers**: `Flagd-Selector` header (highest priority) 2. **Request Body**: `selector` field in request 3. **Configuration**: `selector` field in source configuration (lowest priority) -### Best Practices - -- **Use Flag Sets**: Prefer `flagSetId` over `source` for new deployments -- **Logical Grouping**: Group flags by application, environment, or team -- **Consistent Naming**: Use clear, consistent flag set naming conventions -- **Document Schema**: Maintain documentation of your flag set structure - -For detailed selector syntax and examples, see the [Selector Syntax Reference](selector-syntax.md). +For complete selector syntax, patterns, and examples, see the [Selectors concepts](../concepts/selectors.md) and [Selector Syntax Reference](selector-syntax.md). diff --git a/mkdocs.yml b/mkdocs.yml index 9f2001c87..68de9503b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -79,6 +79,7 @@ nav: - 'Feature Flagging': 'concepts/feature-flagging.md' - 'Syncs': 'concepts/syncs.md' - 'Selectors': 'concepts/selectors.md' + - 'Metadata': 'concepts/metadata.md' - 'Architecture': 'architecture.md' - 'Guides': - 'Migrating to Flag Sets': 'guides/migrating-to-flag-sets.md' From 27e43d74e314e3219627ccd18978d2a19ba1c3f0 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 28 Nov 2025 13:16:03 -0500 Subject: [PATCH 19/21] fixup: lint Signed-off-by: Todd Baert --- docs/concepts/metadata.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/concepts/metadata.md b/docs/concepts/metadata.md index 8880269e9..c7abcc3fa 100644 --- a/docs/concepts/metadata.md +++ b/docs/concepts/metadata.md @@ -91,12 +91,14 @@ Metadata reflection provides transparency by echoing selector and configuration When making requests with selectors, flagd "reflects" the parsed selector information in the "top-level" `metadata` field: **Request:** + ```bash curl -H "Flagd-Selector: flagSetId=payment-service" \ http://localhost:8014/ofrep/v1/evaluate/flags ``` **Response includes reflected metadata:** + ```json { "flags": { @@ -139,7 +141,7 @@ Flag evaluation responses include the complete merged metadata for each flag: ### Standard Fields -Some metadata fields are defined in the flag-definition schema for common use-cases: +Some metadata fields are defined in the flag-definition schema for common use-cases: - **`flagSetId`**: Logical grouping identifier for selectors - **`version`**: Configuration or flag version @@ -171,4 +173,4 @@ You can define any custom metadata fields relevant to your use case: **Multi-Tenancy**: Isolate tenants through flag sets and maintain tenant-specific configurations and governance. -**Observability**: Metadata attributes can be used in telemetry spans and metrics, providing operational visibility into flag usage patterns and configuration context. \ No newline at end of file +**Observability**: Metadata attributes can be used in telemetry spans and metrics, providing operational visibility into flag usage patterns and configuration context. From 35ed7506349ebcdf23ae3475cd5b2d32f503a8b3 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 28 Nov 2025 13:20:23 -0500 Subject: [PATCH 20/21] fixup: blurb Signed-off-by: Todd Baert --- docs/concepts/metadata.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/concepts/metadata.md b/docs/concepts/metadata.md index c7abcc3fa..b869fad7e 100644 --- a/docs/concepts/metadata.md +++ b/docs/concepts/metadata.md @@ -1,6 +1,6 @@ # Metadata -Metadata in flagd provides contextual information about flags, flag sets, and evaluation results. It enables rich configuration, debugging capabilities, and transparent query execution through inheritance and reflection mechanisms. +Metadata in flagd provides contextual information about flags and flag sets. It enables rich observability, debugging capabilities, and transparent query execution through inheritance and reflection mechanisms. ## Overview From 0c50598ce5443585308e9e8922ab644ae597df08 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 28 Nov 2025 13:25:50 -0500 Subject: [PATCH 21/21] fixup: blurb again Signed-off-by: Todd Baert --- docs/concepts/metadata.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/concepts/metadata.md b/docs/concepts/metadata.md index b869fad7e..32b6dd5b2 100644 --- a/docs/concepts/metadata.md +++ b/docs/concepts/metadata.md @@ -1,6 +1,7 @@ # Metadata -Metadata in flagd provides contextual information about flags and flag sets. It enables rich observability, debugging capabilities, and transparent query execution through inheritance and reflection mechanisms. +Metadata in flagd provides contextual information about flags and flag sets. +It enables rich observability, logical separation, and debugging capabilities. ## Overview