---
name: avtero_access_limits
description: avtero — лимиты запросов: TG-юзеры безлимит по умолчанию, админ-оверрайд N/сутки из бота; как устроено и как проверять
metadata:
  node_type: memory
  type: project
  originSessionId: 4eea915e-3b47-4cd5-8a0e-bfa29b78a87c
---

avtero v0.9 квоты (2026-06-29). По умолчанию **TG-авторизованные безлимитны** (бот И сайт через TG-вход); веб-неTG = `BETA_DAILY_LIMIT` (env `AVTERO_DAILY_LIMIT`, деф.3); **админ может выдать конкретному юзеру лимит N/сутки** или снять (безлимит).

Архитектура (collision-safe — `accounts.py` НЕ трогал, на репо была параллельная auth-сессия):
- Оверрайд в **app-DB** `account_limit_override(account_id PK, daily_limit, set_by, set_at)` (ленивое CREATE, аддитивно). Bot-модель/БД не менялись.
- `app/main.py _effective_limit(acc_id,bot)`: оверрайд→N; иначе `bot` или telegram-identity у аккаунта→None(безлимит); иначе `BETA_DAILY_LIMIT`. Единый счётчик `account_daily_usage` по account_id (бот+сайт вместе).
- Бот шлёт заголовок **`X-Bot-User: <tg_id>`** (`avtero_client.submit_ask(tg_id=)`), app резолвит → account_id (`accounts.get_or_create_for_identity("telegram",tg_id)`). Гейт+расход в `api_ask` и `api_ask_async`. `/api/quota` тоже резолвит.
- Внутренние **`POST/GET /api/admin/limit`** под `X-Bot-Auth` (body `{tg_id, daily_limit|null, by}`).
- Бот-админ (карточка юзера, `keyboards.admin_user_card_keyboard`): кнопки **«🔒 Забрать безлимит (2/сут)»** (`access:limit2:` → `set_user_limit(tg,2)`, конст. `REVOKE_UNLIMITED_DAILY=2` в admin.py — юзер: «забрал безлимит → не больше 2/день»), **«♾ Вернуть безлимит»** (`access:unlimit:` → clear), **«🔢 Своё число»** (`access:setlimit:` → FSM `AdminSearch.awaiting_limit`). Команды `/limit <tg_id> <N>` (любое N), `/unlimit <tg_id>`. Гард — существующий `is_admin_user_id` (админ avtero = `1005954706`, НЕ тест-аккаунт 8688355181).
- 429 «🚫 …лимит исчерпан» рендерит `handlers/ask.py` (submit_ask пробрасывает 429-тело).

**Проверять:** app — curl с бот-токеном (`hmac(data/auth_secret, b"bot-internal", sha256)`) на `/api/admin/limit` + `/api/quota` с `X-Bot-User`. Бот — `tg-web-e2e`: VIN→работы, при лимите 0 ждать «🚫 …исчерпан». Оба канала (бот+сайт) — общий гейт. Проверено живьём 2026-06-29 (рестарт API+бот).

**Веб-админка (тоже сделана):** `/api/admin/limit` теперь пускает И веб-админа (`_require_admin` — tg_id сессии ∈ admin_ids) по `account_id`, И бота (X-Bot-Auth) по `tg_id` (`_limit_caller_ok`+`_resolve_target_account`). `/api/admin/users` отдаёт `daily_limit/tg/unlimited/used_today`. Фронт `kodoceh-web/apps/avtero/app/admin/page.tsx` (раздел «Пользователи»): бейдж лимита + кнопки «🔒 Забрать безлимит (2/сут)» / «♾ Вернуть безлимит» / «Задать N/сут» (CustomN) → `POST /api/admin/limit {account_id}`. Сборка фронта = **из папки app + `rm -rf .next`** (gotcha, иначе Tailwind), затем рестарт `npm start` (prod next-server :20413, запущен вручную nohup, ppid 1 — НЕ launchd; npm в `/opt/homebrew/bin`, в ssh PATH его НЕТ).
**Проверено живьём (2026-06-29):** веб — минт admin-cookie (`webauth.make_session({tg_id:1005954706})`, кука `pk_session`) → users с лимитами, set 2 по account_id, 403 без cookie ✅; бот — token-path set/clear ✅, tg-web-e2e блок при 0 ✅.
**Статус git: ЗАКОММИЧЕНО** (2026-06-30) — avtero `264eb07` (app/main.py + bot/{handlers/ask,handlers/admin,services/avtero_client,utils/keyboards}.py), kodoceh-web `b1b1f3b` (apps/avtero/app/admin/page.tsx). Выборочно — чужие файлы auth-сессии (plate_to_vin.py/test_plate_to_vin.py/voice.py) НЕ трогал. См. [[avtero_two_channels_verify]], [[kodoceh_web_build_gotcha]], [[avtero_overview]].
