Do not connect to the shared Redis: CACHE_URL stays empty, own Redis is an open decision

The owner decided (2026-10-07) that we do not connect to anything other teams run on the box, Redis
included. CACHE_URL was emptied on the server and our keys were removed from the shared Redis.

- settings: drop KEY_PREFIX / CACHE_KEY_PREFIX (it only existed because the Redis was shared); comments
  say CACHE_URL is intentionally empty and ResilientRedisCache is ready for our own Redis.
- config/cache.py docstring and tests updated accordingly (11 tests, still run against a local redis-server).
- CLAUDE.md: new server rule 8 (touch only our own files and service), status block, historical line
  that said "db1 is free" now says not to connect. README, operations, handbook, security doc (item 9
  reopened as an open decision) say the same.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 14:41:39 +03:30
parent c129f64180
commit d584d62904
8 changed files with 59 additions and 42 deletions

View File

@@ -88,7 +88,7 @@ python manage.py test accounts # single app
python manage.py test accounts.tests.SomeTestCase.test_something # single test
```
Local dev requires `DJANGO_DEBUG=1` (enables Swagger auth via `DEV_MASTER_OTP`/`devOtp` in responses). `DEBUG` defaults to `False`. Production also requires `DJANGO_SECRET_KEY`, `DJANGO_ALLOWED_HOSTS` (comma-separated), and ideally `CACHE_URL` (Redis). Without it the app still boots on per-process `LocMemCache` and `/api/health` warns — throttles and admin-driven cache invalidation are then per worker. On the server it points at the shared Redis, database 1 (since 2026-10-07).
Local dev requires `DJANGO_DEBUG=1` (enables Swagger auth via `DEV_MASTER_OTP`/`devOtp` in responses). `DEBUG` defaults to `False`. Production also requires `DJANGO_SECRET_KEY`, `DJANGO_ALLOWED_HOSTS` (comma-separated), and ideally `CACHE_URL` (Redis). Without it the app still boots on per-process `LocMemCache` and `/api/health` warns — throttles and admin-driven cache invalidation are then per worker. On the server it is empty on purpose (owner, 2026-10-07): the Redis on that machine belongs to other teams and we do not connect to it. Our own Redis is an open pre-launch decision; until then limits are ≈3× the configured number (3 workers).
No pytest; uses Django's built-in test runner. No linter/formatter configured.
@@ -397,6 +397,11 @@ Rules that follow from this, and are not negotiable:
7. **Disk is the standing risk.** It has been sitting at ~95% full (about 6 GB free on a
113 GB volume). Check `df -h /` before anything that writes a lot — a full disk on
this machine takes the mail server and Gitea down with us, not just our app.
8. **Touch only our own files and our own service.** Do not connect to, write into or "just
use" anything another team runs on this box — their Redis (even an empty database),
databases, queues, nginx or firewall rules. Those are requests to the server admin
(listed in the launch report), not things to do ourselves. Owner's rule, 2026-10-07, after
an agent pointed `CACHE_URL` at the shared Redis.
### Handling the server password (every agent, every time)
@@ -452,16 +457,15 @@ anyone who can read this repo can forge a token for any user.**
با کلیدِ داخلِ ریپو برای کاربرهای ۱۰، ۱۱، ۱۲ پیش از تغییر `200` و بعدش `401` گرفت. توکنِ refresh در
دیتابیس است، نه امضاشده، پس نشست‌ها نمی‌میرند و فرانت بی‌صدا توکنِ تازه می‌گیرد؛ این را با ورودِ واقعی
**نسنجیدم**.
- **`CACHE_URL=redis://127.0.0.1:6379/1`** (Redisِ مشترکِ ماشین، بدونِ رمز، `maxmemory 0`؛ دیتابیسِ ۰ مالِ
دیگران است و دست نخورد). کلیدها پیشوندِ `tadrisino-mathapp` دارند و کش `config.cache.ResilientRedisCache`
است: خطای Redis ۵۰۰ نمی‌شود، خطای ERRORِ لاگ‌شده (هر ۳۰ ثانیه یک بار) و **سقف‌های مبتنی بر کش اعمال
نمی‌شوند** (fail-open: throttleهای DRF، پنجره‌ی ساعتیِ OTP، سقفِ ورودِ ادمین، قفلِ بازسنجیِ پرداخت)،
`/api/health` ۵۰۳ می‌دهد؛ آنچه در دیتابیس شمرده می‌شود (cooldown و سقفِ روزانه‌ی پیامک، حدسِ هر کد) می‌ماند.
Timeoutِ اتصال و خواندن ۱ ثانیه. آزموده شد: ۱۱ تستِ واحد (از جمله redis-serverِ محلی که وسطِ کار می‌میرد و
بدونِ ری‌استارتِ برنامه برمی‌گردد)، تمرینِ قطعی روی خودِ سرور (پروسه‌ی جدا با کش روی پورتِ بسته: همه ۲۰۰،
۰٫۳ ثانیه برای ۷ درخواست، health ۵۰۳)، و رگبارِ ۶۰۰ درخواستی روی سایتِ زنده (۱۲۴ تا ۲۰۰، پیش‌تر ۳۶۰).
**آزموده نشد:** قطعیِ واقعیِ Redisِ مشترک (مالِ ما نیست)، و رفتارِ آن زیرِ فشارِ حافظه. برگشت: خطِ
`CACHE_URL` را در `.env` خالی کنید و ری‌استارت.
- **Redis: وصل نیست، عمداً** (تصمیمِ مالک ۲۰۲۶-۱۰-۰۷). Redisِ روی آن ماشین مالِ تیم‌های دیگر است و به آن، یا به
هیچ حافظه‌ی موقت و سرویسِ دیگرانی، وصل نمی‌شویم. `CACHE_URL` در `.env` خالی و کش `LocMemCache` است، پس
تا Redisِ اختصاصیِ خودمان نیست هر سقفِ مبتنی بر کش ≈ ۳ برابرِ عدد است (۳ worker)؛ سقف‌های دیتابیسی
(پیامک، حدسِ کد) درست‌اند. «Redisِ اختصاصی» در گزارشِ لانچ یک تصمیمِ باز است (کنارِ Postgres).
کد برای آن روز آماده است: `config.cache.ResilientRedisCache` (فقط با `CACHE_URL` فعال می‌شود) قطعیِ Redis را
۵۰۰ نمی‌کند، ERROR لاگ می‌کند، و تا برگشتنش سقف‌های مبتنی بر کش اعمال نمی‌شوند (fail-open) و `/api/health`
۵۰۳ می‌دهد؛ ۱۱ تستِ واحد دارد، از جمله redis-serverِ محلی که وسطِ کار می‌میرد و برمی‌گردد. یک بار ۲۰۲۶-۱۰-۰۷
چند ساعت به آن Redisِ مشترک وصل بود و قطع و کلیدهایمان پاک شد؛ آن‌جا آزموده شد که کش روی سرور کار می‌کند.
**آزموده نشد:** قطعیِ واقعیِ Redis، و رفتارش زیرِ فشارِ حافظه.
- **آزمونِ زنده با کاربرِ آزمایشی** (۳۲ از ۳۲ سبز؛ کاربر و داده‌اش پاک شد، شمارش‌ها با پیش از آزمون برابر):
ورود با OTP، توکنِ تازه، تمدید (توکنِ تمدیدِ قدیمی ۴۰۱)، جزیره‌ی قفل ۴۰۳، بی‌جواب ۴۰۹، ادعای کمتر از
تعدادِ سوال ۴۰۹، پایانِ جزیره و درس بعد از جواب، Idempotency-Key، و خروج. رابطِ کاربریِ فرانت در مرورگر
@@ -920,8 +924,9 @@ Still open, and deliberately not acted on:
can forge one. Fixing it means removing the key from `.env` *and* from the systemd unit
and letting settings generate one. The owner decided this is the server admin's call,
not ours. `manage.py check --deploy` flags it as `security.W009` on every release.
- `CACHE_URL` is unset, so rate limiting is per-worker (`LocMemCache`). Redis is already
running on the box at `127.0.0.1:6379`; `db0` belongs to another app, `db1` is free.
- `CACHE_URL` is unset, so rate limiting is per-worker (`LocMemCache`). (A Redis runs on the box,
but it belongs to other teams: **do not connect to it**, owner's decision 2026-10-07. The
way out is our own Redis, see the status block above.)
- The frontend is still not wired to any API. `src/api/` was merged (PR #1) but is
imported nowhere; `QuestionScreen` in `App.tsx` still renders four hardcoded
multiplication questions, and **PR #3** (`zareian/api-client-2`) is still open upstream.