feat(neighbor-graph): treat ANON_REQ as an originator↔path[0] edge source (#1777) - #1822
feat(neighbor-graph): treat ANON_REQ as an originator↔path[0] edge source (#1777)#1822Saarlandpower wants to merge 4 commits into
Conversation
…urce (Kpa-clawbot#1777) ANON_REQ (payload type 0x07) carries the sender's full Ed25519 ephemeral pubkey (decoder.go's EphemeralPubKey / "ephemeralPubKey" field) — the same trust level as an ADVERT's pubKey. Both the ingestor's persisted-edge builder (neighbor_builder.go, writes neighbor_edges) and the server's in-memory mirror (neighbor_graph.go, BuildFromStore) only ever used ADVERT for the originator↔path[0] edge, missing ANON_REQ as a legitimate second source of full-pubkey originator identity. This implements suggestion Kpa-clawbot#1 from the issue discussion only. Suggestion Kpa-clawbot#2 (treating the 1-byte truncated src/dst hash on REQ/RESP/PATH/TXT as a hop) was explicitly rejected in triage and by the reporter themselves (damn-simple-scripts, 2026-06-22) — a 1-byte hash has ~1/256 collision odds and would manufacture false edges. REQ/RESP/PATH/TXT remain excluded from originator-edge creation; only their existing observer↔path[last] edge (unaffected by this change, already applies to all packet types) still applies. Changes: - cmd/ingestor/maintenance.go: extractPubkeyFromAnonReqJSON, mirroring the existing extractPubkeyFromAdvertJSON helper. - cmd/ingestor/neighbor_builder.go: payloadAnonReq const; hasFullOriginator bool (isAdvert || isAnonReq) gates the originator-edge branches; falls back to the new JSON helper when from_pubkey is empty (from_pubkey is only populated for ADVERT at write time, per db.go's Kpa-clawbot#1143 comment — left untouched here to keep this change scoped to the two call sites the triage identified). - cmd/server/neighbor_graph.go: extractFromNode also checks "ephemeralPubKey" (safe unconditionally — the field only appears in ANON_REQ's decoded_json via the shared Payload struct's omitempty); hasFullOriginator bool gates the zero-hop and Edge-1 branches the same way as the ingestor. Tests: 2 new cases in neighbor_graph_test.go (ANON_REQ produces the edge; REQ still doesn't) and 2 new cases in neighbor_builder_test.go (same, against the persisted DB path via the from_pubkey JSON fallback). All existing neighbor-graph/-builder tests still pass unchanged, including TestBuildNeighborGraph_ADVERTOnlyConstraint (still covers a non-ADVERT, non-ANON_REQ type; no rename needed). cmd/server full suite: pass, except 2 pre-existing flaky tests (TestHandleNodePaths_*_1352, an index-loading race unrelated to this change — reproduces intermittently on unmodified master too). cmd/ingestor full suite: pass. Co-Authored-By: Claude <noreply@anthropic.com>
Bot polish reviewVerdict: LGTM w/ nits Findings
Test disciplineRed-commit not separated (single squash commit), but both new tests fail on revert of the Nits (optional, ≤3)
|
…lPubKey on payload type (Kpa-clawbot#1822 review) Bot review round-1 nits (both minor, no blockers): 1. (munger) extractEdgesFromObs / edgeCandidate in neighbor_persist.go had zero callers anywhere in the codebase, including tests — verified via grep. Its doc comment claimed 'the in-memory graph builder (neighbor_graph.go) also calls it', which was already stale: BuildFromStore has its own inline ADVERT-only loop, unrelated to this function. Keeping an unreferenced parallel implementation around is exactly the drift risk the review flagged: if it were ever wired back up, it would silently reintroduce the ADVERT-only originator-edge bug this PR fixes elsewhere, since it was never updated for ANON_REQ. Deleted rather than duplicated the fix into dead code. 2. (carmack) extractFromNode's safety for including "ephemeralPubKey" unconditionally in the field-check list rested entirely on the invisible premise that no other payload type ever populates that JSON key. Gated the ephemeralPubKey lookup on tx.PayloadType == PayloadANON_REQ instead of trusting key-presence alone, so this stays correct even if a future decoder change reuses the field name for a different, non-originator purpose on some other payload type. No behavior change for ADVERT/ANON_REQ/REQ/RESP/PATH/TXT — all existing and Kpa-clawbot#1777 tests pass unchanged (TestBuildNeighborGraph_AnonReqSingleHopPath, TestBuildNeighborGraph_ReqRespStillExcluded, TestBuildNeighborGraph_ ADVERTOnlyConstraint, full neighbor-graph/-persist suite). cmd/server full suite: pass (2 runs clean; the previously-seen TestHandleNodePaths_*_1352 index-loading race did not reproduce in either run here, consistent with it being an unrelated pre-existing flake, not a regression from this change). Co-Authored-By: Claude <noreply@anthropic.com>
|
Addressed both nits from the round-1 review:
No behavior change — all existing + #1777 tests pass unchanged. Full Co-Authored-By: Claude noreply@anthropic.com |
Bot polish re-review (round 2)Verdict: LGTM — round-1 nits addressed Delta since round-1 (
|
Bot polish re-review (round 3 — merge-from-master)Verdict: LGTM (unchanged from round-2) Delta since round-2 (
|
|
Bot review has been green for a while now — this looks ready whenever a maintainer has a moment to approve the CI run (action_required gate). Let me know if anything else is needed on my end. Thanks! |
Bot polish re-review (round 4 — post-master-merge)Verdict: LGTM (unchanged from round-3) Delta since round-3 (
|
PR #1822 Review —
|
|
Superseded by #1863. That PR carries both commits from this branch unchanged and adds the #1784 path-trust gate to the same function ( Closing here — review continues in #1863. |
Addresses #1777 — implements suggestion #1 only (extend ADVERT-only originator-edge logic to also accept ANON_REQ). Suggestion #2 (treat REQ/RESP/PATH/TXT's 1-byte src/dst hash as a hop) was explicitly rejected in the issue thread by both the meshcore-reviewer and the reporter themselves — ~1/256 collision odds, would manufacture false edges.
What
ANON_REQ (payload type
0x07) carries the sender's full Ed25519 ephemeral pubkey (decoder.go'sEphemeralPubKey/ephemeralPubKeyJSON field) — the same trust level as an ADVERT'spubKey. Both places that build the originator↔path[0] edge only recognized ADVERT:cmd/ingestor/neighbor_builder.go:209(persistedneighbor_edges, read by the server)cmd/server/neighbor_graph.go:296/extractFromNode(in-memory mirror,BuildFromStore)Both now also accept ANON_REQ via a new
hasFullOriginator := isAdvert || isAnonReqgate. REQ/RESP/PATH/TXT remain excluded from originator-edge creation — their existing observer↔path[last] edge (unaffected by this change, already applies to all packet types) still applies.Changes
cmd/ingestor/maintenance.go— newextractPubkeyFromAnonReqJSON, mirroring the existingextractPubkeyFromAdvertJSON.cmd/ingestor/neighbor_builder.go—payloadAnonReqconst; falls back to the new JSON helper whenfrom_pubkeyis empty (that column is only populated for ADVERT at write time, perdb.go's bug: pubkey attribution via LIKE-on-JSON is unsound — adversarial spoofing + same-name false positives + perf #1143 comment — left untouched to keep this change scoped to the two call sites triage identified).cmd/server/neighbor_graph.go—extractFromNodealso checks"ephemeralPubKey"(safe unconditionally: the field only appears in ANON_REQ'sdecoded_json, via the sharedPayloadstruct'somitempty).Tests
neighbor_graph_test.go:TestBuildNeighborGraph_AnonReqSingleHopPath(ANON_REQ produces the edge) +TestBuildNeighborGraph_ReqRespStillExcluded(REQ still doesn't).neighbor_builder_test.go: same two cases against the persisted-DB path (TestNeighborEdgesBuilderUpsertsFromAnonReqEphemeralPubKey,TestNeighborEdgesBuilderExcludesOtherNonAdvertTypes), exercising thefrom_pubkey-empty JSON fallback specifically.TestBuildNeighborGraph_ADVERTOnlyConstraint.cmd/serverfull suite: pass, except 2 pre-existing flaky tests (TestHandleNodePaths_*_1352, an index-loading race unrelated to this change — reproduces intermittently on unmodifiedmastertoo, confirmed separately).cmd/ingestorfull suite: pass.Operator context: SaarMesh (SaarLorLux, DE/FR/LU, 800+ nodes) — more originator edges from ANON_REQ traffic should sharpen the neighbor graph / topology views for us.
Co-Authored-By: Claude noreply@anthropic.com