
Seal v0.2.3
Одноранговый чат со сквозным шифрованием. Без входящих. Нет аккаунта для восстановления. Никто не подслушивает — даже мы.
Seal
Одноранговый чат со сквозным шифрованием.
Никакого инбокса. Никакого аккаунта для восстановления. Никто не подслушивает — даже мы.
Сообщения передаются напрямую между пирами через libp2p и шифруются протоколами Olm/Megolm в стиле Signal (через vodozemac) ещё до того, как покинут ваше устройство. Единственный задействованный сервер — это небольшой каталог, который помогает пирам находить актуальные адреса друг друга. Он никогда не видит содержимое сообщений, и его можно удалить одной командой.
Смотрите docs/THREAT_MODEL.md и
docs/SECURITY.md — что именно защищено и как.
Содержание
- Скриншоты
- Возможности
- Как это работает
- Структура проекта
- 1. Предварительные требования
- 2. Сборка
- 3. Запуск в режиме разработки
- 4. Тестирование
- 5. Настройка бэкенда (сервера-каталога)
- 6. Использование приложения
Скриншоты
Первый запуск — выберите имя; больше ничего настраивать не нужно.
Переписки — групповой сайдбар, список контактов и панель чата со сквозным шифрованием.
Настройки — чувствительность микрофона, push-to-talk, автозапуск при входе в систему, доступность сети.
Возможности
- Сквозное шифрование всегда — каждое сообщение запечатывается с помощью Olm (1:1) или Megolm (группы) ещё до того, как покинет ваше устройство, по схеме в стиле Double Ratchet от vodozemac: у каждого сообщения свой ключ.
- Никакого инбокса, никогда — сообщения передаются по прямому одноранговому соединению (libp2p: QUIC/TCP + Noise, с ретрансляцией и hole punching для NAT). Если получатель офлайн, сообщение ждёт локально и повторяет попытки — оно никогда не попадает в очередь на чужой инфраструктуре.
- Каталог, а не база данных — единственный задействованный сервер
(
crates/directory-server) сопоставляет идентификатор пользователя с текущим сетевым адресом и ничем больше. Он структурно не способен прочитать содержимое сообщений: егоCargo.tomlдаже не зависит от крейтов, которые умеют это делать. - Несколько аккаунтов на одном устройстве — полностью раздельные идентичности (ключи, контакты, сообщения), между которыми можно переключаться без перезапуска.
- Группы с настоящим изменением состава участников — текстовые и голосовые каналы в каждой группе; при исключении участника ключ группы ротируется, чтобы он не мог прочитать ничего из отправленного после этого.
- Голосовая связь из коробки — push-to-talk по глобальной горячей клавише (работает из любого приложения, не только из Seal), настраиваемая чувствительность микрофона и опциональный модулятор голоса.
- Вложения без метаданных — данные EXIF (GPS-координаты, сведения о камере/устройстве) удаляются из изображений до отправки; включено по умолчанию.
- Настоящая кнопка экстренного стирания — «Настройки» → «Данные и конфиденциальность» мгновенно и безвозвратно удаляют все ключи, контакты и сообщения на этом устройстве, никак не влияя на тех, с кем вы общались.
- Автозапуск при входе в систему, если хотите — включён по умолчанию, отключается одним переключателем в настройках.
- Одна кодовая база, три платформы — нативные окна на macOS, Windows и Linux через Tauri.
Как это работает
В этом приложении два вида идентичности, и они намеренно разделены:
- Ваша чат-идентичность — это пара ключей Ed25519/Curve25519, созданная локально
с помощью vodozemac при первом
открытии приложения (
identity::Identity). Ваш публичный «user ID» — это просто отпечаток этого ключа (wire_proto::user_id_from_ed25519). Никакой сервер не может выдать или отозвать его, потому что ни один сервер не участвует в его создании. - Ваша сетевая идентичность — это отдельная пара ключей libp2p (
PeerId), используемая только на транспортном уровне. Она может меняться между перезапусками, никак не влияя на вашу чат-идентичность; связывает их только запись присутствия, подписанная вами самостоятельно.
Найти собеседника и фактически поговорить с ним — это два разных шага:``` ┌────────────────────────┐ │ directory server │ │ (axum + one SQLite │ │ file: users, │ │ presence, group │ │ rosters. Never │ │ message content.) │ └─────────┬───────────────┘ 1. "where is bob │ 2. "here's my current right now?" │ address" (signed, │ expires in minutes) ┌─────────┴───────────────┐ ▼ ▼ ┌───────┐ 3. direct libp2p ┌───────┐ │ alice │◄──── connection ────►│ bob │ └───────┘ (Noise + Olm/ └───────┘ Megolm encrypted)
1. Алиса ищет Боба в каталоге по его user ID. Это возвращает его
открытые ключи и последний объявленный сетевой адрес. Это всё,
что хранит каталог: открытые ключи, отображаемые имена, списки
членства в группах и кратковременные объявления адресов
(`crates/directory-server`).
2. Алиса напрямую связывается с Бобом через libp2p (QUIC или TCP+Noise, с relay +
hole-punching для пиров за NAT; см. `crates/net`). Каталог с этого момента
полностью исключён из процесса.
3. Сообщение шифруется с помощью **Olm** для личного чата 1:1 или
**Megolm** для группового (`crates/crypto-session`), по схеме в стиле Double-Ratchet,
где каждое сообщение получает собственный ключ, прежде чем оно вообще
попадает в это libp2p-соединение. Серверного почтового ящика нет: если Боб
офлайн, сообщение ожидает локально и повторяется, а не хранится в чьей-либо
инфраструктуре.
Всё вышеперечисленное координируется `AppService` из `crates/core` — именно в него
вызывает Rust-бэкенд приложения Tauri (`apps/desktop/src-tauri`);
интерфейс никогда не взаимодействует с сетью напрямую.
## Структура проекта```
crates/
wire-proto shared signed-request types for the directory API
identity vodozemac identity, OS-keychain key management
storage local encrypted store (contacts, messages, groups)
net libp2p transport + directory HTTP client
crypto-session Olm (1:1) / Megolm (group) session management
core orchestrates the above into `AppService` / `ChatNode`
directory-server the one server component (axum + SQLite)
apps/desktop the Tauri + React app
scripts/ build + backend-deployment scripts (§2, §5)
1. Предварительные требования
Вам понадобятся Rust и Node.js на каждой платформе, а также специфичный для платформы
инструментарий, необходимый Tauri для создания нативного окна. storage и directory-server
также компилируют SQLite из исходного кода в составе бандла, для чего нужен обычный C-компилятор (нигде
в этом проекте не требуется OpenSSL или другая нативная криптографическая библиотека).
Общее для всех платформ:
- Rust (стабильный канал; устанавливайте через
rustup, а не через пакетный менеджер вашей ОС) - Node.js 20+ и npm
macOS
```sh xcode-select --install ``` Вот и всё. Xcode Command Line Tools предоставляют как C-компилятор, так и фреймворки, необходимые бэкенду Tauri для macOS (на основе WKWebView).Linux
Установите C-компилятор, pkg-config и dev-пакеты WebKitGTK/AppIndicator,
с которыми линкуется бэкенд Tauri для Linux.
Debian/Ubuntu:```sh
sudo apt update
sudo apt install libwebkit2gtk-4.1-dev build-essential curl wget file
libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev pkg-config
Fedora:```sh
sudo dnf install webkit2gtk4.1-devel openssl-devel curl wget file \
libappindicator-gtk3-devel librsvg2-devel pkgconf-pkg-config
sudo dnf group install "C Development Tools and Libraries"
Архитектура:```sh
sudo pacman -S --needed webkit2gtk-4.1 base-devel curl wget file openssl
appmenu-gtk-module libappindicator-gtk3 librsvg pkgconf
(Имена пакетов меняются между релизами Tauri: если сборка завершается ошибкой из-за
отсутствующего файла `.pc`, проверьте
[актуальные требования Tauri для Linux](https://v2.tauri.app/start/prerequisites/)
для вашего дистрибутива.)
</details>
<details>
<summary><strong>Windows</strong></summary>
1. Установите **Microsoft C++ Build Tools** (Visual Studio Installer →
рабочая нагрузка «Разработка классических приложений на C++»), нужные как для нативной
оболочки Tauri, так и для компиляции встроенного SQLite.
2. Установите тулчейн Rust **MSVC**: `rustup default stable-msvc`.
3. **WebView2**: уже присутствует в Windows 11 и в большинстве актуальных установок Windows
10; если его нет, сборка Tauri предложит установить
Evergreen runtime.
</details>
---
## 2. Сборка
Из корня репозитория:```sh
# Rust workspace (backend crates + the directory server)
cargo build --workspace --release
# Frontend + the actual desktop app bundle (installer/.app/.exe)
cd apps/desktop
npm install
npm run tauri build
npm run tauri build создаёт нативный установщик для платформы в
target/release/bundle/ в корне репозитория (это рабочее пространство Cargo, поэтому все
крейты, включая приложение Tauri, используют один общий каталог верхнего уровня target/).
Кросс-компиляция (например, сборка установщика Windows на macOS) не
настроена: собирайте на каждой целевой платформе или используйте GitHub Actions workflow от Tauri,
если вам нужны релизы, собираемые в CI.
Или используйте скрипты
scripts/ содержит по одному скрипту сборки для каждой платформы/результата, каждый можно запускать независимо,
и каждый проверен на то, что действительно создаёт рабочий артефакт:
| Скрипт | Результат |
|---|---|
scripts/build-mac-dmg.sh | установщик .dmg для macOS |
scripts/build-mac-app.sh | Сырой пакет .app для macOS, без установщика |
scripts/build-linux.sh | Linux .AppImage + .deb |
scripts/build-windows.ps1 | Windows .msi + .exe (NSIS) |
Каждый просто оборачивает npm run tauri build --bundles <...> с правильными флагами
и проверкой платформы; выполните команду напрямую сами, если нужна другая
комбинация бандлов (npx tauri build --help из apps/desktop).
scripts/release.sh vX.Y.Z поднимает версию везде, где это нужно,
и создаёт тег для коммита — см. docs/RELEASING.md. Работает на
macOS и Linux; не коммитит и не пушит.
Встраивание собственной сети "Seal"
Экран выбора сервера (§3) всегда показывает три варианта: Seal (ваша собственная официальная сеть), Custom server и небольшая ссылка Local test server внизу. "Seal" отключена (затемнена, с надписью "В этой сборке ещё не настроено") до тех пор, пока вы не встроите URL на этапе сборки:```sh SEAL_DEFAULT_DIRECTORY_URL=https://directory.example.com npm run tauri build
Как только вы поднимете собственный сервер (§5) и направите на
него реальный домен, задайте это значение и пересоберите: каждая распространяемая вами копия с этого момента показывает
«Seal» как реальную выбираемую опцию с использованием этого URL, не затрагивая
никакой другой код. Оставьте это значение незаданным для обычных/dev-сборок: здесь нет официального
сервера, размещаемого этим репозиторием, поэтому «Seal» остаётся отключённой, и пользователи возвращаются к
пользовательскому серверу или локальному, вместо того чтобы приложение молча указывало на
домен-заполнитель, который на самом деле ничего не запускает.
---
## 3. Запуск в dev-режиме```sh
cd apps/desktop
npm install
npm run tauri dev
Это запускает dev-сервер Vite, компилирует Rust-бэкенд в debug-режиме и открывает нативное окно с hot-reload на фронтенде. Первая сборка компилирует всё дерево зависимостей и занимает несколько минут; последующие запуски быстрые.
Выбор сервера (первый запуск)
При первом запуске Seal спрашивает, какой сервер каталога использовать, в этом порядке:
-
Seal: официальная сеть, если в этой сборке она встроена (см. §2). Недоступен, пока её нет; этот репозиторий не поставляется с указанием на домен-заглушку.
-
Пользовательский сервер: любой, включая ваш собственный (§5).
-
Локальный тестовый сервер: небольшая, намеренно ненавязчивая ссылка внизу. Запускает собственный встроенный сервер приложения (привязывается к
127.0.0.1:47100/47101, данные — в каталоге данных приложения вашей ОС), подходит для опробования Seal или тестирования инстансов на одной машине, не для реального развёртывания. Если второй инстанс обнаруживает, что эти порты уже заняты, он просто использует сервер первого инстанса вместо запуска ещё одного, что и позволяет двум инстансам на одной машине найти друг друга. Именно он выбирается автоматически, если «Seal» не настроен и вы не выбрали ничего другого.
Выбор сохраняется (server.json рядом с остальными локальными данными приложения) и
молча используется при каждом последующем запуске; изменить его можно в Settings → Directory
server, и изменение вступит в силу при следующем запуске приложения, а не
будет пытаться переключить активное соединение на лету. Для скриптов/разработки
переменная окружения полностью пропускает запрос:```sh
P2P_CHAT_DIRECTORY_URL=https://directory.example.com npm run tauri dev
### Запуск двух экземпляров локально (чтобы реально протестировать обмен сообщениями)
Каждому экземпляру нужна своя идентичность. Seal поддерживает несколько аккаунтов нативно (Настройки → Аккаунты на этом устройстве), но для двух *отдельных процессов* на одной машине `P2P_CHAT_PROFILE` — более быстрый путь: он автоматически создаёт (при первом запуске) или автоматически возобновляет (при каждом последующем) аккаунт с этим именем, без интерактивного взаимодействия, полностью пропуская выбор аккаунта:```sh
# terminal 1
P2P_CHAT_PROFILE=alice npm run tauri dev
# terminal 2
P2P_CHAT_PROFILE=bob npm run tauri dev
Выбор сервера (server.json) и список учётных записей (accounts.json)
общие для всех процессов на одной машине, а не для отдельных профилей.
Первый запущенный экземпляр выбирает сервер, и каждый следующий профиль
(включая bob) молча использует его. Оба окна оказываются на одном
встроенном сервере каталогов, поэтому вы можете добавить друг друга в контакты по ID
и переписываться между ними.
Dev-серверу Vite нужен реальный фиксированный порт, на который будет указывать webview Tauri,
поэтому обычно одновременно может работать только один npm run tauri dev —
второй обнаружит, что порт 1420 уже занят, и сразу завершится.
npm run tauri на самом деле является небольшим враппером (apps/desktop/scripts/tauri.mjs),
который для каждого экземпляра после первого выбирает следующий свободный порт (1421, 1422, …)
и автоматически передаёт его, так что выполнение двух указанных выше команд
в двух терминалах просто работает; вам не нужно делать ничего по-другому. Он
меняет поведение только для dev — npm run tauri build и всё остальное
передаётся напрямую настоящему CLI.
Тестирование на реальной сборке (не в dev-режиме)```sh
./scripts/run-two-mac-instances.sh # profiles: alice, bob ./scripts/run-two-mac-instances.sh carol dave
Та же идея, что и выше, но запускает реально собранное приложение (результат `build-mac-app.sh` /
`build-mac-dmg.sh` или установленную копию в `/Applications`) дважды
с разными `P2P_CHAT_PROFILE` вместо `npm run tauri dev`, что ближе
к тому, что запускает реальный пользователь. Выводит их PID и способ остановки обоих.
### Отладка
- **Журналы Rust**: задайте `RUST_LOG` перед запуском, например:
`RUST_LOG=debug npm run tauri dev` (или `RUST_LOG=p2p_core=debug,net=debug`
для сужения области). Логируемые поля ограничены метаданными (идентификаторы
пиров/групп/пользователей, типы ошибок); см. [`docs/SECURITY.md`](https://github.com/emn4tor/seal/blob/HEAD/docs/SECURITY.md) о том, почему
это можно безопасно оставлять подробным.
- **Фронтенд**: окно разработки — это настоящее webview; щелчок правой кнопкой мыши →
«Проверить элемент» (или открыть инструменты разработчика) работает как в обычном браузере.
- **Бэкенд-крейты изолированно**: каждый крейт имеет собственный набор тестов, который можно
запускать и итерировать, вообще не трогая UI; см. §4.
- **Автономный сервер каталогов** вместо встроенного: см. §5.
---
## 4. Тестирование```sh
# everything
cargo test --workspace
# one crate, e.g. the full backend-to-backend flow a Tauri command would trigger
cargo test -p p2p-core --test app_service
# lint + format check (what CI runs)
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
# dependency vulnerability scan
cargo install cargo-audit --locked # once
cargo audit
# frontend type-check + build
cd apps/desktop && npm run build
5. Настройка бэкенда (сервера каталогов)
Коротко о том, что это на самом деле, ведь легко вообразить что-то более сложное: один процесс axum, один файл SQLite, три вида записей (открытые ключи, кратковременные объявления о присутствии, составы групп), и все записи подписаны собственным ключом идентичности вызывающей стороны. Он никогда не находится на пути сообщения. См. docs/THREAT_MODEL.md, почему это верно структурно, а не просто по политике: Cargo.toml пакета directory-server даже не зависит от крейтов, которые умеют читать содержимое сообщений.
Самый быстрый путь: скрипт настройки```sh
sudo ./scripts/setup-backend.sh
Интерактивный, только Linux + systemd (см. шапку скрипта, почему). Он
спрашивает, к какому семейству дистрибутивов вы относитесь (Debian/Ubuntu, Fedora/RHEL/Rocky/Alma,
Arch/Manjaro или openSUSE, с предварительно заполненным значением, угаданным из `/etc/os-release`,
так что обычно достаточно одного нажатия клавиши) и устанавливает зависимости сборки
этого дистрибутива с помощью отдельной функции для каждого семейства, предлагает установить Rust
через `rustup`, если он отсутствует, собирает release-бинарник, создаёт отдельного
системного пользователя, генерирует токен администратора, спрашивает, хотите ли вы,
чтобы он настроил домен с автоматическим HTTPS через [Caddy](https://caddyserver.com)
(устанавливая сам Caddy, для каждого дистрибутива, с откатом на официальный
статический бинарник Caddy, если пакет дистрибутива недоступен), или чтобы он просто привязался
к loopback/обычному HTTP, если вы предпочитаете обслуживать его самостоятельно, затем записывает и
включает systemd-сервис. Безопасно запускать повторно.
Всё, что ниже, — это то, что скрипт на самом деле делает, если вы хотите сделать это вручную
или понять это перед запуском.
### macOS: быстрый тестовый сервер в локальной сети```sh
./scripts/run-mac-test-server.sh
Не для реального хостинга: для тестирования приложения на двух устройствах в одной
сети (например, ваш Mac + другая машина, или два человека в одной Wi-Fi-сети)
без настройки домена, TLS или systemd (которого на macOS всё равно
нет). Он собирает релизный бинарник, генерирует токен администратора (переиспользуется при
последующих запусках), привязывает публичный API ко всем интерфейсам и выводит URL для
использования: фактический LAN-IP вашего Mac (через ipconfig getifaddr), а не просто
127.0.0.1, чтобы другие устройства тоже могли до него добраться. Админ-порт остаётся
только на loopback. Работает в foreground; Ctrl-C останавливает его. Данные хранятся в
~/.seal-test-server.
Быстрый локальный запуск```sh
DIRECTORY_DB_PATH=/var/lib/seal-directory/directory.sqlite3
DIRECTORY_PUBLIC_ADDR=0.0.0.0:8080
DIRECTORY_ADMIN_ADDR=127.0.0.1:8090
DIRECTORY_ADMIN_TOKEN=$(openssl rand -hex 32)
cargo run --release -p directory-server --bin directory-server
| Переменная | Обязательна | Значение |
|---|---|---|
| `DIRECTORY_DB_PATH` | нет (по умолчанию `directory.sqlite3`, cwd) | Где живёт единственный файл SQLite. Родительский каталог должен существовать. |
| `DIRECTORY_PUBLIC_ADDR` | нет (по умолчанию `0.0.0.0:8080`) | Rendezvous API, с которым общаются приложения. Можно безопасно выставлять наружу. |
| `DIRECTORY_ADMIN_ADDR` | нет (по умолчанию `127.0.0.1:8090`) | Эндпоинт очистки (purge). Не открывайте его в публичный интернет; см. ниже. |
| `DIRECTORY_ADMIN_TOKEN` | **да** | Bearer-токен для административного API. Процесс отказывается запускаться без него. Сгенерируйте с помощью `openssl rand -hex 32` или аналогично; не используйте его где-либо ещё. |
Процесс при запуске записывает в журнал, какие адреса он слушает, и громко предупреждает, если
`DIRECTORY_ADMIN_ADDR` не является loopback.
### Как подключить приложение к нему
Три способа — в порядке, в котором вы обычно будете к ним обращаться:
1. **Экран первого запуска**: выберите «Пользовательский сервер» и введите URL. См. §3.
2. **Настройки → Сервер каталога**: измените позже; вступит в силу после
следующего перезапуска.
3. **`P2P_CHAT_DIRECTORY_URL`**, установите перед запуском: полностью пропускает запрос
и переопределяет всё сохранённое; полезно для разработки и скриптованных запусков: ```sh
P2P_CHAT_DIRECTORY_URL=https://directory.example.com npm run tauri dev
Всем, кто хочет найти друг друга, нужно указывать на один и тот же экземпляр каталога; именно так они находят друг друга в первую очередь.
Запуск в качестве реального сервиса (systemd)
Показать systemd-юнит и примечания
```ini # /etc/systemd/system/seal-directory.service [Unit] Description=Seal directory server After=network.target[Service] Type=simple User=seal-directory Group=seal-directory Environment=DIRECTORY_DB_PATH=/var/lib/seal-directory/directory.sqlite3 Environment=DIRECTORY_PUBLIC_ADDR=127.0.0.1:8080 Environment=DIRECTORY_ADMIN_ADDR=127.0.0.1:8090 EnvironmentFile=/etc/seal-directory/admin-token.env ; DIRECTORY_ADMIN_TOKEN=... ExecStart=/usr/local/bin/directory-server Restart=on-failure
Sandboxing: this process needs almost nothing
ProtectSystem=strict ProtectHome=true PrivateTmp=true NoNewPrivileges=true ReadWritePaths=/var/lib/seal-directory
[Install] WantedBy=multi-user.target
Примечания:
- `DIRECTORY_PUBLIC_ADDR` здесь намеренно привязан к **loopback**; поместите перед ним обратный прокси для TLS (см. ниже), а не открывайте axum напрямую в интернет.
- Сначала создайте системного пользователя/группу `seal-directory` и `/var/lib/seal-directory` (`useradd --system --no-create-home seal-directory && install -d -o seal-directory -g seal-directory /var/lib/seal-directory`), затем скопируйте собранный бинарник `directory-server` (из `target/release/`) в `/usr/local/bin/`.
- Поместите административный токен в `EnvironmentFile`, доступный для чтения только root, а не прямо в файл юнита (файлы юнитов часто доступны для чтения всем пользователям).
</details>
### TLS через обратный прокси
<details>
<summary>Показать конфигурацию Caddy / nginx</summary>
[Caddy](https://caddyserver.com) даёт автоматический HTTPS с минимальной конфигурацией:```
# /etc/caddy/Caddyfile
directory.example.com {
reverse_proxy 127.0.0.1:8080
}
caddy run (или systemctl enable --now caddy) самостоятельно занимается
выпуском/продлением сертификатов. Если вы предпочитаете nginx, завершайте TLS на нём
и используйте proxy_pass http://127.0.0.1:8080;, поскольку приложению нужен только обычный HTTP
с точки зрения прокси.
С точки зрения файрвола: снаружи должен быть доступен только публичный порт
(8080 в примерах выше, перед которым через прокси находится 443). Административный порт
никогда не должен быть доступен снаружи; обращайтесь к нему через SSH-проброс портов
(ssh -L 8090:127.0.0.1:8090 your-server), когда нужно выполнить очистку
удалённо.
Очистка```sh
cargo run --release -p directory-server --bin directory-admin --
--admin-url http://127.0.0.1:8090 --token "$DIRECTORY_ADMIN_TOKEN" purge
Это удаляет файл SQLite и заново создаёт пустую схему: никаких `DELETE`
операторов, никакого частичного состояния. Его можно безопасно запускать без предупреждения кого-либо:
каждая запись в нём — это кэш данных, которые каждый клиент уже хранит локально
(свою регистрацию, присутствие и любые списки участников групп, в которые он входит),
поэтому клиенты просто заново заполняют его в течение нескольких мгновений после следующего действия.
Для этой базы данных намеренно не предусмотрена политика резервного копирования; см.
[`docs/SECURITY.md`](https://github.com/emn4tor/seal/blob/HEAD/docs/SECURITY.md) о том, почему её наличие подорвало бы
весь смысл.
---
## 6. Использование приложения
1. **Первый запуск, первый вопрос**: какой сервер каталогов использовать (§3).
По умолчанию используется то, что встроено в запускаемую вами сборку (локальный
тестовый сервер, если только тот, кто собрал её, не настроил официальный); выберите
"Свой сервер", чтобы указать на сервер, который размещаете вы или тот, кому вы доверяете.
2. **Выберите отображаемое имя.** При этом на вашем устройстве создаётся пара ключей
(нечего запоминать, и при потере невозможно ничего восстановить: это сделано намеренно),
и вы проходите краткое объяснение в приложении того, как на самом деле работает шифрование.
Повторить его можно в любой момент в Настройках. Каждый последующий запуск сразу
возвращает вас в приложение без запроса; это происходит только один раз для каждой
учётной записи. Добавляйте другие учётные записи (полностью отдельные личности) через
Настройки → Учётные записи на этом устройстве и переключайтесь между ними без перезапуска.
3. **Добавьте собеседника**: нажмите **+** рядом с "Личные сообщения" и введите его
ID (указан в *его* Настройки → Моя личность). Каталога для просмотра нет — это
сделано намеренно; вы подключаетесь так же, как делитесь номером телефона.
4. **Напишите собеседнику**: выберите имя из списка и вводите текст. Первое сообщение
собеседнику автоматически устанавливает зашифрованный сеанс.
5. **Создайте группу**: нажмите **+** на панели значков, дайте ей имя, затем пригласите
людей по ID так же, как описано выше. Удаление кого-либо ротирует ключ группы, чтобы
удалённый не мог прочитать ничего из отправленного после.
6. **Удалите всё**: Настройки → Данные и конфиденциальность. Это мгновенно, действует
только локально и необратимо: оно уничтожает ваши ключи, контакты и историю на
*этом устройстве* и не оказывает никакого влияния на тех, с кем вы общались.