Kaskad Platform
Платформа управления каскадным VPN.
Смысл проекта: клиент подключается к российскому входному серверу через WireGuard/AirGuard. Этот сервер не выпускает трафик напрямую, а пересылает его на один из зарубежных выходных серверов. Выходной сервер уже отправляет трафик в интернет. Если один зарубежный сервер недоступен, платформа может переключить маршрут на резервный сервер.
Что уже сделано
- Создана FastAPI веб-панель.
- Добавлены вкладки: обзор, клиенты, серверы, конфиги, настройки WireGuard.
- Добавлен выпуск WireGuard ключей и клиентских
.conf. - Добавлен выбор типа клиента:
Обычный WireGuard,AmneziaWG 2.0,AmneziaWG Legacy. - Добавлены дефолтные параметры AmneziaWG 2.0: S3/S4, H1-H4 ranges, I1 DNS-signature.
- Добавлено скачивание клиентских конфигов.
- Добавлена видимость активности клиентов: online/offline, последний handshake, RX/TX.
- Добавлен серверный конфиг RU entry.
- Добавлены шаблоны конфигов для выходных серверов.
- Добавлена установка выходного сервера по SSH.
- Добавлена привязка клиента к конкретному выходу или авто-выбору.
- Добавлена смена выходного сервера клиента после создания:
Автоили конкретный exit. - Смена выхода клиента применяет runtime policy rule на RU сервере: IP клиента -> таблица выбранного exit.
- Добавлены действия клиента: включить, выключить, удалить.
- Добавлены действия exit: включить, выключить, удалить.
- Добавлена проверка доступности выходных серверов через ping.
- Добавлены метрики серверов: ping, CPU, RAM, входящий/исходящий канал exit, число клиентов, общий load score.
- Добавлен режим выходов: резерв/failover или распределение клиентов по нагрузке.
- Добавлен RU direct-routing: выбранные RU GeoIP CIDR/IP и GeoSite домены остаются на RU entry, остальное идет в каскад.
- Добавлен WARP/WARP+ на выходных серверах: трафик после US/foreign exit может уходить через Cloudflare WARP.
- Добавлена автопроверка выходных серверов раз в 60 секунд.
- Добавлен авто-failover: если основной выход недоступен, выбирается доступный резерв.
- Добавлен вход в веб-панель по логину/паролю.
- Добавлена смена логина/пароля из панели.
- Добавлено хранение состояния в JSON с правами
0600. - Добавлена модель каскада: клиент -> RU вход -> US/foreign выход.
- Добавлены несколько RU входов: добавление, установка WireGuard/AmneziaWG по SSH, выбор входа при создании клиента.
- Добавлен SSH key auth для установки RU/exit серверов: можно указать private key вместо пароля.
- Добавлено сохранение SSH доступа серверов encrypted: нужно для CPU/RAM и WARP одной кнопкой.
- Добавлен раздел
$: даты отключения клиентов, expired-клиенты не участвуют в маршрутизации. - Добавлен выборочный
$: клиент попадает в платный раздел только после нажатия$. - Добавлены поля платного раздела: комментарий и внешний адрес.
- Добавлен Telegram bot alerts: offline/online клиенты/серверы и даты отключения.
- Добавлена кнопка ручного failover.
- Настроен systemd-сервис на сервере.
Где открывать
Веб-панель:
http://SERVER_IP:8000
Дефолтный вход:
admin / admin
Важно: это только первичный аварийный вход. Панель показывает красное предупреждение, пока admin/admin активен. Сразу смените логин и пароль в Настройки WG -> Доступ к панели. Новый пароль: минимум 8 символов.
Проверка API:
http://SERVER_IP:8000/api/health
Статус платформы:
http://SERVER_IP:8000/api/status
Как это должно работать в финальной версии
- В панели создается клиент.
- Платформа создает WireGuard/AirGuard учетную запись на российском входном сервере.
- Клиент получает конфиг и подключается к российскому серверу.
- Российский сервер выбирает зарубежный exit:
- вручную
- по пингу
- по загрузке
- по доступности
- через failover
- Если
RU directвключен, трафик на заданные RU CIDR/домены выходит напрямую через RU entry. - Остальной трафик клиента идет по схеме:
Клиент -> российский входной сервер -> зарубежный выходной сервер -> интернет
Где настраивать
- Клиенты: вкладка
Клиенты. Там выбирается тип, RU вход, выход и дата отключения. - RU входы: вкладка
Серверы-> блокRU вход. ВыбериТолько добавить в платформуилиУстановить по SSH, затем режимОбычный WireGuard входилиAmneziaWG вход. - Выход клиента: вкладка
Клиенты, колонкаВыход. Можно выбратьАвтоили конкретный exit. - Даты отключения: вкладка
$. - Комментарий и внешний адрес клиента: вкладка
$. - Telegram уведомления: вкладка
Настройки WG->Telegram уведомления. - Активность клиентов: вкладка
Клиенты, колонкиАктивен,Handshake,Трафик. - US/foreign серверы: вкладка
Серверы. Там жеВыключить,Включить,Удалить. - Авто-установка US/foreign серверов: вкладка
Серверы->Добавить и установить по SSH. - WARP/WARP+ для скрытия IP exit: вкладка
Серверы->WARP на выходе; обычный WARP можно включить кнопкойWARPв строке exit, если SSH сохранен. - Режим exit-серверов: вкладка
Обзор->Режим выходов. - RU direct-routing: вкладка
Обзор-> блокRU direct. - Свой домен и нейтральный сайт на 80: вкладка
Настройки WG->Домен и сайт маскировки. - Бэкап/restore для переезда: отдельная вкладка
Бэкап. - WireGuard сеть, порт, DNS, MTU: вкладка
Настройки WG. - Логин/пароль панели: вкладка
Настройки WG->Доступ к панели. - Конфиги клиента, RU entry и exit-серверов: вкладка
Конфиги.
Бэкап и переезд
Блок находится в отдельной вкладке Бэкап.
Что делает:
Скачать backupотдает полныйstate.json;- backup содержит приватные ключи клиентов/RU, SSH-секреты, Telegram token и настройки;
Восстановить из backupзаменяет состояние панели целиком;- перед restore нужен текущий пароль панели;
- после restore запускается нормализация state, чтобы новые поля появились автоматически.
Переезд на новый RU сервер:
- На старой панели скачать backup.
- Поднять чистую платформу на новом сервере.
- Открыть вкладку
Бэкап. - Выбрать backup JSON, ввести текущий пароль панели, нажать restore.
- Проверить
wg-quick@kaskad-entry, AWG/Mieru и маршруты. - Если у RU входа задан
Домен для клиентов, направить DNS A-запись на новый IP. - Нажать
Сверить маршрутыи проверить handshake клиентов.
Важно: backup — секретный файл. Не хранить в GitHub, не отправлять в чат, лучше держать encrypted.
Если клиентские конфиги выпущены с доменом RU входа (endpoint_host), при переезде достаточно восстановить backup на новом сервере и перевести DNS A-запись домена на новый IP. Клиенты переподключатся после обновления DNS/повторного handshake. Если в старых конфигах был IP, конфиги нужно скачать заново.
Свой домен и сайт на 80
Блок находится в Настройки WG -> Домен и сайт маскировки.
Рекомендуемое разделение DNS:
ru1.example.ru/ru2.example.ru— толькоДомен для клиентову RU входа, попадает вEndpointклиентских конфигов;example.ruиwww.example.ru— нейтральный сайт маскировки на 80/443;Host/IPRU сервера остаётся IP для SSH, мониторинга и каскада.
Пример:
@ A SERVER_IP
www A SERVER_IP
ru1 A SERVER_IP
Что делает:
- ставит нейтральный HTML-сайт на порт 80 выбранного сервера;
- работает отдельно для каждого сервера:
local, RU вход или US/foreign exit; - не ставит сайт автоматически после деплоя;
- перед установкой проверяет порт 80;
- если порт 80 занят чужим процессом, установка блокируется;
- если порт 80 уже слушает nginx с чужим сайтом, установка без домена блокируется;
- если домен указан, платформа добавляет отдельный nginx
server_name.
Как поставить:
- Создать DNS A запись
@ -> SERVER_IP. - Опционально создать
www -> SERVER_IP. - Дождаться обновления DNS.
- В панели выбрать конкретный сервер.
- Ввести домен без
http://, напримерexample.ru. - Нажать
Установить сайт на 80.
Свой HTML:
- Выбрать
.htmlили.htmфайл. - Нажать
Добавить HTML сайт. - Лимит файла: 1 MB.
Удаление:
Удалить сайтубирает symlink и nginx config/etc/nginx/sites-enabled/kaskad-platform-mask;- чужие nginx-конфиги и другие сайты не трогаются;
site_enabled=falseсохраняется в состоянии платформы.
Важно:
- HTML должен быть нейтральным: без секретов, токенов, приватных ссылок и внутренних адресов.
- Default HTML не содержит слов про VPN/proxy/MTProxy/Telegram/название проекта.
- Если nginx уже занят другим default-сайтом, домен должен смотреть на выбранный сервер и совпадать с
server_name. - Если хотите сайт именно по IP без домена, порт 80 должен быть свободен.
Замер скорости выходов и переезды клиентов
Выход проверяется двумя способами: check_exit доказывает только достижимость
(ICMP/TCP/SSH), а раз в THROUGHPUT_INTERVAL (300 с) через каскадный туннель
каждого выхода тянется 10-мегабайтный файл и меряется реальная скорость.
- зеркала перебираются по списку
EXIT_PROBE_URLS, берётся максимум из успешных проб, а не первое попавшееся значение: медленное зеркало занижает оценку канала, а мёртвое (tele2 по HTTPS отдаёт 000) просто пропускается; probe_mbps— медиана последних пяти замеров, одиночный выброс ничего не решает;- в
degradedвыход уходит после двух плохих замеров подряд (нижеEXIT_PROBE_MIN_MBPS = 12), а выходит только после двух хороших подряд и с запасом (EXIT_PROBE_RECOVER_MBPS = 18) — асимметрия не даёт ему мигать; - auto-клиент переезжает на более быстрый выход не чаще раза в
EXIT_MOVE_COOLDOWN(30 минут), но с отказавшего выхода уезжает сразу.
Бесшовной смена выхода не бывает: у клиента меняется внешний IP и открытые TCP-сессии рвутся. Поэтому задача не «переехать незаметно», а не переезжать без причины — этим и заняты медиана, гистерезис и выдержка.
Уборка пиров без клиента
prune_orphan_peers() раз в цикл монитора сверяет пиров на kaskad-entry,
kaskad-awg и kaskad-awg2 с ключами всех клиентов и снимает чужие: пир, которого
нет ни у одного клиента, — это ключ удалённого клиента, и он продолжает пускать
бывшего владельца в туннель. Каждая уборка пишется в журнал панели.
Тесты и боевая машина
Тесты гоняются в том числе на самом sber2, поэтому tests/conftest.py:
- выставляет временный
KASKAD_DATA_DIRдо импортаserver.main— иначеSNAPSHOT_DIRуказывает на боевойdata/backupsи тесты пишут снимки туда; - подменяет
subprocess.runдляwg/awg/ip/nft/iptables/ipset/systemctl/sysctl— без этого прогон добавлял WireGuard-пиров в живой интерфейс (105 → 210 за два прогона); - изолирует
live_interface_public_key, иначе автодетект видит живыеkaskad-awg*и тесты «AWG не установлен» падают именно на боевой машине.
Метрики и режимы exit
В таблице Серверы для каждого выходного сервера показывается:
Ping— задержка до сервера через обычный ping.CPU— загрузка CPU сервера.RAM— загрузка RAM сервера.Канал— текущий RX/TX Mbps по основному сетевому интерфейсу exit.Клиенты— сколько клиентов сейчас закреплено за этим exit.Load— общий score: клиенты + ping + RX/TX Mbps. Чем меньше, тем легче сервер.
Режимы:
Резерв: основной + запасные— используется основной exit. Если он offline, платформа выбирает доступный резерв.Распределение по нагрузке— новый клиент с выборомАвтополучает самый легкий доступный exit поLoad.
Для CPU, RAM и Канал нужен SSH доступ панели к серверу. Если сервер установлен через панель, платформа кладет служебный public key и сохраняет SSH auth encrypted. Если сервер добавлен вручную, открой Серверы -> SSH доступ сервера, выбери сервер и сохрани пароль или private key. В API секрет не показывается.
Авто у клиента хранится как режим. Платформа выбирает фактический exit по текущей политике и показывает его строкой сейчас: .... Если exit выключен или удален, клиент автоматически уходит обратно в Авто.
RU direct-routing
RU direct нужен, когда российский трафик должен оставаться на RU сервере, а зарубежный трафик идти через US/foreign exit.
Как работает:
GeoIP CIDR/IP— IP или сети, например77.88.0.0/16.GeoSite домены— домены, напримерyandex.ru; платформа резолвит их в IPv4 и добавляет/32.- Включено: для каждого WireGuard-клиента платформа ставит правило выше каскада:
from CLIENT_IP to RU_CIDR lookup main. - Выключено: direct-правила удаляются, весь трафик клиента идет в каскадный exit.
GeoSite — DNS-снимок на момент сохранения. Если IP у домена поменялся, нажмите Сохранить RU direct еще раз.
Несколько RU входов
Модель: каждый RU вход держит свои туннели к тем же US/foreign exit.
Пример:
RU-1 -> us1/us2/us3RU-2 -> us1/us2/us3
В панели:
- RU вход добавляется вручную или ставится по SSH.
- Для
AmneziaWG входSSH installer ставит пакетamneziawg, создает/etc/wireguard/kaskad-entry.confс AWG 2.0 параметрами и запускаетawg-quick@kaskad-entry. - При создании клиента выбирается
Вход. - Клиентский конфиг получает endpoint выбранного RU:
Endpoint = RU_HOST:PORT. - В таблице клиентов видна колонка
Вход. - В таблице серверов для RU входов видны status, ping, clients, load.
Важно: локальные runtime route rules применяются только к локальному RU entry, где запущена панель. Для нового удаленного RU входа панель ставит базовый wg-quick@kaskad-entry или awg-quick@kaskad-entry; peers/маршруты на удаленном RU нужно применять через его RU config после диагностики.
$ и даты отключения
Вкладка $ показывает клиентов и дату отключения.
- Клиент попадает во вкладку
$после нажатия кнопки$в строке клиента. - Пустая дата — клиент без срока.
- Будущая дата — клиент активен.
- Прошедшая дата — клиент
expired, effective exit становится пустым, клиент не считается в нагрузке. - Для локального RU runtime rule удаляется автоматически.
КомментарийиВнешний адресхранятся вручную в карточке оплаты.
Telegram уведомления
Блок находится в Настройки WG -> Telegram уведомления.
Поддерживается:
- bot token, не показывается в
/api/status; - chat id вручную или авто через
getUpdatesпосле/startботу; - предупреждение за N дней до даты отключения;
- колокольчик на каждом клиенте.
Бот присылает:
- RU/exit server offline и recovery online;
- client offline и recovery online, если у клиента включен колокольчик;
- скоро дата отключения;
- дата отключения прошла.
Telegram бот: оплаты и админ-меню
Клиент:
/start-> присылает телефон из базы -> бот привязывает TG ID и показывает карточку подписки (аккаунт, телефон, протокол, выход, статус, окончание, осталось N дн.);- кнопки
ОплатилиПроверить срок; Оплатилсоздаёт нумерованную заявкуЗаявка на оплату #Nи отправляет её админу.
Админ (chat_id из настроек Telegram):
/startили/adminпоказывает reply-меню:📊 Отчёт за месяц,💰 Кто оплатил,⚠️ Не оплатили,⏳ Ожидают оплаты,📢 Рассылка,❌ Отмена;- по заявке
#Nкнопки30/60/90/180 дн.и❌ Отклонить; - подтверждение продлевает все конфиги аккаунта и уведомляет клиента; защита от двойного подтверждения (
pending -> processing -> approved); 📢 Рассылка-> следующий текст уходит всем привязанным клиентам.
Заявки и счётчик хранятся в JSON state (payments, payment_seq); цены не считаются (статусы и сроки).
WARP / WARP+
Схема:
Клиент -> RU вход -> US/foreign exit -> Cloudflare WARP -> интернет
Зачем:
- сайты видят Cloudflare WARP IP;
- IP твоего US/foreign VPS скрыт за Cloudflare WARP;
- все клиенты, которые идут через этот exit, получают один WARP-выход.
Как включить:
- Открыть
Серверы. - Если SSH доступ уже сохранен, нажать
WARPв строке exit для обычного WARP. - Для WARP+ или ручных исключений открыть блок
WARP на выходеи выбрать exit. - Выбрать
WARPилиWARP+. - Для
WARP+указать license key. Он не сохраняется. - Указать SSH доступ, если он еще не сохранен.
- Нажать
Применить WARP.
Платформа ставит официальный cloudflare-warp, регистрирует клиент, включает warp-cli mode warp+doh, подключает WARP и показывает public IP. Для WARP+ используется warp-cli registration license <KEY>.
Защита от потери доступа:
- RU входы добавляются в excluded routes;
- текущий SSH client IP добавляется в excluded routes;
- дополнительные исключения можно указать вручную.
Важно: Cloudflare WARP не является способом выбрать конкретную страну. Это Cloudflare egress, страна/город зависят от сети Cloudflare.
Удаление exit:
- удаляет exit из панели;
- убирает его из primary/backup политики;
- переводит клиентов этого exit в
Авто; - если у панели есть SSH-key доступ к exit, останавливает
wg-quick@kaskad-exitи удаляет/etc/wireguard/kaskad-exit.*на exit-сервере.
Активность клиентов
Панель читает локальный вывод wg show all dump и awg show all dump на RU entry/VPS:
- свежий handshake до 180 секунд ->
online; - нет handshake или нет
wg/awgна сервере ->offline; - также показываются RX/TX bytes клиента.
Если RU WireGuard/AWG еще не поднят, клиенты будут отображаться как offline. Это нормально: конфиг создан, но серверная часть не слушает порт.
NaiveProxy
В панели добавлен блок Настройки WG -> NaiveProxy.
Что делает:
- ставит на выбранный
local/RU entry/US exitотдельный Caddy сforwardproxy@naive; - слушает HTTPS порт, по умолчанию
443; - использует реальный домен и Let's Encrypt TLS;
- отдаёт нейтральную HTML-страницу обычному браузеру;
- выдаёт admin-панели
Client URIвидаhttps://user:password@domain.
Как включить:
- Создать DNS
Aзапись домена на IP выбранного сервера. - Проверить, что порт
443свободен. - Открыть
Настройки WG -> NaiveProxy. - Выбрать конкретный сервер.
- Указать домен, email, login/password.
- Нажать
Установить NaiveProxy на 443.
Ограничения:
- без домена установка блокируется: NaiveProxy нужен реальный TLS, иначе маскировка слабая;
- если порт
443занят чужим процессом, установка блокируется; - NaiveProxy является TCP HTTPS proxy и не заменяет WireGuard UDP каскад
RU -> US; - для Telegram нужен клиент/мост с поддержкой Naive, либо используйте уже добавленный SOCKS5 proxy.
Для маскировки именно связи RU -> US поверх UDP WireGuard лучше отдельная транспортная схема: Hysteria2/TUIC/Xray REALITY/ShadowTLS. NaiveProxy можно использовать как дополнительный HTTPS-proxy слой, но не как прямой UDP-туннель WireGuard.
Mieru proxy
В панели добавлен протокол клиента Mieru proxy.
Как работает:
- это отдельный proxy-протокол, не WireGuard peer;
- серверная часть Mieru называется
mita, клиентская часть —mieru; - на RU входе
mitaслушает TCP порт, по умолчанию2999; - каждый клиент получает
username/password, app-import config и CLI config; - для Karing iOS используйте кнопку
Копироватьили QR в панели: они отдают чистый sing-box/Mieru JSON через/api/clients/{id}/share; - старую строку
mierus://...не вставляйте в экран подписки Karing — приложение показывает «Нет доступных серверов», потому что ждёт profile/content JSON; - клиент импортирует JSON командой
mieru apply config client_config.json, потом запускаетmieru start; - после запуска приложения используют локальный SOCKS5
127.0.0.1:1080.
Что копировать:
Копировать/QRу Mieru peer: короткий JSON для Karing/sing-box.Конфиг: полный админский текст. Для Karing копируйте только блокKaring/sing-box JSON; для desktop CLI — блокMieru CLI.
Runtime:
- на RU входе есть кнопка
Mieru: она ставит серверный пакетmitaи проверяет, что выбранный TCP порт не занят чужим процессом; - если
mitaустановлен на RU входе, платформа применяет users/ports черезmita apply configи запускаетmita; - если
mitaне установлен, клиент всё равно создаётся, но runtime status будетmieru-not-installed; - когда Mieru-клиентов нет, платформа держит скрытого idle-пользователя, потому что
mitaпадает сno user foundпри TCP port binding без users; - несколько Mieru пользователей могут жить на одном порту;
max_connectionsхранится в клиенте и отображается в панели.
Лимиты:
- SOCKS5 proxy уже режет активные соединения через встроенный Python runtime;
- Mieru имеет серверные users и traffic/quota команды, но native per-user active connection limit в текущих docs не описан;
- NaiveProxy/Caddy также не режет per-user active connections из коробки;
- для жёсткого лимита Mieru/NaiveProxy нужен отдельный wrapper/firewall accounting слой.
Официальные reference:
https://github.com/enfein/mieru/blob/main/docs/server-install.mdhttps://github.com/enfein/mieru/blob/main/docs/client-install.mdhttps://github.com/enfein/mieru/blob/main/docs/operation.md
VLESS Reality
Основной DPI-устойчивый протокол для телефонов. На RU входе поднимается xray с VLESS-Reality,
маскировка идёт под настоящий сайт SberCloud (obs.ru-moscow-1.hc.sbercloud.ru): входы физически
живут в AS208677, поэтому связка SNI и IP для DPI выглядит обычным облачным трафиком.
- кнопка
Включить Realityна карточке RU входа — ставит xray, генерит Reality-ключи (идемпотентно, повторное нажатие не ломает уже выданные конфиги), занимает:443или:8443, если 443 занят; - клиент с протоколом
VLESS Realityполучает не WireGuard-ключи, а uuid-личность; /api/clients/{id}/subscriptionотдаёт sing-box конфиг сразу с двумя входами (sber_2 + sber_1) иurltest— переключение между входами занимает около 40 секунд;?format=linksотдаётvless://ссылки,/subscription_qr— QR для Karing/sing-box;- лимит устройств считается по access-логу xray (email = uuid): монитор раз в 60 секунд смотрит уникальные source-IP и шлёт алерт при превышении, без автоматического отключения.
Протоколы клиентов
WireGuard, AmneziaWG 2.0, AmneziaWG Legacy, SOCKS5 proxy, Mieru proxy, VLESS Reality.
Протокол AIVPN (анти-DPI мимикрия) был добавлен 22.06.2026 и удалён 26.08.2026 — за два месяца
им пользовались два клиента, последний хендшейк был 22.06.2026. Подробности в CHANGELOG.
Текущий режим
Панель уже выпускает WireGuard, AmneziaWG 2.0 и AmneziaWG Legacy конфиги, ставит WireGuard на выходные серверы по SSH, проверяет выходы, считает метрики и выбирает доступный/легкий выход. На RU сервере она пока не применяет wg0/awg0, iptables и policy routing автоматически. Это сделано специально: перед применением маршрутов нужна диагностика интерфейсов и firewall, иначе можно потерять SSH.
Важно: AmneziaWG 2.0/Legacy клиент требует, чтобы RU вход был поднят через awg / AmneziaWG. Обычный wg не принимает AWG-параметры Jc/Jmin/Jmax/S1-S4/H1-H4/I1-I5.
SSH установка AmneziaWG вход поддерживает Ubuntu/Debian через официальный Amnezia PPA и Fedora/RHEL-like через COPR. Для DKMS нужны kernel headers; если ядро старое или headers недоступны, installer вернет ошибку и покажет stderr.
AmneziaWG 2.0 в панели использует усиленную маскировку по актуальной документации Amnezia: S3/S4, непересекающиеся диапазоны H1-H4, I1 signature packet под DNS.
Важно: панель сейчас доступна по HTTP. Смените дефолтный admin/admin сразу после входа. Для эксплуатации нужен HTTPS или доступ к панели только через VPN/SSH-туннель.
Security baseline:
- session cookie:
HttpOnly,SameSite=Lax,Secureпри HTTPS; - login rate-limit: 5 ошибочных попыток на IP за 10 минут;
- базовые HTTP security headers:
X-Frame-Options,X-Content-Type-Options,Referrer-Policy; - state/secrets файлы:
0600, data directory:0700; /api/statusне отдаёт приватные ключи, SSH секреты и bot token.
Основные компоненты
server/main.py— FastAPI приложение и веб-панель.server/store.py— JSON-хранилище состояния.docs/architecture.md— архитектура каскада.docs/instructions.md— инструкции по запуску, проверке и обслуживанию.docs/audit-2026-06-07.md— последний полный аудит.CHANGELOG.md— история изменений.ROADMAP.md— ближайшие доработки.tests/test_api.py— тесты API.
Правила безопасности
- Не хранить секреты в GitHub.
- Не писать пароли, токены и приватные ключи в чат.
- Хранить доступы только в
.env, password manager или закрытых заметках. - Логи не должны печатать приватные ключи, токены, пароли и полные клиентские конфиги.
- Перед изменениями на проде сначала диагностика.