From 1634499d84db82de72908654e010214a3cb234dc Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 26 Oct 2026 14:30:13 +0100 Subject: [PATCH] Document active feedback, credential dependencies and permission round lifecycle --- src/chat/member_sync_doc.md | 23 ++++++++++++++++----- src/chat/merkle_sync_doc.md | 41 +++++++++++++++++++++++++++---------- 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/src/chat/member_sync_doc.md b/src/chat/member_sync_doc.md index 92e69365..2c475c91 100644 --- a/src/chat/member_sync_doc.md +++ b/src/chat/member_sync_doc.md @@ -4,7 +4,7 @@ Транспортный движок не занимается подписями и версиями участников. Аудит 26.10.2026 частичный: реализации member_sync/merkle_sync/merkle_tree прочитаны, -найденный дефект очистки исправлен по уточнённому плану. Полный аудит остальных +найденные дефекты очистки и загрузки feedback исправлены. Полный аудит остальных контрактов чата не завершён. Подробности и результаты проверок: [отчёт](../../doc/member_merkle_audit_findings.txt). @@ -27,20 +27,28 @@ Уведомления GUI/топологии и changed выполняются после commit. Каждый member_sync_apply_record также имеет savepoint, поэтому ошибка пересчёта не оставляет запись без индекса. При внешней транзакции вызывающая сторона после commit вызывает -member_sync_record_committed для каждой изменённой записи: одного changed недостаточно +member_sync_record_committed с полной маской результата для каждой изменённой записи: одного changed недостаточно для уведомления свойств и планировщика группы. Локальный put доверяет блоку участника и подписи приглашения; подпись непустого блока владельца проверяется также локально. +MS_APPLY_FEEDBACK отмечает изменение удостоверения, trusted key или deleted, влияющее на feedback. +Такое изменение и пересчёт зависимых feedback-листьев во всех каналах входят в транзакцию MEMBERS. +Ошибка пересчёта откатывает запись, общий ключ и оба индекса; сохранённый feedback не удаляется. +Обычное обновление имени/профиля/stream options не выставляет этот бит и не запускает feedback-раунды. +При обработке страницы маска сохраняется до внешнего commit; JOIN также передаёт полный результат. +После commit разрешённые этим битом уведомления будят feedback и обновляют адресные результаты. + verify_and_purge владеет отдельной транзакцией канала: требует autocommit на входе, открывает BEGIN IMMEDIATE до чтения, проверяет записи и размеры BLOB/строк, сохраняет собственный список повреждённых node_id и закрывает SELECT. -Удаление строк и пересчёт их Merkle-листьев выполняются до общего COMMIT. +Удаление строк, пересчёт MEMBERS и зависимых feedback-листьев выполняются до общего COMMIT. +Если FEEDBACK ещё не зарегистрирован при startup purge, его индекс позднее строит chat_feedback_init. При отказе чтения/проверки/памяти/SQL/индекса/commit выполняется rollback; ошибка возвращается как -1 без публикации удаления. Чужая транзакция отклоняется до изменений. Если SQLite не может выполнить rollback и транзакция остаётся открытой, ошибка явно логируется и сбрасывается inst.running: требуется перезапуск приложения. -После commit очистка уведомляет Merkle, снимает mute, удаляет соединение из конкретной +После commit очистка уведомляет оба набора Merkle и затронутые результаты, снимает mute, удаляет соединение из конкретной CHAT-группы и уведомляет props-потребителей; общий транспорт других владельцев сохраняется. Результат >=0 — число подтверждённых удалений. Ошибка публикации после commit не означает откат уже сохранённых данных. Props-подписчик может снять подписку @@ -62,7 +70,12 @@ finish проверяет имеющиеся ненулевые подписи рекурсивного обхода цепочки предков нет, отсутствие сертификата само по себе пропускается. validate_pubkey и validate_peer проверяют наличие строки без фильтра source/deleted; допуск CHAT-группы отдельно проверяет topo_node_sqlite_member_allowed. -verified_ed25519 требует source=0/deleted=0, доверенный ключ, join и сертификат приглашения. +author_key требует source=0/deleted=0, доверенный ключ, join и сертификат непосредственного пригласившего. +Возвращает ACTIVE, INACTIVE, PENDING, INVALID или ERROR; выходной ключ меняется только при ACTIVE. +PENDING означает недостающий ключ/сертификат, а не отзыв членства или ошибку SQLite/OpenSSL. +source=0 само по себе не доказывает полный допуск: dependency может разрешиться позднее. +Ключи nodes общие; поступление ключа через другой канал пересматривает авторов с таким node_id/signed_by. +verified_ed25519 сохраняет строгий контракт 0/-1 для radio/attachments и разрешает только ACTIVE. ## Канонический хеш diff --git a/src/chat/merkle_sync_doc.md b/src/chat/merkle_sync_doc.md index f303c39a..6bb42ea4 100644 --- a/src/chat/merkle_sync_doc.md +++ b/src/chat/merkle_sync_doc.md @@ -1,10 +1,9 @@ # merkle_sync — протокол 3 Аудит 26.10.2026 частичный: движок и адаптер участников прочитаны полностью, -контракты ниже уточнены; найденный дефект очистки member_sync исправлен отдельно. -Разделы feedback/опросов целиком не пересверены в этом аудите. -Продолжение остановлено на подтверждённом отказе запуска feedback после исключения -автора сохранённого состояния. Исправление очистки завершено; новый дефект описан в разделе7 отчёта. +контракты ниже уточнены; дефекты очистки member_sync и запуска feedback исправлены. +Для feedback проверены исключение/возврат, отложенные ключи, атомарность и сетевая сходимость. +Это не завершает аудит всех контрактов чата, db_sync и внешних callers. Результаты: [отчёт](../../doc/member_merkle_audit_findings.txt). ## Разделение ответственности @@ -91,6 +90,11 @@ WAKE также перезапускает незавершённый раунд Истечение завершает ожидания MT_ERR_TIMEOUT; по одному таймеру на активную сессию. Периодического bg_check и отдельного WAL-таймера в текущем merkle_sync нет. +При отзыве допуска модели revalidate_peers завершает ожидания этого пира MT_ERR_DATA, +освобождает подготовленный пакет/таймер/waiter, но сохраняет подписку в FAILED. +После восстановления changed запускает новый раунд: ждать ответа на ранее отброшенный пакет не нужно. +Вызовы выполняются после commit; done callback может отменить сессию без use-after-free. + ## Wire Заголовок: `service:1 | namespace:8 | type:1 | round:8 | request:4 | dataset:1` (23 байта). @@ -140,12 +144,24 @@ ver положителен, максимум INT64_MAX. Меньшая верс Wire-запись: message_id:32, node_id:8, kind:1, ver:8, data_len:2, data, signature:64. Страница: count:2 и count записей; все числа big-endian, data максимум 2048 байт. Хеш листа включает канонические тела без signature: разные корректные подписи одного тела -не создают расхождения корней. Входящие и сохранённые подписи всё равно проверяются. +не создают расхождения корней. Подписи проверяются перед включением записи в активный набор. Членство проверяется через подписи дерева приглашений и join, привязывающий Ed25519 к узлу. -Текущий member_sync_verified_ed25519 также требует deleted=0. Его повторное применение -ко всем сохранённым feedback при init вызывает отказ запуска после исключения автора; -собственное членство узла и другие каналы могут оставаться корректными. -Контракт исторических состояний после исключения ещё требует согласования. +Для init/hash/page используется общий отбор: только author_key=ACTIVE, с повторной проверкой +подписанного состояния и его record_key/bucket. INACTIVE/PENDING/INVALID не включаются в набор. +SQLite/OpenSSL-ошибки не кодируются пустым листом или отсутствующим автором. +Исключённый/отсутствующий автор и ожидающий ключ сохраняются в БД с прежними ver и отменами, +не блокируют startup, но не входят в корень, выдачу и результаты. После возврата они проверяются заново. +Новые состояния INACTIVE/PENDING не сохраняются; такая запись устаревшей страницы +не мешает применению остальных корректных записей. Локальное действие требует ACTIVE даже при no-op. + +Изменение удостоверения/deleted/общего ключа атомарно пересчитывает feedback автора и записей, +зависящих от его ключа как signed_by, во всех каналах. Отложенный ключ не блокирует пачку MEMBERS; +его поступление, включая другой канал, включает ранее сохранённые допустимые состояния. +Отдельного сохраняемого флага активности нет: проверка принадлежит member_sync. +После внешнего commit обновляются сессии и адресные FEEDBACK_CHANGED; профиль без изменения +удостоверения/допуска не требует такого пересчёта и уведомлений. +Формат не содержит подписанной границы отзыва: после восстановления применяются обычные ver/data, +без утверждения, что подпись была создана до исключения. Слой feedback хранит подписанные байты независимо от наличия исходного сообщения; семантику выбранных пунктов и эмодзи проверяет вызывающий модуль до учёта результата. @@ -181,5 +197,8 @@ chat_feedback_command в потоке ядра. Desktop и Android получа это нужно и при restart поверх сохранившегося UP-транспорта, когда нового conn_up не будет. test_chat_poll проверяет пользовательский API, кодеки, ограничения, отмены и отложенный опрос. -test_chat_poll_network проверяет A ↔ B ↔ C, собственный Unicode-ответ, реакцию и восстановление C -после остановки чата без возвращения отменённых состояний. +test_chat_poll_network проверяет A ↔ B ↔ C, Unicode-ответ, реакцию, остановку/запуск C, +исключение A, позднее подключение D без неактивной истории и возврат A. +После возврата D получает сохранённые состояния, A передаёт новую отмену через прежний UP-транспорт; +результаты и корни активного набора сходятся. test_chat_feedback проверяет также purge, +межканальную зависимость ключа, отсутствие уведомлений от профиля и rollback SQL/index/commit.