Track visited nodes outside the signed body for all routed packet types. Recheck queued transit routes without losing FIFO priority, reject unknown-group transit, and suppress BGP announcements caused only by RTT jitter.
Document routing rules and wire format. Validate graph churn, REINIT, encrypted transit, malformed paths, loop drops and queue cleanup; full suite: 106 passed, 1 skipped, plus proxy integration and sanitizer checks.
queue_set_callback_defer(q, 1) переносит первый запуск callback из
queue_data_put* в uasync_call_soon (очистка стека производителя).
Повторные запуски (queue_resume_callback) и так всегда deferred.
- db_sync insert-cb gets initial_sync flag (SEND_DATA=1, PUSH/local=0)
- on_msg_inserted skips media download during initial sync
- new chat_media_autodownload_backfill_channel (newest-first) triggered
on SYNC_DONE per channel (2s debounce)
- radio_recv: кэш (src,stream,seq) с TTL 2с гасит кадры, пришедшие по второму пути (diamond/mesh)
- фикс записи за границу new_hops[hop_count] при hop_count==RADIO_MAX_HOPS
- счётчик c_dup_dropped + radio_dup_dropped() для диагностики
- тест test_radio_mesh (4 узла, diamond): C получает ровно N кадров, dup>0
Новый модуль src/radio (radio/radio_audio/radio_headless) — push-to-talk
радио поверх topo-групп, доставка аудио в канал, headless-режим и тесты.
Интеграция в chatgui (ptt_hotkey_manager, radio_audio_engine, радионастройки,
вызовы) и chatgui-android (RadioAudioEngine, RadioOverlayService, UI).
При REQUEST_TABLE для ещё не созданной группы (CHAT-канал не загружен) пир
дропал запрос, и проситель навсегда не узнавал таблицу — односторонняя
видимость (звонок android→linux падал «peer offline»). Теперь запрос
запоминается в pending_table_reqs[32] и выполняется при создании группы
(topo_group_fulfill_table_reqs → handle_request_table).
Попутно: test_chat_join_e2e — критерий успеха через CHAT_EVT_CONNECT_RESULT
(JOIN_READY) вместо senders_list; doxygen-комментарии в etcp_api.h.
ip_is_public() вынесен в lib/platform_compat (единый классификатор IPv4 с ntohl).
auto_socket_reconcile_config: тип сокета выставляется по фактическому адресу.
sock_collect_views: has_priv/has_inet/is_eim — по классификации адреса и NAT-детекции.
is_local_subnet упрощён до !ip_is_public(). Тест test_sock_match обновлён.
Инвайтер добавлял мембера без проверки join-ключа — любой известный узел
мог самопригласиться в канал через REQUEST_FWD напрямую владельцу.
register_key теперь всегда хранит выданный ключ локально,
process_request отклоняет запрос с неизвестным/чужим ключом.
Тесты: unknown join_key rejected + регистрация ключа в t_process_request.
- sock_match: единая классификация сокетов (DIRECT/NAT/LOCAL) и адресов (PUB/PRIV/UNREACH)
- eim приравнен к direct; на приватный адрес — только с приватного интерфейса;
оба EIM за одним NAT (одинаковый публичный IP) — только через LAN
- topo_node: LOCAL-сокеты рекламируются (INTERFACE), private скрыт,
NAT-адрес не рекламируется для strict и при оффлайне
- ntp: флаг reachable по циклам + пере-анонс nodeinfo при смене доступности
- cm_direct_add_links / ncd_create_links / conn_match_candidate_sockets на sock_match
- test_sock_match: unit-тесты классификации и сопоставления