Боты

Его находят так же, как человека.

Бот PrivChat — это автоматическая учётная запись: за ней стоит не человек с приложением, а программа, которую кто-то — её оператор — держит запущенной на собственном компьютере, всегда включённом. Поскольку здесь нет ничьих серверов, нет и каталога ботов, имён с @ и API в Интернете: бота находят по его приглашению, как человека. У этой страницы две половины: первая — для любого, кто пользуется приложением; от раздела «Запуск бота» и ниже — для операторов и программистов.

Небольшой компьютер, работающий на столе в темноте.
Чтобы пользоваться ботом, ничего из этого не нужно Что здесь называется ботом Что бот видит, а чего нет Запуск бота Параметры Каналы со стороны бота Личные контакты Бот как домашний почтовый ящик Программирование бота: локальный API Правила, которые бот должен соблюдать Частые проблемы

01

Чтобы пользоваться ботом, ничего из этого не нужно

Пользоваться ботом — значит просто пользоваться приложением. Если вам передали приглашение бота (текст или QR, обычно опубликованные на его сайте):

  • Если это канал: Новый → Присоединиться к каналу, вставить текст приглашения (начинается с UEND) или отсканировать его QR, и Присоединиться. Внутри Обновить забирает записи; первый опрос может занять пару минут, пока адрес канала не опубликован в Tor.
  • Если это контакт: Новый → Новый контакт, вставить приглашение или отсканировать анимированный QR бота, дать имя и Добавить. Первое сообщение несёт зашифрованное рукопожатие; когда оно доходит, бот видит вас как контакт и отвечает то, что велит его программа.
  • Проверить его: в чате, Ещё → Проверить лично → Вставить его код — код оператор публикует с подписью на своём сайте.

А вот чего бот не может — по устройству протокола:

  • Не может прочитать ничего, что вы не отправили ему сами. Ваши переписки, группы и каналы он не видит и не знает об их существовании.
  • Не может войти туда, куда его не звали. В группу он попадает только если администратор представит его, как любого участника; и не может публиковать в чужой канал: в каждый канал пишет только владелец его ключа.
  • Не знает, кто читает его канал. Читатели забирают записи через анонимные цепочки, никогда не называя себя.
  • «Официальных» ботов PrivChat не существует. Каждый бот отвечает перед своим оператором — именно он получает то, что вы пишете боту. Если вам важно, кто за ним стоит, проверьте его по опубликованному коду.

02

Что здесь называется ботом

  • Просто ещё один узел. У него своя личность, свой onion-сервис Tor, своя зашифрованная база и свои каналы. Это движок приложения, собранный без Android.
  • Неотличим на линии. Те же конверты, то же шифрование (Double Ratchet и PQXDH), тот же маскирующий трафик, те же каналы. Приложение не знает, что говорит с программой, потому что узнать это невозможно.
  • Без каталога. Центральный каталог был бы тем самым сервером, ради отсутствия которого существует это приложение. Оператор раздаёт приглашение своего бота где хочет: на сайте, на плакате, в сообщении.
  • Локальный API. Ваша программа (Python, Go, shell — что угодно) общается с ботом через Unix-сокет в его каталоге, в JSON. Удалённую часть обеспечивает Tor.
  • Два применения, именно в этом порядке. Канал: бот публикует, а читает кто угодно, анонимно. И личная переписка: люди пишут боту, бот отвечает.

03

Что бот видит, а чего нет

О читателях своего канала — ничего. Читатель забирает записи через анонимную цепочку, не называя себя; бот видит, что кто-то читал, когда и сколько, и не может увидеть больше, потому что в протоколе для этого нет места. Канал с тысячей читателей знает о них ровно столько же, сколько канал с десятью.

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

Если сервер бота попадёт в плохие руки, заберут личность бота (а с ней возможность выдавать себя за него навсегда: отзыва нет), ключи его каналов и список контактов. Не заберут ничего ни у одного пользователя и ни у одного читателя. Восстановление — уничтожить бота, создать нового и опубликовать его новый отпечаток по другому каналу.

Ничто не скажет вам, что контакт — бот, и что это именно тот бот, за которого он себя выдаёт. Серьёзный оператор публикует код проверки своего бота с подписью на своём сайте; в приложении, в чате, Ещё → Проверить лично → Вставить его код (или отсканировать его QR) проверяет его так же, как человека.

04

Запуск бота

Эта часть — для операторов. Нужен собственный Linux — сервер или 64-битная Raspberry Pi 4 или 5, — Java 17 или новее (sudo apt install openjdk-17-jre-headless), около 300 МБ свободной памяти и выход в Tor. Телефон ботом быть не может: бот должен быть всегда включён и всегда в сети, а это компьютер оператора, не мобильный.

Пакет: privchat-bot-2.19.2.zip (скрипты, библиотеки и нативный arti; Linux x86_64 — Windows только для разработки). Пакет для Raspberry (aarch64) будет опубликован после проверки на настоящей плате, не раньше.

SHA-256 пакета:

18d98f258b9206dd2be9f8e89bd19b18db5fb55eee53ee8fc6e7745573f8f6fe
sudo unzip privchat-bot-2.19.2.zip -d /opt && sudo mv /opt/privchat-bot* /opt/privchat-bot
sudo useradd --system --home /var/lib/privchat-bot --shell /usr/sbin/nologin privchat-bot
sudo install -d -o privchat-bot -g privchat-bot -m 0700 /var/lib/privchat-bot

# Создать бота: печать, личность, prekeys, база. Парольная фраза показывается ОДИН раз.
sudo -u privchat-bot /opt/privchat-bot/bin/privchat-bot --datos /var/lib/privchat-bot init --passphrase-generada

# Парольная фраза — в учётных данных, которые читает только root (никогда в переменной окружения)
sudo install -d -m 0700 /etc/privchat-bot
sudo install -m 0600 /dev/stdin /etc/privchat-bot/passphrase <<< 'фраза-которую-показал-init'

# Служба
sudo cp /opt/privchat-bot/etc/privchat-bot.service /etc/systemd/system/
sudo systemctl enable --now privchat-bot

Парольная фраза — вся защита от того, кто скопирует диск. У сервера нет Keystore телефона, и делать вид, что есть, было бы хуже, чем сказать об этом. Длинная или сгенерированная; с TPM 2.0 — привязанная к машине через systemd-creds encrypt --with-key=tpm2 и LoadCredentialEncrypted= в юните; без TPM — файл 0600 на зашифрованном диске. У бота нет PIN и нет блокировки экрана: перед ним никто не печатает.

05

Параметры

опциячто делаетпо умолчанию
--datos DIRкаталог бота./privchat-bot
--credencial FILEпарольная фраза в файле 0600учётные данные systemd или консоль
--socket PATHUnix-сокет API (короткий путь: AF_UNIX допускает ~100 байт)DIR/api.sock
--api-tcp PORTдополнительно API на 127.0.0.1 с токеномвыключено
--api-grupo GROUPсокет может открывать и эта группа (0660)только пользователь (0600)
--retencion-dias Nсколько дней живёт неподтверждённое входящее сообщение7 (максимум 30)
--max-contactos Nпредел контактов по публичным приглашениям1000
--sin-rellenoвыключает маскирующий трафиквключён, как в приложении
--buzonработает как почтовый ящик владельца: локальная сеть «Лично» вдобавок к Torвыключен
--bluetoothс --buzon — ещё и по Bluetooth LE (только Linux, BlueZ)выключен
--malla ПОРТсеть LoRa: узел Meshtastic по USB (/dev/ttyACM0, COM4) или по TCP (host:4403); тексты уходят по радио, когда Tor не доставляет (бот 2.16.1)выключен
--verbosoподробный журнал, включая журнал Tor. Только для отладкивыключено

Без демона: init, passphrase cambiar, copia-seguridad DEST.zip (чтобы восстановить на другой машине, но никогда — чтобы держать двух одинаковых ботов), restaurar SOURCE.zip и destruir --si. В журнале никогда нет onion-адресов и полных идентификаторов, и ни одно обслуженное чтение канала не оставляет строки.

06

Каналы со стороны бота

B="sudo -u privchat-bot /opt/privchat-bot/bin/privchat-bot --datos /var/lib/privchat-bot"
$B canal crear "Объявления посёлка" "Отключения воды и праздники"   # возвращает canal_id
$B canal invitacion <canal_id> --png /var/www/avisos.png            # текст для вставки, и QR в PNG
$B canal publicar <canal_id> "Завтра нет воды с 9 до 12"
$B canal publicar-foto <canal_id> /path/afisha.jpg "Афиша"          # пережимается: EXIF умирает здесь
$B canal listar                                                     # записи, опросы и отданные байты
$B canal recortar <canal_id> 50                                     # оставить только последние 50

Каждая запись подписана ключом канала, зашифрована его ключом чтения и сцеплена с предыдущей: историю можно перестать отдавать, но нельзя переписать незаметно для читателей. До 12 КБ на текст; фото пережимается, пока не уместится в 900 КБ.

07

Личные контакты

Два вида приглашений, в том же формате, что и в приложении:

личноепубличное (--publica)
кто используетодин человек: первый; второго отклоняюткто угодно, до --max-contactos
одноразовые prekeysпять, как в приложениинет: розданные тысячам, они не были бы одноразовыми
ценаничего новогопервое сообщение каждого пользователя боту остаётся без дополнительной защиты prekey от будущей компрометации ключей бота; ratchet исцеляется, как только бот ответит
для чегопомощник для себя, уведомления для семьибот, открытый для всех
$B invitacion crear web --publica                        # текст для вставки
$B invitacion crear web --publica --gif /var/www/bot.gif  # плюс четыре QR приглашения в анимированном GIF
$B verificacion codigo --png /var/www/verificacion.png    # опубликуйте с подписью: так узнают, что бот — ТОТ САМЫЙ бот
$B mensaje escuchar --reponer                             # события JSON, по одному на строку
$B mensaje enviar <peer_id> "Привет"
$B mensaje enviar <peer_id> "Привет" --retardo 3000      # уйдёт в случайный момент в ближайшие 3 с
$B mensaje confirmar <msg_id>                             # бот забывает сообщение

08

Бот как домашний почтовый ящик

С --buzon бот становится почтовым ящиком одного человека — своего владельца: на Raspberry или домашнем компьютере он хранит и передаёт его сообщения, пока того нет. Тот, кто оказался в этой сети Wi‑Fi, оставляет то, что у него для владельца, и забирает то, что владелец оставил ему; а если владелец включит «С интернетом» на телефоне, его контакты смогут оставлять сообщения и через Tor, пока телефон выключен. В юните systemd добавьте --buzon в ExecStart перед run.

$B buzon invitar                        # текст вставляется в телефоне: Настройки → Мой ящик; первый, кто его использует, — владелец
$B buzon estado                         # владелец и что хранится

Bluetooth (только Linux, по желанию). С --buzon --bluetooth ящик можно найти и по Bluetooth LE: тот, кто проходит мимо двери с включённым для владельца режимом «Поговорить лично», оставляет и забирает сообщения, не подключаясь к Wi‑Fi. Нужны запущенный BlueZ, адаптер, пользователь службы в группе bluetooth и в /etc/bluetooth/main.conf, раздел [General], ReverseServiceDiscovery = false (затем перезапустить службу bluetooth): без этого телефон показывает запрос на сопряжение и канал не открывается. Этот параметр отключает и GATT-клиент BlueZ для других устройств Bluetooth LE, поэтому лучше на отдельном устройстве. По умолчанию выключено: объявление сообщает любому, кто сканирует с улицы, что здесь есть PrivChat, хотя и не чей.

Хранит только сообщения владельца, в запечатанном виде, и ключи дня тех контактов, которым владелец разрешил: никогда не его личность. Если ящик украдут, он ничего не прочитает, а примерно через 48 часов перестанет кого‑либо узнавать; отвязать его можно с телефона. Его адрес .onion постоянен с первого запуска, даже если у него никогда не было интернета. Как это выглядит в телефоне — в руководстве.

09

Программирование бота: локальный API

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

→ {"id": 1, "op": "estado"}
← {"id": 1, "ok": true, "peer_id": "…", "huella": "…", "onion": "….onion", "tor": "online", "contactos": 3, "canales": 1}
→ {"id": 2, "op": "canal.publicar", "canal_id": "…", "texto": "Доброе утро"}
← {"id": 2, "ok": true, "seq": 42}
← {"evento": "mensaje.recibido", "peer_id": "…", "msg_id": "…", "texto": "привет", "fecha": 1788500000}
операцияпараметрывозвращает
estado—личность, onion, состояние Tor, счётчики
suscribirreponereventos[] с неподтверждённым; затем события вживую
invitacion.crearetiqueta, publica, qrinvite_id, texto, qr_gif
contacto.listar / renombrar / borrarpeer_id…—
verificacion.codigo / comprobarpeer_id, codigotexto и QR / resultado
mensaje.enviarpeer_id, texto, respuesta_a, retardo_msmsg_id
adjunto.enviar / adjunto.leerpeer_id, nombre, base64 или ruta / msg_idmsg_id / байты
mensaje.estado / mensaje.confirmarmsg_idсостояние / — (удаляется)
canal.crear / invitacion / publicar / publicar_foto / recortar / borrarcanal_id, texto, pie…seq, texto и QR…

События: mensaje.recibido, adjunto.recibido, contacto.nuevo, entregado, tor. Они доставляются как минимум один раз: сообщение повторяется при каждом suscribir, пока вы его не подтвердите, а msg_id позволяет убрать дубли. Ошибки несут стабильный код (sin_sesion, invitacion_invalida, demasiado_grande…); внутренняя ошибка никогда не передаёт подробностей клиенту. Имена операций и полей — на испанском, языке проекта: это стабильный контракт, а не проза.

Целый бот-эхо на Python, без зависимостей:

import json, socket
s = socket.socket(socket.AF_UNIX); s.connect("/var/lib/privchat-bot/api.sock")
f = s.makefile("rw", encoding="utf-8")
def call(**p):
    f.write(json.dumps(p) + "\n"); f.flush(); return json.loads(f.readline())
r = call(id=1, op="suscribir", reponer=True)
for ev in list(r["eventos"]) + [json.loads(l) for l in f]:
    if ev.get("evento") == "mensaje.recibido":
        call(id=2, op="mensaje.enviar", peer_id=ev["peer_id"], texto="эхо: " + ev["texto"], respuesta_a=ev["msg_id"])
        call(id=3, op="mensaje.confirmar", msg_id=ev["msg_id"])

С пакетом идут два полных примера: eco.py (отделяет ответы от событий, которые с ними пересекаются, — так и нужно делать, как только бот отвечает, продолжая слушать) и rss_a_canal.py, который публикует в канал каждую новую запись ленты RSS или Atom.

10

Правила, которые бот должен соблюдать

  • Подтверждайте то, что обработали; неподтверждённое повторяется и через семь дней удаляется.
  • Не скачивайте то, на что вам дали ссылку: бота, которого можно заставить запросить любой URL, кто-нибудь для этого использует.
  • Считайте текст данными: он приходит от незнакомцев.
  • Пределы: 12 КБ на текст канала, 7,5 МБ на вложение, 900 КБ на фото канала, 16 МБ на строку API.
  • Масштаб: сотни личных контактов — да; тысячи — через канал.
  • Onion, который никогда не выключается, проследить до его сторожевого узла легче, чем телефон; поэтому бот работает с vanguards, и поэтому его не стоит запускать рядом с тем, что вы не хотели бы с ним связывать.

11

Частые проблемы

симптомвероятная причиначто делать
tor: arrancando в течение минутнет сети, или антивирус либо прокси перехватывает TLSотключить перехват; --verboso покажет причину от Tor
«Unix domain path too long»путь к сокету длиннее ~100 байт--socket /tmp/pb.sock
первый опрос из приложения ничего не приноситадрес канала ещё не опубликован в Torподождать минуту-две и Обновить
сообщение какое-то время остаётся «в очереди»цепочка rendezvous строится не сразуподождать; бот подтверждает, как только получит
contacto listar не показывает того, кто только что добавил ботабот узнаёт о контакте по его первому сообщениюпусть что-нибудь напишет
sin_sesion при отправкеэтот peer_id ещё не контактcontacto listar