Skip to content

fix(protocol): resolve 0xC511_0004 magic collision (closes #928)#931

Merged
ruvnet merged 2 commits into
mainfrom
fix/issue-928-magic-collision
Jun 3, 2026
Merged

fix(protocol): resolve 0xC511_0004 magic collision (closes #928)#931
ruvnet merged 2 commits into
mainfrom
fix/issue-928-magic-collision

Conversation

@ruvnet
Copy link
Copy Markdown
Owner

@ruvnet ruvnet commented Jun 3, 2026

Summary

Closes #928. 0xC511_0004 was double-assigned in firmware (ADR-063 fused-vitals AND ADR-040 WASM output) and both packet types were transmitted. The sensing-server only knew the WASM shape, so on the C6+mmWave config the 48-byte fused-vitals packet was being silently misparsed as malformed WASM events — fused vitals were lost, spurious WASM events leaked to subscribers.

Approach

Per the issue's recommended direction (smaller blast radius — the registry already treats 0xC511_0004 as fused-vitals canonical):

  1. Reassign WASM_OUTPUT_MAGIC from 0xC511_00040xC511_0007 (next free slot per rv_feature_state.h registry). Firmware-side change in wasm_runtime.h.
  2. Add parse_edge_fused_vitals + EdgeFusedVitalsPacket on the server. Byte layout mirrors edge_fused_vitals_pkt_t from edge_processing.h:129 exactly, gated by the firmware's _Static_assert(sizeof(...) == 48).
  3. Dispatch arm added to the UDP receive loop, tried before the WASM parser so stale firmware (old WASM on 0004) fails to parse as both and gets dropped — deliberate fail-loud rather than the pre-fix silent garbage.
  4. Registry comment update in rv_feature_state.h documenting the new 0x...0007 row.
  5. 5 new tests in issue_928_magic_collision_tests mod guarding both halves of the swap.

Test plan

  • cargo test -p wifi-densepose-sensing-server --no-default-features --bin sensing-server122 passed / 0 failed (5 new + 117 existing, unchanged).
  • parse_edge_fused_vitals round-trips a synthetic 48-byte packet — every field including breathing/100, heartrate/10000, mmWave block.
  • parse_wasm_output rejects the legacy 0xC511_0004.
  • parse_wasm_output accepts the new 0xC511_0007.
  • Hardware validation deferred — issue notes it can't run in CI without a real C6+MR60BHA2 board.

WebSocket schema

New broadcast type: {\"type\": \"edge_fused_vitals\", \"node_id\": ..., \"breathing_rate_bpm\": ..., \"heartrate_bpm\": ..., \"mmwave\": {\"hr_bpm\": ..., \"br_bpm\": ..., \"distance_cm\": ..., \"targets\": ..., \"confidence\": ..., \"type\": ...}, ...}.

Additive — existing subscribers gain a new branch; none break.

Deployment

Wire-protocol change. Firmware older than this commit emits WASM output on 0xC511_0004; updated hosts will drop those frames. Operators running C6+mmWave should reflash firmware concurrent with the host upgrade. The pre-fix silent misparsing is the worse failure mode; this is the right tradeoff.

🤖 Generated with claude-flow

ruvnet added 2 commits June 3, 2026 11:11
… action

Two real problems in the Static Application Security Testing job:

1. **It scanned a path that no longer exists.** `bandit -r src/` and
   `semgrep … src/` pointed at the repo-root `src/`, but the Python code
   moved to `archive/v1/src/` (64 .py files) when the runtime was rewritten
   in Rust. So the SAST scan matched nothing — a silent no-op (this is also
   why `bandit-results.sarif` was "Path does not exist" on recent runs).
   Fixed both to `archive/v1/src/`.

2. **Deprecated + redundant + flaky semgrep step.** The
   `returntocorp/semgrep-action@v1` step pulled `returntocorp/semgrep-agent:v1`
   from Docker Hub every run (intermittently timing out → red check, e.g. on
   #929) and is EOL. It was redundant: the pip `semgrep --sarif` step is what
   feeds GitHub Security; the action only pushed to the Semgrep cloud app via
   SEMGREP_APP_TOKEN. Removed it and folded its `p/docker` + `p/kubernetes`
   rulesets into the pip semgrep command, so coverage is preserved with no
   Docker pull.

The job stays `continue-on-error: true` (non-gating). YAML validated.

Co-Authored-By: claude-flow <ruv@ruv.net>
Background

`0xC511_0004` was assigned to two different packet formats in firmware
— `EDGE_FUSED_MAGIC` (ADR-063, 48-byte `edge_fused_vitals_pkt_t`) and
`WASM_OUTPUT_MAGIC` (ADR-040, variable-length `wasm_output_pkt_t`).
Both were transmitted. The sensing-server only had a WASM parser for
that magic and no fused-vitals parser, so on the ESP32-C6 + MR60BHA2
mmWave configuration the fused-vitals packet was silently misparsed
as a malformed WASM output — `breathing_rate` was read as
`event_count`, mmWave-fused vitals were lost, and spurious WASM events
were emitted to subscribers.

Fix

1. Reassign `WASM_OUTPUT_MAGIC` to `0xC511_0007` (next free slot per
   the registry in `rv_feature_state.h`). Smaller blast radius than
   moving fused-vitals — the registry already treats `0xC511_0004` as
   fused-vitals canonical and several years of deployed feature
   tracking depends on that assignment.

2. Add `parse_edge_fused_vitals` + `EdgeFusedVitalsPacket` in
   `wifi-densepose-sensing-server::main`. Byte layout taken directly
   from `edge_processing.h:129`, mirroring the firmware's
   `_Static_assert(sizeof(edge_fused_vitals_pkt_t) == 48)` so future
   firmware changes that grow the packet will break this parser
   loudly instead of silently.

3. Add a dispatch arm in the UDP receive loop. Fused-vitals is tried
   BEFORE WASM so a stale firmware (still emitting 0xC511_0004 with
   the WASM payload) fails to parse as fused-vitals (size mismatch),
   then fails to parse as WASM (magic mismatch on the new 0x...0007),
   and gets dropped — a deliberate "fail loud" outcome rather than the
   pre-fix silent garbage.

4. Update the registry comment in `rv_feature_state.h` to add the new
   0x...0007 row.

5. Add five tests in a new `issue_928_magic_collision_tests` mod:
   - `parse_edge_fused_vitals_extracts_fields_correctly`
   - `parse_edge_fused_vitals_rejects_short_buffer`
   - `parse_edge_fused_vitals_rejects_wrong_magic`
   - `parse_wasm_output_rejects_legacy_0004_magic`
   - `parse_wasm_output_accepts_new_0007_magic`

WebSocket payload

Fused-vitals now broadcasts as `{"type": "edge_fused_vitals", ...}`
with the mmWave-specific block nested under `mmwave`. Schema is
additive — existing subscribers that only inspect `type` are
unaffected; subscribers that switch on `type` gain a new branch.

Deployment note

This is a wire-protocol change. Firmware older than this commit that
emits WASM output on 0xC511_0004 will lose its WASM event stream
against an updated host (host expects 0xC511_0007). Per the issue
discussion, "fail loud" is preferred to silent misparsing. Operators
running C6+mmWave should reflash firmware concurrent with the host
upgrade.

Test results
  cargo test -p wifi-densepose-sensing-server --no-default-features
  --bin sensing-server
  → 122 passed / 0 failed (5 new + 117 existing, unchanged)

Co-Authored-By: claude-flow <ruv@ruv.net>
@ruvnet
Copy link
Copy Markdown
Owner Author

ruvnet commented Jun 3, 2026

Verified this end-to-end (I filed #928) — the protocol fix is correct and complete. 👍

Offsets match the firmware struct byte-for-byte. Checked parse_edge_fused_vitals against edge_fused_vitals_pkt_t (edge_processing.h, __attribute__((packed)), 48 bytes):

field firmware offset parser
node_id [4] buf[4]
flags [5] buf[5]
breathing (×100) [6..8) buf[6..8]
heartrate (×10000) [8..12) buf[8..12]
rssi [12] buf[12] as i8
n_persons [13] buf[13]
mmwave_type [14] buf[14]
fusion_confidence [15] buf[15]
motion_energy [16..20)
presence_score [20..24)
timestamp_ms [24..28)
mmwave_hr/br/distance [28..40)
mmwave_targets/confidence [40]/[41]

Note that [14]/[15] correctly use what's reserved[2] in the 32-byte edge_vitals_pkt_t — the fused variant repurposes them for mmwave_type/fusion_confidence, and the parser gets that right.

  • Dispatch wiring ✓ — if let Some(fused) = parse_edge_fused_vitals(&buf[..len]) is present, so fused packets are actually consumed (not just parseable).
  • Tests ✓ — well-formed parse + 47-byte short-packet rejection.
  • Magic reassignment ✓ — WASM_OUTPUT_MAGIC0xC5110007 in both firmware (wasm_runtime.h) and both server copies (csi.rs + main.rs), and the registry updated. Right call per the issue (registry treats 0xC5110004 as canonical fused-vitals).

Two things before merge:

  1. security-scan.yml is now redundant — this PR includes the same SAST path/semgrep fix that just merged as fix(ci): SAST actually scans the code + drop deprecated flaky semgrep action #930. Rebase on main and drop that file from the diff (it'll conflict otherwise).
  2. Backward-compat (worth a line in the PR/changelog): a still-deployed node running pre-Wire-protocol magic collision: 0xC5110004 used for BOTH fused-vitals and WASM output — C6+mmWave vitals misparsed #928 firmware sends WASM as 0xC5110004; the updated server now reads that as fused-vitals. That's unavoidable with any reassignment and acceptable (WASM on 0xC5110004 was already broken by the collision), but flashing nodes + host together is the clean upgrade path.

Nice work — closes #928 correctly. LGTM after the rebase.

@ruvnet ruvnet merged commit 2c136ac into main Jun 3, 2026
50 checks passed
@ruvnet ruvnet deleted the fix/issue-928-magic-collision branch June 3, 2026 09:56
github-actions Bot pushed a commit to TheArkhitect/RuView that referenced this pull request Jun 3, 2026
…loses ruvnet#928) (ruvnet#931)

* fix(ci): SAST actually scans the code + drop deprecated flaky semgrep action

Two real problems in the Static Application Security Testing job:

1. **It scanned a path that no longer exists.** `bandit -r src/` and
   `semgrep … src/` pointed at the repo-root `src/`, but the Python code
   moved to `archive/v1/src/` (64 .py files) when the runtime was rewritten
   in Rust. So the SAST scan matched nothing — a silent no-op (this is also
   why `bandit-results.sarif` was "Path does not exist" on recent runs).
   Fixed both to `archive/v1/src/`.

2. **Deprecated + redundant + flaky semgrep step.** The
   `returntocorp/semgrep-action@v1` step pulled `returntocorp/semgrep-agent:v1`
   from Docker Hub every run (intermittently timing out → red check, e.g. on
   ruvnet#929) and is EOL. It was redundant: the pip `semgrep --sarif` step is what
   feeds GitHub Security; the action only pushed to the Semgrep cloud app via
   SEMGREP_APP_TOKEN. Removed it and folded its `p/docker` + `p/kubernetes`
   rulesets into the pip semgrep command, so coverage is preserved with no
   Docker pull.

The job stays `continue-on-error: true` (non-gating). YAML validated.

Co-Authored-By: claude-flow <ruv@ruv.net>

* fix(protocol): resolve 0xC511_0004 magic collision (closes ruvnet#928)

Background

`0xC511_0004` was assigned to two different packet formats in firmware
— `EDGE_FUSED_MAGIC` (ADR-063, 48-byte `edge_fused_vitals_pkt_t`) and
`WASM_OUTPUT_MAGIC` (ADR-040, variable-length `wasm_output_pkt_t`).
Both were transmitted. The sensing-server only had a WASM parser for
that magic and no fused-vitals parser, so on the ESP32-C6 + MR60BHA2
mmWave configuration the fused-vitals packet was silently misparsed
as a malformed WASM output — `breathing_rate` was read as
`event_count`, mmWave-fused vitals were lost, and spurious WASM events
were emitted to subscribers.

Fix

1. Reassign `WASM_OUTPUT_MAGIC` to `0xC511_0007` (next free slot per
   the registry in `rv_feature_state.h`). Smaller blast radius than
   moving fused-vitals — the registry already treats `0xC511_0004` as
   fused-vitals canonical and several years of deployed feature
   tracking depends on that assignment.

2. Add `parse_edge_fused_vitals` + `EdgeFusedVitalsPacket` in
   `wifi-densepose-sensing-server::main`. Byte layout taken directly
   from `edge_processing.h:129`, mirroring the firmware's
   `_Static_assert(sizeof(edge_fused_vitals_pkt_t) == 48)` so future
   firmware changes that grow the packet will break this parser
   loudly instead of silently.

3. Add a dispatch arm in the UDP receive loop. Fused-vitals is tried
   BEFORE WASM so a stale firmware (still emitting 0xC511_0004 with
   the WASM payload) fails to parse as fused-vitals (size mismatch),
   then fails to parse as WASM (magic mismatch on the new 0x...0007),
   and gets dropped — a deliberate "fail loud" outcome rather than the
   pre-fix silent garbage.

4. Update the registry comment in `rv_feature_state.h` to add the new
   0x...0007 row.

5. Add five tests in a new `issue_928_magic_collision_tests` mod:
   - `parse_edge_fused_vitals_extracts_fields_correctly`
   - `parse_edge_fused_vitals_rejects_short_buffer`
   - `parse_edge_fused_vitals_rejects_wrong_magic`
   - `parse_wasm_output_rejects_legacy_0004_magic`
   - `parse_wasm_output_accepts_new_0007_magic`

WebSocket payload

Fused-vitals now broadcasts as `{"type": "edge_fused_vitals", ...}`
with the mmWave-specific block nested under `mmwave`. Schema is
additive — existing subscribers that only inspect `type` are
unaffected; subscribers that switch on `type` gain a new branch.

Deployment note

This is a wire-protocol change. Firmware older than this commit that
emits WASM output on 0xC511_0004 will lose its WASM event stream
against an updated host (host expects 0xC511_0007). Per the issue
discussion, "fail loud" is preferred to silent misparsing. Operators
running C6+mmWave should reflash firmware concurrent with the host
upgrade.

Test results
  cargo test -p wifi-densepose-sensing-server --no-default-features
  --bin sensing-server
  → 122 passed / 0 failed (5 new + 117 existing, unchanged)

Co-Authored-By: claude-flow <ruv@ruv.net> 2c136ac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wire-protocol magic collision: 0xC5110004 used for BOTH fused-vitals and WASM output — C6+mmWave vitals misparsed

1 participant