Skip to content

fix(dx_evidence_graph): coalesce node identity to pod→service→IP (don't collapse empty-pod peers)#70

Merged
entlein merged 1 commit into
entlein/dx-evidence-graph-vizfrom
fix/dx-evidence-graph-ip-fallback
Jul 18, 2026
Merged

fix(dx_evidence_graph): coalesce node identity to pod→service→IP (don't collapse empty-pod peers)#70
entlein merged 1 commit into
entlein/dx-evidence-graph-vizfrom
fix/dx-evidence-graph-ip-fallback

Conversation

@entlein

@entlein entlein commented Jun 22, 2026

Copy link
Copy Markdown

The dx_evidence_graph Graph widget keys nodes on requestor_pod/responder_pod. Edges whose peer is not a pod — the k8s API server (responder_service), external/IP endpoints, the new consulted-socket edges (responder_ip) — have an empty *_pod, so they ALL collapse into a single bogus "" node, merging unrelated peers.

Fix: coalesce node identity to pod → service → IP (the same idiom net_flow_graph uses, px.select(src==, src_ip, src)), and point adjacencyList.fromColumn/toColumn at the coalesced requestor/responder columns. Raw *_pod/*_service/*_ip are still returned for hover.

Validated live (px run against forensic_db): control-plane → k8s-apiserver; the 5 consulted sockets resolve to 5 distinct IPs (10.43.143.34, 10.42.0.1, ::1, 127.0.0.1, 10.42.0.43) instead of one merged node.

Pairs with entlein/dx#87 (the consulted-evidence edges, which is what surfaced the empty-pod collapse).

🤖 Generated with Claude Code

…peers don't collapse

Edges whose peer is not a pod (k8s API server, external/IP endpoints, consulted
sockets) carry an empty requestor_pod/responder_pod. The Graph widget keyed on
*_pod alone, so EVERY such edge collapsed into one bogus "" node — visually
merging unrelated peers (the API server, every distinct remote IP) into one.

Coalesce node identity to pod, else service, else IP (the same idiom
net_flow_graph uses: px.select(src=='', src_ip, src)) and point the adjacencyList
from/to at the coalesced columns. Now each distinct peer is its own node.

Validated live (px run, forensic_db): control-plane → k8s-apiserver; the 5
consulted sockets → 5 distinct IPs (10.43.143.34, 10.42.0.1, ::1, 127.0.0.1,
10.42.0.43) instead of one merged node.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a3acdd10-e42d-496b-addf-1c7b8ae2d625

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/dx-evidence-graph-ip-fallback

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@entlein
entlein merged commit dbc204b into entlein/dx-evidence-graph-viz Jul 18, 2026
3 of 6 checks passed
entlein added a commit that referenced this pull request Jul 19, 2026
)

* dx_evidence_graph: AE evidence-graph viz, rebased onto main

Consolidates PR #62 (entlein/dx-evidence-graph-viz, 28 commits) onto current main.
Squashed because ~13 of its commits were UI-build CI fixes (use_default_shell_env,
yarn-by-abs-path, set -x, license/runner-label) that main already got via #64 — a
linear rebase collided on bazel/ui.bzl repeatedly; a 3-way squash-merge reconciles
them cleanly.

Net deliverable: dx_evidence_graph PxL script + vis.json + manifest, vispb/vis.proto
graph fields, and the GraphWidget (graph.tsx/graph-utils.ts) edge-label/pin/popup
rendering wired to the forensic ClickHouse DSN — the dx→pixie attack-graph viz.

* fix(dx_evidence_graph): node identity = pod→service→IP, so empty-pod peers don't collapse

Edges whose peer is not a pod (k8s API server, external/IP endpoints, consulted
sockets) carry an empty requestor_pod/responder_pod. The Graph widget keyed on
*_pod alone, so EVERY such edge collapsed into one bogus "" node — visually
merging unrelated peers (the API server, every distinct remote IP) into one.

Coalesce node identity to pod, else service, else IP (the same idiom
net_flow_graph uses: px.select(src=='', src_ip, src)) and point the adjacencyList
from/to at the coalesced columns. Now each distinct peer is its own node.

Validated live (px run, forensic_db): control-plane → k8s-apiserver; the 5
consulted sockets → 5 distinct IPs (10.43.143.34, 10.42.0.1, ::1, 127.0.0.1,
10.42.0.43) instead of one merged node.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(dx_evidence_graph): node identity = pod→service→IP, so empty-pod peers don't collapse (#70)

Edges whose peer is not a pod (k8s API server, external/IP endpoints, consulted
sockets) carry an empty requestor_pod/responder_pod. The Graph widget keyed on
*_pod alone, so EVERY such edge collapsed into one bogus "" node — visually
merging unrelated peers (the API server, every distinct remote IP) into one.

Coalesce node identity to pod, else service, else IP (the same idiom
net_flow_graph uses: px.select(src=='', src_ip, src)) and point the adjacencyList
from/to at the coalesced columns. Now each distinct peer is its own node.

Validated live (px run, forensic_db): control-plane → k8s-apiserver; the 5
consulted sockets → 5 distinct IPs (10.43.143.34, 10.42.0.1, ::1, 127.0.0.1,
10.42.0.43) instead of one merged node.

Co-authored-by: Entlein <eineintlein@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* revert(#62): drop hardcoded /opt/px_dev yarn paths in bazel/ui.bzl

#62 replaced the PATH-resolved `yarn` with the dev-machine absolute
`/opt/px_dev/tools/node/bin/yarn` in three actions (webpack deps, build_prod,
license check) and dropped the build_prod error-capture. Hardcoding the toolchain
path breaks portability (CI runners / any machine where node lives elsewhere).
Revert bazel/ui.bzl to main: bare `yarn` resolved via the existing PATH export,
with the original build_prod retval/error handling restored.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017k7uYSNUctQvkTAZYJbaB3

* Update manifest.yaml

Signed-off-by: Duck <70207455+entlein@users.noreply.github.com>

* Update dx_evidence_graph.pxl

Signed-off-by: Duck <70207455+entlein@users.noreply.github.com>

* Delete src/pxl_scripts/px/dx_evidence_graph/README.md

Signed-off-by: Duck <70207455+entlein@users.noreply.github.com>

* Update vis.json

Signed-off-by: Duck <70207455+entlein@users.noreply.github.com>

* Update vis.json

Signed-off-by: Duck <70207455+entlein@users.noreply.github.com>

* genfiles+lint(#62): regenerate vis proto codegen + strip trailing ws

- vis.proto changed (new Graph fields: edge_label_column, node_label_column,
  node_color_column, node_thresholds, node_hover_info) but the generated files
  were stale -> run-genfiles red. Regenerated via update_{go,ts}_protos.sh:
  src/api/proto/vispb/vis.pb.go and src/ui/src/types/generated/vis_pb.d.ts.
- run-container-lint: Error (S&RX) trailing-spaces on manifest.yaml:6 -> stripped.

Data model unchanged and consistent with AE dx_evidence_graph(_malignant) + dx#99.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017k7uYSNUctQvkTAZYJbaB3

* genfiles(#62): regenerate src/pxl_scripts/README.md for dx_evidence_graph

The aggregate pxl-scripts README is generated from each script's manifest.yaml
(make -C src/pxl_scripts update_readme). The new px/dx_evidence_graph script
wasn't reflected in it -> run-genfiles red. Regenerated; it now lists
dx_evidence_graph.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017k7uYSNUctQvkTAZYJbaB3

* lint(#62): drop unwired node-coloring scaffolding in graph.tsx

run-container-lint failed on 5 @typescript-eslint/no-unused-vars (arc runs eslint
with --max-warnings 0): getColorForNode() was defined but never called, and
nodeLabelColumn/nodeColorColumn/nodeThresholds/nodeHoverInfo were destructured but
never used (edge coloring is wired; node coloring is not). Removed the dead
function and un-destructured the 4 unused props. The GraphProps/NodeThresholds
TYPES keep the node fields, so the component API is unchanged for when node
coloring is actually implemented. eslint clean locally.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017k7uYSNUctQvkTAZYJbaB3

---------

Signed-off-by: Duck <70207455+entlein@users.noreply.github.com>
Co-authored-by: pixie-agent <noreply@local>
Co-authored-by: Entlein <eineintlein@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Duck <70207455+entlein@users.noreply.github.com>
Co-authored-by: constanze <croedig@sba-research.org>
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.

1 participant