The idea that attackers “think in graphs” is now common vocabulary, especially around Active Directory analysis with tools like BloodHound, or when distinguishing structured activity from a plain sequence of commands. But it almost always describes how we analyze the target, and rarely how we run the operation while it is still in progress.

Once an engagement starts, many teams fall back into a linear model. The work gets organized as an ordered sequence of phases: initial access, expansion, privilege escalation, objective completion. Anything that doesn’t fit the sequence loses operational relevance. It gets noted, archived as “interesting,” and then dropped from the decisions actually being made. That is where context starts to erode.

Real operations rarely move in a straight line. They jump, stall, branch off. The pace changes, the objectives move mid-engagement, the people rotate in and out. Something found early looks marginal because you can’t act on it yet, something found later looks worthless because it has been cut off from the context that gave it meaning. The problem isn’t skill or method. It’s that each new piece gets judged only against the current state of the operation, not against everything learned so far.

treating the operation as a graph

A graph-based representation during the operation gets around this. The question stops being only “what’s the next step” and becomes the model you’re building of what you’ve seen so far. Each discovery is a node, each hypothesis or possible relationship an edge. Some paths lead somewhere quickly, others sit inactive for a long time. That isn’t the model failing. It’s how the model is supposed to work.

A finding that leads nowhere right now stays visible in the structure instead of getting buried in side notes. When new data shows up, you don’t have to consciously recall every past discovery: dropping the new element into the model surfaces whatever it connects to. Sometimes those connections matter immediately, sometimes they don’t. Either way you’re deciding from a full picture rather than from whatever you happen to remember.

What graph thinking buys you here is continuity. Every new piece of information has a place to go without overwriting what came before. It won’t hand you new compromise paths on its own, but it cuts the risk of losing a relevant correlation just because the operation moved on. You keep the map while you’re still moving through it.

shared context and continuity

This matters most when several people work the same engagement. Without a shared model, each operator builds their own limited view, tied to their window of time and their assigned tasks. The overall picture then rides on informal chat, individual memory, and who happened to be online when something turned up. A shared graph gives everyone one reference, and it records the interrupted attempts and the areas nobody has touched, not just the things that worked.

If you only keep the path that reached the objective, you get a thin story: how you got there, but not why that route was open in the first place. Keeping the dead ends, the unverified guesses, and the half-made connections tells you more about how the infrastructure actually hangs together and where it’s lopsided. Patterns start to show: clusters of critical nodes, isolated segments, machines that everything seems to route through.

This is not the same as drawing diagrams for the report. Those have their place for explaining things afterward, but they don’t change how the operation runs. What I mean is a living model, incomplete and sometimes wrong, that grows alongside the work. Hypotheses can stay hypotheses, connections can be flagged as uncertain, and interrupted paths don’t have to be tidied up. Which leaves the practical question: what does this model actually look like during a live engagement, given that no purpose-built tool exists for it (or at least none I’ve found).

putting it into practice

I looked for a purpose-built tool and didn’t find one. After a while I stopped looking, because not having one forced me to work out what the model actually needed to be, instead of inheriting whatever data structure some existing tool would have handed me.

In the meantime I improvised, keeping a Mermaid graph next to my operational notes. Each node was a discovery, each edge labelled with its current state: verified, suspected but not confirmed, known to exist but not traversable, or attempted and interrupted. Fine with four nodes and one brain. Pure panic with two neurons and a month-long engagement. Seemed simple back then, except I’ve since discovered I can DDoS my own brain.

What I did find, though, was that the core idea worked well enough: enough to pull up an older attack path or a forgotten branch of the operation when I needed it. The graph didn’t need to be clean or to match the original plan, it needed to reflect what I’d actually seen, including the parts that didn’t fit anywhere obvious yet.

The workaround worked, so the real tool waited, and this post waited with it: months sitting here, not unfinished so much as deliberately incomplete. Writing it was part of working out what a proper tool would need to do: arguing with myself about which constraints were real and which were just habit, what I’d accept losing and what I wouldn’t. I knew I could build something functional with AI, but I wanted to be sure of the underlying model first, because building on a vague intuition would have produced a vague tool.

The result is ops-board.

ops-board

Run it locally or deploy it. It ships with what a shared model needs, authentication and sharing included. You create an operation and open a canvas. Nodes are what you’ve found: hosts, identities, credentials, services, findings, open questions. Each node carries where it currently sits in the operation, and each edge describes the relationship between elements, including the ones that are blocked, incomplete, or not yet understood.

If any of this matches how your engagements actually go, clone it and try it on the next one: github.com/brmkit/ops-board. Issues and pull requests welcome, especially on the parts of the model I got wrong.


I built it with AI. I wouldn’t have learned the stack it needed on my own, not for this. What I had was a clear enough picture of what the tool should be, and that was enough to steer the prompting.