Marcus: seeing the relationships between projects
August 30, 20265 min read
I have forty projects on disk, spread across three companies and a dozen clients, hanging off some twenty paid services. No surface anywhere told me how they relate to each other.
That isn't an aesthetic complaint. It cost me money: I paid for two services for months while not a single project used them. I didn't fail to cancel them out of carelessness — I had nowhere to look that would have shown me.
The three questions I wanted answered at a glance:
- If this service goes down, which clients break?
- This client pays me X — what does it cost me to serve them?
- What am I paying for that nobody uses?
The name
In the Twilight lore, the Volturi are the ruling authority among vampires, and one of them — Marcus — has an unusual gift: he sees the bonds between people. He walks into a room and knows who leads, who owes loyalty to whom, which relationship is about to break. He doesn't read minds; he reads structure.
That's what I wanted for my projects. Point at a node and see what it hangs from and what hangs off it.
I built the viewer. It was fine. I threw it out.
I wrote a layered graph in hand-rolled SVG: clients on the left, then projects, then services, then the hosts everything lives on. Money enters from the left, dependency propagates right. No libraries — five hundred-odd lines, with zoom, per-project favicons and a rank bar under each box.
It worked. And halfway through building it I noticed something uncomfortable: I was reinventing a graph viewer. Obsidian has shipped one for years, with zoom, folder filters, color groups and — the thing I wanted most — a local graph: open a note and see only its ties.
I deleted the five hundred lines. It hurt less than I expected.
What did survive
Here's the part that matters, and the reason the project wasn't wasted time.
Obsidian draws relationships you already wrote down. If I had to hand-document which project uses which service, I'd own a file that starts lying within three weeks: dependencies change, notes don't. A hand-written registry rots.
The hard part was never drawing the graph. It was knowing the edges without writing them. And that can be derived from sources that already exist and that nobody maintains on purpose:
- The code — a detector that reads declared dependencies, environment variable names and endpoint literals, and returns the
file:linethat proves each claim. Prose doesn't count: a comment mentioning a service is not an integration. - The books — my Firefly III instance knows what gets charged, how much and how often. Cost per service and revenue per client come from there, without me typing a number.
- DNS — and this one was the surprise.
One script walks all three, does the join, and generates the Obsidian vault: a note per node, [[wikilinks]] for edges, the numbers in frontmatter. It regenerates wholesale whenever I want. You don't edit it by hand; if a fact is wrong, you fix it at its source.
The lesson I keep: some facts don't live in the code
I wanted to see which projects have mail on the server I run on my VPS. I searched all forty repos and found nothing — because there is nothing to find: a mail server is infrastructure, not a dependency a project declares.
Same pattern, measured: the word that identifies my VPS appears across all forty projects' code exactly twice, and both are comments.
The real evidence lived somewhere else: the MX records. One DNS lookup per domain and the edge shows up on its own, with hard proof — MX mydomain.com -> mail.myserver.com. No file-content detector, however clever, was ever going to reach that.
Which gave me the rule I'm keeping: before hardening a detector for the third time, ask whether the fact lives in the files at all. If it doesn't, no amount of cleverness will find it — change the source, or declare it by hand.
And a nice stumble along the way: at first I counted "has mail here" for any project that referenced my CDN, because the CDN shares a domain with the mail. Eighteen projects. False — using my CDN is not having mail. Excluding shared-infrastructure domains left seven real ones, each with its own domain. A soft inference dressed as a hard edge looks exactly like a finding, right up until you read it slowly.
The other thing I learned, while drawing
Before throwing the viewer out I gave it an interaction: hover a node and everything it reaches lights up. I tried it on my CDN and it lit 22 of 23 nodes.
Obvious in hindsight: in a connected graph, the transitive closure reaches everything. CDN → projects → other services → other projects → the world. The answer was technically correct and completely useless.
The fix was making the walk monotone: from the node you travel one way and the other way separately, never bouncing back. From a service you reach the projects that use it and the clients of those projects, and you stop. Eight nodes instead of twenty-two — and those eight are the answer to "what dies if this dies?"
That distinction outlives the viewer. It's what makes "seeing the relationships" mean something instead of lighting up the whole screen.
Where it landed
Two scripts and a vault. Obsidian does the pretty viewer; I kept the part nobody else could do for me.