Signet

Приватность

Что сервер знает о вас

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

Это описание кода, а не юридический документ. Мы писали его, читая свой собственный код, а не переписывая чужую политику. Юрист его не смотрел. Там, где реализация отстаёт от обещаний, здесь написано «не включено», а не «включено».

Обновлено 3 сентября 2026 · оператор — тот, кто держит relay.signets.social

Коротко

Пять строк вместо десяти страниц

  • Переписку, файлы и звонки сервер не читает: они зашифрованы между устройствами.
  • Ни номера телефона, ни почты при регистрации нет — этих полей просто не существует в базе.
  • Уведомления идут по своему постоянному соединению. Сервисы Google в доставке не участвуют.
  • Аналитики, рекламы и трекеров нет ни одного. Продавать нам нечего и некому.
  • Но сервер сейчас может определить, кто кому пишет. Разбор ниже, честно и подробно.

Что закрыто шифрованием

Здесь сервер видит только шифротекст

Сообщения и всё, что к ним прицеплено

Текст, фото, видео, голосовые, файлы, стикеры, реакции, правки и удаления. Всё шифруется протоколом Signal — X3DH и двойной храповик, с пост-квантовым гибридом X25519 + Kyber-1024, когда обе стороны на свежей сборке. Ключи создаются на телефоне и с него не уходят.

Звук и картинка звонка

WebRTC с DTLS-SRTP между устройствами. Когда сеть позволяет — напрямую, мимо сервера. Когда нет — через наш TURN-ретранслятор, который перекладывает уже зашифрованный поток и в него не заглядывает. Само установление связи (SDP и ICE) тоже едет внутри сквозного шифрования.

База на самом телефоне

Локальная база под SQLCipher, пароль лежит в аппаратном хранилище телефона. Плюс код блокировки, скрытие от скриншотов (в релизной сборке включено по умолчанию) и код-самоуничтожение, который вместо входа стирает данные.

Ключей у нас нет

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

Главное на этой странице

Что сервер видит, а что нет

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

ДанныеСерверКак именно
Текст сообщенийне видитшифруется на устройстве; на сервере лежит только шифротекст
Файлы, фото, голосовыене видитшифруются тем же ключом сессии, что и текст
Содержимое звонковне видитDTLS-SRTP между устройствами; TURN пересылает шифропоток
Номер телефона, почтане существуетпри регистрации не спрашиваются — таких полей нет в базе
Список ваших контактовне видитхранится на телефоне, целиком на сервер не выгружается. Оговорка: включённый статус «в сети» заставляет приложение спрашивать сервер про ваших собеседников — тогда сервер видит этот набор. Поэтому статус по умолчанию выключен
Ваш ID и публичные ключивидитбез них никто не может начать с вами шифрованную сессию
Ник и @имя, если задаливидитнеобязательны; @имя можно скрыть из поиска, и это проверяет сервер, а не только приложение
Кто кому пишетможет определитьполе отправителя у конверта пустое, но рядом лежит служебный сертификат, который сервер пока читает. Шифрование этого сертификата написано и проверено, но в раздаваемой сборке не включено. Разбор ниже
Время отправки и доставкивидитбез метки времени невозможно ни доставить, ни удалить по сроку
Кто кому звонил и когдавидитсервер сводит звонок, поэтому знает обе стороны. Запись живёт до 4 часов, маркер пропущенного звонка — 7 дней, и в нём обе стороны в открытом виде
Когда вы в сетичастичнов приложении переключатель по умолчанию выключен, и приложение тогда ничего не шлёт. Но на сервере у нового аккаунта этот флаг заводится включённым, и выключается он только когда вы впервые откроете этот экран — до тех пор вас спасает лишь то, что докладывать серверу нечего. «Был в сети» огрублён до минуты; пока статус включён, спросить может любой, кто знает ваш ID, кроме заблокированных
«Печатает…»частичнопо умолчанию выключено. Если включить, приложение раз в ~4 секунды сообщает серверу, кому вы пишете, в реальном времени
IP-адрес подключениявидитнеизбежно для любого сервера. Хотите скрыть — подключайтесь через VPN или Tor
Сервисы Googleне участвуютуведомления идут по своему постоянному соединению, без FCM

Сервер может определить, кто кому пишет

Это главная незакрытая вещь, и мы не спрячем её в сноску.

В записи на сервере поле «от кого» действительно пустое — sealed sender работает. Но рядом с конвертом едет сертификат отправителя: короткая подписанная справка, по которой получатель убеждается, что писали именно вы. Сейчас она едет открытой, и сервер её читает. Значит, сервер способен связать «этот аккаунт — с этим».

Есть и вторая причина, независимая от первой. Чтобы вернуть вам галочку «доставлено», сервер должен знать, кому её вернуть. Поэтому у непрочитанного сообщения он держит служебную ссылку на настоящего отправителя. Она обнуляется, когда приходит отметка о прочтении. Если квитки о прочтении выключены — живёт весь срок хранения сообщения.

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

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

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

Отправитель остаётся не только в таблице сообщений

Когда мы измерили это первый раз, оказалось, что смотрели в одно место, а отправитель жил в трёх. Чтобы не отправлять одно и то же дважды, сервер держит короткие служебные записи «это сообщение я уже принимал»: в Redis около суток и в базе — рядом с самим сообщением, то есть до 30 дней, если адресат так и не вышел в сеть.

Раньше в этих записях отправитель лежал открытым текстом, включая резервные копии Redis, где шифрования нет вовсе. Сейчас вместо него хранится HMAC. Это не полное слепление: у того, у кого есть и база, и серверный ключ, HMAC перебирается по списку аккаунтов, потому что идентификатор сообщения лежит рядом открытым. Закрыть это — большая правка протокола, и она не сделана.

IP-адрес и журналы сервера

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

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

Но перед ним стоит веб-сервер, и он аккуратность не соблюдает. Мы это замерили на работающей машине 3 сентября: входной сервер (Caddy) пишет в свой журнал полный адрес запроса вместе с IP клиента. Для запросов вида «забрать свою почту» там нет ничего лишнего, но у путей, в которых есть чужой идентификатор — например запрос ключей собеседника, — в журнал попадает и он, и адрес спросившего. То есть цель, ради которой бэкенд пишет шаблоны, на входе теряется. Журналы лежат в контейнере, ограничены по размеру, ротируются и наружу не отправляются, но это смягчение, а не решение. Чинится настройкой формата журнала на входном сервере; на момент этой правки не исправлено.

Журналы контейнеров ограничены по размеру и ротируются, наружу не отправляются.

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

Сроки

Сколько что лежит на сервере

Сервер не архив. Всё, что он держит, держится ради доставки и удаляется по расписанию, а не «по запросу в поддержку».

Сообщение, которое не забралидо 30 дней
Сообщение после доставки5 минут, затем удаляется
Вложениедо 30 дней
Ссылка на скачивание вложения2 минуты · до 10 загрузок
Запись о звонке90 с на дозвон · до 4 ч в разговоре
Маркер пропущенного звонка7 дней
Служебная запись «уже принимал»24 ч в Redis · до 30 дней в базе
«Был в сети»огрублён до минуты · 90 с в памяти
Токен доступа / обновления24 часа / 30 дней

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

Что удаляете вы

  • Исчезающие сообщения. Таймер на чат: по истечении срока запись стирается с перезаписью, а не помечается удалённой. По умолчанию выключено — включаете вы.
  • Удаление аккаунта. Каскадом стирает ваши строки на сервере: сообщения, вложения вместе с блобами, ключи, маркеры звонков.
  • Код-самоуничтожение. Введённый вместо обычного кода блокировки, он стирает данные на телефоне и рассылает собеседникам команду стереть переписку у себя. У тех, кто офлайн, она срабатывает при следующем подключении. Это лучшее усилие, а не гарантия: скриншот чужого экрана мы отозвать не можем.
  • Блокировка. Проверяется на сервере, а не только в приложении: новые сообщения от заблокированного не примутся, и статус «в сети» он тоже не увидит.

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

Уведомления

Без Google — и это проверяемо

Сообщения доставляет постоянное соединение самого приложения (WebSocket к нашему серверу), а держит его обычная фоновая служба Android. Никакого посредника между сервером и телефоном нет.

Полная честность: библиотека Firebase Messaging в APK лежит, и код для приёма «пинка» через неё написан. Но проект Firebase в сборке — заглушка с нулевым номером, а на сервере отправка выключена по умолчанию. Поэтому через серверы Google сейчас не уходит ни одного уведомления.

Если мы когда-нибудь включим этот путь как резервный, здесь появится строка об этом до включения, а не после. «Пинок» не будет содержать текста сообщения, и его можно будет выключить.

Ваши переключатели

Что можно выключить, и что это меняет

  • Статус «в сети». Выключен по умолчанию, и это двусторонний тумблер: приложение перестаёт и объявлять вас, и спрашивать сервер про ваших собеседников. Именно второе и держит набор ваших контактов вне сервера.
  • «Печатает…». Выключено по умолчанию. Включённое, оно раз в четыре секунды рассказывает серверу, кому вы пишете.
  • Квитки о прочтении. Включены. Выключенные — экономят собеседнику знание, но продлевают жизнь служебной ссылки на отправителя: обнулять её нечему до самого истечения срока. Такой трейд-офф, и лучше знать о нём.
  • Поиск по @имени: все / только контакты / никто. Запрет проверяет сервер и отвечает «не найдено» — так же, как на несуществующее имя.
  • Кто может звонить и добавлять в группы — по тем же трём уровням.
  • «Скрыть мой IP» в звонках. Гонит звонок только через TURN, чтобы собеседник не увидел ваш адрес. Ценой задержки, поэтому по умолчанию выключено.
  • Исчезающие сообщения — таймер на чат или общий.
  • Код блокировки и биометрия плюс отдельный код-самоуничтожение.
  • Защита от снимков экрана. В релизной сборке включена.

Проверка

Не верьте странице — проверьте сами

  • Число безопасности. Считается на устройстве из двух ключей — сервер в этом не участвует и подменить его не может. Сверьте с собеседником лично или голосом. Если его ключ сменится, приложение предупредит и снимет прежнюю отметку «проверен», а не сделает вид, что всё в порядке.
  • Журнал сети внутри приложения. Экран, показывающий каждый исходящий запрос: адрес, размер, начало тела. Для переписки там всегда шифротекст. Это возможность увидеть, что уходит с телефона, а не обещание.
  • Отпечаток файла и подписи. Хэш APK и отпечатки ключа опубликованы на главной. Сверяйте их через отдельный доверенный канал: страница, которую вам могли подменить, сама себя не подтверждает.

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

Связь

Как достать оператора

Почтового сервера у Signet нет, и публиковать адрес мы не хотим: он соберёт спам к проекту, который ведёт один человек. Замысел был простой — добавить контакт @signet в приложении и написать, чтобы вопрос о приватности сам не утёк по дороге.

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

Если эта страница изменится

Существенные изменения мы объявим в приложении и на странице загрузки, с новой датой сверху. Правки в сторону «звучит приятнее» без изменений в коде мы не делаем: страница меняется, когда меняется то, что она описывает.