{"id":20368021,"url":"https://github.com/mcaptcha/cache","last_synced_at":"2025-04-12T05:36:54.527Z","repository":{"id":53838978,"uuid":"374141107","full_name":"mCaptcha/cache","owner":"mCaptcha","description":"Redis module that implements mCaptcha cache and counter","archived":false,"fork":false,"pushed_at":"2024-03-15T11:04:38.000Z","size":4686,"stargazers_count":5,"open_issues_count":5,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-26T01:02:54.863Z","etag":null,"topics":["mcaptcha","redis","redis-module"],"latest_commit_sha":null,"homepage":"https://mcaptcha.org","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mCaptcha.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","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},"funding":{"open_collective":null,"liberapay":"mcaptcha","issuehunt":null,"custom":["https://mcaptcha.org/donate"]}},"created_at":"2021-06-05T14:56:31.000Z","updated_at":"2024-07-09T09:48:20.000Z","dependencies_parsed_at":"2023-02-06T09:31:13.457Z","dependency_job_id":"f10ecda7-ff3b-4ba4-adbc-1afb9afe64f0","html_url":"https://github.com/mCaptcha/cache","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mCaptcha%2Fcache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mCaptcha%2Fcache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mCaptcha%2Fcache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mCaptcha%2Fcache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mCaptcha","download_url":"https://codeload.github.com/mCaptcha/cache/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248524852,"owners_count":21118615,"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":["mcaptcha","redis","redis-module"],"created_at":"2024-11-15T00:36:08.619Z","updated_at":"2025-04-12T05:36:54.498Z","avatar_url":"https://github.com/mCaptcha.png","language":"Rust","funding_links":["https://liberapay.com/mcaptcha","https://mcaptcha.org/donate"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003ch1\u003emCaptcha Cache\u003c/h1\u003e\n  \u003cp\u003e\n    \u003cstrong\u003e\n      Redis module that implements\n      \u003ca href=\"https://en.wikipedia.org/wiki/Leaky_bucket\"\n        \u003eleaky bucket algorithm\u003c/a\n      \u003e\n    \u003c/strong\u003e\n  \u003c/p\u003e\n\n[![CI Linux)](https://github.com/mCaptcha/cache/actions/workflows/linux.yml/badge.svg)](https://github.com/mCaptcha/cache/actions/workflows/linux.yml)\n[![Docker](https://img.shields.io/docker/pulls/mcaptcha/cache)](https://hub.docker.com/r/mcaptcha/cache)\n[![dependency status](https://deps.rs/repo/github/mCaptcha/cache/status.svg)](https://deps.rs/repo/github/mCaptcha/cache)\n\u003cbr /\u003e\n[![AGPL License](https://img.shields.io/badge/license-AGPL-blue.svg?style=flat-square)](http://www.gnu.org/licenses/agpl-3.0)\n[![Chat](https://img.shields.io/badge/matrix-+mcaptcha:matrix.batsense.net-purple?style=flat-square)](https://matrix.to/#/+mcaptcha:matrix.batsense.net)\n\n\u003c/div\u003e\n\n## Features\n\n- [x] Timers for individual count\n- [x] Clustering\n- [x] Persistence through RDB\n- [ ] Persistence through AOF\n\n## Motivation\n\n[mCaptcha](https://github.com/mCaptcha/mCaptcha) uses a [leaky-\nbucket](https://en.wikipedia.org/wiki/Leaky_bucket)-enabled counter to\nkeep track of traffic/challenge requests.\n\n- At `t=0`(where `t` is time), if someone is visiting an mCaptcha-protected website, the\n  counter for that website will be initialized and set to 1.\n\n- It should also automatically decrement(by 1) after a certain period, say\n  `t=cooldown`. We call this cool down period and is constant for a\n  website.\n\n- If at `t=x`(where `x\u003ccooldown`), another user visits the same website,\n  the counter becomes 2 and will auto decrement at `t = cooldown + x`\n  for second user.\n\n  Note that, for the decrement to work, we require two different timers\n  that goes off at two different instants. The current(`v0.1.3`) of\n  [`libmcaptcha`](https://github.com/mCaptcha/libmcaptcha/) implements\n  this with internal data structures and timers --- something that can't\n  be shared across several machines in a distributed setting.\n\n  So we figured we'd use Redis to solve this problem and get\n  synchronisation and persistence for free.\n\n  This Redis module implements auto decrement on a special\n  data type(which is also defined in this module).\n\n## How does it work?\n\nIf a timer is supposed to go off to\ndecrement key `myCounter` at `t=y`(where y is an instant in future),\n\n1. A hashmap called `mcaptcha_cache:decrement:y`(prefix might vary) is\n   created with key-value pairs `keyName: DecrementCount`(`myCounter: 1` in\n   our case)\n\n2. A timer will be created to go off at `t=y`\n3. Any further decrement operations that are scheduled for `t=y` are\n   registered with the same hashmap(`mcaptcha_cache:decrement:y`).\n\n4. At `t=y`, a procedure will be executed to read\n   all values of the hashmap(`mcaptcha_cache:decrement:y`) and performs\n   all registered decrements. When its done, it cleans itself up.\n\nThis way, we are not spinning timers for every decrement operation but\ninstead, one for every \"pocket\".\n\n### Gotchas:\n\nThis module creates and manages data of three types:\n\n1.  `mcaptcha_cache:captcha:y` where `y`(last character) is variable\n2.  `mcaptcha_cache:pocket:x` where `x`(last character) is variable\n3.  `mcaptcha:timer:z` where `z`(last character) is pocket name from\n    step 2(See [Hacks](#hacks)).\n\n**WARNING: Please don't modify these manually. If you do so, then Redis\nwill panic**\n\nThis module is capable of cleaning up after itself so manual clean up is\nunnecessary. If you have needs that are not met my this module and you\nwhich access/mutate data manually, please open an\n[issue](https://github.com/mCaptcha/cache/issues). I'd be happy to help.\n\n## Usage\n\nThere are two ways to run `cache`:\n\n1. [Using docker](#docker)\n2. [On bare-metal](#bare-metal)\n\n### Docker\n\nUse image from DockerHub:\n\n```bash\n$  docker run -p 6379:6379 mcaptcha/cache\n```\n\nor build from source:\n\n#### Build\n\n```bash\n$ docker build -t mcaptcha/cache .\n```\n\n#### Run\n\n```bash\n$  docker run -p 6379:6379 mcaptcha/cache\n```\n\n### Bare-metal\n\n#### Build\n\nMake sure you have Rust installed:\nhttps://www.rust-lang.org/tools/install\n\nThen, build as usual:\n\n```bash\ncargo build --release\n```\n\n#### Run\n\n```\nredis-server --loadmodule ./target/release/libcache.so\n```\n\n### Commands\n\nEvery counter has a name and a leak-rate in seconds.\n\n## Create/Increment counter\n\nIf counter exists, then count is incremented. Otherwise, it is created.\n\n```redis\nMCAPTCHA_CACHE.COUNT \u003ccounter-name\u003e \u003cleak-rate-in-seconds\u003e\n```\n\n## Get counter value\n\n```redis\nMCAPTCHA_CACHE.GET \u003ccounter-name\u003e\n```\n\n## Benchmark\n\n**NOTE:** These benchmarks are for reference only. Do not depend upon\nthem too much. When in doubt, please craft and run benchmarks that are\nbetter suited to your workload.\n\nTo run benchmarks locally, launch Redis server with module loaded and:\n\n```bash\n$ make bench\n```\n\n- platform: `Intel core i7-9750h`\n\n```bash\n➜  cache git:(master) ✗ make bench\n./scripts/bench.sh\nrunning set and get without pipelining\nSET: 128600.82 requests per second, p50=0.191 msec\nGET: 128617.36 requests per second, p50=0.191 msec\n\nmCaptcha cache without piplining\nMCAPTCHA_CACHE.ADD_VISITOR mycounter: 127811.86 requests per second, p50=0.207 msec\nMCAPTCHA_CACHE.GET mycounter: 123243.77 requests per second, p50=0.199 msec\nrunning set and get with pipelining\nSET: 1416430.62 requests per second, p50=0.479 msec\nGET: 1644736.88 requests per second, p50=0.391 msec\n\nmCaptcha cache with piplining\nMCAPTCHA_CACHE.ADD_VISITOR mycounter: 396039.59 requests per second, p50=1.903 msec\nMCAPTCHA_CACHE.GET mycounter: 889679.75 requests per second, p50=0.791 msec\n```\n\n## Hacks\n\nI couldn't find any ways to persist timers to disk(`RDB`/`AOF`). So I'm\nusing a dummy record(`mcaptcha:timer:*` see [Gotchas](#gotchas)) which\nwill expire after an arbitrary time(see `POCKET_EXPIRY_OFFSET` in\n[`lib.rs`](./src/lib.rs)). When that expiry occurs, I derive the key of\nthe pocket from the values that are passed to expiration event handlers\nand perform clean up of both the pocket and counters registered with the\npocket.\n\nIdeally, I should be able to persist timers but I couldn't find ways to\ndo that.\n\n## Funding\n\n### NLnet\n\n\u003cdiv align=\"center\"\u003e\n\t\u003cimg\n\t\theight=\"150px\"\n\t\talt=\"NLnet NGIZero logo\"\n\t\tsrc=\"./docs/third-party/NGIZero-green.hex.svg\"\n\t/\u003e\n\u003c/div\u003e\n\n\u003cbr /\u003e\n\n2023 development is funded through the [NGI0 Entrust\nFund](https://nlnet.nl/entrust), via [NLnet](https://nlnet.nl/). Please\nsee [here](https://nlnet.nl/project/mCaptcha/) for more details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmcaptcha%2Fcache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmcaptcha%2Fcache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmcaptcha%2Fcache/lists"}