Новый модуль src/radio (radio/radio_audio/radio_headless) — push-to-talk
радио поверх topo-групп, доставка аудио в канал, headless-режим и тесты.
Интеграция в chatgui (ptt_hotkey_manager, radio_audio_engine, радионастройки,
вызовы) и chatgui-android (RadioAudioEngine, RadioOverlayService, UI).
conn_mgr: src node_id вместо conn->peer_node_id, препенд ETCP_RT_ID_CONN_MGR к dgram, bind CONN_MGR после etcp_router_init
call: guard CALL_ST_CONNECTING при accept
- 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-тесты классификации и сопоставления
- topo_node: сокет рекламирует INTERFACE (до NAT) + NAT (после NAT, EIM);
strict/direct отдельным адресом не передаются
- topo_node_sqlite: addr_type_classify различает NAT_VERIFIED_*; добавлен
ADDR_TYPE_NAT (тип неизвестен), fallback больше не даёт DIRECT по IP
- chat_member: бейдж NAT-типа по приоритету (public>nat_eim>nat>nat_strict>local)
вместо MIN(addr_type)
- GUI (desktop+android): адреса мембера с socket_id и ролью (local/public/nat/nat_eim/nat_strict)
Полностью убрана фича периодического обмена CSV-метриками с Ed25519-подписью:
- ETCP_SECTION_METRICS и парсинг/верификация секции
- struct etcp_metrics, etcp_metrics_add_*/start/stop и снапшот-таймер
- мёртвый API on_metrics_rcvd/metrics_user_ptr (никто не подписан)
- link->remote_ed25519_pubkey (заполнялся только на клиенте → верификация на сервере всегда падала)
- stats_dir в UTUN_INSTANCE
Общий sc_stream_sign_* (деривация Ed25519 из X25519) и INIT-передача обоих ключей сохранены.
Добавлен 64-битный reset_id (эпоха ресета) в INIT-фреймы и STCP-handshake:
- генерится один раз при создании conn, меняется только при фатальном ресете
(normalizer desync / bad fragment size);
- при приёме чужого id slave принимает эпоху пира и ресетится, master держит свою;
- первый id принимается без ресета (свежие при init);
- «tcp peer clean» реинит гейтится по смене id — нет повторного сброса seq.
Это устраняет коллизию seq=1 (RX dup) из-за двойного реинита нескольких TCP-линков,
из-за которой NODEINFO пира терялся и member не возвращался online.
Также: chat member online теперь из topo_group (без БД/merkle), тест test_etcp_seq_collision.
topo_node_update_my_addresses теперь собирает адреса во временные списки,
сравнивает со старыми и при отсутствии изменений выходит без бампа ver,
записи в БД и broadcast. Гейт member_exists убран: self-адреса пишутся
в node_addresses при любом запуске/изменении (с гарантией строки nodes для FK).
addrs_put логирует ошибки INSERT.
- Добавлен device_type (1 байт, CLIENT_TYPE_*) в INIT_REQUEST и INIT_RESPONSE
- Добавлен keepalive_interval (2 байта) в INIT_RESPONSE
- STCP handshake расширен с 43 до 46 байт (device_type+keepalive)
- negotiate_keepalive(): оба desktop/server → min, иначе (есть mobile) → max
- keepalive_interval вынесен в UTUN_INSTANCE для использования в handshake
- stcp_client_connect() принимает device_type и keepalive_interval
- peer_device_type/peer_keepalive_interval передаются через stcp_link в ETCP_LINK
- u_async.c: process immediate_queue BEFORE epoll_wait in uasync_poll
- stcp_link.c: defensive NULL etcp_conn/etcp_link in stcp_link_close
- utun_instance.c: move stcp_server_list_destroy_all to Phase J (before ETCP),
add deferred drain in both Phase J and L
- stcp.h/c: STCP_HS_ENC_CLIENT 6→38, STCP_HS_ENC_SERVER 7→39,
exchange ed25519_pubkey in both handshake directions
- etcp_connections.c: copy peer_ed25519 from STCP to ETCP_CONN in
etcp_link_enter_ready_tcp; guard link->etcp in tcp_link_close_cb
- chat_sync.c: fix lk->conn NULL dereference for TCP links
- test_stcp.c: update stcp_server_create/stcp_client_connect callsites
- tools/chat_tcp_test: change transport udp→tcp
- auto_socket.c/h: создание/удаление UDP сокетов автоматически при изменении интерфейсов через netlink/route socket
При auto_sockets=yes пропускает ручные [server] сокеты
Тип сокета: PUBLIC если публичный IP, иначе NAT
Для новых сокетов добавляются линки ко всем активным NCD-соединениям
При удалении интерфейса линки убираются, сокет закрывается
Platform-agnostic API для использования из Android/chatgui
- node_conn_direct.h/c: ncd_add_socket_links / ncd_remove_socket_links — управление линками на конкретном сокете
- socket_monitor.h/c: экспорт socket_monitor_update_if_addr для auto_socket
- config_parser.h/c: опция auto_sockets в [global]
- init_sockets(): пропуск ручных сокетов при auto_sockets=yes
- utun_instance: интеграция auto_socket_init/destroy
- media_delivery: fix stack-buffer-overflow ack[3] → ack[9]
- topo_node: fix heap-use-after-free в topo_group_update_my_nodeinfo — сохранение node_id/ver перед registry_store, использование return value
- member_sync apply_items: empty payload is normal sync completion, not error
- topo_node_sqlite node_load: no addresses is normal for stub/seed nodes
- conn_mgr_core: node skipped when not in routing (normal for nodes w/o addrs)
- topo_group send_nodeinfo: downgrade ERROR→WARN in DEBUG category
- db_sync INIT_RESP: protocol details belong in DEBUG, not INFO
- stcp_server_create: support AF_INET and AF_INET6 V6ONLY=1 (no dual-stack)
- Add listen_family to stcp_link_config
- init_sockets: return error (not partial) on any bind failure
- add chat_core_sync_my_addresses() call at chatgui startup
- collect_my_addrs: add proto byte (1=UDP, 2=TCP) to binary format
- chat_sync.c JOIN/WELCOME/PEER_UPSERT: read proto byte, write protocol column
- member_sync.c: read proto byte, dynamic protocol bind
- topo_node.c: debug logs for nodeinfo changed detection
- node_config.cpp: type=public for all servers
- CONN_MGR entries: replace dynamic array with ll_queue (hash=256, key_size=8)
cm_find_entry now O(1) via queue_find_data_by_index
cm_ensure_entry uses queue_entry_new + queue_data_put_with_index
cm_entry_cleanup removes from queue and frees entry
- TOPO_GROUP_NODE: replace handles** array with single CONN_MGR_HANDLE*
remove topo_node_handle_register/unregister/count — dead indirection
nq->handle = h / nq->handle = NULL in all call sites
- TOPO_GROUP_NODE: remove dead fields dirty, best_socket, tranzit_data/count
dirty replaced with local variable in nat_detection.c
tranzit_data removed from wire format (TOPOMSG_NODE), serialize, deserialize
update struct layout: one field per line with comments
- New files: transport_layer/socket_monitor.c/h — socket state monitoring with per-instance ctx
- etcp_api: expose get_latency/1s_pkt_count measuring, add etcp_hashrnd for tests
- utun_instance: store socket_monitor per instance, add is_socket_ok check
- topo_node: detect IPv4 addresses with port from listening sockets
- topo_group: use node_name for display in routes
- Makefile.am: add socket_monitor.c to build
Android chatgui:
- New MemberListScreen with member list, detail card, edit dialog
- Link status dots (green/red/grey) per address in detail card (1s poll)
- Self-node name coloured green
- ChatScreen: people icon in TopAppBar
IPv6 autoconnect fixes (3 bugs in conn_mgr/topo):
- topo_node_sqlite_node_load: load v6 addresses from DB
- topo_node_registry_store: merge v6_addrs into existing node
- cm_sock_v6_classify: wildcard [::] treated as CM_V6_DIR
- cm_add_v6_link: htons(port) was missing
Memory fixes:
- strdup→u_strdup in bridge (7 instances)
- u_realloc cap update before success check (3 instances)
- char sql[256]→[512] for truncated SELECT
Chat member hash now includes addresses for change detection
Linux chatgui:
- Link status dots in AccountList detail panel (1s poll)
- Self-node name in green in member list
struct TOPO_NODE_REG_ENTRY имел node и ll.data[0] (гибкий массив) на одном смещении.
memcpy(re->ll.data, &node_id, 8) затирало re->node значением node_id,
из-за чего node_registry_find возвращал не указатель а битый node_id → SIGSEGV.
Убрал struct TOPO_NODE_REG_ENTRY, перешёл на явные смещения:
data[0..7]=ключ, data[8..15]=указатель, queue_entry_new(16).
- conn_mgr_init(TOPO_GROUP* group) — работает внутри заданной группы
- conn_mgr_connect_node: загрузка узла из SQLite если нет в BGP-таблице
- topo_node_sqlite_node_load(): загрузка TOPO_NODE (ref=0) из БД
- cm_start_phase_direct: db_loaded-ветка — перебор всех адресов без NAT-фильтра
- cm_cleanup_db_node: удаление TOPO_NODEQ из группы при неудаче, только если нет путей
- cm_start_phase_indirect: исправлено зависание при best_candidates==0 (cm_deliver_result)
- conn_mgr_set_direct_timeout_ms(): конфигурируемый таймаут фазы DIRECT
- conn_mgr_destroy до etcp_router_destroy (исправлен порядок)
- test_conn_mgr: +2 сценария (DB-успех, DB-недоступен), проверка topo_group
- etcp_router: SVC_ROUTE_HDR (+8B), ETCP_ROUTER_CONN/TRANSIT_QUEUE хеши расширены под group_id
- Все caller'ы (proxy, routing, conn_mgr, nat, chatgui, tests) обновлены
- topo_group: глобальный реестр TOPO_NODE по node_id, ref_count=0 при создании
- BGP-обмен активируется для всех групп (а не только дефолтной)
- Протокол: group_id добавлен в WITHDRAW/TABLE_REQ/TABLE_COMPLETE/ERR_GROUP_MISMATCH
- Per-connection коллбэки заменены на instance-level conn_status
- msg_transport полностью удалён из проекта
57/57 тестов
- Remove is_dm column from channels table (dm detection by member count)
- Rename member name column to userinfo — extensible json_flat format
- Add json_flat.c/h library for compact JSON serialization
- Fix join_sig message format: use ch_x25519+ch_ed25519 instead of channel_id string
- Remove name from join_msg body (now in userinfo via json_flat)
- Update chat_core, chat_sync, member_sync, db_manager to new format
- Add topo_strategy.txt — архитектурные заметки по топологии
- CHAT groups don't send subnets and skip route insert/delete
- Group type mismatch sends ERR_GROUP_MISMATCH to peer
- New topo_groups_create_group() public API for creating typed groups