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 (last 50 lines by default)
nirn logs

# View last 100 lines of logs
nirn logs --lines=100

# View all logs
nirn logs --lines=0

# Check status
nirn status

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.2.tar.gz (41.1 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.2-py3-none-any.whl (47.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: nirn_py-1.0.2.tar.gz
  • Upload date:
  • Size: 41.1 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.2.tar.gz
Algorithm Hash digest
SHA256 3a2f992061e0a4731c5d031c04e84f49c3169e2f6b4c57aa280aa71c82e4e49f
MD5 a82499b6769366873fc22cdfc3b2b19a
BLAKE2b-256 583f8ce1a30fffb5402fa27644ab1df5595247aeab5846071ffdb1f1d4dd841a

See more details on using hashes here.

File details

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

File metadata

  • Download URL: nirn_py-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 47.0 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.2-py3-none-any.whl
Algorithm Hash digest
SHA256 bc0872528faacda35cb94efc8c1bb7458b264d2d803862b8b216bf836bcf70b5
MD5 2ece96994c93ede60e0516d2e6be0313
BLAKE2b-256 45ed819f25f295dcee58ef5c249eec84672a0b0d9e629f042f6b62cafd41b006

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