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.