# OMEMO (XEP-0384) — проверка и публикация ключей (проверено 2026-08-28, nixg.ru) ## Главное: серверный модуль НЕ нужен OMEMO полностью работает на клиентах. Prosody хранит ключи/девайс-листы через PEP (`pep` в modules_enabled — уже включён). Если PEP включён — OMEMO работает для любого клиента (Gajim, Conversations, Dino, Converse). Серверного `mod_omemo` в prosody-modules НЕТ и он не нужен (это норма, не потеря). ## Converse v14 (веб-чат) - OMEMO встроен: в dist лежат `libomemo.esm.min.js` + `curve25519_compiled.wasm` (проверить: `grep -c omemo converse.min.js` > 0, есть `eu.siacs.conversations.axolotl`). - ЕДИНСТВЕННАЯ опция в initialize(): `omemo_default: true` (default false) — «шифровать по умолчанию, когда контакт поддерживает». Опции `allow_omemo` в v14 нет (всегда доступен). - При логине с omemo_default:true Converse сам генерит device id и публикует devicelist + bundle в PEP. Проверять НЕ через UI — смотреть на диск. ## Авторитетная проверка — файлы PEP на диске (host bigbox) Каталог данных: `data/` из docker-compose (в контейнере `/var/lib/prosody`), имена узлов URL-encoded: - devicelist: `data//pep_eu%2esiacs%2econversations%2eaxolotl%2edevicelist/admin.list` → grep `["id"] = "N"` — все устройства пользователя (например 4040, 14035). - bundle: `data//pep_eu%2esiacs%2econversations%2eaxolotl%2ebundles%3a/admin.list` → должен содержать `identityKey`, 100× `preKeyPublic` (preKeyId 0..99), `signedPreKeyPublic` + `signedPreKeySignature`, имя `bundle`. - : nixg.ru → `nixg%2eru` (старый chin: `chat%2enixg%2eru`). ## slixmpp 1.17: publish в PEP-узел (рабочий рецепт) Подключение по WebSocket, паттерн 1.17 (нет `.process()`, `connect` возвращает Future): ```python import asyncio, ssl, slixmpp from slixmpp.xmlstream import ET ctx = ssl.create_default_context(); ctx.check_hostname=False; ctx.verify_mode=ssl.CERT_NONE bot = slixmpp.ClientXMPP('user@domain', 'pass') bot.ssl_context = ctx bot.register_plugin('xep_0060') bot.add_event_handler('session_start', ...) # в handler: send_presence(); await get_roster() fut = bot.connect(('wss://xmpp.example.com/xmpp-websocket',)) await asyncio.wait_for(fut, 20); await asyncio.sleep(6) # дать событиям отработать ``` Publish (payload — СЫРОЙ lxml-элемент, не высокоуровневый API!): ```python lst = ET.Element('{eu.siacs.conversations.axolotl}list') ET.SubElement(lst, '{eu.siacs.conversations.axolotl}device', {'id': '7777'}) await bot['xep_0060'].publish(jid=JID, node='eu.siacs.conversations.axolotl.devicelist', id='current', payload=lst) ``` Засады: - `iq['pubsub']['publish']['item']...` + `ElementBase.append('list', {'xmlns':...})` падает: `TypeError: ElementBase.append() takes 2 positional arguments but 3 were given`. - `get_items` может вернуть 0 items даже когда узел НЕ пуст (семантика доступа PEP) — диск `admin.list` авторитетнее. Проверять publish через чтение файла. ## Проверка веб-клиента headless (camofox) - `camofox-browser open https://chat...` затем `eval` с `JSON.stringify(...)` — иначе возвращается только `resultType`, без значения. - Форма входа: `input[name=jid]`, `input[name=password]`, чекбокс `trusted`; submit-кнопка `button.btn-primary` (текст «Войти»). Заполнять через `el.value=...; el.dispatchEvent(new Event('input',{bubbles:true}))`, потом click. - После входа в DOM: `document.body.innerText` содержит «Я на связи». В DOM появляется строка «OMEMO» (кнопка в чате). - Новый device от сессии виден в devicelist admin.list + каталог bundles:. ## Очистка/удаление своего устройства из devicelist Republish devicelist с оставшимися id (паттерн выше), id='current' — примет, тестовый id исчезает из admin.list. ## Сопутствующее - `omemo_default` в доке Converse: https://conversejs.org/docs/configuration/ (redirect с /docs/html/...). Единственная omemo-опция. - Converse api.omemo недоступен из window.converse — проверять по диску, не по JS API.