jetio-ratelimit
Rate limiting for Jetio: sliding-window
by default, IP- or account-keyed (or both, stacked), usable as middleware
for routes you don't own the handler for (like jetio-auth's /login) and
as a Depends()-composable dependency for routes you do (a CrudRouter
policy, a hand-written route).
- Usage Guide -- the full guide: every mode, every key function, stacking/reusing policies, handling a 429, testing your integration, troubleshooting. Start here for "how do I do X."
- Design & Architecture -- the reasoning: why sliding window over token bucket, why IP-only limiting is weak against real credential stuffing, and the real bugs found in Jetio/jetio-auth along the way. Start here for "why does it work this way."
Install
pip install jetio-ratelimit
For local development (running the test suite, contributing):
pip install -e .[dev] # from this directory
Quickstart
from jetio import Jetio, CrudRouter
from jetio_auth import AuthRouter
from jetio_ratelimit import RateLimiter, InMemoryStore, Limit, by_ip, by_field, by_user
app = Jetio()
auth = AuthRouter(User, company_name="My App")
auth.register_routes(app) # POST /register, POST /login
limiter = RateLimiter(store=InMemoryStore())
# Middleware mode: protects /login, which AuthRouter registers internally --
# there's no Depends() hook to attach to on a route we don't define.
# protect_many() stacks two independent limits in one call; either tripping
# blocks the request. AUTH_POLICY is a plain list, so the same two rules
# can be applied to /register or any other auth-adjacent route with one
# more protect_many() call each -- see "Reusing a policy across routes" below.
AUTH_POLICY = [
Limit(max_attempts=5, window_seconds=60, key_func=by_ip),
Limit(max_attempts=3, window_seconds=60, key_func=by_field("username")),
]
limiter.protect_many(app, path="/login", limits=AUTH_POLICY)
# Dependency mode: composes into a CrudRouter policy, keyed by the
# authenticated user rather than IP.
CrudRouter(
model=Order,
secure=True,
policy={
"POST": limiter.dependency(
max_attempts=10, window_seconds=60,
key_func=by_user, identity_dependency=auth.get_auth_dependency(),
),
},
).register_routes(app)
Run examples/demo_app.py
and hit it with curl to see both modes working against a real
jetio-auth-backed app. For why /login gets two stacked limits instead of
one, how to reuse one policy across many routes, by_header-keyed API
endpoints, using dependency mode outside CrudRouter, and more, see the
Usage Guide.
Status
Published on PyPI as v0.1.0:
sliding window algorithm, in-memory store, both API modes, IP/account/
header/user keying, protect_many() for stacking multiple limits (or
reusing one policy across many routes) in one call. Not yet done: Redis store (for
anything running more than one worker -- InMemoryStore's state is
per-process), progressive lockout on repeat violations, an equivalent
stacking helper for dependency mode, trusted-proxy-aware X-Forwarded-For
support. See Roadmap.
Known limitations
- InMemoryStore is single-process. Behind multiple workers/replicas,
each one has its own counters, so the effective limit becomes
limit * worker_count. Don't treat this as sufficient for a horizontally-scaled deployment yet. - No
X-Forwarded-Forsupport. Behind a reverse proxy,by_ipsees the proxy's IP, not the real client's. This is a deliberate default, not an oversight:X-Forwarded-Foris a client-suppliable header, so trusting it unconditionally lets any caller spoof their rate-limit identity -- evading their own limit, or framing another IP for one. Other rate limiters that do support it require you to explicitly configure how many proxy hops (or which ones) to trust, precisely to avoid this; naive header-trusting is a known footgun, not just a missing feature. See Roadmap.
Release files for jetio-ratelimit 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| jetio_ratelimit-0.1.1.tar.gz | 18.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| jetio_ratelimit-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 33.1 kB
Release files / jetio_ratelimit-0.1.1.tar.gz
| Download URL | jetio_ratelimit-0.1.1.tar.gz |
|---|---|
| Size | 18.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4ea4059ea3071ffde647b710ddecc1326c951361631f0a42b52a1d77eee41543
|
|
BLAKE2b-256 checksum How to use checksums |
820db2d930c386d330333b90ec1f7bf499fdc715c810bcb23e6bd4505bbdb2ec
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|
Release files / jetio_ratelimit-0.1.1-py3-none-any.whl
| Download URL | jetio_ratelimit-0.1.1-py3-none-any.whl |
|---|---|
| Size | 14.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1adce3b21388fcb94723f07dbbf73eb2abf8cccebb9c3e0df153d6f3452942c6
|
|
BLAKE2b-256 checksum How to use checksums |
596e9e0c433388d6e6e6133c98f84e48ce1c8156cd19bd63e0fe3019ca55ddd2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|