From b3b8bec5ecae223ed27fb53780432bd73e177a28 Mon Sep 17 00:00:00 2001 From: Kpa-clawbot Date: Sun, 21 Jun 2026 20:52:09 -0700 Subject: [PATCH] feat(filter): expose payload.destHash + payload.srcHash in filter autocomplete (#1774) (#1775) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves #1774. ## What Adds `payload.destHash` and `payload.srcHash` to the filter autocomplete suggestions array in `public/packet-filter.js`. ## Why The decoder already emits `destHash` and `srcHash` JSON keys for `REQ` / `RESPONSE` / `TXT_MSG` / `PATH` / `ANON_REQ` packets (see `cmd/ingestor/decoder.go`), and the generic `payload.*` accessor in the filter language (`packet-filter.js:296-308`) already evaluates these fields correctly — i.e. `payload.destHash == "2f"` has always worked. The gap was purely autocomplete + docs: the two names were missing from the `FIELDS` (SUGGESTIONS) array, so operators discovering filters via the suggestion popup couldn't find them. Per the triage on #1774, the canonical names match the decoder's serialization (`destHash` / `srcHash`), not the reporter's proposed `dest` / `src` — so the filter token agrees with the raw-JSON view and the packet detail's `Dest Hash (1B)` label. ## Tests - Red commit (`test-packet-filter.js`): asserts `filter('payload.destHash == "2f"')` matches a fake REQ packet AND that `FIELDS` contains entries named `payload.destHash` / `payload.srcHash`. Fails on the FIELDS assertion only (filter() already works). - Green commit: adds the two entries. All 83 packet-filter tests pass. ## Scope Single-file change in `public/packet-filter.js` (+2 lines) + 23 lines of test coverage. No decoder changes, no backend changes, no API signature changes. Fixes #1774. --------- Co-authored-by: clawbot --- public/packet-filter.js | 2 ++ test-packet-filter.js | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/public/packet-filter.js b/public/packet-filter.js index 5cfa71d9..3bc4b2a7 100644 --- a/public/packet-filter.js +++ b/public/packet-filter.js @@ -459,6 +459,8 @@ { name: 'payload.flags.repeater', desc: 'Decoded payload: advert flag (repeater role)' }, { name: 'payload.flags.room', desc: 'Decoded payload: advert flag (room server)' }, { name: 'payload.flags.hasLocation', desc: 'Decoded payload: advert has location' }, + { name: 'payload.destHash', desc: 'Decoded payload: destination hash byte (1B hex, REQ/RESPONSE/TXT_MSG/PATH/ANON_REQ)' }, + { name: 'payload.srcHash', desc: 'Decoded payload: source hash byte (1B hex, REQ/RESPONSE/TXT_MSG/PATH/ANON_REQ)' }, { name: 'code1', desc: 'Transport route Code1 (hex, e.g. AABB) — present on TRANSPORT_FLOOD/DIRECT' }, { name: 'code2', desc: 'Transport route Code2 (hex, e.g. CCDD) — present on TRANSPORT_FLOOD/DIRECT' }, ]; diff --git a/test-packet-filter.js b/test-packet-filter.js index 1320e2bb..fa95ea06 100644 --- a/test-packet-filter.js +++ b/test-packet-filter.js @@ -232,5 +232,28 @@ test('observer_iata and iata appear in suggest field list', () => { assert(names.indexOf('iata') !== -1, 'iata in FIELDS'); }); +// --- Issue #1774: payload.destHash / payload.srcHash filterable + in suggestions --- +const reqPkt = { + route_type: 1, payload_type: 0, // REQ + hash: 'deadbeef', raw_hex: '0000', + decoded_json: JSON.stringify({ destHash: '2f', srcHash: '0b' }), +}; +test('#1774: payload.destHash filters REQ by destination hash byte', () => { + assert(PF.compile('payload.destHash == "2f"').filter(reqPkt)); + assert(!PF.compile('payload.destHash == "ff"').filter(reqPkt)); +}); +test('#1774: payload.srcHash filters REQ by source hash byte', () => { + assert(PF.compile('payload.srcHash == "0b"').filter(reqPkt)); + assert(!PF.compile('payload.srcHash == "ff"').filter(reqPkt)); +}); +test('#1774: payload.destHash listed in FIELDS suggestions', () => { + const names = PF.FIELDS.map(f => f.name); + assert(names.indexOf('payload.destHash') !== -1, 'payload.destHash missing from FIELDS'); +}); +test('#1774: payload.srcHash listed in FIELDS suggestions', () => { + const names = PF.FIELDS.map(f => f.name); + assert(names.indexOf('payload.srcHash') !== -1, 'payload.srcHash missing from FIELDS'); +}); + console.log(`\n=== Results: ${pass} passed, ${fail} failed ===`); process.exit(fail > 0 ? 1 : 0);