Repository navigation
Conversation
efiten
force-pushed
the
fix/certain-liveness
branch
from
October 22, 2026 09:59
d82bf85 to
1b40d8a
Compare
efiten
force-pushed
the
fix/certain-liveness
branch
from
October 22, 2026 11:06
1b40d8a to
f7a809d
Compare
Owner
Author
|
Review feedback addressed (commit
Not changed, with reasons:
|
…awbot#2260) A repeater silent since 18 Oct showed "Active, last relayed 4m ago" with Last Heard 36m, and its offline mail never fired. Every hit after that date was the 1-byte hop 67, shared by nine nodes: the live resolver's affinity tiebreak put it in byNode, and last_relayed folds in the 1-byte prefix bucket on purpose (Kpa-clawbot#662). The store now keeps certainHeard per node: the newest transmission that can only be that node. Its own packets (pubKey / srcPubKey) count, and a flood relay hop counts when its prefix at the packet's path hash size matches exactly one path-capable node. Direct-route paths (hops still to come, Mesh::removeSelfFromPath) and TRACE (SNR bytes) never count. The same rule feeds every load path: Load, loadChunk, scanAndMergeChunk, live ingest and late observations. Liveness reads only that: - /api/nodes and the node detail: last_heard is the newer of last_seen and the certain last heard, no longer a copy of last_seen; - health and bulk-health stats.lastHeard; - LastHeardMap, so the notify offline check; the separate relay input (lastRelayed / relayTimes) is gone; - roles.js getEffectiveHeardMs: the newest known timestamp, without last_relayed; the side pane no longer says "Last relayed". Attribution for analytics (byNode, packet counts, last_relayed, relay counters) is unchanged. Cost: noteCertainRelays 116 ns per observation for 8 hops, 0 allocs; LastHeardMap 0.77 ms for 5,000 nodes (was 3.7 ms), one map lookup each. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
efiten
force-pushed
the
fix/certain-liveness
branch
from
October 22, 2026 12:22
f7a809d to
b5556bd
Compare
Owner
Author
|
Review notes addressed (commit Answers to the question: everything is addressed in this PR; it lands first, Kpa-clawbot#2262 right after.
|
…lawbot#2260) Retention copies a node into inactive_nodes and nothing removes the row when the node is heard again. markRetired loaded those rows too, so such a node's own prefix counted as a retired namesake and it got no certain relay at any width. markRetired now skips rows whose public_key is also in nodes. On a live instance 838 of the 7877 repeater and room rows in inactive_nodes are also in nodes; repeaters and rooms with a certain relay in the last 72 h went from 1256 to 629 with the rule, 1206 with this fix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes Kpa-clawbot#2260 (part 1 of 2: liveness; the adaptive offline threshold follows in a separate PR).
Problem
A repeater silent since 2026-10-18 showed Active — Last relayed 4m ago and Last Heard 36m, and its offline mail never fired. Every hit after that date was the 1-byte hop
67, shared by nine nodes:resolvePathForObs) put those transmissions inbyNode, which fedstats.lastHeardandLastHeardMap;last_relayedfolds in the 1-byte prefix bucket on purpose (feat: repeater liveness — detect if repeater is actively relaying traffic Kpa-clawbot/CoreScope#662), and drove the status inroles.jsand the notify offline check.Fix
The store keeps
certainHeardper node: the newest transmission that can only be that node.pubKey,srcPubKey)Mesh::removeSelfFromPath)destPubKey)A multibyte repeater legitimately appears as a 1-byte hop (the originator picks the hash size,
Mesh::routeRecvPacketappends at that size), so only uniqueness decides. The rule is applied in every load path throughindexResolvedPathHopsand the newindexColdLoadRelayHops: Load, loadChunk, scanAndMergeChunk, live ingest, late observations.Liveness reads only that:
/api/nodesand the node detail:last_heardis the newer oflast_seenand the certain last heard. Before, it was a plain copy oflast_seen.stats.lastHeard.LastHeardMap→ notify offline check. The separate relay input (lastRelayed,relayTimes,notifyInput.Relayed) is removed.roles.js getEffectiveHeardMs: newest known timestamp, withoutlast_relayed. The side pane no longer says "Last relayed"; the Last Relayed row stays (analytics) with an updated tooltip.uncertain(repeaters and rooms): nothing certain within the silent window, but the shared prefix (last_relayed) still in paths. Grey badge with explanation, an Uncertain filter on the nodes list and the map, dimmed like stale. The offline mail for such a node says only its shared prefix was still seen.inactive_nodesis not unique; adverts withsignatureValid: falseare no evidence;/api/nodes/searchreports the samelast_heard.Attribution for analytics (
byNode, packet counts,last_relayed, relay counters, usefulness) is unchanged.Performance
Hot path:
noteCertainRelaysruns per observation on ingest under the write lock: two hex bytes of header parsing plus one prefix-map lookup per resolved hop.Memory: one string per node ever certainly heard, dropped together with the node's
byNodelist on eviction.Tests
certain_heard_loadpaths_test.go: every load path (Load, loadChunk, LoadChunked — each with and without resolved_path — live ingest, late observations) feedscertainHeard; each of the 8 call sites verified red with a nil prefix map.certain_heard_test.go: hop width per route/payload type; retired namesake prefix; failed signature; uncertain offline mail; shared vs unique 1-byte vs 2-byte hops; direct/TRACE/nil prefix map; cold-load path; sender vs destination; eviction;lastHeardValue;/api/nodes, detail and health agree.LastHeardMapstays at X andnode.offlinegoes bad after 72 h. Verified red with the uniqueness check mutated out.TestEvaluateCertainHeardreplaces the relay-input test.last_relayedalone no longer keeps a repeater or room active; newest timestamp wins (an old_liveSeencan't hide a fresh certain relay).cmd/servergo test ./...andtest-all.shpass locally, also on a trial merge into a downstream fork with 457 extra commits.Validation
Ran the server on the freshened e2e fixture: list, detail and health report the same last heard; e.g. a repeater whose
last_relayedis 3 min newer than anything certain keeps its certain time. No browser screenshot: Playwright isn't installed on this machine; CI's E2E covers the pages.Not changed
nodes.last_seen(bug(server): touchRelayLastSeen has been a no-op since the mode=ro refactor — nodes.last_seen never reflects relay activity Kpa-clawbot/CoreScope#1854) resolves a shared hop only when exactly one candidate is adjacent in the neighbor graph; it did not fire in this case. Left as is.lastHeard=list filter still filterslast_seenin SQL.🤖 Generated with Claude Code