Skip to main content

Highly available, transparent & dynamic HTTP proxy for Discord API rate limiting

Project description

nirn.py

A highly available, transparent & dynamic HTTP proxy that handles Discord rate limits and exports Prometheus metrics.

This is a Python implementation using asyncio and aiohttp for high-performance concurrent request handling.

Features

  • ✅ Highly available, horizontally scalable
  • ✅ Transparent ratelimit handling, per-route and global
  • ✅ Works with any API version
  • ✅ Small resource footprint
  • ✅ Works with webhooks
  • ✅ Works with Bearer tokens
  • ✅ Supports an unlimited number of clients (Bots and Bearer)
  • ✅ Prometheus metrics exported out of the box
  • ✅ No hardcoded routes, therefore no need of updates for new routes introduced by Discord

Installation

From PyPI

pip install nirn.py

From Source

git clone https://github.com/lorenzo132/nirn.py
cd nirn.py
pip install -e .

Docker

docker build -t nirn.py .
docker run -p 8080:8080 -p 9000:9000 nirn.py

Usage

Command Line

nirn.py provides a simple CLI to manage the proxy as a background service:

# Start nirn as a background process (loads .env from current directory)
nirn start

# Stop nirn
nirn stop

# View logs (shows last 50 lines and follows new output)
nirn logs

# View last 100 lines and follow
nirn logs --lines=100

# View all logs and follow
nirn logs --lines=0

# View logs without following (exits after showing)
nirn logs --no-follow

# Check status
nirn status
Command Description
nirn start Start nirn as a background process
nirn stop Stop the running nirn process
nirn logs View logs and follow new output (Ctrl+C to stop)
nirn logs -n <N> Show last N lines before following
nirn logs --no-follow Show logs without following
nirn status Show nirn status and process info

The nirn start command:

  • Loads environment variables from .env in the current directory
  • Runs as a background process (survives terminal close)
  • Logs are saved to %LOCALAPPDATA%\nirn\nirn.log (Windows) or ~/.local/share/nirn/nirn.log (Linux/Mac)

Python API

from nirn_proxy import QueueManager, Config

config = Config(
    port=8080,
    log_level="info",
    enable_metrics=True
)

queue_manager = QueueManager(config)
app = queue_manager.create_app()

from aiohttp import web
web.run_app(app, host=config.bind_ip, port=config.port)

Configuration

The proxy sits between the client and Discord. Instead of pointing to discord.com, you point to whatever IP and port the proxy is running on, so discord.com/api/v9/gateway becomes 10.0.0.1:8080/api/v9/gateway.

Variable Default Description
LOG_LEVEL info Log level (debug, info, warning, error)
PORT 8080 Proxy port
METRICS_PORT 9000 Metrics server port
ENABLE_METRICS true Enable Prometheus metrics
BUFFER_SIZE 50 Request buffer size per bucket
BIND_IP 0.0.0.0 IP to bind to
REQUEST_TIMEOUT 5000 Request timeout in milliseconds
MAX_BEARER_COUNT 1024 Maximum bearer token queues
BOT_RATELIMIT_OVERRIDES "" Bot rate limit overrides (bot_id:limit,bot_id:limit)
DISABLE_GLOBAL_RATELIMIT_DETECTION false Disable global rate limit detection
CLUSTER_PORT 7654 Cluster communication port
CLUSTER_MEMBERS "" Comma-separated list of cluster members
CLUSTER_DNS "" DNS address for cluster discovery

Information on each config var can be found in CONFIG.md

.env files are loaded if present

Behaviour

The proxy listens on all routes and relays them to Discord, while keeping track of ratelimit buckets and making requests wait if there are no tokens to spare. The proxy fires requests sequentially for each bucket and ordering is preserved. The proxy does not modify the requests in any way so any library compatible with Discord's API can be pointed at the proxy and it will not break the library.

When using the proxy, it is safe to remove the ratelimiting logic from clients and fire requests instantly, however, the proxy does not handle retries. If for some reason the proxy encounters a 429, it will return that to the client. It is safe to immediately retry requests that return 429.

The proxy also guards against known scenarios that might cause a Cloudflare ban, like too many webhook 404s or too many 401s.

Proxy Specific Responses

The proxy may return a 408 Request Timeout if Discord takes more than REQUEST_TIMEOUT milliseconds to respond.

High Availability

The proxy can be run in a cluster by setting either CLUSTER_MEMBERS or CLUSTER_DNS env vars. When in cluster mode, all nodes are a suitable gateway for all requests and the proxy will route requests consistently using the bucket hash.

Bearer Tokens

Bearer tokens are first class citizens. They are treated differently than bot tokens - while bot queues are long-lived and never get evicted, Bearer queues are put into an LRU and are spread out by their token hash. You can control how many bearer queues to keep at any time with the MAX_BEARER_COUNT env var.

Metrics / Health

Key Labels Description
nirn_proxy_error none Counter for errors
nirn_proxy_requests method, status, route, clientId Histogram for request metrics
nirn_proxy_open_connections route, method Gauge for open connections

The proxy has an internal endpoint located at /nirn/healthz for liveliness and readiness probes.

Development

# Install development dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Format code
black nirn_proxy/

# Lint code
flake8 nirn_proxy/

License

MIT License - see LICENSE file for details.

Acknowledgements

  • nirn - used as inspiration and reference

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nirn_py-1.0.3.tar.gz (41.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nirn_py-1.0.3-py3-none-any.whl (47.5 kB view details)

Uploaded Python 3

File details

Details for the file nirn_py-1.0.3.tar.gz.

File metadata

  • Download URL: nirn_py-1.0.3.tar.gz
  • Upload date:
  • Size: 41.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for nirn_py-1.0.3.tar.gz
Algorithm Hash digest
SHA256 3fb6231d78dd82da3c04be94e8ee0d4a68669320615376ba4c2312fdd037c73b
MD5 427c9ebc37513e15b6eecbb1c6e454ac
BLAKE2b-256 07a5b78b2a32c1997bc6db51a96c4bcf0d2391e0254e71fb9b637223ed970055

See more details on using hashes here.

File details

Details for the file nirn_py-1.0.3-py3-none-any.whl.

File metadata

  • Download URL: nirn_py-1.0.3-py3-none-any.whl
  • Upload date:
  • Size: 47.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for nirn_py-1.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 5e732e00a8f89d42abd893e909213644ad356792f0587403e78d6a314c43e31a
MD5 ad74aada801e6818580290e0dd8d6056
BLAKE2b-256 329a86329009bcdb5e5dd613f5acee76799d94f362cf9ebcf3cf9445d2696a47

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page