{"id":13491985,"url":"https://github.com/envoyproxy/ratelimit","last_synced_at":"2025-05-14T21:02:07.585Z","repository":{"id":38447713,"uuid":"80164872","full_name":"envoyproxy/ratelimit","owner":"envoyproxy","description":"Go/gRPC service designed to enable generic rate limit scenarios from different types of applications.","archived":false,"fork":false,"pushed_at":"2025-05-05T07:23:36.000Z","size":1141,"stargazers_count":2409,"open_issues_count":35,"forks_count":469,"subscribers_count":149,"default_branch":"main","last_synced_at":"2025-05-07T19:47:42.408Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","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/envoyproxy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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":"2017-01-26T23:12:13.000Z","updated_at":"2025-05-07T10:28:24.000Z","dependencies_parsed_at":"2023-07-13T10:37:24.906Z","dependency_job_id":"4b42b557-6614-49ce-ac77-735930484fcc","html_url":"https://github.com/envoyproxy/ratelimit","commit_stats":{"total_commits":276,"total_committers":96,"mean_commits":2.875,"dds":0.7427536231884058,"last_synced_commit":"6a2e8262874f012d08830cc34ba8058e66a33819"},"previous_names":["lyft/ratelimit"],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/envoyproxy%2Fratelimit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/envoyproxy%2Fratelimit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/envoyproxy%2Fratelimit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/envoyproxy%2Fratelimit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/envoyproxy","download_url":"https://codeload.github.com/envoyproxy/ratelimit/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254227603,"owners_count":22035667,"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","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-07-31T19:01:02.128Z","updated_at":"2025-05-14T21:02:07.437Z","avatar_url":"https://github.com/envoyproxy.png","language":"Go","funding_links":[],"categories":["Go","Reliability \u0026 Chaos Engineering","Documentation"],"sub_categories":[],"readme":"\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n- [Overview](#overview)\n- [Docker Image](#docker-image)\n- [Supported Envoy APIs](#supported-envoy-apis)\n  - [API Deprecation History](#api-deprecation-history)\n- [Building and Testing](#building-and-testing)\n  - [Docker-compose setup](#docker-compose-setup)\n  - [Full test environment - Configure rate limits through files](#full-test-environment---configure-rate-limits-through-files)\n  - [Full test environment - Configure rate limits through an xDS Management Server](#full-test-environment---configure-rate-limits-through-an-xds-management-server)\n  - [Self-contained end-to-end integration test](#self-contained-end-to-end-integration-test)\n- [Configuration](#configuration)\n  - [The configuration format](#the-configuration-format)\n    - [Definitions](#definitions)\n    - [Descriptor list definition](#descriptor-list-definition)\n    - [Rate limit definition](#rate-limit-definition)\n    - [Replaces](#replaces)\n    - [ShadowMode](#shadowmode)\n    - [Including detailed metrics for unspecified values](#including-detailed-metrics-for-unspecified-values)\n    - [Examples](#examples)\n      - [Example 1](#example-1)\n      - [Example 2](#example-2)\n      - [Example 3](#example-3)\n      - [Example 4](#example-4)\n      - [Example 5](#example-5)\n      - [Example 6](#example-6)\n      - [Example 7](#example-7)\n      - [Example 8](#example-8)\n      - [Example 9](#example-9)\n  - [Loading Configuration](#loading-configuration)\n    - [File Based Configuration Loading](#file-based-configuration-loading)\n    - [xDS Management Server Based Configuration Loading](#xds-management-server-based-configuration-loading)\n  - [Log Format](#log-format)\n  - [GRPC Keepalive](#grpc-keepalive)\n  - [Health-check](#health-check)\n    - [Health-check configurations](#health-check-configurations)\n  - [GRPC server](#grpc-server)\n- [Request Fields](#request-fields)\n- [GRPC Client](#grpc-client)\n  - [Commandline flags](#commandline-flags)\n- [Global ShadowMode](#global-shadowmode)\n  - [Configuration](#configuration-1)\n  - [Statistics](#statistics)\n- [Statistics](#statistics-1)\n  - [Statistics options](#statistics-options)\n  - [DogStatsD](#dogstatsd)\n    - [Example](#example)\n    - [Continued example:](#continued-example)\n  - [Prometheus](#prometheus)\n- [HTTP Port](#http-port)\n  - [/json endpoint](#json-endpoint)\n- [Debug Port](#debug-port)\n- [Local Cache](#local-cache)\n- [Redis](#redis)\n  - [Redis type](#redis-type)\n  - [Pipelining](#pipelining)\n  - [One Redis Instance](#one-redis-instance)\n  - [Two Redis Instances](#two-redis-instances)\n  - [Health Checking for Redis Active Connection](#health-checking-for-redis-active-connection)\n- [Memcache](#memcache)\n- [Custom headers](#custom-headers)\n- [Tracing](#tracing)\n- [TLS](#tls)\n- [mTLS](#mtls)\n- [Contact](#contact)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n# Overview\n\nThe rate limit service is a Go/gRPC service designed to enable generic rate limit scenarios from different types of\napplications. Applications request a rate limit decision based on a domain and a set of descriptors. The service\nreads the configuration from disk via [runtime](https://github.com/lyft/goruntime), composes a cache key, and talks to the Redis cache. A\ndecision is then returned to the caller.\n\n[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/envoyproxy/ratelimit/badge)](https://securityscorecards.dev/viewer/?uri=github.com/envoyproxy/ratelimit)\n\n# Docker Image\n\nFor every main commit, an image is pushed to [Dockerhub](https://hub.docker.com/r/envoyproxy/ratelimit/tags?page=1\u0026ordering=last_updated). There is currently no versioning (post v1.4.0) and tags are based on commit sha.\n\n# Supported Envoy APIs\n\n[v3 rls.proto](https://github.com/envoyproxy/data-plane-api/blob/master/envoy/service/ratelimit/v3/rls.proto) is currently supported.\nSupport for [v2 rls proto](https://github.com/envoyproxy/data-plane-api/blob/master/envoy/service/ratelimit/v2/rls.proto) is now deprecated.\n\n## API Deprecation History\n\n1. `v1.0.0` tagged on commit `0ded92a2af8261d43096eba4132e45b99a3b8b14`. Ratelimit has been in production use at Lyft for over 2 years.\n2. `v1.1.0` introduces the data-plane-api proto and initiates the deprecation of the legacy [ratelimit.proto](https://github.com/lyft/ratelimit/blob/0ded92a2af8261d43096eba4132e45b99a3b8b14/proto/ratelimit/ratelimit.proto).\n3. `e91321b` [commit](https://github.com/envoyproxy/ratelimit/commit/e91321b10f1ad7691d0348e880bd75d0fca05758) deleted support for the legacy [ratelimit.proto](https://github.com/envoyproxy/ratelimit/blob/0ded92a2af8261d43096eba4132e45b99a3b8b14/proto/ratelimit/ratelimit.proto).\n   The current version of ratelimit protocol is changed to [v3 rls.proto](https://github.com/envoyproxy/data-plane-api/blob/master/envoy/service/ratelimit/v3/rls.proto)\n   while [v2 rls.proto](https://github.com/envoyproxy/data-plane-api/blob/master/envoy/service/ratelimit/v3/rls.proto) is still supported\n   as a legacy protocol.\n4. `4bb32826` deleted support for legacy [v2 rls.proto](https://github.com/envoyproxy/data-plane-api/blob/master/envoy/service/ratelimit/v3/rls.proto)\n\n# Building and Testing\n\n- Install Redis-server.\n- Make sure go is setup correctly and checkout rate limit service into your go path. More information about installing\n  go [here](https://golang.org/doc/install).\n- In order to run the integration tests using a local Redis server please run two Redis-server instances: one on port `6379` and another on port `6380`\n  ```bash\n  redis-server --port 6379 \u0026\n  redis-server --port 6380 \u0026\n  ```\n- To setup for the first time (only done once):\n  ```bash\n  make bootstrap\n  ```\n- To compile:\n\n  ```bash\n  make compile\n  ```\n\n  Ensure you set the correct platform if running OSX host with a linux container e.g.\n\n  ```bash\n  GOOS=linux make compile\n  ```\n\n- To compile and run tests:\n  ```bash\n  make tests\n  ```\n- To run the server locally using some sensible default settings you can do this (this will setup the server to read the configuration files from the path you specify):\n  ```bash\n  USE_STATSD=false LOG_LEVEL=debug REDIS_SOCKET_TYPE=tcp REDIS_URL=localhost:6379 RUNTIME_ROOT=/home/user/src/runtime/data RUNTIME_SUBDIRECTORY=ratelimit RUNTIME_APPDIRECTORY=config\n  ```\n\n## Docker-compose setup\n\nThe docker-compose setup has three containers: redis, ratelimit-build, and ratelimit. In order to run the docker-compose setup from the root of the repo, run\n\n```bash\ndocker-compose up\n```\n\nThe ratelimit-build container will build the ratelimit binary. Then via a shared volume the binary will be shared with the ratelimit container. This dual container setup is used in order to use a\na minimal container to run the application, rather than the heftier container used to build it.\n\nIf you want to run with [two redis instances](#two-redis-instances), you will need to modify\nthe docker-compose.yml file to run a second redis container, and change the environment variables\nas explained in the [two redis instances](#two-redis-instances) section.\n\n## Full test environment - Configure rate limits through files\n\nTo run a fully configured environment to demo Envoy based rate limiting, run:\n\n```bash\nexport CONFIG_TYPE=FILE\ndocker-compose -f docker-compose-example.yml up --build --remove-orphans\n```\n\nThis will run ratelimit, redis, prom-statsd-exporter and two Envoy containers such that you can demo rate limiting by hitting the below endpoints.\n\n```bash\ncurl localhost:8888/test\ncurl localhost:8888/header -H \"foo: foo\" # Header based\ncurl localhost:8888/twoheader -H \"foo: foo\" -H \"bar: bar\" # Two headers\ncurl localhost:8888/twoheader -H \"foo: foo\" -H \"baz: baz\"  # This will be rate limited\ncurl localhost:8888/twoheader -H \"foo: foo\" -H \"bar: banned\" # Ban a particular header value\ncurl localhost:8888/twoheader -H \"foo: foo\" -H \"baz: shady\" # This will never be ratelimited since \"baz\" with value \"shady\" is in shadow_mode\ncurl localhost:8888/twoheader -H \"foo: foo\" -H \"baz: not-so-shady\" # This is subject to rate-limiting because the it's now in shadow_mode\n```\n\nEdit `examples/ratelimit/config/example.yaml` to test different rate limit configs. Hot reloading is enabled.\n\nThe descriptors in `example.yaml` and the actions in `examples/envoy/proxy.yaml` should give you a good idea on how to configure rate limits.\n\nTo see the metrics in the example\n\n```bash\n# The metrics for the shadow_mode keys\ncurl http://localhost:9102/metrics | grep -i shadow\n```\n\n## Full test environment - Configure rate limits through an xDS Management Server\n\nTo run a fully configured environment to demo Envoy based rate limiting, run:\n\n```bash\nexport CONFIG_TYPE=GRPC_XDS_SOTW\ndocker-compose -f docker-compose-example.yml --profile xds-config up --build --remove-orphans\n```\n\nThis will run in `xds-config` docker-compose profile which will run example xDS-Server, ratelimit, redis, prom-statsd-exporter and two Envoy containers such that you can demo rate limiting by hitting the below endpoints.\n\n```bash\ncurl localhost:8888/test\ncurl localhost:8888/header -H \"foo: foo\" # Header based\ncurl localhost:8888/twoheader -H \"foo: foo\" -H \"bar: bar\" # Two headers\ncurl localhost:8888/twoheader -H \"foo: foo\" -H \"baz: baz\"  # This will be rate limited\ncurl localhost:8888/twoheader -H \"foo: foo\" -H \"bar: banned\" # Ban a particular header value\ncurl localhost:8888/twoheader -H \"foo: foo\" -H \"baz: shady\" # This will never be ratelimited since \"baz\" with value \"shady\" is in shadow_mode\ncurl localhost:8888/twoheader -H \"foo: foo\" -H \"baz: not-so-shady\" # This is subject to rate-limiting because the it's now in shadow_mode\n```\n\nEdit[`examples/xds-sotw-config-server/resource.go`](examples/xds-sotw-config-server/resource.go) to test different rate limit configs.\n\nTo see the metrics in the example\n\n```bash\n# The metrics for the shadow_mode keys\ncurl http://localhost:9102/metrics | grep -i shadow\n```\n\n## Self-contained end-to-end integration test\n\nIntegration tests are coded as bash-scripts in `integration-test/scripts`.\n\nThe test suite will spin up a docker-compose environment from `integration-test/docker-compose-integration-test.yml`\n\nIf the test suite fails it will exit with code 1.\n\n```bash\nmake integration_tests\n```\n\n# Configuration\n\n## The configuration format\n\nThe rate limit configuration file format is YAML (mainly so that comments are supported).\n\n### Definitions\n\n- **Domain:** A domain is a container for a set of rate limits. All domains known to the Ratelimit service must be\n  globally unique. They serve as a way for different teams/projects to have rate limit configurations that don't conflict.\n- **Descriptor:** A descriptor is a list of key/value pairs owned by a domain that the Ratelimit service uses to\n  select the correct rate limit to use when limiting. Descriptors are case-sensitive. Examples of descriptors are:\n  - (\"database\", \"users\")\n  - (\"message_type\", \"marketing\"),(\"to_number\",\"2061234567\")\n  - (\"to_cluster\", \"service_a\")\n  - (\"to_cluster\", \"service_a\"),(\"from_cluster\", \"service_b\")\n\n### Descriptor list definition\n\nEach configuration contains a top level descriptor list and potentially multiple nested lists beneath that. The format is:\n\n```yaml\ndomain: \u003cunique domain ID\u003e\ndescriptors:\n  - key: \u003crule key: required\u003e\n    value: \u003crule value: optional\u003e\n    rate_limit: (optional block)\n      name: (optional)\n      replaces: (optional)\n       - name: (optional)\n      unit: \u003csee below: required\u003e\n      requests_per_unit: \u003csee below: required\u003e\n    shadow_mode: (optional)\n    detailed_metric: (optional)\n    descriptors: (optional block)\n      - ... (nested repetition of above)\n```\n\nEach descriptor in a descriptor list must have a key. It can also optionally have a value to enable a more specific\nmatch. The \"rate_limit\" block is optional and if present sets up an actual rate limit rule. See below for how the\nrule is defined. If the rate limit is not present and there are no nested descriptors, then the descriptor is\neffectively whitelisted. Otherwise, nested descriptors allow more complex matching and rate limiting scenarios.\n\n### Rate limit definition\n\n```yaml\nrate_limit:\n  unit: \u003csecond, minute, hour, day\u003e\n  requests_per_unit: \u003cuint\u003e\n```\n\nThe rate limit block specifies the actual rate limit that will be used when there is a match.\nCurrently the service supports per second, minute, hour, and day limits. More types of limits may be added in the\nfuture based on user demand.\n\n### Replaces\n\nThe replaces key indicates that this descriptor will replace the configuration set by another descriptor.\n\nIf there is a rule being evaluated, and multiple descriptors can apply, the replaces descriptor will drop evaluation of\nthe descriptor which it is replacing.\n\nTo enable this, any descriptor which should potentially be replaced by another should have a name keyword in the\nrate_limit section, and any descriptor which should potentially replace the original descriptor should have a name\nkeyword in its respective replaces section. Whenever limits match to both rules, only the rule which replaces the\noriginal will take effect, and the limit of the original will not be changed after evaluation.\n\nFor example, let's say you have a bunch of endpoints and each is classified under read or write, with read having a\ncertain limit and write having another. Each user has a certain limit for both endpoints. However, let's say that you\nwant to increase a user's limit to a single read endpoint. The only option without using replaces would be to increase\ntheir limit for the read category. The replaces keyword allows increasing the limit of a single endpoint in this case.\n\n### ShadowMode\n\nA shadow_mode key in a rule indicates that whatever the outcome of the evaluation of the rule, the end-result will always be \"OK\".\n\nWhen a block is in ShadowMode all functions of the rate limiting service are executed as normal, with cache-lookup and statistics\n\nAn additional statistic is added to keep track of how many times a key with \"shadow_mode\" has overridden result.\n\nThere is also a Global Shadow Mode\n\n### Including detailed metrics for unspecified values\n\nSetting the `detailed_metric: true` for a descriptor will extend the metrics that are produced. Normally a descriptor that matches a value that is not explicitly listed in the configuration will from a metrics point-of-view be rolled-up into the base entry. This can be problematic if you want to have those details available for analysis.\n\nNB! This should only be enabled in situations where the potentially large cardinality of metrics that this can lead to is acceptable.\n\n### Examples\n\n#### Example 1\n\nLet's start with a simple example:\n\n```yaml\ndomain: mongo_cps\ndescriptors:\n  - key: database\n    value: users\n    rate_limit:\n      unit: second\n      requests_per_unit: 500\n\n  - key: database\n    value: default\n    rate_limit:\n      unit: second\n      requests_per_unit: 500\n```\n\nIn the configuration above\nthe domain is \"mongo_cps\" and we setup 2 different rate limits in the top level descriptor list. Each of the limits\nhave the same key (\"database\"). They have a different value (\"users\", and \"default\"), and each of them setup a 500\nrequest per second rate limit.\n\n#### Example 2\n\nA slightly more complex example:\n\n```yaml\ndomain: messaging\ndescriptors:\n  # Only allow 5 marketing messages a day\n  - key: message_type\n    value: marketing\n    descriptors:\n      - key: to_number\n        rate_limit:\n          unit: day\n          requests_per_unit: 5\n\n  # Only allow 100 messages a day to any unique phone number\n  - key: to_number\n    rate_limit:\n      unit: day\n      requests_per_unit: 100\n```\n\nIn the preceding example, the domain is \"messaging\" and we setup two different scenarios that illustrate more\ncomplex functionality. First, we want to limit on marketing messages to a specific number. To enable this, we make\nuse of _nested descriptor lists._ The top level descriptor is (\"message_type\", \"marketing\"). However this descriptor\ndoes not have a limit assigned so it's just a placeholder. Contained within this entry we have another descriptor list\nthat includes an entry with key \"to_number\". However, notice that no value is provided. This means that the service\nwill match against any value supplied for \"to_number\" and generate a unique limit. Thus, (\"message_type\", \"marketing\"),\n(\"to_number\", \"2061111111\") and (\"message_type\", \"marketing\"),(\"to_number\", \"2062222222\") will each get 5 requests\nper day.\n\nThe configuration also sets up another rule without a value. This one creates an overall limit for messages sent to\nany particular number during a 1 day period. Thus, (\"to_number\", \"2061111111\") and (\"to_number\", \"2062222222\") both\nget 100 requests per day.\n\nWhen calling the rate limit service, the client can specify _multiple descriptors_ to limit on in a single call. This\nlimits round trips and allows limiting on aggregate rule definitions. For example, using the preceding configuration,\nthe client could send this complete request (in pseudo IDL):\n\n```\nRateLimitRequest:\n  domain: messaging\n  descriptor: (\"message_type\", \"marketing\"),(\"to_number\", \"2061111111\")\n  descriptor: (\"to_number\", \"2061111111\")\n```\n\nAnd the service will rate limit against _all_ matching rules and return an aggregate result; a logical OR of all\nthe individual rate limit decisions.\n\n#### Example 3\n\nAn example to illustrate matching order.\n\n```yaml\ndomain: edge_proxy_per_ip\ndescriptors:\n  - key: remote_address\n    rate_limit:\n      unit: second\n      requests_per_unit: 10\n\n  # Black list IP\n  - key: remote_address\n    value: 50.0.0.5\n    rate_limit:\n      unit: second\n      requests_per_unit: 0\n```\n\nIn the preceding example, we setup a generic rate limit for individual IP addresses. The architecture's edge proxy can\nbe configured to make a rate limit service call with the descriptor `(\"remote_address\", \"50.0.0.1\")` for example. This IP would\nget 10 requests per second as\nwould any other IP. However, the configuration also contains a second configuration that explicitly defines a\nvalue along with the same key.\nIf the descriptor `(\"remote_address\", \"50.0.0.5\")` is received, the service\nwill _attempt the most specific match possible_. This means\nthe most specific descriptor at the same level as your request. Thus, key/value is always attempted as a match before just key.\n\n#### Example 4\n\nThe Ratelimit service matches requests to configuration entries with the same level, i.e\nsame number of tuples in the request's descriptor as nested levels of descriptors\nin the configuration file. For instance, the following request:\n\n```\nRateLimitRequest:\n  domain: example4\n  descriptor: (\"key\", \"value\"),(\"subkey\", \"subvalue\")\n```\n\nWould **not** match the following configuration. Even though the first descriptor in\nthe request matches the 1st level descriptor in the configuration, the request has\ntwo tuples in the descriptor.\n\n```yaml\ndomain: example4\ndescriptors:\n  - key: key\n    value: value\n    rate_limit:\n      requests_per_unit: 300\n      unit: second\n```\n\nHowever, it would match the following configuration:\n\n```yaml\ndomain: example4\ndescriptors:\n  - key: key\n    value: value\n    descriptors:\n      - key: subkey\n        rate_limit:\n          requests_per_unit: 300\n          unit: second\n```\n\n#### Example 5\n\nWe can also define unlimited rate limit descriptors:\n\n```yaml\ndomain: internal\ndescriptors:\n  - key: ldap\n    rate_limit:\n      unlimited: true\n\n  - key: azure\n    rate_limit:\n      unit: minute\n      requests_per_unit: 100\n```\n\nFor an unlimited descriptor, the request will not be sent to the underlying cache (Redis/Memcached), but will be quickly returned locally by the ratelimit instance.\nThis can be useful for collecting statistics, or if one wants to define a descriptor that has no limit but the client wants to distinguish between such descriptor and one that does not exist.\n\nThe return value for unlimited descriptors will be an OK status code with the LimitRemaining field set to MaxUint32 value.\n\n#### Example 6\n\nA rule using shadow_mode is useful for soft-launching rate limiting. In this example\n\n```\nRateLimitRequest:\n  domain: example6\n  descriptor: (\"service\", \"auth-service\"),(\"user\", \"user-a\")\n```\n\n`user-a` of the `auth-service` would not get rate-limited regardless of the rate of requests, there would however be statistics related to the breach of the configured limit of 10 req / sec.\n\n`user-b` would be limited to 20 req / sec however.\n\n```yaml\ndomain: example6\ndescriptors:\n  - key: service\n    descriptors:\n      - key: user\n        value: user-a\n        rate_limit:\n          requests_per_unit: 10\n          unit: second\n        shadow_mode: true\n      - key: user\n        value: user-b\n        rate_limit:\n          requests_per_unit: 20\n          unit: second\n```\n\n#### Example 7\n\nWhen the replaces keyword is used, that limit will replace any limit which has the name being replaced as its name, and\nthe original descriptor's limit will not be affected.\n\nIn the example below, the following limits will apply:\n\n```\n(key_1, value_1), (user, bkthomps): 5 / sec\n(key_2, value_2), (user, bkthomps): 10 / sec\n(key_1, value_1), (key_2, value_2), (user, bkthomps): 10 / sec since the (key_1, value_1), (user, bkthomps) rule was replaced and this will not affect the 5 / sec limit that would take effect with (key_2, value_2), (user, bkthomps)\n```\n\n```yaml\ndomain: example7\ndescriptors:\n  - key: key_1\n    value: value_1\n    descriptors:\n      - key: user\n        value: bkthomps\n        rate_limit:\n          name: specific_limit\n          requests_per_unit: 5\n          unit: second\n  - key: key_2\n    value: value_2\n    descriptors:\n      - key: user\n        value: bkthomps\n        rate_limit:\n          replaces:\n            - name: specific_limit\n          requests_per_unit: 10\n          unit: second\n```\n\n#### Example 8\n\nIn this example we demonstrate how a descriptor without a specified value is configured to override the default behavior and include the matched-value in the metrics.\n\nRate limiting configuration and tracking works as normally\n\n```\n(key_1, unspecified_value): 10 / sec\n(key_1, unspecified_value2): 10 / sec\n(key_1, value_1): 20 / sec\n```\n\n```yaml\ndomain: example8\ndescriptors:\n  - key: key1\n    detailed_metric: true\n    rate_limit:\n      unit: minute\n      requests_per_unit: 10\n  - key: key1\n    value: value1\n    rate_limit:\n      unit: minute\n      requests_per_unit: 20\n```\n\nThe metrics keys will be the following:\n\n\"key1_unspecified_value\"\n\"key1_unspecified_value2\"\n\"key1_value1\"\n\nrather than the normal\n\"key1\"\n\"key1_value1\"\n\n#### Example 9\n\nValue supports wildcard matching to apply rate-limit for nested endpoints:\n\n```\n(key_1, value_1): 20 / sec\n(key_1, value_2): 20 / sec\n```\n\n```yaml\ndomain: example9\ndescriptors:\n  - key: key1\n    value: value*\n    rate_limit:\n      unit: minute\n      requests_per_unit: 20\n```\n\n## Loading Configuration\n\nRate limit service supports following configuration loading methods. You can define which methods to use by configuring environment variable `CONFIG_TYPE`.\n\n| Config Loading Method                                                             | Value for Environment Variable `CONFIG_TYPE` |\n| --------------------------------------------------------------------------------- | -------------------------------------------- |\n| [File Based Configuration Loading](#file-based-configuration-loading)             | `FILE` (Default)                             |\n| [xDS Server Based Configuration Loading](#xds-server-based-configuration-loading) | `GRPC_XDS_SOTW`                              |\n\nWhen the environment variable `FORCE_START_WITHOUT_INITIAL_CONFIG` set to `false`, the Rate limit service will wait for initial rate limit configuration before\nstarting the server (gRPC, Rest server endpoints). When set to `true` the server will start even without initial configuration.\n\n### File Based Configuration Loading\n\nThe Ratelimit service uses a library written by Lyft called [goruntime](https://github.com/lyft/goruntime) to do configuration loading. Goruntime monitors\na designated path, and watches for symlink swaps to files in the directory tree to reload configuration files.\n\nThe path to watch can be configured via the [settings](https://github.com/envoyproxy/ratelimit/blob/master/src/settings/settings.go)\npackage with the following environment variables:\n\n```\nRUNTIME_ROOT default:\"/srv/runtime_data/current\"\nRUNTIME_SUBDIRECTORY\nRUNTIME_APPDIRECTORY default:\"config\"\nRUNTIME_IGNOREDOTFILES default:\"false\"\n```\n\n**Configuration files are loaded from RUNTIME_ROOT/RUNTIME_SUBDIRECTORY/RUNTIME_APPDIRECTORY/\\*.yaml**\n\nThere are two methods for triggering a configuration reload:\n\n1. Symlink RUNTIME_ROOT to a different directory.\n2. Update the contents inside `RUNTIME_ROOT/RUNTIME_SUBDIRECTORY/RUNTIME_APPDIRECTORY/` directly.\n\nThe former is the default behavior. To use the latter method, set the `RUNTIME_WATCH_ROOT` environment variable to `false`.\n\nThe following filesystem operations on configuration files inside `RUNTIME_ROOT/RUNTIME_SUBDIRECTORY/RUNTIME_APPDIRECTORY/` will force a reload of all config files:\n\n- Write\n- Create\n- Chmod\n- Remove\n\nFor more information on how runtime works you can read its [README](https://github.com/lyft/goruntime).\n\nBy default it is not possible to define multiple configuration files within `RUNTIME_SUBDIRECTORY` referencing the same domain.\nTo enable this behavior set `MERGE_DOMAIN_CONFIG` to `true`.\n\n### xDS Management Server Based Configuration Loading\n\nxDS Management Server is a gRPC server which implements the [Aggregated Discovery Service (ADS)](https://github.com/envoyproxy/data-plane-api/blob/97b6dae39046f7da1331a4dc57830d20e842fc26/envoy/service/discovery/v3/ads.proto).\nThe xDS Management server serves [Discovery Response](https://github.com/envoyproxy/data-plane-api/blob/97b6dae39046f7da1331a4dc57830d20e842fc26/envoy/service/discovery/v3/discovery.proto#L69) with [Ratelimit Configuration Resources](api/ratelimit/config/ratelimit/v3/rls_conf.proto)\nand with Type URL `\"type.googleapis.com/ratelimit.config.ratelimit.v3.RateLimitConfig\"`.\n\nThe xDS client in the Rate limit service configure Rate limit service with the provided configuration.\nIn case of connection failures, the xDS Client retries the connection to the xDS server with exponential backoff and the backoff parameters are configurable.\n\n1. `XDS_CLIENT_BACKOFF_JITTER`: set to `\"true\"` to add jitter to the exponential backoff.\n2. `XDS_CLIENT_BACKOFF_INITIAL_INTERVAL`: The base amount of time the xDS client waits before retrying the connection after failure. Default: \"10s\"\n3. `XDS_CLIENT_BACKOFF_MAX_INTERVAL`: The max backoff interval is the upper limit on the amount of time the xDS client will wait between retries. After reaching the max backoff interval, the next retries will continue using the max interval. Default: \"60s\"\n4. `XDS_CLIENT_BACKOFF_RANDOM_FACTOR`: This is a factor by which the initial interval is multiplied to calculate the next backoff interval. Default: \"0.5\"\n\nThe followings are the gRPC connection options.\n\n1. `XDS_CLIENT_MAX_MSG_SIZE_IN_BYTES`: The maximum message size in bytes that the xDS client can receive.\n\nFor more information on xDS protocol please refer to the [envoy proxy documentation](https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_protocol).\n\nYou can refer to [the sample xDS configuration management server](examples/xds-sotw-config-server/README.md).\n\nThe xDS server for listening for configuration can be set via [settings](https://github.com/envoyproxy/ratelimit/blob/master/src/settings/settings.go)\npackage with the following environment variables:\n\n```\nCONFIG_GRPC_XDS_NODE_ID default:\"default\"\nCONFIG_GRPC_XDS_SERVER_URL default:\"localhost:18000\"\nCONFIG_GRPC_XDS_SERVER_CONNECT_RETRY_INTERVAL default:\"3s\"\n```\n\nAs well Ratelimit supports TLS connections, these can be configured using the following environment variables:\n\n1. `CONFIG_GRPC_XDS_SERVER_USE_TLS`: set to `\"true\"` to enable a TLS connection with the xDS configuration management server.\n2. `CONFIG_GRPC_XDS_CLIENT_TLS_CERT`, `CONFIG_GRPC_XDS_CLIENT_TLS_KEY`, and `CONFIG_GRPC_XDS_SERVER_TLS_CACERT` to provides files to specify a TLS connection configuration to the xDS configuration management server.\n3. `CONFIG_GRPC_XDS_SERVER_TLS_SAN`: (Optional) Override the SAN value to validate from the server certificate.\n\nWhen using xDS you can configure extra headers that will be added to GRPC requests to the xDS Management server.\nExtra headers can be useful for providing additional authorization information. This can be configured using the following environment variable:\n\n`CONFIG_GRPC_XDS_CLIENT_ADDITIONAL_HEADERS` - set to `\"\u003ck1:v1\u003e,\u003ck2:v2\u003e\"` to add multiple headers to GRPC requests.\n\n## Log Format\n\nA centralized log collection system works better with logs in json format. JSON format avoids the need for custom parsing rules.\nThe Ratelimit service produces logs in a text format by default. For Example:\n\n```\ntime=\"2020-09-10T17:22:35Z\" level=debug msg=\"loading domain: messaging\"\ntime=\"2020-09-10T17:22:35Z\" level=debug msg=\"loading descriptor: key=messaging.message_type_marketing\"\ntime=\"2020-09-10T17:22:35Z\" level=debug msg=\"loading descriptor: key=messaging.message_type_marketing.to_number ratelimit={requests_per_unit=5, unit=DAY}\"\ntime=\"2020-09-10T17:22:35Z\" level=debug msg=\"loading descriptor: key=messaging.to_number ratelimit={requests_per_unit=100, unit=DAY}\"\ntime=\"2020-09-10T17:21:55Z\" level=warning msg=\"Listening for debug on ':6070'\"\ntime=\"2020-09-10T17:21:55Z\" level=warning msg=\"Listening for HTTP on ':8080'\"\ntime=\"2020-09-10T17:21:55Z\" level=debug msg=\"waiting for runtime update\"\ntime=\"2020-09-10T17:21:55Z\" level=warning msg=\"Listening for gRPC on ':8081'\"\n```\n\nJSON Log format can be configured using the following environment variables:\n\n```\nLOG_FORMAT=json\n```\n\nOutput example:\n\n```\n{\"@message\":\"loading domain: messaging\",\"@timestamp\":\"2020-09-10T17:22:44.926010192Z\",\"level\":\"debug\"}\n{\"@message\":\"loading descriptor: key=messaging.message_type_marketing\",\"@timestamp\":\"2020-09-10T17:22:44.926019315Z\",\"level\":\"debug\"}\n{\"@message\":\"loading descriptor: key=messaging.message_type_marketing.to_number ratelimit={requests_per_unit=5, unit=DAY}\",\"@timestamp\":\"2020-09-10T17:22:44.926037174Z\",\"level\":\"debug\"}\n{\"@message\":\"loading descriptor: key=messaging.to_number ratelimit={requests_per_unit=100, unit=DAY}\",\"@timestamp\":\"2020-09-10T17:22:44.926048993Z\",\"level\":\"debug\"}\n{\"@message\":\"Listening for debug on ':6070'\",\"@timestamp\":\"2020-09-10T17:22:44.926113905Z\",\"level\":\"warning\"}\n{\"@message\":\"Listening for gRPC on ':8081'\",\"@timestamp\":\"2020-09-10T17:22:44.926182006Z\",\"level\":\"warning\"}\n{\"@message\":\"Listening for HTTP on ':8080'\",\"@timestamp\":\"2020-09-10T17:22:44.926227031Z\",\"level\":\"warning\"}\n{\"@message\":\"waiting for runtime update\",\"@timestamp\":\"2020-09-10T17:22:44.926267808Z\",\"level\":\"debug\"}\n```\n\n## GRPC Keepalive\n\nClient-side GRPC DNS re-resolution in scenarios with auto scaling enabled might not work as expected and the current workaround is to [configure connection keepalive](https://github.com/grpc/grpc/issues/12295#issuecomment-382794204) on server-side.\nThe behavior can be fixed by configuring the following env variables for the ratelimit server:\n\n- `GRPC_MAX_CONNECTION_AGE`: a duration for the maximum amount of time a connection may exist before it will be closed by sending a GoAway. A random jitter of +/-10% will be added to MaxConnectionAge to spread out connection storms.\n- `GRPC_MAX_CONNECTION_AGE_GRACE`: an additive period after MaxConnectionAge after which the connection will be forcibly closed.\n\n## Health-check\n\nHealth check status is determined internally by individual components.\nCurrently, we have three components that determine the overall health status of the rate limit service.\nEach of the individual component's health needs to be healthy for the overall to report healthy.\nSome components may be turned OFF via configurations so overall health is not effected by that component's health status.\n\n- Redis health (Turned ON. Defaults to healthy)\n- Configuration status (Turned OFF unless configured to be ON via `HEALTHY_WITH_AT_LEAST_ONE_CONFIG_LOADED` see below section. Defaults to unhealthy)\n  - If the environment variable is enabled then, it will start in an unhealthy state and become healthy when at least one config is loaded. If we later fail to load any configs, it will go unhealthy again.\n- Sigterm (Turned ON. Defaults to healthy)\n  - Turns unhealthy if receives sigterm signal\n    All components needs to be healthy for overall health to be healthy.\n\n### Health-check configurations\n\nHealth check can be configured to check if rate-limit configurations are loaded using the following environment variable.\n\n```\nHEALTHY_WITH_AT_LEAST_ONE_CONFIG_LOADED default:\"false\"`\n```\n\nIf `HEALTHY_WITH_AT_LEAST_ONE_CONFIG_LOADED` is enabled then health check will start as unhealthy and becomes healthy if\nit detects at least one domain is loaded with the config. If it detects no config again then it will change to unhealthy.\n\n## GRPC server\n\nBy default the ratelimit gRPC server binds to `0.0.0.0:8081`. To change this set\n`GRPC_HOST` and/or `GRPC_PORT`. If you want to run the server on a unix domain\nsocket then set `GRPC_UDS`, e.g. `GRPC_UDS=/\u003cdir\u003e/ratelimit.sock` and leave\n`GRPC_HOST` and `GRPC_PORT` unmodified.\n\n# Request Fields\n\nFor information on the fields of a Ratelimit gRPC request please read the information\non the RateLimitRequest message type in the Ratelimit [proto file.](https://github.com/envoyproxy/envoy/blob/master/api/envoy/service/ratelimit/v3/rls.proto)\n\n# GRPC Client\n\nThe [gRPC client](https://github.com/envoyproxy/ratelimit/blob/master/src/client_cmd/main.go) will interact with ratelimit server and tell you if the requests are over limit.\n\n## Commandline flags\n\n- `-dial_string`: used to specify the address of ratelimit server. It defaults to `localhost:8081`.\n- `-domain`: used to specify the domain.\n- `-descriptors`: used to specify one descriptor. You can pass multiple descriptors like following:\n\n```\ngo run main.go -domain test \\\n-descriptors name=foo,age=14 -descriptors name=bar,age=18\n```\n\n# Global ShadowMode\n\nThere is a global shadow-mode which can make it easier to introduce rate limiting into an existing service landscape. It will override whatever result is returned by the regular rate limiting process.\n\n## Configuration\n\nThe global shadow mode is configured with an environment variable\n\nSetting environment variable `SHADOW_MODE` to `true` will enable the feature.\n\n## Statistics\n\nThere is an additional service-level statistics generated that will increment whenever the global shadow mode has overridden a rate limiting result.\n\n# Statistics\n\nThe rate limit service generates various statistics for each configured rate limit rule that will be useful for end\nusers both for visibility and for setting alarms. Ratelimit uses [gostats](https://github.com/lyft/gostats) as its statistics library. Please refer\nto [gostats' documentation](https://godoc.org/github.com/lyft/gostats) for more information on the library.\n\nStatistics default to using [StatsD](https://github.com/statsd/statsd) and configured via the env vars from [gostats](https://github.com/lyft/gostats).\n\nTo output statistics to stdout instead, set env var `USE_STATSD` to `false`\n\nConfigure statistics output frequency with `STATS_FLUSH_INTERVAL`, where the type is `time.Duration`, e.g. `10s` is the default value.\n\nTo disable statistics entirely, set env var `DISABLE_STATS` to `true`\n\nRate Limit Statistic Path:\n\n```\nratelimit.service.rate_limit.DOMAIN.KEY_VALUE.STAT\n```\n\nDOMAIN:\n\n- As specified in the domain value in the YAML runtime file\n\nKEY_VALUE:\n\n- A combination of the key value\n- Nested descriptors would be suffixed in the stats path\n\nThe default mode is that the value-part is omitted if the rule that matches is a descriptor without a value. Specifying the `detailed_metric` configuration parameter changes this behavior and creates a unique metric even in this situation.\n\nSTAT:\n\n- near_limit: Number of rule hits over the NearLimit ratio threshold (currently 80%) but under the threshold rate.\n- over_limit: Number of rule hits exceeding the threshold rate\n- total_hits: Number of rule hits in total\n- shadow_mode: Number of rule hits where shadow_mode would trigger and override the over_limit result\n\nTo use a custom near_limit ratio threshold, you can specify with `NEAR_LIMIT_RATIO` environment variable. It defaults to `0.8` (0-1 scale). These are examples of generated stats for some configured rate limit rules from the above examples:\n\n```\nratelimit.service.rate_limit.mongo_cps.database_default.over_limit: 0\nratelimit.service.rate_limit.mongo_cps.database_default.total_hits: 2846\nratelimit.service.rate_limit.mongo_cps.database_users.over_limit: 0\nratelimit.service.rate_limit.mongo_cps.database_users.total_hits: 2939\nratelimit.service.rate_limit.messaging.message_type_marketing.to_number.over_limit: 0\nratelimit.service.rate_limit.messaging.message_type_marketing.to_number.total_hits: 0\nratelimit.service.rate_limit.messaging.auth-service.over_limit.total_hits: 1\nratelimit.service.rate_limit.messaging.auth-service.over_limit.over_limit: 1\nratelimit.service.rate_limit.messaging.auth-service.over_limit.shadow_mode: 1\n```\n\n## Statistics options\n\n1. `EXTRA_TAGS`: set to `\"\u003ck1:v1\u003e,\u003ck2:v2\u003e\"` to tag all emitted stats with the provided tags. You might want to tag build commit or release version, for example.\n\n## DogStatsD\n\nTo enable dogstatsd integration set:\n\n1. `USE_DOG_STATSD`: `true` to use [DogStatsD](https://docs.datadoghq.com/developers/dogstatsd/?code-lang=go)\n\ndogstatsd also enables so called `mogrifiers` which can\nconvert from traditional stats tags into a combination of stat name and tags.\n\nTo enable mogrifiers, set a comma-separated list of them in `DOG_STATSD_MOGRIFIERS`.\n\ne.g. `USE_DOG_STATSD_MOGRIFIERS`: `FOO,BAR`\n\nFor each mogrifier, define variables that declare the mogrification\n\n1. `DOG_STATSD_MOGRIFIERS_%s_PATTERN`: The regex pattern to match on\n2. `DOG_STATSD_MOGRIFIERS_%s_NAME`: The name of the metric to emit. Can contain variables.\n3. `DOG_STATSD_MOGRIFIERS_%s_TAGS`: Comma-separated list of tags to emit. Can contain variables.\n\nVariables within mogrifiers are strings such as `$1`, `$2`, `$3` which can be used to reference\na match group from the regex pattern.\n\n### Example\n\nIn the example below we will set mogrifier DOMAIN to adjust\n`some.original.metric.TAG` to `some.original.metric` with tag `domain:TAG`\n\nFirst enable a single mogrifier:\n\n1. `USE_DOG_STATSD_MOGRIFIERS`: `DOMAIN`\n\nThen, declare the rules for the `DOMAIN` modifier:\n\n1. `DOG_STATSD_MOGRIFIER_DOMAIN_PATTERN`: `^some\\.original\\.metric\\.(.*)$`\n2. `DOG_STATSD_MOGRIFIER_DOMAIN_NAME`: `some.original.metric`\n3. `DOG_STATSD_MOGRIFIER_DOMAIN_TAGS`: `domain:$1`\n\n### Continued example:\n\nLet's also set another mogrifier which outputs the hits metrics with a domain and descriptor tag\n\nFirst, enable an extra mogrifier:\n\n1. `USE_DOG_STATSD_MOGRIFIERS`: `DOMAIN,HITS`\n\nThen, declare additional rules for the `DESCRIPTOR` mogrifier\n\n1. `DOG_STATSD_MOGRIFIER_HITS_PATTERN`: `^ratelimit\\.service\\.rate_limit\\.([^.]+)\\.(.*)\\.([^.]+)$`\n2. `DOG_STATSD_MOGRIFIER_HITS_NAME`: `ratelimit.service.rate_limit.$3`\n3. `DOG_STATSD_MOGRIFIER_HITS_TAGS`: `domain:$1,descriptor:$2`\n\n## Prometheus\n\nTo enable Prometheus integration set:\n\n1. `USE_PROMETHEUS`: `true` to use [Prometheus](https://prometheus.io/)\n2. `PROMETHEUS_ADDR`: The port to listen on for Prometheus metrics. Defaults to `:9090`\n3. `PROMETHEUS_PATH`: The path to listen on for Prometheus metrics. Defaults to `/metrics`\n4. `PROMETHEUS_MAPPER_YAML`: The path to the YAML file that defines the mapping from statsd to prometheus metrics.\n\nDefine the mapping from statsd to prometheus metrics in a YAML file.\nFind more information about the mapping in the [Metric Mapping and Configuration](https://github.com/prometheus/statsd_exporter?tab=readme-ov-file#metric-mapping-and-configuration).\nThe default setting is:\n\n```yaml\nmappings: # Requires statsd exporter \u003e= v0.6.0 since it uses the \"drop\" action.\n  - match: \"ratelimit.service.rate_limit.*.*.near_limit\"\n    name: \"ratelimit_service_rate_limit_near_limit\"\n    timer_type: \"histogram\"\n    labels:\n      domain: \"$1\"\n      key1: \"$2\"\n  - match: \"ratelimit.service.rate_limit.*.*.over_limit\"\n    name: \"ratelimit_service_rate_limit_over_limit\"\n    timer_type: \"histogram\"\n    labels:\n      domain: \"$1\"\n      key1: \"$2\"\n  - match: \"ratelimit.service.rate_limit.*.*.total_hits\"\n    name: \"ratelimit_service_rate_limit_total_hits\"\n    timer_type: \"histogram\"\n    labels:\n      domain: \"$1\"\n      key1: \"$2\"\n  - match: \"ratelimit.service.rate_limit.*.*.within_limit\"\n    name: \"ratelimit_service_rate_limit_within_limit\"\n    timer_type: \"histogram\"\n    labels:\n      domain: \"$1\"\n      key1: \"$2\"\n\n  - match: \"ratelimit.service.rate_limit.*.*.*.near_limit\"\n    name: \"ratelimit_service_rate_limit_near_limit\"\n    timer_type: \"histogram\"\n    labels:\n      domain: \"$1\"\n      key1: \"$2\"\n      key2: \"$3\"\n  - match: \"ratelimit.service.rate_limit.*.*.*.over_limit\"\n    name: \"ratelimit_service_rate_limit_over_limit\"\n    timer_type: \"histogram\"\n    labels:\n      domain: \"$1\"\n      key1: \"$2\"\n      key2: \"$3\"\n  - match: \"ratelimit.service.rate_limit.*.*.*.total_hits\"\n    name: \"ratelimit_service_rate_limit_total_hits\"\n    timer_type: \"histogram\"\n    labels:\n      domain: \"$1\"\n      key1: \"$2\"\n      key2: \"$3\"\n  - match: \"ratelimit.service.rate_limit.*.*.*.within_limit\"\n    name: \"ratelimit_service_rate_limit_within_limit\"\n    timer_type: \"histogram\"\n    labels:\n      domain: \"$1\"\n      key1: \"$2\"\n      key2: \"$3\"\n\n  - match: \"ratelimit.service.call.should_rate_limit.*\"\n    name: \"ratelimit_service_should_rate_limit_error\"\n    match_metric_type: counter\n    labels:\n      err_type: \"$1\"\n\n  - match: \"ratelimit_server.*.total_requests\"\n    name: \"ratelimit_service_total_requests\"\n    match_metric_type: counter\n    labels:\n      grpc_method: \"$1\"\n\n  - match: \"ratelimit_server.*.response_time\"\n    name: \"ratelimit_service_response_time_seconds\"\n    timer_type: histogram\n    labels:\n      grpc_method: \"$1\"\n\n  - match: \"ratelimit.service.config_load_success\"\n    name: \"ratelimit_service_config_load_success\"\n    match_metric_type: counter\n  - match: \"ratelimit.service.config_load_error\"\n    name: \"ratelimit_service_config_load_error\"\n    match_metric_type: counter\n\n  - match: \"ratelimit.service.rate_limit.*.*.*.shadow_mode\"\n    name: \"ratelimit_service_rate_limit_shadow_mode\"\n    timer_type: \"histogram\"\n    labels:\n      domain: \"$1\"\n      key1: \"$2\"\n      key2: \"$3\"\n```\n\n# HTTP Port\n\nThe ratelimit service listens to HTTP 1.1 (by default on port 8080) with two endpoints:\n\n1. /healthcheck → return a 200 if this service is healthy\n1. /json → HTTP 1.1 endpoint for interacting with ratelimit service\n\n## /json endpoint\n\nTakes an HTTP POST with a JSON body of the form e.g.\n\n```json\n{\n  \"domain\": \"dummy\",\n  \"descriptors\": [\n    { \"entries\": [{ \"key\": \"one_per_day\", \"value\": \"something\" }] }\n  ]\n}\n```\n\nThe service will return an http 200 if this request is allowed (if no ratelimits exceeded) or 429 if one or more\nratelimits were exceeded.\n\nThe response is a RateLimitResponse encoded with\n[proto3-to-json mapping](https://developers.google.com/protocol-buffers/docs/proto3#json):\n\n```json\n{\n  \"overallCode\": \"OVER_LIMIT\",\n  \"statuses\": [\n    {\n      \"code\": \"OVER_LIMIT\",\n      \"currentLimit\": {\n        \"requestsPerUnit\": 1,\n        \"unit\": \"MINUTE\"\n      }\n    },\n    {\n      \"code\": \"OK\",\n      \"currentLimit\": {\n        \"requestsPerUnit\": 2,\n        \"unit\": \"MINUTE\"\n      },\n      \"limitRemaining\": 1\n    }\n  ]\n}\n```\n\n# Debug Port\n\nThe debug port can be used to interact with the running process.\n\n```\n$ curl 0:6070/\n/debug/pprof/: root of various pprof endpoints. hit for help.\n/rlconfig: print out the currently loaded configuration for debugging\n/stats: print out stats\n```\n\nYou can specify the debug server address with the `DEBUG_HOST` and `DEBUG_PORT` environment variables. They currently default to `0.0.0.0` and `6070` respectively.\n\n# Local Cache\n\nRatelimit optionally uses [freecache](https://github.com/coocood/freecache) as its local caching layer, which stores the over-the-limit cache keys, and thus avoids reading the\nredis cache again for the already over-the-limit keys. The local cache size can be configured via `LocalCacheSizeInBytes` in the [settings](https://github.com/envoyproxy/ratelimit/blob/master/src/settings/settings.go).\nIf `LocalCacheSizeInBytes` is 0, local cache is disabled.\n\n# Redis\n\nRatelimit uses Redis as its caching layer. Ratelimit supports two operation modes:\n\n1. One Redis server for all limits.\n1. Two Redis instances: one for per second limits and another one for all other limits.\n\nAs well Ratelimit supports TLS connections and authentication. These can be configured using the following environment variables:\n\n1. `REDIS_TLS` \u0026 `REDIS_PERSECOND_TLS`: set to `\"true\"` to enable a TLS connection for the specific connection type.\n1. `REDIS_TLS_CLIENT_CERT`, `REDIS_TLS_CLIENT_KEY`, and `REDIS_TLS_CACERT` to provides files to specify a TLS connection configuration to Redis server that requires client certificate verification. (This is effective when `REDIS_TLS` or `REDIS_PERSECOND_TLS` is set to to `\"true\"`).\n1. `REDIS_TLS_SKIP_HOSTNAME_VERIFICATION` set to `\"true\"` will skip hostname verification in environments where the certificate has an invalid hostname, such as GCP Memorystore.\n1. `REDIS_AUTH` \u0026 `REDIS_PERSECOND_AUTH`: set to `\"password\"` to enable password-only authentication to the redis host.\n1. `REDIS_AUTH` \u0026 `REDIS_PERSECOND_AUTH`: set to `\"username:password\"` to enable username-password authentication to the redis host.\n1. `CACHE_KEY_PREFIX`: a string to prepend to all cache keys\n\nFor controlling the behavior of cache key incrementation when any of them is already over the limit, you can use the following configuration:\n\n1. `STOP_CACHE_KEY_INCREMENT_WHEN_OVERLIMIT`: Set this configuration to `true` to disallow key incrementation when one of the keys is already over the limit.\n\n`STOP_CACHE_KEY_INCREMENT_WHEN_OVERLIMIT` is useful when multiple descriptors are included in a single request. Setting this to `true` can prevent the incrementation of other descriptors' counters if any of the descriptors is already over the limit.\n\n## Redis type\n\nRatelimit supports different types of redis deployments:\n\n1. Single instance (default): Talk to a single instance of redis, or a redis proxy (e.g. https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_protocols/redis)\n1. Sentinel: Talk to a redis deployment with sentinel instances (see https://redis.io/topics/sentinel)\n1. Cluster: Talk to a redis in cluster mode (see https://redis.io/topics/cluster-spec)\n\nThe deployment type can be specified with the `REDIS_TYPE` / `REDIS_PERSECOND_TYPE` environment variables. Depending on the type defined, the `REDIS_URL` and `REDIS_PERSECOND_URL` are expected to have the following formats:\n\n1. \"single\": Depending on the socket type defined, either a single hostname:port pair or a unix domain socket reference.\n1. \"sentinel\": A comma separated list with the first string as the master name of the sentinel cluster followed by hostname:port pairs. The list size should be \u003e= 2. The first item is the name of the master and the rest are the sentinels.\n1. \"cluster\": A comma separated list of hostname:port pairs with all the nodes in the cluster.\n\n## Pipelining\n\nBy default, for each request, ratelimit will pick up a connection from pool, write multiple redis commands in a single write then reads their responses in a single read. This reduces network delay.\n\nFor high throughput scenarios, ratelimit also support [implicit pipelining](https://github.com/mediocregopher/radix/blob/v3.5.1/pool.go#L238) . It can be configured using the following environment variables:\n\n1. `REDIS_PIPELINE_WINDOW` \u0026 `REDIS_PERSECOND_PIPELINE_WINDOW`: sets the duration after which internal pipelines will be flushed.\n   If window is zero then implicit pipelining will be disabled.\n1. `REDIS_PIPELINE_LIMIT` \u0026 `REDIS_PERSECOND_PIPELINE_LIMIT`: sets maximum number of commands that can be pipelined before flushing.\n   If limit is zero then no limit will be used and pipelines will only be limited by the specified time window.\n\n`implicit pipelining` is disabled by default. To enable it, you can use default values [used by radix](https://github.com/mediocregopher/radix/blob/v3.5.1/pool.go#L278) and tune for the optimal value.\n\n## One Redis Instance\n\nTo configure one Redis instance use the following environment variables:\n\n1. `REDIS_SOCKET_TYPE`\n1. `REDIS_URL`\n1. `REDIS_POOL_SIZE`\n1. `REDIS_TYPE` (optional)\n\nThis setup will use the same Redis server for all limits.\n\n## Two Redis Instances\n\nTo configure two Redis instances use the following environment variables:\n\n1. `REDIS_SOCKET_TYPE`\n1. `REDIS_URL`\n1. `REDIS_POOL_SIZE`\n1. `REDIS_PERSECOND`: set this to `\"true\"`.\n1. `REDIS_PERSECOND_SOCKET_TYPE`\n1. `REDIS_PERSECOND_URL`\n1. `REDIS_PERSECOND_POOL_SIZE`\n1. `REDIS_PERSECOND_TYPE` (optional)\n\nThis setup will use the Redis server configured with the `_PERSECOND_` vars for\nper second limits, and the other Redis server for all other limits.\n\n## Health Checking for Redis Active Connection\n\nTo configure whether to return health check failure if there is no active redis connection\n\n1. `REDIS_HEALTH_CHECK_ACTIVE_CONNECTION` : (default is \"false\")\n\n# Memcache\n\nExperimental Memcache support has been added as an alternative to Redis in v1.5.\n\nTo configure a Memcache instance use the following environment variables instead of the Redis variables:\n\n1. `MEMCACHE_HOST_PORT=`: a comma separated list of hostname:port pairs for memcache nodes (mutually exclusive with `MEMCACHE_SRV`)\n1. `MEMCACHE_SRV=`: an SRV record to lookup hosts from (mutually exclusive with `MEMCACHE_HOST_PORT`)\n1. `MEMCACHE_SRV_REFRESH=0`: refresh the list of hosts every n seconds, if 0 no refreshing will happen, supports duration suffixes: \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\".\n1. `BACKEND_TYPE=memcache`\n1. `CACHE_KEY_PREFIX`: a string to prepend to all cache keys\n1. `MEMCACHE_MAX_IDLE_CONNS=2`: the maximum number of idle TCP connections per memcache node, `2` is the default of the underlying library\n1. `MEMCACHE_TLS`: set to `\"true\"` to connect to the server with TLS.\n1. `MEMCACHE_TLS_CLIENT_CERT`, `MEMCACHE_TLS_CLIENT_KEY`, and `MEMCACHE_TLS_CACERT` to provide files that parameterize the memcache client TLS connection configuration.\n1. `MEMCACHE_TLS_SKIP_HOSTNAME_VERIFICATION` set to `\"true\"` will skip hostname verification in environments where the certificate has an invalid hostname.\n\nWith memcache mode increments will happen asynchronously, so it's technically possible for\na client to exceed quota briefly if multiple requests happen at exactly the same time.\n\nNote that Memcache has a max key length of 250 characters, so operations referencing very long\ndescriptors will fail. Descriptors sent to Memcache should not contain whitespaces or control characters.\n\nWhen using multiple memcache nodes in `MEMCACHE_HOST_PORT=`, one should provide the identical list of memcache nodes\nto all ratelimiter instances to ensure that a particular cache key is always hashed to the same memcache node.\n\n# Custom headers\n\nRatelimit service can be configured to return custom headers with the ratelimit information. It will populate the response_headers_to_add as part of the [RateLimitResponse](https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/ratelimit/v3/rls.proto#service-ratelimit-v3-ratelimitresponse).\n\nThe following environment variables control the custom response feature:\n\n1. `LIMIT_RESPONSE_HEADERS_ENABLED` - Enables the custom response headers\n1. `LIMIT_LIMIT_HEADER` - The default value is \"RateLimit-Limit\", setting the environment variable will specify an alternative header name\n1. `LIMIT_REMAINING_HEADER` - The default value is \"RateLimit-Remaining\", setting the environment variable will specify an alternative header name\n1. `LIMIT_RESET_HEADER` - The default value is \"RateLimit-Reset\", setting the environment variable will specify an alternative header name\n\n# Tracing\n\nRatelimit service supports exporting spans in OLTP format. See [OpenTelemetry](https://opentelemetry.io/) for more information.\n\nThe following environment variables control the tracing feature:\n\n1. `TRACING_ENABLED` - Enables the tracing feature. Only \"true\" and \"false\"(default) are allowed in this field.\n1. `TRACING_EXPORTER_PROTOCOL` - Controls the protocol of exporter in tracing feature. Only \"http\"(default) and \"grpc\" are allowed in this field.\n1. `TRACING_SERVICE_NAME` - Controls the service name appears in tracing span. The default value is \"RateLimit\".\n1. `TRACING_SERVICE_NAMESPACE` - Controls the service namespace appears in tracing span. The default value is empty.\n1. `TRACING_SERVICE_INSTANCE_ID` - Controls the service instance id appears in tracing span. It is recommended to put the pod name or container name in this field. The default value is a randomly generated version 4 uuid if unspecified.\n1. Other fields in [OTLP Exporter Documentation](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.8.0/specification/protocol/exporter.md). These section needs to be correctly configured in order to enable the exporter to export span to the correct destination.\n1. `TRACING_SAMPLING_RATE` - Controls the sampling rate, defaults to 1 which means always sample. Valid range: 0.0-1.0. For high volume services, adjusting the sampling rate is recommended.\n\nYou may use the following commands to quickly setup a openTelemetry collector together with a Jaeger all-in-one binary for quickstart:\n\n```bash\ndocker run --name otlp -d -p 4318 -p 4317 -v examples/otlp-collector:/tmp/otlp-collector otel/opentelemetry-collector:0.48.0 -- --config /tmp/otlp-collector/config.yaml\notelcol-contrib --config examples/otlp-collector/config.yaml\n\ndocker run -d --name jaeger -p 16686:16686 -p 14250:14250 jaegertracing/all-in-one:1.33\n```\n\n# TLS\n\nRatelimit supports TLS for it's gRPC endpoint.\n\nThe following environment variables control the TLS feature:\n\n1. `GRPC_SERVER_USE_TLS` - Enables gRPC connections to server over TLS\n1. `GRPC_SERVER_TLS_CERT` - Path to the file containing the server cert chain\n1. `GRPC_SERVER_TLS_KEY` - Path to the file containing the server private key\n\nRatelimit uses [goruntime](https://github.com/lyft/goruntime) to watch the TLS certificate and key and will hot reload them on changes.\n\n# mTLS\n\nRatelimit supports mTLS when Envoy sends requests to the service.\n\nTLS must be enabled on the gRPC endpoint in order for mTLS to work see [TLS](#TLS).\n\nThe following variables can be set to enable mTLS on the Ratelimit service.\n\n1. `GRPC_CLIENT_TLS_CACERT` - Path to the file containing the client CA certificate.\n1. `GRPC_CLIENT_TLS_SAN` - (Optional) DNS Name to validate from the client cert during mTLS auth\n\nIn the envoy config use, add the `transport_socket` section to the ratelimit service cluster config\n\n```yaml\n\"name\": \"ratelimit\"\n\"transport_socket\":\n  \"name\": \"envoy.transport_sockets.tls\"\n  \"typed_config\":\n    \"@type\": \"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext\"\n    \"common_tls_context\":\n      \"tls_certificates\":\n        - \"certificate_chain\":\n            \"filename\": \"/opt/envoy/tls/ratelimit-client-cert.pem\"\n          \"private_key\":\n            \"filename\": \"/opt/envoy/tls/ratelimit-client-key.pem\"\n      \"validation_context\":\n        \"match_subject_alt_names\":\n          - \"exact\": \"ratelimit.server.dnsname\"\n        \"trusted_ca\":\n          \"filename\": \"/opt/envoy/tls/ratelimit-server-ca.pem\"\n```\n\n# Contact\n\n- [envoy-announce](https://groups.google.com/forum/#!forum/envoy-announce): Low frequency mailing\n  list where we will email announcements only.\n- [envoy-users](https://groups.google.com/forum/#!forum/envoy-users): General user discussion.\n  Please add `[ratelimit]` to the email subject.\n- [envoy-dev](https://groups.google.com/forum/#!forum/envoy-dev): Envoy developer discussion (APIs,\n  feature design, etc.). Please add `[ratelimit]` to the email subject.\n- [Slack](https://envoyproxy.slack.com/): Slack, to get invited go [here](http://envoyslack.cncf.io).\n  We have the IRC/XMPP gateways enabled if you prefer either of those. Once an account is created,\n  connection instructions for IRC/XMPP can be found [here](https://envoyproxy.slack.com/account/gateways).\n  The `#ratelimit-users` channel is used for discussions about the ratelimit service.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fenvoyproxy%2Fratelimit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fenvoyproxy%2Fratelimit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fenvoyproxy%2Fratelimit/lists"}