diff --git a/.gitmodules b/.gitmodules index 643f35ec4..48f54f86d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "spec"] path = spec url = https://github.com/open-feature/spec.git +[submodule "schemas"] + path = schemas + url = git@github.com:open-feature/schemas.git diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml index fec47c686..7f0d0827a 100644 --- a/.markdownlint-cli2.yaml +++ b/.markdownlint-cli2.yaml @@ -17,3 +17,4 @@ ignores: - "docs/specification" - "node_modules" - "tmp" + - "**/protos.md" # auto-generated diff --git a/Makefile b/Makefile index c0d4453b1..1e8c0b941 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,8 @@ ZD_CLIENT_IMG ?= zd-client:latest FLAGD_PROXY_IMG ?= flagd-proxy:latest FLAGD_PROXY_IMG_ZD ?= flagd-proxy:zd +DOCS_DIR ?= web-docs + workspace-init: workspace-clean go work init $(foreach module, $(ALL_GO_MOD_DIRS), go work use $(module);) @@ -107,6 +109,17 @@ markdownlint: markdownlint-fix: $(MDL_CMD) --entrypoint="markdownlint-cli2-fix" davidanson/markdownlint-cli2-rules:$(MDL_DOCKER_VERSION) "**/*.md" +.PHONY: pull-schemas-submodule +pull-schemas-submodule: + git submodule update schemas + +.PHONY: generate-proto-docs +generate-proto-docs: pull-schemas-submodule + docker run --rm -v ${PWD}/$(DOCS_DIR)/reference/specifications:/out -v ${PWD}/schemas/protobuf:/protos pseudomuto/protoc-gen-doc --doc_opt=markdown,protos-with-toc.md schema/v1/schema.proto sync/v1/sync_service.proto \ + && echo '' > ${PWD}/$(DOCS_DIR)/reference/specifications/protos.md \ + && sed '/^## Table of Contents/,/#top/d' ${PWD}/$(DOCS_DIR)/reference/specifications/protos-with-toc.md >> ${PWD}/$(DOCS_DIR)/reference/specifications/protos.md \ + && rm -f ${PWD}/$(DOCS_DIR)/reference/specifications/protos-with-toc.md + .PHONY: run-web-docs -run-web-docs: generate-docs +run-web-docs: generate-docs generate-proto-docs docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 06195bbe5..1359b59a1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -69,10 +69,9 @@ nav: - 'Providers': 'reference/providers.md' - 'Monitoring': 'reference/monitoring.md' - 'Specifications': - - 'Flag Evaluation Protocol': 'reference/specifications/flag-evaluation-protocol.md' - - 'Flag Sync Protocol': 'reference/specifications/flag-sync-protocol.md' - 'RPC Providers': 'reference/specifications/rpc-providers.md' - 'In-Process Providers': 'reference/specifications/in-process-providers.md' + - 'Protobuf schemas': 'reference/specifications/protos.md' - 'Custom Operations': - 'Fractional Specification': 'reference/specifications/custom-operations/fractional-operation-spec.md' - 'Semantic Version Specification': 'reference/specifications/custom-operations/semver-operation-spec.md' diff --git a/schemas b/schemas new file mode 160000 index 000000000..f3e419c5e --- /dev/null +++ b/schemas @@ -0,0 +1 @@ +Subproject commit f3e419c5ea676b6e0a4384b1ba3c9ad43b047eed diff --git a/web-docs/faq.md b/web-docs/faq.md index e69de29bb..4615f3365 100644 --- a/web-docs/faq.md +++ b/web-docs/faq.md @@ -0,0 +1,36 @@ +# Frequently Asked Questions + +> Why do I need this? Can't I just use envronment variables? + +Feature-flags are not environment varianles. +If you don't need to update your flag values without restarting your application, target specific users, randomly assign values for experimentation, or perform scheduled roll-outs, you don't need feature flags and you can probably use simple static configuration. + +For more information on feature-flagging concepts, see [feature-flagging](./concepts/feature-flagging.md). + +--- + +> Why is it called "flagd"? + +Please see [naming](./reference/naming.md). + +--- + +> What is flagd's relationship to OpenFeature? + +flagd is fully [OpenFeature-compliant](./concepts/feature-flagging.md#openfeature-compliance). + +--- + +> How do I run flagd? + +You can run flagd as a standalone application, accessible over HTTP or gRPC, or you can embed it into your application. +Please see [architecture](./architecture.md) and [deployment](./deployment.md) for more information. + +--- + +> Why doesn't flagd support {_my desired feature_}? + +Because you haven't opened a PR or created an issue! + +We're always adding new functionality to flagd, and welcome additions and ideas from new contributors. +Don't hesitate to [open an issue](https://github.com/open-feature/flagd/issues)! \ No newline at end of file diff --git a/web-docs/reference/providers.md b/web-docs/reference/providers.md index e69de29bb..46694aca5 100644 --- a/web-docs/reference/providers.md +++ b/web-docs/reference/providers.md @@ -0,0 +1,9 @@ +# Providers + +flagd was built from the ground up to be [Openfeature-compliant](../concepts/feature-flagging.md#openfeature-compliance). +To use it in your application, you must use the [OpenFeature SDK](https://openfeature.dev/docs/reference/technologies/) for your language, along witht the associated OpenFeature _provider_. +For more information about Openfeature providers, see the [OpenFeature documentation](https://openfeature.dev/docs/reference/concepts/provider). + +Providers for flagd come in two flavors: those that are built to communicate with a flagd instance (over HTTP or gRPC) and those that embed flagd's evaluation engine directly (note that some providers are capable of operating in either mode). For more information on how to deploy and use flagd, see [architecture](../architecture.md) and [deployment](../deployment.md). + +For a catalog of available flagd providers, check out the [OpenFeature ecosystem](https://openfeature.dev/ecosystem?instant_search%5Bquery%5D=flagd&instant_search%5BrefinementList%5D%5Btype%5D%5B0%5D=Provider) page. \ No newline at end of file diff --git a/web-docs/reference/specifications/flag-evaluation-protocol.md b/web-docs/reference/specifications/flag-evaluation-protocol.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/web-docs/reference/specifications/flag-sync-protocol.md b/web-docs/reference/specifications/flag-sync-protocol.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/web-docs/reference/specifications/in-process-providers.md b/web-docs/reference/specifications/in-process-providers.md index 58fc1eeb9..020804617 100755 --- a/web-docs/reference/specifications/in-process-providers.md +++ b/web-docs/reference/specifications/in-process-providers.md @@ -32,7 +32,7 @@ Other sources may be desired eventually, so separation of concerns should be mai ## gRPC sources gRPC sync sources are identified by the `provider` field set to `grpc`. -When such a sync source is specified, the in-process flagd provider should connect to the gRPC service located at the `uri` of the sync source, and use its `sync` API ([see here](https://github.com/open-feature/schemas)) to retrieve the feature flag definition. +When such a sync source is specified, the in-process flagd provider should connect to the gRPC service located at the `uri` of the sync source, and use its [sync API](./protos.md#syncv1sync_serviceproto) to retrieve the feature flag definition. If the `selector` field of the sync source is set, that selector should be passed through to the `Sync` and `FetchAllFlags` requests sent to the gRPC server. ### Protobuf diff --git a/web-docs/reference/specifications/protos.md b/web-docs/reference/specifications/protos.md new file mode 100644 index 000000000..8f1340cee --- /dev/null +++ b/web-docs/reference/specifications/protos.md @@ -0,0 +1,432 @@ + +# Protocol Documentation + + + +## schema/v1/schema.proto +Flag evaluation API + +This proto forms the basis of a flag-evaluation API. +It supports single and bulk evaluation RPCs, and flags of various types, as well as establishing a stream for getting notifications about changes in a flag definition. +It supports the inclusion of a "context" with each evaluation, which may contain arbitraty attributes relevant to flag evaluation. + + + + +### AnyFlag +A variant type flag response. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| reason | [string](#string) | | The reason for the given return value, see https://openfeature.dev/docs/specification/types#resolution-details | +| variant | [string](#string) | | The variant name of the returned flag value. | +| bool_value | [bool](#bool) | | | +| string_value | [string](#string) | | | +| double_value | [double](#double) | | | +| object_value | [google.protobuf.Struct](#google-protobuf-Struct) | | | + + + + + + + + +### EventStreamRequest +Empty stream request body + + + + + + + + +### EventStreamResponse +Response body for the EventStream stream response + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | String key indicating the type of event that is being received, for example, provider_ready or configuration_change | +| data | [google.protobuf.Struct](#google-protobuf-Struct) | | Object structure for use when sending relevant metadata to provide context to the event. Can be left unset when it is not required. | + + + + + + + + +### ResolveAllRequest +Request body for bulk flag evaluation, used by the ResolveAll rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| context | [google.protobuf.Struct](#google-protobuf-Struct) | | Object structure describing the EvaluationContext used in the flag evaluation, see https://openfeature.dev/docs/reference/concepts/evaluation-context | + + + + + + + + +### ResolveAllResponse +Response body for bulk flag evaluation, used by the ResolveAll rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| flags | [ResolveAllResponse.FlagsEntry](#schema-v1-ResolveAllResponse-FlagsEntry) | repeated | Object structure describing the evaluated flags for the provided context. | + + + + + + + + +### ResolveAllResponse.FlagsEntry + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| key | [string](#string) | | | +| value | [AnyFlag](#schema-v1-AnyFlag) | | | + + + + + + + + +### ResolveBooleanRequest +Request body for boolean flag evaluation, used by the ResolveBoolean rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| flag_key | [string](#string) | | Flag key of the requested flag. | +| context | [google.protobuf.Struct](#google-protobuf-Struct) | | Object structure describing the EvaluationContext used in the flag evaluation, see https://openfeature.dev/docs/reference/concepts/evaluation-context | + + + + + + + + +### ResolveBooleanResponse +Response body for boolean flag evaluation. used by the ResolveBoolean rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| value | [bool](#bool) | | The response value of the boolean flag evaluation, will be unset in the case of error. | +| reason | [string](#string) | | The reason for the given return value, see https://openfeature.dev/docs/specification/types#resolution-details | +| variant | [string](#string) | | The variant name of the returned flag value. | +| metadata | [google.protobuf.Struct](#google-protobuf-Struct) | | Metadata for this evaluation | + + + + + + + + +### ResolveFloatRequest +Request body for float flag evaluation, used by the ResolveFloat rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| flag_key | [string](#string) | | Flag key of the requested flag. | +| context | [google.protobuf.Struct](#google-protobuf-Struct) | | Object structure describing the EvaluationContext used in the flag evaluation, see https://openfeature.dev/docs/reference/concepts/evaluation-context | + + + + + + + + +### ResolveFloatResponse +Response body for float flag evaluation. used by the ResolveFloat rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| value | [double](#double) | | The response value of the float flag evaluation, will be empty in the case of error. | +| reason | [string](#string) | | The reason for the given return value, see https://openfeature.dev/docs/specification/types#resolution-details | +| variant | [string](#string) | | The variant name of the returned flag value. | +| metadata | [google.protobuf.Struct](#google-protobuf-Struct) | | Metadata for this evaluation | + + + + + + + + +### ResolveIntRequest +Request body for int flag evaluation, used by the ResolveInt rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| flag_key | [string](#string) | | Flag key of the requested flag. | +| context | [google.protobuf.Struct](#google-protobuf-Struct) | | Object structure describing the EvaluationContext used in the flag evaluation, see https://openfeature.dev/docs/reference/concepts/evaluation-context | + + + + + + + + +### ResolveIntResponse +Response body for int flag evaluation. used by the ResolveInt rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| value | [int64](#int64) | | The response value of the int flag evaluation, will be unset in the case of error. | +| reason | [string](#string) | | The reason for the given return value, see https://openfeature.dev/docs/specification/types#resolution-details | +| variant | [string](#string) | | The variant name of the returned flag value. | +| metadata | [google.protobuf.Struct](#google-protobuf-Struct) | | Metadata for this evaluation | + + + + + + + + +### ResolveObjectRequest +Request body for object flag evaluation, used by the ResolveObject rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| flag_key | [string](#string) | | Flag key of the requested flag. | +| context | [google.protobuf.Struct](#google-protobuf-Struct) | | Object structure describing the EvaluationContext used in the flag evaluation, see https://openfeature.dev/docs/reference/concepts/evaluation-context | + + + + + + + + +### ResolveObjectResponse +Response body for object flag evaluation. used by the ResolveObject rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| value | [google.protobuf.Struct](#google-protobuf-Struct) | | The response value of the object flag evaluation, will be unset in the case of error. + +NOTE: This structure will need to be decoded from google/protobuf/struct.proto before it is returned to the SDK | +| reason | [string](#string) | | The reason for the given return value, see https://openfeature.dev/docs/specification/types#resolution-details | +| variant | [string](#string) | | The variant name of the returned flag value. | +| metadata | [google.protobuf.Struct](#google-protobuf-Struct) | | Metadata for this evaluation | + + + + + + + + +### ResolveStringRequest +Request body for string flag evaluation, used by the ResolveString rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| flag_key | [string](#string) | | Flag key of the requested flag. | +| context | [google.protobuf.Struct](#google-protobuf-Struct) | | Object structure describing the EvaluationContext used in the flag evaluation, see https://openfeature.dev/docs/reference/concepts/evaluation-context | + + + + + + + + +### ResolveStringResponse +Response body for string flag evaluation. used by the ResolveString rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| value | [string](#string) | | The response value of the string flag evaluation, will be unset in the case of error. | +| reason | [string](#string) | | The reason for the given return value, see https://openfeature.dev/docs/specification/types#resolution-details | +| variant | [string](#string) | | The variant name of the returned flag value. | +| metadata | [google.protobuf.Struct](#google-protobuf-Struct) | | Metadata for this evaluation | + + + + + + + + + + + + + + +### Service +Service defines the exposed rpcs of flagd + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| ResolveAll | [ResolveAllRequest](#schema-v1-ResolveAllRequest) | [ResolveAllResponse](#schema-v1-ResolveAllResponse) | | +| ResolveBoolean | [ResolveBooleanRequest](#schema-v1-ResolveBooleanRequest) | [ResolveBooleanResponse](#schema-v1-ResolveBooleanResponse) | | +| ResolveString | [ResolveStringRequest](#schema-v1-ResolveStringRequest) | [ResolveStringResponse](#schema-v1-ResolveStringResponse) | | +| ResolveFloat | [ResolveFloatRequest](#schema-v1-ResolveFloatRequest) | [ResolveFloatResponse](#schema-v1-ResolveFloatResponse) | | +| ResolveInt | [ResolveIntRequest](#schema-v1-ResolveIntRequest) | [ResolveIntResponse](#schema-v1-ResolveIntResponse) | | +| ResolveObject | [ResolveObjectRequest](#schema-v1-ResolveObjectRequest) | [ResolveObjectResponse](#schema-v1-ResolveObjectResponse) | | +| EventStream | [EventStreamRequest](#schema-v1-EventStreamRequest) | [EventStreamResponse](#schema-v1-EventStreamResponse) stream | | + + + + + + +
+ +## sync/v1/sync_service.proto +Flag definition sync API + +This proto defines a simple API to synchronize a feature flag definition. +It supports establishing a stream for getting notifications about changes in a flag definition. + + + + +### FetchAllFlagsRequest +FetchAllFlagsRequest is the request to fetch all flags. Flagd sends this request as the client in order to resync its internal state + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| provider_id | [string](#string) | | Optional: A unique identifier for flagd(grpc client) initiating the request. The server implementations may utilize this identifier to uniquely identify, validate(ex:- enforce authentication/authorization) and filter flag configurations that it can expose to this request. This field is intended to be optional. However server implementations may enforce it. ex:- provider_id: flagd-weatherapp-sidecar | +| selector | [string](#string) | | Optional: A selector for the flag configuration request. The server implementation may utilize this to select flag configurations from a collection, select the source of the flag or combine this to any desired underlying filtering mechanism. ex:- selector: 'source=database,app=weatherapp' | + + + + + + + + +### FetchAllFlagsResponse +FetchAllFlagsResponse is the server response containing feature flag configurations + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| flag_configuration | [string](#string) | | flagd feature flag configuration. Must be validated to schema - https://raw.githubusercontent.com/open-feature/schemas/main/json/flagd-definitions.json | + + + + + + + + +### SyncFlagsRequest +SyncFlagsRequest is the request initiating the sever-streaming rpc. Flagd sends this request, acting as the client + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| provider_id | [string](#string) | | Optional: A unique identifier for flagd(grpc client) initiating the request. The server implementations may utilize this identifier to uniquely identify, validate(ex:- enforce authentication/authorization) and filter flag configurations that it can expose to this request. This field is intended to be optional. However server implementations may enforce it. ex:- provider_id: flagd-weatherapp-sidecar | +| selector | [string](#string) | | Optional: A selector for the flag configuration request. The server implementation may utilize this to select flag configurations from a collection, select the source of the flag or combine this to any desired underlying filtering mechanism. ex:- selector: 'source=database,app=weatherapp' | + + + + + + + + +### SyncFlagsResponse +SyncFlagsResponse is the server response containing feature flag configurations and the state + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| flag_configuration | [string](#string) | | flagd feature flag configuration. Must be validated to schema - https://raw.githubusercontent.com/open-feature/schemas/main/json/flagd-definitions.json | +| state | [SyncState](#sync-v1-SyncState) | | State conveying the operation to be performed by flagd. See the descriptions of SyncState for an explanation of supported values | + + + + + + + + + + +### SyncState +SyncState conveys the state of the payload. These states are related to flagd isync.go type definitions but +contains extras to optimize grpc use case. Refer - https://github.com/open-feature/flagd/blob/main/pkg/sync/isync.go + +| Name | Number | Description | +| ---- | ------ | ----------- | +| SYNC_STATE_UNSPECIFIED | 0 | Value is ignored by the listening flagd | +| SYNC_STATE_ALL | 1 | All the flags matching the request. This is the default response and other states can be ignored by the implementation. Flagd internally replaces all existing flags for this response state. | +| SYNC_STATE_ADD | 2 | Convey an addition of a flag. Flagd internally handles this by combining new flags with existing ones | +| SYNC_STATE_UPDATE | 3 | Convey an update of a flag. Flagd internally attempts to update if the updated flag already exist OR if it does not, it will get added | +| SYNC_STATE_DELETE | 4 | Convey a deletion of a flag. Flagd internally removes the flag | +| SYNC_STATE_PING | 5 | Optional server ping to check client connectivity. Handling is ignored by flagd and is to merely support live check | + + + + + + + + + +### FlagSyncService +FlagService implements a server streaming to provide realtime flag configurations + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| SyncFlags | [SyncFlagsRequest](#sync-v1-SyncFlagsRequest) | [SyncFlagsResponse](#sync-v1-SyncFlagsResponse) stream | | +| FetchAllFlags | [FetchAllFlagsRequest](#sync-v1-FetchAllFlagsRequest) | [FetchAllFlagsResponse](#sync-v1-FetchAllFlagsResponse) | | + + + + + +## Scalar Value Types + +| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby | +| ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- | +| double | | double | double | float | float64 | double | float | Float | +| float | | float | float | float | float32 | float | float | Float | +| int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) | +| uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) | +| sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) | +| fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum | +| sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| bool | | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass | +| string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) | +| bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) | + diff --git a/web-docs/reference/specifications/rpc-providers.md b/web-docs/reference/specifications/rpc-providers.md index e69de29bb..78514b12b 100644 --- a/web-docs/reference/specifications/rpc-providers.md +++ b/web-docs/reference/specifications/rpc-providers.md @@ -0,0 +1,122 @@ +# Creating an RPC flagd provider + +By default, **flagd** is a remote service that is accessed via **grpc** by a client application to retrieve feature flags. +Depending on the environment, flagd therefore is usually deployed as a standalone service, e.g. as a Kubernetes Deployment, +or injected as a sidecar container into the pod running the client application, +as it is done in the [OpenFeature Operator](https://github.com/open-feature/open-feature-operator). + +Prerequisites: + +- Understanding of [general provider concepts](https://openfeature.dev/docs/reference/concepts/provider/) +- Proficiency in the chosen programming language (check the language isn't already covered by the [existing providers](../usage/flagd_providers.md)) + +## flagd Evaluation API + +Fundamentally, RPC providers use the [evaluation schema](./protos.md#schemav1schemaproto) to connect to flagd, initiate evaluation RPCs, and listen for changes in the flag definitions. +In order to do this, you must generate the gRPC primitives (message types and client) using the protobuf code generation mechanisms available in your language. +If you are unable to use gRPC code generation, you can also use REST (via the [connect protocol](https://buf.build/blog/connect-a-better-grpc)) to communivate with flagd, though in this case, you will not be able to open a stream to listen for changes. + +### Protobuf + +Protobuf schemas define the contract between the flagd evaluation API and a client. + +#### Code generation for gRPC sync + +Leverage the [buf CLI](https://docs.buf.build/installation) or protoc to generate a `flagd-proxy` client in the chosen technology: + +Add the [open-feature schema repository](https://github.com/open-feature/schemas) as a submodule + +```shell +git submodule add --force https://github.com/open-feature/schemas.git +``` + +Create a `buf.gen.{chosen language}.yaml` for the chosen language in `schemas/protobuf` (if it doesn't already exist) using one of the other files as a template (find a plugin for the chosen language [here](https://buf.build/protocolbuffers/plugins)) and create a pull request with this file. + +Generate the code (this step ought to be automated in the build process for the chosen technology so that the generated code is never committed) + +```shell +cd schemas/protobuf +buf generate --template buf.gen.{chosen language}.yaml +``` + +As an alternative to buf, use the .proto file directly along with whatever protoc-related tools or plugins avaialble for your language. + +Move the generated code (following convention for the chosen language) and add its location to .gitignore + +Note that for the in-process provider only the `schema` package will be relevant, since RPC providers communicate directly to flagd. + +## Provider lifecycle, initialization and shutdown + +With the release of the v0.6.0 spec, OpenFeature now outlines a lifecycle for in-process flagd provider initialization and shutdown. + +In-process flagd providers should do the following to make use of OpenFeature v0.6.0 features: + +- start in a `NOT_READY` state +- fetch the flag definition specified in the sync provider sources and set `state` to `READY` or `ERROR` in the `initialization` function + - note that the SDK will automatically emit `PROVIDER_READY`/`PROVIDER_ERROR` according to the termination of the `initialization` function +- throw an exception or terminate abnormally if a connection cannot be established during `initialization` +- For gRPC based sources (i.e. flagd-proxy), attempt to restore the streaming connection to flagd-proxy (if the connection cannot be established or is broken): + - If flag definition have been retrieved previously, go into `STALE` state to indicate that flag resolution responsees are based on potentially outdated Flag definition. + - reconnection should be attempted with an exponential back-off, with a max-delay of `maxSyncRetryInterval` (see [configuration](#configuration)) + - reconnection should be attempted up to `maxSyncRetryDelay` times (see [configuration](#configuration)) + - `PROVIDER_READY` and `PROVIDER_CONFIGURATION_CHANGED` should be emitted, in that order, after successful reconnection +- For Kubernetes sync sources, retry to retrieve the FlagConfiguration resource, using an exponential back-off strategy, with a max-delay of `maxSyncRetryInterval` (see [configuration](#configuration)) +- emit `PROVIDER_CONFIGURATION_CHANGED` event and update ruleset when a `configuration_change` message is received on the streaming connection +- close the streaming connection in the`shutdown` function + +```mermaid +stateDiagram-v2 + [*] --> NOT_READY + NOT_READY --> READY: initialize(), connection to flagd established, stream connected + NOT_READY --> ERROR: initialize(), unable to connect or establish stream(retry) + READY --> ERROR: stream or connection disconnected + READY --> READY: configuration_change (emit changed*) + ERROR --> READY: reconnect successful (emit ready*, changed*) + ERROR --> ERROR: maxSyncRetries reached + ERROR --> [*]: shutdown(), stream disconnected +``` + +\* ready=`PROVIDER_READY`, changed=`PROVIDER_CONFIGURATION_CHANGED`, stale=`PROVIDER_STALE`, error=`PROVIDER_ERROR` + +## Configuration + +Expose means to configure the provider aligned with the following priority system (highest to lowest). + +```mermaid +flowchart LR + constructor-parameters -->|highest priority| environment-variables -->|lowest priority| defaults +``` + +### Explicit declaration + +This takes the form of parameters to the provider's constructor, it has the highest priority. + +### Environment variables + +Read environment variables with sensible defaults (before applying the values explicitly declared to the constructor). + +| Option name | Environment variable name | Type & Values | Default | +|-----------------------|--------------------------------|------------------------|-----------| +| host | FLAGD_HOST | String | localhost | +| port | FLAGD_PORT | int | 8013 | +| tls | FLAGD_TLS | boolean | false | +| socketPath | FLAGD_SOCKET_PATH | String | null | +| certPath | FLAGD_SERVER_CERT_PATH | String | null | +| deadline | FLAGD_DEADLINE_MS | int | 500 | +| cache | FLAGD_CACHE | String - lru, disabled | lru | +| maxCacheSize | FLAGD_MAX_CACHE_SIZE | int | 1000 | +| maxEventStreamRetries | FLAGD_MAX_EVENT_STREAM_RETRIES | int | 5 | +| retryBackoffMs | FLAGD_RETRY_BACKOFF_MS | int | 1000 | + +## Error handling + +Handle flag evaluation errors by using the error constructors exported by the SDK (e.g. `openfeature.NewProviderNotReadyResolutionError(ConnectionError)`), thereby allowing the SDK to parse and handle the error appropriately. + +## Post creation + +The following steps will extend the reach of the newly created provider to other developers of the chosen technology. + +### Open an issue to document the provider + +Create an issue in openfeature.dev [here](https://github.com/open-feature/openfeature.dev/issues/new?assignees=&labels=provider&template=document-provider.yaml&title=%5BProvider%5D%3A+). +This will ensure the provider is added to OpenFeature's website.