Skip to content

ShadowClaw 1.3.0

A finding names one provider; a host reaches several. The headline provider is chosen by ranking a finding's endpoints — unsanctioned egress, then a named provider, then confidence, then hostname alphabetically — which is the right field for "what do I look at first" and the wrong one for "which providers did this host reach".

The loser was invisible, not under-reported

One process talking to two providers produces one finding, so grouping by provider over finding.recorded counted only the winner. Because the choice is a sort and not a race, the loser was not under-reported but invisible permanently.

Replaying data/findings.jsonl through the real ranking: a single Python agent process reaching Anthropic and Fireworks headlined Anthropic 584 times out of 584, and Fireworks zero — purely because api.anthropic.com sorts before api.fireworks.ai. The same collapse hid github_copilot behind a co-occurring provider.

The dangerous part is that it does not fail. No error, no empty panel — just a smaller number that looks plausible.

Counting correctly has to happen at emit time

The joined providers field already carried the full set, but a comma-joined string cannot be grouped: LogQL has no way to split one line into several series. So shadowclaw.provider.reached is emitted once per distinct provider a finding reached — the same reason agent activity is its own event rather than a list on a finding. See shadowclaw.provider.reached.

Per provider rather than per endpoint, so a provider behind rotating CDN addresses still counts once. severity and risk_score travel from the finding so the dashboard's severity filter selects the same population on these panels as on the findings panels. is_headline marks the one that would have won, so the gap stays measurable. Unattributed egress keeps reading finding.recorded with provider = "", since a finding with no attributed provider emits no provider record and would otherwise drop off the chart.

Replayed over the 2,697 findings on disk, the provider counts go from cursor 1810 / anthropic 805 / fireworks 0 / github_copilot 0 to cursor 1832 / anthropic 805 / fireworks 584 / github_copilot 25.

Verification

A test fails the build if any panel ever groups by provider over the finding event again — verified by reverting one panel and watching it fail. All 35 dashboard queries run against a live Loki 3.x and accepted.

Minor rather than patch

The fix is additive, and consumers can build on the new event shape.

Next