- 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
Root cause: instance_lite_restart was creating new thread without
re-setting g_event_handler, causing all chat events (CHANNEL_UPDATED,
CONNECT_RESULT, etc.) to be silently dropped after app restart.
Fixes:
- instance_lite_restart: save/restore g_event_handler across hard restart
- instance_thread restart loop: re-call chat_event_set_handler after uasync_create
- utun_bridge_restart: call instance_lite_set_event_handler before restart
- AppEventHandler SharedFlow replay: 1 → 16 to not lose early init events
- ViewModel: added diagnostic logging for channel/connect events
- Keep diagnostic logs in instance_lite (set_handler, chat_event_forward NULL check)
- instance_event_handler: removed static for cross-block forward declaration
Android: file picker → pending preview bar (name+size+X) →
combined send with text; cache copy, save dialog for downloads
Desktop: attach stores pending, preview bar before send,
combined with text in onSendClicked
C: attachment_sender.c — include filename in msg data for display
- 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_core.c: убрана запись node_name из cm_handle_invite_info_resp
- topo_node_sqlite.c: name в nodes не перезаписывается пустой строкой
- android_jni_bridge.c: bridge_get_node_name читает peers_<ch>.userinfo если nodes.name пуст
node_conn_direct:
- ncd_init_cb: skip UP when links_up==0, ncd_up_cb delivers when link comes up
- close/force_close: handle_count safety checks, callback clearing
- open/open_node case 2: create links on reused dead conn
- ncd_create_links: skip duplicate link on same conn (stale->etcp==conn)
- set_callback: optional callback change on live handle
conn_mgr:
- cm_ncd_callback: DOWN only delivers event (handle stays alive)
- cm_ncd_callback: TIMEOUT force_close BEFORE cm_deliver_event
- cm_deliver_event: read handle fields to locals before callback (reentrany-safe)
- INVITE_INFO_REQ/RESP: add duplicate cmd byte for direct-send protocol
- cm_handle_invite_info_resp: search invite by group_id in channel conn_mgr
Android:
- node_id computed by sc_derive_node_id_from_pubkey in C, not Kotlin
- utun_bridge_join_channel: recompute node_id, log mismatch
- invite_link_c: compute nodeId from pubkey via sc_derive_node_id_from_pubkey
- NativeLib.deriveNodeId, InviteLink.kt uses C function
- voice_recorder: waveform_levels[100] accumulated during feed, normalized to 100 values in stop()
stored as 'wf=...;dur=...;' in chat_msg_submit data
- bridge: filePath in get_messages_json from local_attrs.fp, g_db_path moved global
- Message model: waveform: List<Float>, voiceDurationMs: Int, filePath: String
- VoiceBubble: Canvas waveform (100 bars, log scale), play button, duration
- ChatViewModel.playVoiceMessage → AudioRecorderManager.playVoiceFile
g_cc.db_path is '/data/.../files/chats.db' (file path), but media blocks
are written to '/data/.../files/media/<channel>/'. chat_msg was concatenating
'chats.db/media/...' — now strips the database filename, using only the
directory portion for the media path.
- voice_recorder: proper mkdir with errno/EEXIST check, errno logged on fopen failure
- MessageBubble: pointerInput wrapped in while(true) so PTT works after first gesture
- PTT Box: same composable identity in idle/recording states (just changes bg color)
Gesture coroutine survives recomposition → onPttReleased() fires on finger lift.
When locked, Box is replaced by cancel/send IconButtons.
- Min voice duration: 500→1000ms (C and ViewModel)
- audio_compressor.h/c moved to lib/ (alongside opus_codec, mem, debug_config)
- lib/Makefile.am: added to libuasync_a_SOURCES
- utun_sources.cmake: removed from _cfg_src (GLOB from lib/ picks it up)
- tests/Makefile.am: test_audio_compressor added to check_PROGRAMS
- test includes simplified (debug_config.h, mem.h — lib/ in include path)
- voice_recorder: compressor config API (max_gain, lookback, lookahead, rise_rate, target_level)
- bridge/JNI: compressor_config export
- AudioRecorderManager: startMicTest/stopMicTest for level preview
- SettingsScreen: TabRow with Settings + Audio tabs
- Audio tab: mic test with live level bar, Opus preset selector, 5 compressor sliders matching desktop
- instance_lite: cleanup moved to uasync thread (self-detach), events on start/stop
- udp_log: cancel_timer before uasync_destroy, flush on stop, stopping flag
- jni_bridge: fixed UDP stop order (cancel_timer → destroy → flush)
- instance_lite: u_report_unfreed_blocks at all shutdown paths
- chat_event: CHAT_EVT_SERVICE_STARTED/STOPPED for GUI notifications
- AGENTS: logreceiver section
- instance_lite.h/c: full uTun stack from INI config text
(keygen, uasync, UTUN_INSTANCE, SQLite, chat_core/sync, BGP, etcp_router)
- jni_bridge: nativeStart/Stop, nativeJoinChannel → chat_sync_connect_from_invite,
nativeSubscribeLogs, C logs → logcat + Kotlin LogManager
- Kotlin: direct read-only SQLite queries (channels, messages),
ViewModel with live event callbacks, reactive LogScreen (tail -f),
CameraX QR scanner with proper lifecycle, JoinChannelDialog
- HeadlessService: generates INI config from Kotlin settings → C instance