feat(client): report per-protocol presence in HTB and network_monitor

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uc9V1v7iXruubpoLiSE24M
This commit is contained in:
2026-09-21 09:23:33 -04:00
co-authored by Claude Sonnet 5
parent c61a571995
commit 1898bc52e5
4 changed files with 88 additions and 7 deletions
+23 -7
View File
@@ -25,6 +25,7 @@ from .. import __version__
# Import plugin system
from .plugin import PluginRegistry, PluginLoader, InfoPlugin, MonitorPlugin
from .connectivity import Connectivity, set_shared
# Constants
PORT = 50003
@@ -281,7 +282,16 @@ async def handle_update(conn: AsyncConnection, _msg: dict): # pyright: ignore[r
stop()
async def heartbeat_sender(conn: AsyncConnection, interval: int):
def build_heartbeat_msg(conn, interval: int, connectivity: Optional[Connectivity] = None) -> dict:
"""HTB payload; carries p4/p6 presence flags when the probe knows them."""
msg = {"acks": conn.ackcount, "rtt": conn.rtts[-1], "interval": interval}
if connectivity is not None:
msg.update(connectivity.flags())
return msg
async def heartbeat_sender(conn: AsyncConnection, interval: int,
connectivity: Optional[Connectivity] = None):
"""Send periodic heartbeats, retrying the connection if it is not open.
IPv6 connections that fail to open before their first successful send are
@@ -291,6 +301,7 @@ async def heartbeat_sender(conn: AsyncConnection, interval: int):
Args:
conn: Connection to send on
interval: Heartbeat interval in seconds
connectivity: Optional probe whose presence flags are attached to every heartbeat
"""
logger = logging.getLogger("hbc.heartbeat")
IPV6_EARLY_FAIL_LIMIT = 3
@@ -328,11 +339,7 @@ async def heartbeat_sender(conn: AsyncConnection, interval: int):
continue
try:
msg = {
"acks": conn.ackcount,
"rtt": conn.rtts[-1],
"interval": interval
}
msg = build_heartbeat_msg(conn, interval, connectivity)
await conn.sendto(msg, "HTB")
except asyncio.CancelledError:
@@ -620,8 +627,17 @@ async def async_main(args, config):
# Start async tasks
# Heartbeat senders (one per connection)
# Per-protocol internet presence probe (shared with the network_monitor plugin)
net_cfg = dict((config.get("plugins") or {}).get("network_monitor")
or config.get("network_monitor") or {})
connectivity = None
if net_cfg.get("probe_enabled", True):
connectivity = Connectivity.from_config(net_cfg)
set_shared(connectivity)
active_tasks.append(asyncio.create_task(connectivity.run(shutdown_event)))
for conn in connections:
task = asyncio.create_task(heartbeat_sender(conn, interval))
task = asyncio.create_task(heartbeat_sender(conn, interval, connectivity))
active_tasks.append(task)
# Plugin collector (uses all connections, but we'll use first one)