В декабре 2025 года Стас приехал к родителям и обнаружил, что РКН тестирует белые списки: его регион попал в первую волну блокировок. ChatGPT, Claude, YouTube, Telegram — всё недоступно. OpenVPN, которым он пользовался через университет, тоже заблокировали. Из этой точки вырос полноценный SaaS-сервис.
Решением стал протокол Hysteria2 — он работает поверх QUIC (UDP) и маскируется под обычный HTTPS-трафик, что делает его трудноразличимым для систем глубокой инспекции пакетов (DPI). Развернув VPS с контейнером Hysteria2, автор обнаружил, что сервер предоставляет REST API: через него можно в реальном времени получать список подключённых пользователей, управлять трафиком, добавлять и удалять клиентов. Это стало отправной точкой — сначала curl-запросы, потом Python-скрипт с библиотекой requests, потом желание сделать из этого полноценный продукт.
| Нода | Страна | Назначение |
|---|---|---|
| docker-compose.ge.yaml | Грузия | Hysteria2 VPN-нода |
| docker-compose.nl.yaml | Нидерланды | Hysteria2 VPN-нода |
| docker-compose.pl.yaml | Польша | Hysteria2 VPN-нода |
| docker-compose.se.yaml | Швеция | Hysteria2 VPN-нода |
Архитектура проекта htrBox разделена на три изолированных Docker-контейнера: бэкенд на FastAPI с PostgreSQL, фронтенд на React + Vite и сам Hysteria2-сервер. Зоны ответственности не пересекаются: бэкенд общается с Hysteria2 по REST, фронт — с бэкендом по REST, VPN-сервер ничего не знает об остальных частях системы. Весь стек поднимается одной командой через docker-compose. В продакшне работают четыре Hysteria2-ноды — в Грузии, Нидерландах, Польше и Швеции, бэкенд живёт в Yandex Cloud отдельно.
Проект разбит на три изолированных Docker-контейнера: бэкенд (FastAPI + PostgreSQL), фронтенд (React + Vite) и сам VPN-сервер.
Авторизация построена на JWT с двумя токенами: короткоживущий access-токен и refresh-токен, который хранится в httpOnly cookie. Это осознанное решение безопасности: JavaScript на странице не имеет доступа к такому cookie, поэтому XSS-атака не сможет украсть токен через document.cookie. Хранить refresh-токен в localStorage — распространённая ошибка, которую автор явно исключил. Параллельно написана WebSocket-инфраструктура, но она закомментирована: на текущем масштабе поллинг через TanStack Query справляется, усложнять без необходимости не стали.
Отдельного внимания заслуживает подход к работе с ИИ-ассистентом. Проблема стандартная: ИИ не удерживает контекст между сессиями, и каждый раз объяснять структуру проекта — потеря времени и токенов. Решение — bash-скрипт pack.sh с тремя режимами упаковки: весь проект, только бэкенд или только фронтенд. Claude и Manus умеют работать с tar-архивами напрямую. Если правится роутер на бэкенде, 50+ компонентов фронтенда в контекст не попадают — экономия токенов ощутимая.
Рабочий процесс с ИИ тоже формализован. Вместо «давай реализуем фичу» — сначала запрос на план с TODO-маркерами, подробным описанием каждого шага. После согласования плана — реализация пошагово. ИИ отдаёт только изменённые файлы и обновлённый TODO.md после каждого шага. Если ИИ галлюцинирует или заканчиваются токены на аккаунте — архив с актуальным TODO.md переносится на новый аккаунт, и работа продолжается с той же точки.
Самым нетривиальным решением оказалась дизайн-система для ИИ. Без неё ИИ расставляет Tailwind-классы случайно: на одной странице text-sm, на другой text-xs, отступы и цвета вразнобой. Файл PROMT.md — 250+ строк инструкций, описывающих дизайн-систему и алгоритм работы с ней. Стили разбиты по файлам с жёстким правилом маппинга: компонент из components/ui/ получает стили только из uiStls.ts, компонент из pages/ — только из pagesStls.ts. ИИ не может положить стили куда попало, и визуальная консистентность сохраняется на всём проекте.
Кейс показывает не столько возможности конкретных инструментов, сколько воспроизводимый процесс: формализованный контекст, пошаговое планирование, явные правила для ИИ-ассистента. Именно это отличает разовый эксперимент от системной разработки.


