fix(cinny-web): per-client rate limits; own budget for decorations
Lint / Shell (shellcheck) (pull_request) Successful in 7s
Lint / JS (eslint) (pull_request) Successful in 7s
Lint / No secrets in webhook configs (pull_request) Successful in 3s
Lint / Landing page is rendered (matrix (pull_request) Successful in 3s
Lint / Python (ruff) (pull_request) Successful in 4s
Lint / Python deps (pip-audit) (pull_request) Successful in 49s
Lint / Secret scan (gitleaks) (pull_request) Successful in 5s
Lint / Shell (shellcheck) (push) Successful in 7s
Lint / JS (eslint) (push) Successful in 6s
Lint / No secrets in webhook configs (push) Successful in 4s
Lint / Landing page is rendered (matrix (push) Successful in 4s
Lint / Python (ruff) (push) Successful in 4s
Lint / Python deps (pip-audit) (push) Successful in 30s
Lint / Secret scan (gitleaks) (push) Successful in 4s

Every request reaches LXC 106 from Nginx Proxy Manager (10.10.10.27), and
nginx had no realip config, so chat_limit (20 r/s, burst 60, keyed by
$binary_remote_addr) was ONE bucket for the whole community: a scanner
burst 503ed real users. Trust X-Real-IP from the proxy only, in both
server blocks.

Decorations (#22) now come from this host, and the profile picker can
fetch all 629 thumbnails: ~500 of them 503ed in testing. /decorations/
gets its own zone (100 r/s, burst 700) instead of chat_limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
This commit is contained in:
Lotus CI
2026-10-19 12:41:46 -04:00
co-authored by Claude Opus 5.5
parent adeb872abb
commit a52d6a89bc
+18
View File
@@ -7,6 +7,17 @@ map $uri $lotus_shell_cache_control {
default "";
}
# All traffic arrives through Nginx Proxy Manager (10.10.10.27), so without
# realip every visitor shared ONE chat_limit bucket (20 r/s, keyed by
# $binary_remote_addr = the proxy): a scanner burst got real users 503s.
# Each server block below trusts X-Real-IP from the proxy only (NPM sets it
# to the client address, overwriting anything the client sent).
#
# [matrix #22] Decorations get their own, larger budget: opening the profile
# decoration picker can fetch hundreds of thumbnails, which would otherwise use
# up chat_limit and 503 the app's own files.
limit_req_zone $binary_remote_addr zone=decorations_limit:10m rate=100r/s;
server {
listen 80;
listen [::]:80;
@@ -23,6 +34,8 @@ server {
server_tokens off;
client_max_body_size 50m;
set_real_ip_from 10.10.10.27;
real_ip_header X-Real-IP;
limit_req zone=chat_limit burst=60 nodelay;
limit_conn chat_conn 25;
index index.html;
@@ -113,6 +126,9 @@ server {
# can be replaced in place under the same name, so a week, not immutable.
location ^~ /decorations/ {
alias /var/www/decorations/;
# Replaces the server-level chat_limit here (nginx doesn't inherit
# limit_req into a location that sets its own).
limit_req zone=decorations_limit burst=700 nodelay;
include snippets/cinny-security-headers.conf;
add_header Cache-Control "public, max-age=604800" always;
location ~ /MANIFEST\.sha256$ { return 404; }
@@ -151,6 +167,8 @@ server {
root /var/www/html;
server_tokens off;
set_real_ip_from 10.10.10.27;
real_ip_header X-Real-IP;
limit_req zone=chat_limit burst=60 nodelay;
limit_conn chat_conn 25;