SessionProxy: data plane и control plane
Архитектурная схема (archify) →
Data plane: сам прокси
Реализован на стандартном net/http/httputil.ReverseProxy, не на самописном HTTP-клиенте — меньше шансов забыть какую-то деталь протокола (keep-alive, чанкованную передачу), которую стандартная библиотека уже решила.
Путь запроса гостя: гость идёт на /r/{token}/..., прокси резолвит shared_link по токену из URL, находит связанную original_session, расшифровывает куки/токены владельца через AES-256-GCM непосредственно перед самим запросом к целевому сайту.
Расшифрованные значения кук существуют только в памяти процесса на время одного запроса — они не попадают ни в лог, ни в промежуточное хранилище. Это узкое окно жизни секрета — стандартный принцип «расшифровывай как можно позже, как можно ближе к месту использования».
На обратном пути, когда ответ от целевого сайта возвращается прокси, происходит ключевой шаг: прокси вырезает Set-Cookie и другие идентифицирующие заголовки из ответа ДО того, как он дойдёт до гостя. Это техническая гарантия «гость не видит настоящих кук», реализованная кодом, а не организационным правилом «мы обещаем не показывать».
Честная граница гарантии
Вырезание Set-Cookie из заголовков ответа — надёжный, детерминированный механизм: любой заголовок с этим именем физически стирается перед отправкой гостю, без исключений.
Но если токен или идентифицирующие данные владельца утекли бы прямо в ТЕЛЕ ответа (например, в HTML-странице личного кабинета или в JSON от API целевого сайта), прокси это НЕ перехватывает — он не парсит и не фильтрует произвольный контент ответа на предмет чувствительных данных.
Это осознанно не решается на уровне прокси и не заявляется как гарантия. На собеседовании называть эту границу явно, до того как её найдёт интервьюер вопросом, — сильный сигнал: это показывает, что автор понимает реальные пределы своей же системы, а не продаёт её как абсолютную защиту.
Control plane: три входа в одну бизнес-логику
REST на chi — импорт сессии, создание и terminate ссылок, управление политиками и blacklist, просмотр логов и статистики. Контракт зафиксирован в openapi.yaml, то есть API документирован формально, а не только читаем из кода хендлеров.
gRPC — два сервиса. ImportService идёт тем же путём шифрования, что и REST-версия импорта, но авторизуется через api_keys, а не через JWT — этот вход рассчитан на CLI-инструменты и браузерные расширения, где хранить JWT неудобнее, чем статический ключ. AdminService.StreamLinkActivity — server-streaming RPC, отдающий живые события (нарушения blacklist, автоматический terminate) в реальном времени, без поллинга со стороны клиента.
Веб-дашборд на templ (типобезопасные Go-шаблоны) + htmx + SSE: логин, список ссылок с terminate в один клик, форма импорта сессии, живая лента security-событий. Обошлись без отдельного JS-фреймворка (React/Vue) — интерактивность даёт связка htmx (запросы по атрибутам HTML) + SSE (сервер сам толкает обновления).
Три равноправных входа поверх одной и той же бизнес-логики — практическая демонстрация того, что транспортный слой (REST/gRPC/веб) реально отделён от бизнес-логики, а не размазан по хендлерам каждого протокола отдельно. Изменение правила («самый строгий лимит выигрывает», например) правится в одном месте и мгновенно действует во всех трёх входах.