Безопасность

Сквозное шифрование, с ключами на ваших собственных устройствах

Happier держит одну сессию программирования синхронной между компьютером, на котором она работает, и каждым устройством, с которого вы за ней наблюдаете, а посередине стоит сервер релея. Эта страница — архитектура под этой фразой: какой ключ где создаётся, что релей на самом деле держит, что он всё-таки видит и что меняется на сервере, настроенном выключить шифрование.

Как шифруется сообщение между вашим телефоном и вашим компьютером

Сообщение, которое вы отправляете с телефона, показано в трёх точках, через которые оно проходит. Всё, что агент отвечает, идёт тем же путём в обратную сторону, запечатанное на вашем компьютере до того, как его покинет.

Ваш телефонЗапечатывается здесь
РелейПереносится запечатанным
Ваш компьютерОткрывается здесь
Ваш телефон
Запечатывается здесь. Сообщение шифруется на том устройстве, где вы его набрали, ключом, который принадлежит этой сессии и больше ничему: 32 случайных байта, взятые при создании сессии, AES-256-GCM, новый nonce на каждое сообщение. Затем ключ сессии запечатывается собственным ключом вашего аккаунта эфемерным обменом X25519, и релею передаётся именно запечатанная копия.
Релей
Переносится запечатанным. Сервер записывает строку вида { t: "encrypted", c: "…" } — base64 плюс запечатанный конверт с ключом, секретного ключа к которому у него нет. Он может маршрутизировать эту строку, посчитать её, сохранить и отдать обратно. Открыть её ему нечем, и ни один эндпоинт его об этом не просит.
Ваш компьютер
Открывается здесь. CLI открывает запечатанный ключ сессии тем ключом аккаунта, который он получил, когда вы подключали терминал, расшифровывает и передаёт открытый текст в Claude Code, Codex, OpenCode или тот агент, который работает. Ответ проделывает тот же путь в обратную сторону, запечатанный до того, как уйдёт.

Что хранит сервер релея

Шифрование — это утверждение о содержимом, а утверждение о содержимом — только половина ответа. Вот вторая половина, с той же подробностью: колонки, которые сервер, обслуживающий этот релей, может прочитать без ключа.

Хранит
Шифротекст и запечатанный конверт с ключом
По одной строке на сообщение, и в ней только base64, плюс завёрнутый ключ данных этой сессии. Заворачивание делается на устройстве; ни один серверный маршрут его не производит.
Идентификаторы и форма
Идентификаторы аккаунта, устройства и сессии, случайная метка на каждую сессию, порядковые номера, метки времени и размеры. Это та часть, которую шифрование не покрывает ни в одной системе с синхронизацией, и о ней стоит сказать прямо: релей знает, сколько вы отправили и когда.
С какой стороны пришло каждое сообщение
У каждого сообщения есть роль — user, agent, event или unknown, — которую клиент передаёт вместе с шифротекстом. Это колонка, чтобы транскрипт можно было листать и считать, не открывая его. На зашифрованной строке сервер не может её вывести; он может только хранить то, что подал клиент.
Намеренно маленькая проекция активности
Прочитана ли сессия, сколько запросов разрешений ждут ответа, сколько вопросов задал агент, думает ли он сейчас, чем закончился последний ход. Всё это производное состояние — счётчик, флаг или метка времени, которые клиент пишет по ходу сессии, — и ни слова из сказанного. Оно существует, чтобы телефон, пролежавший выключенным час, показал верный бейдж в момент открытия, и подсчёт — это максимум, что серверу для этого дано делать.
Не хранит
Что вы набрали и что ответил агент
Транскрипт — это и есть шифротекст. Второй копии нет, серверного индекса по нему нет, и шага суммирования, которому такой индекс понадобился бы, тоже нет.
Где идёт сессия
Путь проекта, имя вашего хоста, ваш домашний каталог и версия CLI — это метаданные сессии, а метаданные шифруются тем же ключом сессии ещё до её создания. Это поля внутри запечатанного блоба, а не колонки, по которым сервер может сортировать.
Ваши учётные данные у провайдера
Подключённая учётная запись — те учётные данные Claude или ChatGPT, под которыми идёт сессия, — запечатывается на устройстве до загрузки, ключом, выведенным ровно для этой одной цели из секрета вашего аккаунта. Релей хранит конверт и отдаёт его обратно вашим же устройствам.
Ваш репозиторий
Ваш репозиторий никогда не синхронизируется и не зеркалится. Агент читает и пишет файлы на том компьютере, где он работает, и этот компьютер остаётся единственной полной копией. Когда вы запрашиваете файл, папку или дифф, они едут тем же запечатанным путём, что и всё остальное, — потому что вы их запросили, а не потому что Happier держит копию.

Ваши ключи шифрования создаются на вашем собственном устройстве

В корне вашего аккаунта лежит один секрет, и он создаётся на устройстве, а не выдаётся сервером. Всё остальное выводится из него деревом ключей: корень на HMAC-SHA-512, затем по одному шагу HMAC на каждый именованный элемент пути. Пара ключей, которая открывает запечатанные ключи сессий, берётся из этого дерева под меткой "content", и так же берётся отдельный ключ для каждого другого вида хранимого блоба, так что ни один ключ не делает две работы.

Именно поэтому совершенно новый браузер может войти в ваш аккаунт и всё равно не показать вам ничего из прошлой недели. Вход доказывает релею, кто вы. Чтобы прочитать старую сессию, нужен ключ, а ключа на релее нет и отправить его неоткуда. Восстановление устройства — это и есть шаг, который его переносит: с устройства, где он уже есть, или из секретного ключа, который вы сохранили.

Это также значит, что сценарий потери реален и о нём стоит сказать один раз: если все устройства, где выполнен вход, пропали и секретный ключ пропал вместе с ними, зашифрованные сессии не сможем восстановить ни мы, ни кто-либо ещё. Релей, который мог бы их вернуть, был бы релеем, который мог бы их прочитать.

Добавление нового устройства и как до него доходят ваши ключи

Добавление устройства — тот момент, когда перемещается ключевой материал, поэтому именно его стоит понимать. Сканирование QR-кода поднимает запрос, и только его: подтверждение происходит на устройстве, где вход уже выполнен, а когда на обоих экранах показывается короткий код подтверждения, их полагается сравнить до подтверждения.

Что подтверждение делает на самом деле — запечатывает ключ контента открытым ключом устройства, которое запросило доступ, в конверте с эфемерным ключом отправителя и свежим nonce. Этот конверт едет через релей, как и всё остальное. Подключение терминала добавляет сверху ещё одну привязку: полезная нагрузка аутентифицируется по секрету сопряжения, который приехал в QR-коде, а не через сервер, так что релей, подменивший нагрузку, не смог бы получить подходящий тег.

Политика хранения: сквозное шифрование по умолчанию

Всё выше описывает поведение по умолчанию, и это умолчание — не предложение, от которого клиент может отговорить сервер. Свежий релей настроен требовать хранение со сквозным шифрованием: запись открытым текстом отклоняется ошибкой политики, а не принимается молча. Клиенты читают политику из собственного эндпоинта features сервера до того, как создать сессию, так что режим, в котором находится сессия, согласован до её первого сообщения.

У оператора сервера есть ещё две настройки. Одна разрешает выбирать аккаунту, так что зашифрованные и открытые сессии сосуществуют на одном релее, — для этого нужны и опциональная политика, и разрешение на отказ, а разрешение выключено, пока оператор его не включит. Другая требует открытого текста — для организаций, которые управляют шифрованием на уровне инфраструктуры и которым нужно, чтобы сервер мог читать и обрабатывать содержимое сессий. Последняя — настоящий размен, и сказать об этом стоит без смягчения: в открытой сессии сервер может прочитать транскрипт. В этом и смысл настройки.

Режим фиксируется для каждой сессии при её создании, так что одна сессия никогда не бывает наполовину той и наполовину другой, а смена режима у аккаунта меняет то, что происходит с новыми сессиями, а не дотягивается до старых. Ничто не расшифровывается задним числом, и ничто задним числом не шифруется.

Переменные, которые задают все три, лежащие под ними варианты хранения на диске и окружающие их механизмы идентичности — это половина оператора, и живёт она на странице про развёртывание у себя для команд.

Пуш-уведомления отправляет ваш собственный компьютер

Пуш-уведомление — единственное, что выходит за пределы этой модели, потому что оно должно прийти на телефон, где ничего не открыто. Ответ Happier здесь в том, что отправляет их ваш собственный компьютер. Сервер хранит ваши push-токены; CLI на вашем компьютере читает их обратно и отправляет уведомление сам, что оставляет релей вне пути, на котором ему иначе пришлось бы узнать содержимое.

Полезная нагрузка намеренно тонкая: тип инструмента или запроса и короткая подсказка там, где она помогает, — файл вроде src/file.ts, имя команды, счётчик вроде «3 вопроса». Сырой ввод инструмента в неё не кладётся. Достаточно, чтобы решить, тянуться ли за телефоном, — на экране, который может прочитать любой, кто стоит у вас за спиной.

Релей, развёрнутый у себя: метаданные тоже остаются на вашем железе

Всё это верно и на релее, который держите не вы. Причина держать свой — строка выше про идентификаторы и форму: шифрование не скрывает, кто с кем говорит и как часто, а на вашем собственном релее этот журнал тоже ваш. Одна команда ставит релей на ваше собственное железо, и путь становится таким: ваше устройство, ваш релей, ваш компьютер — и ничего в нём, что развернули бы не вы.

Прочитайте код шифрования сами

На этой странице нет сертификации, потому что сообщать нечего, а страница о безопасности, намекающая на соответствие, которого у неё нет, хуже той, которая о нём не заикается. Вместо этого здесь код, из которого написано каждое предложение выше, — под лицензией MIT и доступный для чтения до запуска: примитивы в packages/protocol/src/crypto, шифрование сессий в apps/cli/src/api/encryption.ts, политика хранения, которую сервер применяет в своём эндпоинте features, и схема, показывающая ровно то, какие колонки лежат открытым текстом.

Это же честный ответ на то, как относиться к этой странице: как к карте того, куда смотреть, а не как к обещанию, которое принимают на веру. Прочитайте эти четыре файла или запустите релей сами и прочитайте, что он объявляет своим клиентам.

Справочник по модели шифрования покрывает ту же территорию как процедуру: чего требует от вас восстановление, что каждый режим хранения означает для аккаунта, к какому сценарию тянуться, когда устройство ещё не может прочитать сессию.