Skip to content

Namespace level - #21

Merged
FathiQ merged 3 commits into
mainfrom
namespace-level
Aug 20, 2026
Merged

FathiQ merged 3 commits into
mainfrom
namespace-level

Conversation

@FathiQ

@FathiQ FathiQ commented Aug 20, 2026

Copy link
Copy Markdown
Owner

No description provided.

FathiQ and others added 3 commits August 19, 2026 18:00
At 200 workloads across 40 namespaces the whole picture is not a picture. Focus
reduces the graph to a node's neighbourhood and reports what it left out, so a
view showing a tenth of a cluster says so rather than implying the cluster is
that size. What is excluded becomes one counted card — "14 more namespaces, 96
nodes not drawn" — because a graph that silently omits most of itself is worse
than one that omits most of itself and admits it.

The traversal is undirected, which is not an oversight. "Within two hops of
payments" is a question about relationship, not traffic: a workload that only
ever sends to the thing you are looking at is exactly as related to it as one
that only receives. It runs on the finished graph rather than during the build,
because adjacency depends on which edges survived peer collapsing and port
merging, and doing it earlier would answer a subtly different question that
looks identical on small inputs.

Reachability from outside is directed, for the opposite reason. Being able to
call the internet is not the same as being reachable from it, and conflating
them would mark every workload with egress as exposed — which is to say, would
mark nothing. Private ranges are not the internet either: a rule naming
10.0.0.0/8 is somebody's VPC, and counting it would drown the finding. Both
distinctions are tested, because both are the kind of thing that looks like a
detail until the filter returns everything.

Past 220 nodes the graph is refused rather than drawn. The layout is a hairball
no amount of panning recovers, and shipping it only moves the discovery to the
browser; the screen offers search or the namespace level instead, and says
plainly why.

The minimap keeps one rule: a workload nothing protects is red there even when
it is nowhere near the current view. Focus and zoom exist to hide things, and
the moment they can hide what somebody is hunting they have made the tool
worse. Its dots come from the node data rather than from what the canvas is
painting, so being off screen cannot change a colour.

Exposure is computed before focus narrows anything — whether the internet can
reach a workload is a fact about the cluster, not about what is currently
drawn, and computing it after would make it change as you look around.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both sides appended to web/e2e/smoke.spec.ts — the palette tests on main, the
scale tests here — and neither replaces the other, so both are kept. The rest of
main's changes to that file, which rename the search placeholder and swap the
removed Simulate button for its keyboard shortcut, merged cleanly and none of
the scale tests touch either.

71 tests, 66 from main plus 5 here, none lost.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
On most clusters kube-system and its neighbours hold the only unprotected
workloads there are, so "N unprotected" ends up counting things nobody is going
to write a NetworkPolicy for. They collapse to one counted card each — not
hidden: still counted, still searchable, still expandable, and the card says
what is inside it and how much of that is unprotected.

Reversibility is the whole condition of the idea being honest rather than a way
of hiding findings, so there are two ways back: the card opens on click, and a
strip along the bottom names what was collapsed with an Expand all. The card
alone was not enough — a viewer who has not worked out that the card is a
button has no way in, and the affordance has to be somewhere they are already
looking.

Expanding degrades better than the brief feared. Six red kube-system cards come
back inside their own namespace container as a grid below the rest, not
scattered through the middle, because grouping by namespace was already doing
that work.

What counts as system is configuration, not a fixed list: a platform team's own
namespace is system to the application teams and the whole job to them. A flag,
a Helm value, and "-" to collapse nothing. Marsad's own namespace comes from
the downward API rather than a value that can drift from where it is running.

Empty namespaces are reported along the bottom rather than drawn. They have no
posture to show and no edges to place them, so the layout puts them wherever
unconnected nodes go, which is through the middle of everything else.

A namespace card now says "1 of 3 unprotected" rather than "3". The count says
how big it is; the posture says whether anything is wrong with it, which is the
question the graph exists to answer. Aggregated edges carry a port pill, since
at namespace level there are no card rows to carry ports and the edge otherwise
says only that something is allowed.

On A3, unanswered after three asks, so decided and easy to reverse: the card
rows stay the permanent signal, and the lines are drawn when the only-
unprotected filter is on — where the fan-out is the picture rather than noise
on top of one. `showDefaultEdges` in overlay.ts is the switch.

Two things the screenshots caught. The expand affordance was drawing on top of
the count badge, so it took the badge's place — the posture line underneath
already carries the number. And the rail read "0 of 0 workloads" beside a
screen full of them, because at namespace level there are no workload nodes to
count; the footer counts the unit it names now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@FathiQ
FathiQ merged commit 0334a48 into main Aug 20, 2026
6 checks passed
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