{"id":19149823,"url":"https://github.com/harness/ff-erlang-server-sdk","last_synced_at":"2025-09-10T11:35:35.990Z","repository":{"id":60194036,"uuid":"519006813","full_name":"harness/ff-erlang-server-sdk","owner":"harness","description":null,"archived":false,"fork":false,"pushed_at":"2024-09-26T16:10:15.000Z","size":993,"stargazers_count":1,"open_issues_count":0,"forks_count":5,"subscribers_count":20,"default_branch":"main","last_synced_at":"2025-06-04T22:06:57.809Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Erlang","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/harness.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-07-28T22:08:31.000Z","updated_at":"2024-09-26T16:09:52.000Z","dependencies_parsed_at":"2023-11-07T01:23:59.484Z","dependency_job_id":"44b06007-72be-4040-ace5-c9ea516099e7","html_url":"https://github.com/harness/ff-erlang-server-sdk","commit_stats":null,"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"purl":"pkg:github/harness/ff-erlang-server-sdk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harness%2Fff-erlang-server-sdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harness%2Fff-erlang-server-sdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harness%2Fff-erlang-server-sdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harness%2Fff-erlang-server-sdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/harness","download_url":"https://codeload.github.com/harness/ff-erlang-server-sdk/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harness%2Fff-erlang-server-sdk/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274455578,"owners_count":25288557,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-10T02:00:12.551Z","response_time":83,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T08:09:46.010Z","updated_at":"2025-09-10T11:35:35.962Z","avatar_url":"https://github.com/harness.png","language":"Erlang","funding_links":[],"categories":[],"sub_categories":[],"readme":"Erlang SDK For Harness Feature Flags\n========================\n\n[Harness](https://www.harness.io/) is a feature management platform that helps\nteams to build better software and to test features quicker.\n\nThis repository contains our Feature Flags SDK for Erlang and other BEAM\nlanguages such as Elixir.\n\n## Table of Contents\n\n**[Intro](#Intro)**\u003cbr\u003e\n**[Requirements](#Requirements)**\u003cbr\u003e\n**[Quickstart](#Quickstart)**\u003cbr\u003e\n**[Further Reading](docs/further_reading.md)**\u003cbr\u003e\n**[Build Instructions](docs/build.md)**\u003cbr\u003e\n\n\n## Intro\n\nThis sample doesn’t include configuration\noptions. For in depth steps and configuring the SDK, e.g. disabling\nstreaming or using our Relay Proxy, see the\n[Erlang SDK Reference](https://developer.harness.io/docs/feature-flags/ff-sdks/server-sdks/erlang-sdk-reference)\n\nFor a sample FF Erlang SDK project, see our\n[test Erlang project](https://github.com/harness-apps/ff-erlang-server-sample).\n\nFor a sample FF Erlang SDK Project for Elixir, see our\n[test Elixir Project](https://github.com/harness/ff-elixir-server-sample).\n\n![FeatureFlags](https://github.com/harness/ff-erlang-server-sdk/raw/main/docs/images/ff-gui.png)\n\n* **For Erlang** applications, install:\n\n    * Erlang/OTP 24 or later\n    * Rebar3 3.20.0 or later\n    * Important, since version 2.0.1 the SDK depends on an Elixir hashing library, so the following is also required for Erlang applications:\n        * Elixir 1.13.4 or later available on your build system\n        * Rebar3 `rebar_mix` plugin installed in your Rebar3 plugins\n\n* **For Elixir** applications, install:\n    * Elixir version 1.13.4 or later\n    * OTP 24 or later\n\n\n## Quickstart\n\nTo follow along with our test code sample, make sure you have:\n\n- [Created a Feature Flag on the Harness Platform](https://ngdocs.harness.io/article/1j7pdkqh7j-create-a-feature-flag)\n  called `harnessappdemodarkmode`\n- [Created a server SDK key and made a copy of it](https://ngdocs.harness.io/article/1j7pdkqh7j-create-a-feature-flag#step_3_create_an_sdk_key)\n\n## Install the SDK\n\n### For Erlang applications\n\nTo install the SDK for Erlang based applications:\n\n1. Add the SDK as a dependency to your `rebar.config` file:\n\n  ```\n  {deps, [{cfclient, \"2.0.1\", {pkg, harness_ff_erlang_server_sdk}}]}.\n  ```\n\n2. Add the dependency to your project's `app.src`.\n\n  ```erlang\n  {applications,\n[kernel, stdlib, cfclient]\n},\n  ```\n\n2. Add the `rebar_mix` plugin to your `rebar.config` file:\n\n  ```erlang\n  {project_plugins, [rebar_mix]}.\n  ```\n\nImporatant: for this plugin to work ensure you have Elixir 1.13.4 or later installed onto your build system\n\n\n### For Elixir applications\n\nTo install the SDK for Elixir based applications:\n\n* Add the SDK as a dependency to `mix.exs` `deps()`:\n\n  ```\n    defp deps do\n      [\n          {:cfclient, \"~\u003e 2.0.1\", hex: :harness_ff_erlang_server_sdk}\n      ]\n  ```\n\n## Configuration\n\n### Erlang\n\nProvide your API key in `sys.config` using an environment variable:\n\n```erlang\n[\n{cfclient, [\n{api_key, {environment_variable, \"YOUR_API_KEY_ENV_VARIABLE\"},\n]}\n].\n```\n\nOr you may provide the API key directly if required:\n\n```erlang\n[\n{cfclient, [\n{api_key, \"YOUR_API_KEY\"},\n]}\n].\n```\n\n### Elixir\n\nProvide your API key in `config/prod.exs` using an environment variable: :\n\n```elixir\nconfig :cfclient,\n  api_key: System.get_env(\"YOUR_API_KEY_ENVIRONMENT_VARIABLE\")\n```\n\nOr you may provide the API key directly if required:\n\n```elixir\nconfig :cfclient,\n  api_key: \"YOUR_API_KEY\"\n```\n\n## Set logging level\nOptionally you may set the required log level of the SDK. If not provided, the SDK will default to `warning`.\n\n\n#### Elixir logging configuration example\n```elixir\nconfig :cfclient,\n  # Set the log level of the SDK to debug\n    log_level: :debug\n    [api_key: System.get_env(\"FF_API_KEY_0\"),\n    # For additional config you can pass in, see Erlang SDK docs: https://github.com/harness/ff-erlang-server-sdk/blob/main/docs/further_reading.md#further-reading\n    # we are just using the main config url here as an example.\n    config: [\n      config_url: \"https://config.ff.harness.io/api/1.0\",\n      events_url: \"https://events.ff.harness.io/api/1.0\",\n      poll_interval: 60000,\n      analytics_enabled: true\n    ]]\n```\n\n#### Erlang logging configuration example\n\n```erlang\n[{cfclient, [\n%% Set the log level of the SDK to debug\n{log_level, debug},\n{api_key, {envrionment_variable, \"YOUR_API_KEY_ENV_VARIABLE\"},\n{config, [\n{config_url, \"https://config.ff.harness.io/api/1.0\"},\n{events_url, \"https://config.ff.harness.io/api/1.0\"},\n{poll_interval, 60},\n{analytics_enabled, true},\n]},\n]}]\n```\n\n### Enable Verbose Evaluation Logs\n\nEvaluation logs are `debug` level by default. If required, they can be changed to `info` level. This is useful if production environments do not use `debug` level, but there is a requirement to check low level evaluation logs.\nNote that this will only affect evaluation log statements.\n\n#### Elixir\n```elixir\nconfig :cfclient,\n  # Set the log level of the SDK to debug\n    log_level: :debug,\n    verbose_evaluation_logs: true,\n    [api_key: System.get_env(\"FF_API_KEY_0\"),\n    # For additional config you can pass in, see Erlang SDK docs: https://github.com/harness/ff-erlang-server-sdk/blob/main/docs/further_reading.md#further-reading\n    # we are just using the main config url here as an example.\n    config: [\n      config_url: \"https://config.ff.harness.io/api/1.0\",\n      events_url: \"https://events.ff.harness.io/api/1.0\",\n      poll_interval: 60000,\n      analytics_enabled: true\n    ]]\n```\n\n#### Erlang\n\n```erlang\n[{cfclient, [\n    %% Set the log level of the SDK to debug\n    {log_level, error},\n    {verbose_evaluation_logs, true},\n    {api_key, {envrionment_variable, \"YOUR_API_KEY_ENV_VARIABLE\"},\n    {config, [\n        {config_url, \"https://config.ff.harness.io/api/1.0\"},\n        {events_url, \"https://config.ff.harness.io/api/1.0\"},\n        {poll_interval, 60},\n        {analytics_enabled, true},\n    ]},\n    ]}]\n```\n\n## Run multiple instances of the SDK\n\nThe SDK by default starts up a single instance called `default` which is configured with your project API key.\nIf different parts of your application need to use specific [projects](https://developer.harness.io/docs/feature-flags/ff-using-flags/ff-creating-flag/create-a-project/), you can start up additional client instances using by defining additional configuration for each unique project.\n\n### Erlang Project Config\n\nThe additional project config is defined in `sys.config`\n\nThe following `sys.config` snippet starts up two additional instances as well along with the default instance:\n\n```erlang\n[\n  %% Project config name: This is an arbitrary identifier, but it must be unique per project config you define.\n  {harness_project_1_config, [\n    {cfclient, [\n      {config, [\n        %% Instance name: This must be unique across all of the project configs. E.g. it cannot be the same as an instance name\n        %% in another project config.\n        %% It will be the name you use when calling SDK API functions like `bool_variation/4`, \n        {name, instance_name_1}\n      ]},\n      %% The API key for the Harness project you want to use with this SDK instance.\n      {api_key, {environment_variable, \"PROJECT_1_API_KEY\"}}]\n    }\n  ]},\n  \n  {harness_project_2_config, [\n    {cfclient, [\n      {config, [\n        {name, instance_name_2}\n      ]},\n      {api_key, {environment_variable, \"PROJECT_2_API_KEY\"}}]\n    }\n  ]},\n\n  {cfclient, [\n    {api_key, {environment_variable, \"FF_API_KEY\"}},\n    {config, [\n      {config_url, \"https://config.ff.harness.io/api/1.0\"},\n      {events_url, \"https://config.ff.harness.io/api/1.0\"}\n    ]},\n    {analytics_push_interval, 60000}\n  ]\n}].\n```\nNote: if the default instance fails to start, for example due to an authentication error with the API key, then the SDK\nwill fail to boot and the additional instances won't start.\n\nIf you don't require the default instance to be started up, you can do:\n\n```erlang\n  % ... additional project config\n\n  {cfclient, [\n    {start_default_instance, false},\n    %% The remaining tuples will be ignored, so you can choose to include or omit them.\n    {api_key, {environment_variable, \"FF_API_KEY\"}},\n    {config, [\n      {config_url, \"https://config.ff.harness.io/api/1.0\"},\n      {events_url, \"https://config.ff.harness.io/api/1.0\"}\n    ]},\n    {analytics_push_interval, 60000}\n  ]\n},\n```\n\nIn your application supervisor, e.g. `src/myapp_sup.erl`, start up a `cfclient_instance`\nfor each additional project. As the default instance is booted when your application starts, you cannot (and don't need to) start it here.\n\n```erlang\ninit(Args) -\u003e\n  HarnessProject1Args = application:get_env(harness_project_1_config, cfclient, []),\n  HarnessProject2Args = application:get_env(harness_project_2_config, cfclient, []),\n  \n  ChildSpec1 = #{id =\u003e project1_cfclient_instance, start =\u003e {cfclient_instance, start_link, [HarnessProject1Args]}},\n  ChildSpec2 = #{id =\u003e project2_cfclient_instance, start =\u003e {cfclient_instance, start_link, [HarnessProject2Args]}},\n\n  MaxRestarts = 1000,\n  MaxSecondsBetweenRestarts = 3600,\n  SupFlags = #{strategy =\u003e one_for_one,\n    intensity =\u003e MaxRestarts,\n    period =\u003e MaxSecondsBetweenRestarts},\n\n  {ok, {SupFlags, [ChildSpec1, ChildSpec2]}}.\n```\n### Using a specific instance of the SDK\n\nTo use a specific SDK instance, you provide the instance name to the public function you are calling. For example `bool_variation/4`.\n\nThe following is an example of referencing the instances we have created above:\n\n```erlang\n-module(multi_instance_example).\n\n-export([multi_instance_evaluations/0]).\n\nmulti_instance_evaluations() -\u003e\n  Target = #{\n    identifier =\u003e \"Harness_Target_1\",\n    name =\u003e \"HT_1\",\n    attributes =\u003e #{email =\u003e \u003c\u003c\"demo@harness.io\"\u003e\u003e}\n  },\n\n  %% Instance 1\n  Project1Flag = \u003c\u003c\"harnessappdemodarkmodeproject1\"\u003e\u003e,\n  Project1Result = cfclient:bool_variation(instance_name_1, Project1Flag, Target, false),\n  logger:info(\"Instance Name 1 : Variation for Flag ~p with Target ~p is: ~p~n\",\n    [Project1Flag, maps:get(identifier, Target), Project1Result]),\n\n  %% Instance 2\n  Project2Flag = \u003c\u003c\"harnessappdemodarkmodeproject2\"\u003e\u003e,\n  Project2Result = cfclient:bool_variation(instance_name_2, Project2Flag, Target, false),\n  logger:info(\"Instance name 2 Variation for Flag ~p with Target ~p is: ~p~n\",\n  [Project2Flag, maps:get(identifier, Target), Project2Result]).\n\n  %% Default instance\n  DefaultProjectFlag = \u003c\u003c\"harnessappdemodarkmodeprojectdefault\"\u003e\u003e,\n  DefaultProjectResult = cfclient:bool_variation(Project2Flag, Target, false),\n  logger:info(\"Default instance Variation for Flag ~p with Target ~p is: ~p~n\",\n  [DefaultProjectFlag, maps:get(identifier, Target), DefaultProjectResult]).\n```\n\n### Elixir\n\n1. Create project configurations for each new instance you would like to start in your `config/config.exs` file:\n\n    ```elixir\n    # Config for \"project 1\"\n    config :elixirsample,  project1:\n           [\n            api_key: System.get_env(\"FF_API_KEY_1\"),\n            config: [name: :project1]\n           ]\n    \n    # Config for \"project 2\"\n    config :elixirsample,  project2:\n      [\n      api_key: System.get_env(\"FF_API_KEY_2\"),\n      config: [name: :project2]\n    ]\n    ```\n\n2. In your application supervisor, e.g. `lib/myapp/supervisor.ex`, start up `cfclient_instance`\n   for each of the additional project configurations you provided above. As the default instance is booted when your application starts, you cannot (and don't need to) start it here:\n\n    ```elixir\n      def init(_opts) do\n        project_1_config = Application.get_env(:elixirsample, :project1, [])\n        project_2_config = Application.get_env(:elixirsample, :project2, [])\n        children = [\n          %{\n            id: :project1_cfclient_instance,\n            start: {:cfclient_instance, :start_link, [project_1_config]},\n            type: :supervisor\n          },\n          %{\n            id: :project2_cfclient_instance,\n            start: {:cfclient_instance, :start_link, [project_2_config]},\n            type: :supervisor\n          },\n        ]\n        Supervisor.init(children, strategy: :one_for_one)\n      end\n    ```\n\n3. To use a specific SDK instance, you provide the instance name to the public function you are calling. For example use `bool_variation/4` instead of `bool_variation/3` - see the following code sample:\n\n    ```elixir\n    defmodule ElixirSample.EvaluationSample do\n      require Logger\n    \n      def getFlagLoop() do\n        target = %{\n          identifier: \"harness\",\n          name: \"Harness\",\n          anonymous: false,\n          attributes: %{}\n        }\n    \n        # Default instance\n        flag = \"projectflag\"\n        result = :cfclient.bool_variation(flag, target, false)\n    \n        Logger.info(\n          \"SVariation for Flag #{flag} with Target #{inspect(target)} is: #{result}\"\n        )\n    \n        # Instance 1\n        project_1_flag = \"project1flag\"\n        project_1_result = :cfclient.number_variation(:project1, project_1_flag, target, 3)\n    \n        Logger.info(\n          \"SDK instance 1: Variation for Flag #{project_1_flag} with Target #{inspect(target)} is: #{project_1_result}\"\n        )\n    \n        # Instance 2\n        project_2_flag = \"project2flag\"\n        project_2_result = :cfclient.bool_variation(:project2, project_2_flag, target, false)\n    \n        Logger.info(\n          \"SDK instance 2: Variation for Flag #{project_2_flag} with Target #{inspect(target)} is: #{project_2_result}\"\n        )\n    \n        Process.sleep(10000)\n        getFlagLoop()\n   \n        # Default instance\n        default_project_flag = \"defaultflag\"\n        default_project_result = :cfclient.bool_variation(default_project_flag, target, false)\n    \n        Logger.info(\n          \"Default instance: Variation for Flag #{default_project_flag} with Target #{inspect(target)} is: #{default_project_result}\"\n        )\n    \n        Process.sleep(10000)\n        getFlagLoop()\n      end\n    end\n    ```\n\n## Code Sample\n\n### Erlang\n\nCall the API to get the value of the `harnessappdemodarkmode` flag you created\nvia https://www.harness.io/.\n\n```erlang\nget_flag_loop() -\u003e\n  Target = #{identifier =\u003e \"Harness_Target_1\",\n    name =\u003e \"HT_1\",\n    %% Attribute keys must be atoms. \n    %% Values must be either bitstrings, atoms, or a list of bitstrings/atoms - see Targets with custom attributes section below.\n    attributes =\u003e #{email =\u003e \u003c\u003c\"demo@harness.io\"\u003e\u003e}\n  },\n  FlagIdentifier = \"harnessappdemodarkmode\",\n  Result = cfclient:bool_variation(FlagIdentifier, Target, false),\n  logger:info(\"Variation for Flag ~p witih Target ~p is: ~p~n\", [FlagIdentifier, maps:get(identifier, Target), Result]),\n  timer:sleep(10000),\n  get_flag_loop().\n```\n\n### Elixir\n\nCall the API to get the value of the `harnessappdemodarkmode` flag you created\nvia https://www.harness.io/.\n\n```elixir\ndef getFlagLoop() do\n  target = %{\n    identifier: \"Harness_Target_1\",\n    name: \"HT_1\"\n  \n    # Attribute keys must be atoms. \n    # Values must be either binaries, atoms, or a list of binaries/atoms.\n    # See \"targets with custom attributes\" below.\n    attributes: %{email: \"demo@harness.io\"}\n  }\n  \n  flag_identifier = \"harnessappdemodarkmode\"\n  \n  result = :cfclient.bool_variation(flag_identifier, target, false)\n  Logger.info(\"Variation for Flag #{flag_identifier} with Target #{inspect(target)} is: #{result)\")\n  Process.sleep(10000)\n  getFlagLoop()\n\n```\n\n## Targets with custom attributes\n\nYou can use the `attributes` map to provide custom attributes. If the target\nisn't anonymous, the attributes will shortly appear in the Harness UI after an\nevaluation using the target.\n\nYou can create [Group Rules](https://docs.harness.io/article/5qz1qrugyk-add-target-groups)\nbased on these attributes.\n\nNote: `attribute` keys must be `atoms` and the values must either be `binaries`\nor `atoms` or a list of `binaries` or `atoms`.\n\n### Erlang:\n\n```erlang\n  TargetBetaGroup = #{'identifier' =\u003e \u003c\u003c\"my_target\"\u003e\u003e,\n    name =\u003e \u003c\u003c\"my_target_name\"\u003e\u003e,\n    anonymous =\u003e \u003c\u003c\"\"\u003e\u003e,\n    attributes =\u003e #{beta =\u003e \u003c\u003c\"beta_group_1\"\u003e\u003e}\n    },\n  TargetBetaGroups = #{'identifier' =\u003e \u003c\u003c\"my_other_target\"\u003e\u003e,\n    name =\u003e \u003c\u003c\"my_other_target_name\"\u003e\u003e,\n    anonymous =\u003e \u003c\u003c\"\"\u003e\u003e,\n    attributes =\u003e #{beta =\u003e [\u003c\u003c\"beta_group_1\"\u003e\u003e, 'beta_group_2']}}\n    },\n  TargetAlphaGroup = #{'identifier' =\u003e \u003c\u003c\"my_alpha_target\"\u003e\u003e,\n    name =\u003e \u003c\u003c\"my_alpha_target_name\"\u003e\u003e,\n    anonymous =\u003e \u003c\u003c\"\"\u003e\u003e,\n    attributes =\u003e #{alpha =\u003e 'alpha_group_1'}\n    },\n```\n\n### Elixir\n\n```elixir\ntarget_beta_group = %{\n  identifier: \"my_target\",\n  name: \"my_target_name\",\n  anonymous: \"\",\n  attributes: %{beta: \"beta_group_1\"}\n}\n\ntarget_beta_groups = %{\n  identifier: \"my_other_target\",\n  name: \"my_other_target_name\",\n  anonymous: \"\",\n  attributes: %{\n    beta: [\"beta_group_1\", :beta_group_2]\n  }\n}\n\ntarget_alpha_group = %{\n  identifier: \"my_alpha_target\",\n  name: \"my_alpha_target_name\",\n  anonymous: \"\",\n  attributes: %{alpha: :alpha_group_1}\n}\n```\n\n## Additional Reading\n\nFor further examples and config options, see the [Erlang SDK Further\nReading](https://github.com/harness/ff-erlang-server-sdk/blob/main/docs/further_reading.md).\n\nFor more information about Feature Flags, see our [Feature Flags\ndocumentation](https://ngdocs.harness.io/article/0a2u2ppp8s-getting-started-with-feature-flags).\n\n## Contributing\n\nIn order to run the tests, pull the submodules:\n\n```command\ngit submodule update --init\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharness%2Fff-erlang-server-sdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fharness%2Fff-erlang-server-sdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharness%2Fff-erlang-server-sdk/lists"}