Commit Graph
29 Commits
Author SHA1 Message Date
kamilkrzywda 33a88f3d83 Watchdog tracks decoded frames, not raw bytes; buffer-overflow backstop (v0.21.2)
CI / build + unit (pull_request) Failing after 39s
Second production zombie slipped past v0.21.1's watchdog: text+telemetry RX
dead for >1 h while TX worked and the watchdog never fired. Root cause proven
at runtime via /proc/net/snmp — InSegs delta = 2 segments per 30 s (OutSegs 0):
the bridge peer keeps dribbling small NON-frame bytes (~every 15 s, keepalive
noise or a desynced stream) and v0.21.1 updated lastPacketAt on EVERY socket
chunk, so 'no traffic for MESH_IDLE_TIMEOUT_MS' was never true even though zero
valid Meshtastic frames decoded.

- The 'data' handler no longer updates lastPacketAt per chunk; it is updated
  only when extractFrames yields >=1 valid packet (the reset in the 'connect'
  handler is unchanged), so a stream that decodes nothing trips the watchdog
  after MESH_IDLE_TIMEOUT_MS even while raw bytes keep arriving.
- Buffer-overflow backstop: if the undecoded frame buffer exceeds
  MESH_MAX_FRAME_BUFFER (new env, default 256000) — e.g. one data event of a
  desynced stream that never forms frames — log ERROR and destroy the socket;
  the existing close -> scheduleReconnect loop self-heals it. The undecoded
  buffer is also reset on every (re)connect so leftover garbage from a dead
  session can't re-trigger the backstop or corrupt the first frames of a
  healthy new stream.

Tests: 169 -> 171 — raw non-frame bytes do NOT keep the watchdog alive
(garbage-only stream forces disconnect + reconnect, today's incident shape),
and an undecoded chunk over MESH_MAX_FRAME_BUFFER triggers destroy + a fresh
session that decodes normally (cap env-overridden to 100 since Node delivers
socket data in ~64 KB events, far below the 256_000 default).
2026-09-26 08:50:27 +00:00
kamilkrzywda af1c77fd54 Force reconnect when the bridge connection goes silent (v0.21.1)
CI / build + unit (pull_request) Successful in 1m8s
Production incident: the bridge TCP connection went zombie — outbound sends
kept working for ~10 h while inbound packets stopped entirely, and
/api/status stayed connected:true because nothing detects inbound silence on
an established connection.

- Track lastPacketAt: updated on every chunk received from the bridge in the
  lowest-level socket 'data' path (any frame kind, decodable or not) and reset
  when a connection successfully starts, so a fresh connection can't
  self-trigger.
- Idle watchdog: a 30 s interval checks connected AND now - lastPacketAt >
  MESH_IDLE_TIMEOUT_MS (new env, default 600000 = 10 min) — logs WARN with the
  idle duration and destroys the socket so the EXISTING connect/retry loop
  re-establishes it with a fresh sync. No changes to connect-error retry or
  backoff; this only adds detection for established-but-silent connections.
- TCP keepalive: socket.setKeepAlive(true, 30_000) on the bridge socket —
  cheap insurance against dead peers at the TCP level.

Tests (fake-bridge based): silence past an overridden timeout forces a
disconnect + reconnect; inbound traffic keeps resetting the idle clock so no
false trigger; the default timeout does not fire in a test window; and
setKeepAlive(true, 30_000) is asserted on the socket. checkIdle() is public so
tests drive a tick without waiting for the real interval (like flushHistory()).
2026-09-26 06:50:01 +00:00
kamilkrzywda 9d4c9594af Route command replies through MeshService.respond() — same channel, threaded quote (v0.18.1)
CI / build + unit (pull_request) Successful in 42s
2026-09-25 17:30:28 +00:00
kamilkrzywda 4ae932befe mesh: MESH_NOTIFY_CHANNEL as comma-separated list + ch0 DM-only notify gate; v0.17.0
CI / build + unit (pull_request) Successful in 1m8s
2026-09-25 16:18:07 +00:00
kamilkrzywda e62f831ee5 mesh: in-memory ring of the last 100 messages forwarded to opencode 2026-09-25 15:48:09 +00:00
kamilkrzywda c33f4613b5 mesh: POST /api/respond + MCP respond tool — reply to a message by packet id 2026-09-25 15:32:34 +00:00
kamilkrzywda 88b617f8b7 opencode: notifier wrapper gains the packet id (id <n>) 2026-09-25 15:06:31 +00:00
kamilkrzywda 34e7bfe0bb mesh: persist destination (to) on captured rx history records 2026-09-25 15:02:21 +00:00
kamilkrzywda 5eb1a79e70 mesh: replace notify webhook with in-process opencode notifier; drop forwarder service 2026-09-25 14:14:37 +00:00
kamilkrzywda 8ddd91d4b5 mesh: update node batteryLevel from live NODEINFO_APP packets
battery_level was only populated during the initial sync; NodeInfo
frames streamed as NODEINFO_APP (port 4) now refresh deviceMetrics in
the node database so /api/nodes and downstream consumers see current
values between syncs. decodeNodeInfo + PORT_NODEINFO_APP added to the
codec, one branch in the existing packet-handling path.
2026-09-25 12:39:45 +00:00
kamilkrzywda 15d93ec2a4 Optional replyId on send (API + MCP) — protocol-level threaded replies (#31)
CI / build + unit (push) Successful in 33s
2026-09-25 13:53:47 +02:00
kamilkrzywda 5999159e17 Forwarder: resolve opencode workspace/session dynamically per request (#28)
CI / build + unit (push) Successful in 32s
2026-09-25 12:24:39 +02:00
kamilkrzywda 36021ec2e8 Retry notify webhook POST with backoff (2 s / 10 s / 30 s) before dropping (#27)
CI / build + unit (push) Successful in 1m2s
2026-09-25 11:48:39 +02:00
kamilkrzywda d3d1913b83 Self-echo filter for notify webhook (skip own-node messages) (#22)
CI / build + unit (push) Successful in 43s
2026-09-25 08:51:03 +02:00
kamilkrzywda 3d0e369230 Notify webhook also fires for injected messages (channel match) + compose env passthroughs (#21)
CI / build + unit (push) Successful in 35s
2026-09-25 03:56:53 +02:00
kamilkrzywda 024dabda97 Outbound notification webhook (MESH_NOTIFY_URL) on RX text — issue #17 (#20)
CI / build + unit (push) Successful in 36s
2026-09-25 03:35:50 +02:00
kamilkrzywda 74f3080626 Persist message history to SQLite with 1s poller (#15) (#19)
CI / build + unit (push) Successful in 1m4s
2026-09-25 02:46:17 +02:00
kamilkrzywda 756086d107 Inbox injection endpoint: POST /api/messages + MCP inject_message (#12) (#18)
CI / build + unit (push) Successful in 1m14s
2026-09-25 01:51:50 +02:00
kamilkrzywda 0571c7095e Telemetry stream: GET /api/telemetry + MCP get_telemetry (#16)
CI / build + unit (push) Successful in 21s
2026-09-24 23:56:11 +02:00
kamilkrzywda ba5e60daf0 Fix BigInt serialization in GET /api/device (500 on real hardware) (#14)
CI / build + unit (push) Successful in 18s
2026-09-24 23:14:32 +02:00
kamilkrzywda 4beaa377c4 Device info endpoint + richer node summaries (GET /api/device, MCP device_info) (#13)
CI / build + unit (push) Successful in 36s
2026-09-24 22:46:40 +02:00
kamilkrzywda b31fb35464 MCP server at POST /mcp (official SDK, stateless Streamable HTTP) (#11)
CI / build + unit (push) Successful in 1m6s
2026-09-24 20:39:45 +02:00
kamilkrzywda 108aa400d3 Publish on 'test' channel (index 3) by default (#10)
CI / build + unit (push) Successful in 34s
2026-09-24 19:44:11 +02:00
kamilkrzywda f640f6658c Send to by node name or broadcast sentinel (GET-free resolution via node DB) (#9)
CI / build + unit (push) Successful in 33s
2026-09-24 16:47:51 +02:00
kamilkrzywda 57ad27d66f Message history with delivery tracking (GET /api/messages) (#8)
CI / build + unit (push) Successful in 32s
2026-09-24 16:19:32 +02:00
kamilkrzywda 5292303786 Node database: collect nodes during sync, expose GET /api/nodes (#7)
CI / build + unit (push) Successful in 39s
2026-09-24 15:37:03 +02:00
kamilkrzywda adfbc9800b Send want_config_id on connect to trigger initial sync (#6)
CI / build + unit (push) Successful in 39s
Send want_config_id on connect to trigger initial sync (#6)

Firmware only streams myInfo/channels/configComplete after receiving a
ToRadio with want_config_id — the service previously wrote nothing on
connect, so /api/channels stayed [] and myNodeNum null. v0.1.4.
2026-09-23 22:42:03 +02:00
kamilkrzywda 3062f40899 feat: pin sends to testowy channel (index 2), never primary (v0.1.3) (#5)
CI / build + unit (push) Successful in 36s
feat: pin sends to testowy channel (index 2), never primary (v0.1.3)
2026-09-23 22:06:01 +02:00
kamilkrzywda 75a10c195f feat: /api/channels endpoint, /api namespace, Scalar at root (v0.1.0) (#2)
feat: /api/channels endpoint, /api namespace, Scalar at root (v0.1.0) — closes #1
2026-09-23 21:05:22 +02:00