diff --git a/plan/RATE-LIMITER-PLAN.md b/plan/RATE-LIMITER-PLAN.md index b1445df4d..adfc27cb7 100644 --- a/plan/RATE-LIMITER-PLAN.md +++ b/plan/RATE-LIMITER-PLAN.md @@ -1614,6 +1614,53 @@ changes. --- +## nginx Lua layer — implementation notes + +### Current stack: nginx + libnginx-mod-http-lua + +`blocklist.lua` uses Debian's `libnginx-mod-http-lua` + `lua-resty-redis` packages. These are built against the same `nginx` version as `libnginx-mod-http-geoip2`, so all three modules are ABI-compatible and available for both `amd64` and `arm64`. + +What the Lua layer does: + +| Check | Mechanism | Redis I/O | +|-------|-----------|-----------| +| IP-prefix block | `ngx.shared.ip_prefix_blocked` dict (60 s refresh) | 0 per request | +| Global IP block | `GET rl:ip:{ip}:blocked` | pipelined | +| Per-tenant API block | `GET rl:api:ns:{ns}:ip:{ip}:blocked` | pipelined (1 round trip for both GETs) | + +ASN blocking and UA blocking remain in nginx `if()` blocks (rewrite phase, before Lua access phase) — no Redis involved. + +### Why OpenResty was not used + +OpenResty was evaluated and rejected for this deployment: + +1. **No arm64 Debian packages.** The `openresty.org/package/debian` repo publishes only `amd64`. Local Mac builds (Apple Silicon) and any future ARM node pools would need `--platform linux/amd64` + QEMU emulation. + +2. **No functional difference for this use case.** The only OpenResty features beyond `libnginx-mod-http-lua` that were considered: + - *Bundled resty.* libraries* — all needed ones (`resty.redis`, `resty.core`, `resty.lrucache`) are in Debian packages. + - *OPM* — used only to install `lua-resty-maxminddb` for ASN lookups in Lua. Dropped when ASN blocking moved back to the GeoIP2 C module, which requires no OPM. + - *Slightly newer LuaJIT* — irrelevant at this workload. + +### Future opportunity: migrate to OpenResty + +Migration would be worth considering **only if** one of the following becomes a requirement: + +| Trigger | Why it needs OpenResty | +|---------|----------------------| +| ASN lookup in Lua (remove GeoIP2 C module dependency) | Needs `lua-resty-maxminddb` via OPM; not in Debian repos | +| Per-request rate counting in nginx (move counters out of Django) | `resty.limit.req` / `resty.limit.conn` — not in Debian's lua packages | +| Cosocket-based upstream health checks or dynamic upstreams | OpenResty's `resty.upstream.*` ecosystem | +| JWT validation at nginx layer | `lua-resty-jwt` via OPM | + +Migration steps when the time comes: +1. Add `--platform linux/amd64` to `docker build` (or use a multi-arch CI builder). +2. Replace `nginx libnginx-mod-http-geoip2 libnginx-mod-http-lua lua-resty-redis` with `openresty libmaxminddb0` + `opm get anjia0532/lua-resty-maxminddb`. +3. Update config paths: `/etc/nginx/` → `/usr/local/openresty/nginx/conf/`; binary `/usr/sbin/nginx` → `/usr/local/openresty/nginx/sbin/nginx`. +4. Replace `geoip2` C-module directives + `$bot_asn` map with `mmdb.lookup(ip)` in `blocklist.lua`. +5. Remove `lua_package_path` directive (OpenResty bundles all resty.* paths automatically). + +--- + ## Open Questions | # | Question | Status | Blocks |