{"id":33275767,"url":"https://github.com/cchexcode/grenze","last_synced_at":"2026-06-20T14:31:56.451Z","repository":{"id":319969071,"uuid":"1080278464","full_name":"cchexcode/grenze","owner":"cchexcode","description":null,"archived":false,"fork":false,"pushed_at":"2025-10-21T06:26:08.000Z","size":21,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-11-17T08:13:39.304Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cchexcode.png","metadata":{"files":{"readme":"README.md","changelog":null,"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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-21T06:18:37.000Z","updated_at":"2025-10-21T06:26:11.000Z","dependencies_parsed_at":"2025-10-21T08:39:15.210Z","dependency_job_id":"a4a9eeba-9bf4-442f-a446-dda270fca6fc","html_url":"https://github.com/cchexcode/grenze","commit_stats":null,"previous_names":["cchexcode/grenze"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/cchexcode/grenze","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cchexcode%2Fgrenze","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cchexcode%2Fgrenze/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cchexcode%2Fgrenze/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cchexcode%2Fgrenze/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cchexcode","download_url":"https://codeload.github.com/cchexcode/grenze/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cchexcode%2Fgrenze/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34573729,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-20T02:00:06.407Z","response_time":98,"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":"2025-11-17T08:03:59.887Z","updated_at":"2026-06-20T14:31:56.396Z","avatar_url":"https://github.com/cchexcode.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Grenze\n\n\u003e A little HTTP rate limiting for everyone.\n\n**Grenze** (German for \"border\" or \"boundary\") is a lightweight, high-performance HTTP proxy server that implements rate limiting using the leaky bucket algorithm. Built with Rust and Redis, it provides a simple yet powerful way to control request rates for any HTTP endpoint.\n\n## Features\n\n- **🚦 Rate Limiting**: Implements a leaky bucket algorithm for smooth, predictable rate limiting\n- **🔑 Custom Keys**: Clients provide their own rate limit keys for flexible multi-tenant usage\n- **🔄 HTTP Proxy**: Forward any HTTP request (GET, POST, etc.) through the rate limiter\n- **⚡ Redis-Backed**: Uses Redis with Lua scripts for atomic, distributed rate limiting\n- **🐳 Docker Ready**: Includes Docker Compose setup for easy deployment\n- **🛡️ Graceful Shutdown**: Handles SIGINT/SIGTERM signals properly\n- **📊 Health Checks**: Built-in health endpoint for monitoring\n\n## Architecture\n\nThe server acts as a rate-limiting proxy that:\n1. Accepts HTTP requests with a rate limit key\n2. Checks the rate limit using Redis\n3. If allowed, forwards the request to the target URL\n4. Returns the response (or 429 Too Many Requests if rate limited)\n\n## Quick Start\n\n### Using Docker Compose (Recommended)\n\n```bash\ndocker compose -f docker/compose.yml up\n```\n\nThis starts both Redis and the grenze-server on port 8080.\n\n### Manual Setup\n\n1. **Start Redis:**\n   ```bash\n   docker run -d -p 6379:6379 redis:7-alpine\n   ```\n\n2. **Set environment variables:**\n   ```bash\n   export REDIS_URL=redis://localhost:6379/\n   ```\n\n3. **Build and run:**\n   ```bash\n   cargo build --release\n   cargo run --release -p grenze-server\n   ```\n\nThe server will start on `0.0.0.0:8080`.\n\n## API Reference\n\n### Health Check\n\n**Endpoint:** `GET /health`\n\nReturns the server's health status and version.\n\n**Response:**\n```json\n{\n  \"status\": \"ok\",\n  \"version\": \"0.0.0\"\n}\n```\n\n### Proxy Request\n\n**Endpoint:** `POST /proxy`\n\nForwards an HTTP request through the rate limiter.\n\n**Request Body:**\n```json\n{\n  \"key\": \"user-123\",           // Required: Rate limit key\n  \"url\": \"https://api.example.com/data\",\n  \"method\": \"POST\",            // GET, POST, PUT, DELETE, etc.\n  \"headers\": {                 // Optional: Custom headers\n    \"Authorization\": \"Bearer token\",\n    \"Content-Type\": \"application/json\"\n  },\n  \"query\": {                   // Optional: Query parameters\n    \"page\": \"1\",\n    \"limit\": \"10\"\n  },\n  \"body\": {                    // Optional: Request body (JSON)\n    \"name\": \"value\"\n  },\n  \"timeout_ms\": 5000          // Optional: Request timeout in milliseconds\n}\n```\n\n**Success Response:**\n- Returns the downstream API's response with status code and body\n- Passes through `Content-Type`, `Content-Length`, and `Cache-Control` headers\n\n**Error Responses:**\n\n**400 Bad Request** - Missing or empty rate limit key:\n```json\n{\n  \"error\": \"missing_key\",\n  \"message\": \"Request must include non-empty 'key'\"\n}\n```\n\n**429 Too Many Requests** - Rate limit exceeded:\n```json\n{\n  \"error\": \"rate_limited\",\n  \"message\": \"Too many requests\"\n}\n```\n\n**502 Bad Gateway** - Downstream request failed:\n```json\n{\n  \"error\": \"downstream_error\",\n  \"message\": \"Error details...\"\n}\n```\n\n## Rate Limiting\n\n### Algorithm: Leaky Bucket\n\nGrenze uses the **leaky bucket** algorithm, which:\n- Has a fixed capacity (bucket size)\n- \"Leaks\" at a constant rate over time\n- Allows bursts up to the bucket capacity\n- Rejects requests when the bucket is full\n\n### Configuration\n\nCurrently configured in the code (see `main.rs`):\n- **Capacity**: 1 request per second (RPS)\n- **Leak Rate**: 1 request per second\n\nEach unique `key` gets its own independent bucket stored in Redis with automatic TTL expiration.\n\n### Rate Limit Keys\n\nThe `key` field in the proxy request determines which rate limit bucket to use. This design allows for:\n- **Per-user rate limiting**: `user-{user_id}`\n- **Per-tenant rate limiting**: `tenant-{tenant_id}`\n- **Per-endpoint rate limiting**: `api-{endpoint_name}`\n- **Per-IP rate limiting**: `ip-{ip_address}`\n- **Combined keys**: `user-{user_id}-api-{endpoint}`\n\n## Configuration\n\n### Environment Variables\n\n| Variable | Required | Default | Description |\n|----------|----------|---------|-------------|\n| `REDIS_URL` | Yes | - | Redis connection URL (e.g., `redis://localhost:6379/`) |\n| `RUST_LOG` | No | `info` | Log level (`error`, `warn`, `info`, `debug`, `trace`) |\n| `RUST_BACKTRACE` | No | `1` | Enable backtraces on panic |\n\n## Development\n\n### Prerequisites\n\n- Rust 1.90+ (using 2024 edition)\n- Docker (for Redis)\n- cargo-chef (optional, for Docker builds)\n\n### Building\n\n```bash\n# Debug build\ncargo build -p grenze-server\n\n# Release build\ncargo build -p grenze-server --release\n\n# Run tests\ncargo test\n\n# Generate documentation\ncargo doc --open\n```\n\n### Docker Build\n\n```bash\n# Build the image\ndocker build -f docker/rust.Dockerfile \\\n  --build-arg PACKAGE=grenze-server \\\n  --build-arg CRATE_DIR=crates/grenze-server \\\n  -t grenze-server .\n\n# Run the container\ndocker run -p 8080:8080 \\\n  -e REDIS_URL=redis://host.docker.internal:6379/ \\\n  grenze-server\n```\n\n## Example Usage\n\n### cURL\n\n```bash\n# Health check\ncurl http://localhost:8080/health\n\n# Proxy a GET request\ncurl -X POST http://localhost:8080/proxy \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"key\": \"demo-user\",\n    \"url\": \"https://api.github.com/users/octocat\",\n    \"method\": \"GET\",\n    \"headers\": {\n      \"User-Agent\": \"my-app\"\n    }\n  }'\n\n# Proxy a POST request with body\ncurl -X POST http://localhost:8080/proxy \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"key\": \"demo-user\",\n    \"url\": \"https://httpbin.org/post\",\n    \"method\": \"POST\",\n    \"headers\": {\n      \"Content-Type\": \"application/json\"\n    },\n    \"body\": {\n      \"message\": \"Hello, World!\"\n    }\n  }'\n```\n\n### Python\n\n```python\nimport requests\n\nresponse = requests.post(\n    \"http://localhost:8080/proxy\",\n    json={\n        \"key\": \"user-42\",\n        \"url\": \"https://api.example.com/data\",\n        \"method\": \"GET\",\n        \"headers\": {\n            \"Authorization\": \"Bearer your-token\"\n        },\n        \"timeout_ms\": 5000\n    }\n)\n\nprint(response.status_code)\nprint(response.json())\n```\n\n### JavaScript/Node.js\n\n```javascript\nconst response = await fetch('http://localhost:8080/proxy', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({\n    key: 'user-42',\n    url: 'https://api.example.com/data',\n    method: 'GET',\n    headers: {\n      Authorization: 'Bearer your-token'\n    },\n    timeout_ms: 5000\n  })\n});\n\nconst data = await response.json();\nconsole.log(data);\n```\n\n## Security Considerations\n\n⚠️ **Important**: This is a basic implementation suitable for internal services or development. For production use, consider:\n\n1. **URL Allowlisting**: Add validation to restrict which URLs can be proxied\n2. **Authentication**: Implement API key or OAuth authentication\n3. **Key Validation**: Validate and sanitize rate limit keys\n4. **Request Size Limits**: Add limits on request body size\n5. **TLS/HTTPS**: Use HTTPS in production\n6. **Network Policies**: Restrict which networks the server can access\n7. **Rate Limit Configuration**: Make capacity and leak rate configurable per key\n8. **Monitoring**: Add metrics and alerting for production deployments\n\n## Technology Stack\n\n- **[Rust](https://www.rust-lang.org/)**: Systems programming language\n- **[Axum](https://github.com/tokio-rs/axum)**: Web framework\n- **[Tokio](https://tokio.rs/)**: Async runtime\n- **[Redis](https://redis.io/)**: In-memory data store for rate limit state\n- **[Reqwest](https://github.com/seanmonstar/reqwest)**: HTTP client\n- **[Docker](https://www.docker.com/)**: Containerization\n\n## License\n\nMIT - See [LICENSE](LICENSE) file for details.\n\n## Contributing\n\nContributions are welcome! Please feel free to submit issues or pull requests.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcchexcode%2Fgrenze","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcchexcode%2Fgrenze","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcchexcode%2Fgrenze/lists"}