Сквозное шифрование, с ключами на ваших собственных устройствах
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, и схема, показывающая ровно то, какие колонки лежат открытым текстом.
Это же честный ответ на то, как относиться к этой странице: как к карте того, куда смотреть, а не как к обещанию, которое принимают на веру. Прочитайте эти четыре файла или запустите релей сами и прочитайте, что он объявляет своим клиентам.
Справочник по модели шифрования покрывает ту же территорию как процедуру: чего требует от вас восстановление, что каждый режим хранения означает для аккаунта, к какому сценарию тянуться, когда устройство ещё не может прочитать сессию.

