From 1cfafc43a48507cd4f8705d44bad2a751f5fdeee Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 27 Oct 2026 00:54:36 +0100 Subject: [PATCH] docs(dm): complete mailbox and storage coordinator contracts --- src/dm/dm_mailbox.h | 14 +++++++++++--- src/dm/dm_mailbox_media.h | 14 +++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/dm/dm_mailbox.h b/src/dm/dm_mailbox.h index 6bcba55d..517a0dab 100644 --- a/src/dm/dm_mailbox.h +++ b/src/dm/dm_mailbox.h @@ -6,7 +6,13 @@ * Копия удаляется только по квитанции получателя после commit в dm_core. * Квитанция сохраняется отдельно для повторов PUT и доставки отправителю. * - * Сервис ETCP_RT_ID_DM_MAILBOX (0x35); контрольные пакеты подписаны router. + * Роль S — supernode=yes в подписанных adm_tags общей CHAT-группы A/B; storage её не заменяет. + * При PUT от A в одной транзакции фиксируются сообщение и, для file, задание dm_mailbox_media. + * Сервис ETCP_RT_ID_DM_MAILBOX (0x35); все пакеты подписаны router, числа native-endian: + * PUT=1 [recipient:8][message]; DELIVER=2 [message]; RECEIPT=3 [receipt:136]; STORED=4 [conv:8][seq:8]. + * STORED подтверждает запись на S, только подписанная B квитанция подтверждает доставку сообщения. + * Текст не имеет TTL. Таймер 1с обрабатывает до 32 задач с retry_at через 5с. + * Входные буферы заимствуются на время вызова; их копией в пакете/SQLite владеет соответствующий owner. */ #ifndef DM_MAILBOX_H @@ -37,10 +43,12 @@ void dm_mailbox_destroy(struct UTUN_INSTANCE* inst); int dm_mailbox_put(struct UTUN_INSTANCE* inst, uint64_t recipient, uint64_t sender, const uint8_t* msg, size_t msg_len); -/* Передать точную квитанцию всем доступным суперузлам общей группы сторон. +/* Попытаться передать точную квитанцию всем доступным суперузлам общих групп сторон. * receipt — DM_RECEIPT_SIZE байт (см. dm_core.h), проверяется подпись получателя. * Это попытка отправки текущим доступным S; собственную durable очередь пересылки вызов не создаёт. - * Источник продолжает повторять PUT до получения этой квитанции. */ + * Источник продолжает повторять PUT до получения этой квитанции. + * На S сохранение receipt и удаление точного pending атомарны; повтор PUT возвращает receipt. + * Локальный S здесь пропускается; вызов нужен endpoint, а не для локального применения на S. */ void dm_mailbox_receipt(struct UTUN_INSTANCE* inst, const uint8_t* receipt); #ifdef __cplusplus diff --git a/src/dm/dm_mailbox_media.h b/src/dm/dm_mailbox_media.h index 50d379fa..30d41da2 100644 --- a/src/dm/dm_mailbox_media.h +++ b/src/dm/dm_mailbox_media.h @@ -2,6 +2,11 @@ * метаданных получателю и освобождение назначений по DELETE_ACK/EXPIRED либо подтверждённому TTL. * Жизненный цикл принадлежит dm_mailbox, control принимает dm_media. Все API в uasync. * Координатор хранится в dm_media_jobs/dm_media_holders SQLite ядра; таймер — только триггер повторов. + * Таймер 1с обрабатывает до 8 задач, retry_at через 5с; выбранный M сохраняется до STORE. + * Выбор M: storage=yes в общей группе A/B, достижимый узел по node_id, кроме A/B и прежних назначений. + * S может одновременно быть M: local control проходит тот же обработчик без router-соединения к себе. + * SQLite и группы принадлежат ядру; instance должен пережить owner. Входные тела заимствованы до возврата. + * Из C++ этот внутренний C API включать внутри extern "C". */ #ifndef DM_MAILBOX_MEDIA_H #define DM_MAILBOX_MEDIA_H @@ -17,21 +22,24 @@ struct dm_mb_media* dm_mailbox_media_create(struct UTUN_INSTANCE* inst); /* Отменить таймер и освободить owner, сохранив задания/назначения в БД. NULL допустим. */ void dm_mailbox_media_destroy(struct dm_mb_media* media); /* Вызвать в транзакции PUT после проверки роли S, получателя и подписи полного msg/len. - * 0=не file либо зарегистрирован точный body/UUID, -1=ошибка/конфликт. Не отправляет STORE. + * 0=не file либо зарегистрирован точный SHA256 полного body для UUID, -1=ошибка/конфликт. Не отправляет STORE. * media обязателен для file; commit/rollback принадлежит вызывающему. */ int dm_mailbox_media_register(struct dm_mb_media* media, uint64_t group, uint64_t recipient, const uint8_t* msg, size_t len); /* После проверки router-подписи и размера команды в dm_media: group/source — доверенные поля router. * body заимствован на время вызова; RECEIPT=152, STORED=24, DELETE_ACK/EXPIRED=16, REJECT=17 байт. + * RECEIPT имеет формат dm_media.h; STORED=[UUID:16][expires:8], ACK/EXPIRED=[UUID:16], REJECT=[UUID:16][reason:1]. * STORED/REJECT/ACK относятся к сохранённому назначению M; RECEIPT проверяется отдельно подписью B. * EXPIRED/REJECT атомарно освобождают назначение и сбрасывают текущий holder; ошибка откатывает оба изменения. * Таймер завершает stored-назначение по expires даже при offline B/потерянном EXPIRED, также после restart. - * После квитанции тело удаляется только при отсутствии неосвобождённых назначений; ошибка сканирования откладывает очистку. + * После квитанции повторяется DELETE до 16 неосвобождённым M за проход cleanup. + * Тело удаляется только при отсутствии неосвобождённых назначений; ошибка сканирования откладывает очистку. * Для неподтверждённого STORE повтор запрашивает результат из custody/tombstone M; квитанция B не отменяется. */ void dm_mailbox_media_recv(struct dm_mb_media* media, uint64_t group, uint64_t source, uint8_t cmd, const uint8_t* body, size_t len); /* Проверить 152-байтовую квитанцию и попытаться отправить доступным S общей группы. - * За один вызов выбирается до64 подходящих S на группу; поздние копии также получают подтверждение. */ + * За один вызов выбирается до 64 подходящих S на группу, включая локальный S; A/B исключаются. + * Это попытка отправки; durable повтор обеспечивает квитанция в dm_files/dm_media_jobs, а не сам вызов. */ void dm_mailbox_media_receipt(struct UTUN_INSTANCE* inst, const uint8_t* receipt); #endif