skulk

Home  ›  Blog

22 September 2026

Correlation is not a feature. It is the product.

The follow-up to "Four tools said fine": how four surfaces are read one at a time, as all access is granted, then joined into one model before a single check runs — and why the triage after that is mechanics you can read rather than a score you have to trust.

Last time I told you about Sarah’s refactor at Northwind — the one where a tenant filter moved from before a search to after it, four security tools each honestly reported clean, and every customer’s search ended up running across every customer’s documents. The finding lived in three facts held together, and no single tool was holding all three.

That post was the story. This one is the machinery: what it actually takes to hold four surfaces at once, and what happens after you do.

Every security platform advertises correlation. Almost all of them mean the same thing by it: detect first, correlate later. Each scanner produces its own findings from its own surface, and then a layer on top — rules, ML, a graph database, a “context engine” — tries to stitch the alerts together after the fact. The stitching is where the noise comes from, and it is why correlation features ship with tuning guides.

Skulk is built the other way around. The join happens before detection, not after it. And for the class of risk that matters in AI applications, that inversion is not an implementation detail — it is the only architecture that can see the findings at all.

The finding that lives in no single place

Sarah’s finding is the general case, so it is worth stating plainly: a document search runs with no entitlement filter, against an index that holds more than one customer’s data.

Look for it in any one place and it is not there. The code is a search call — unremarkable. The store is an index with documents in it — unremarkable. The fact that the index is multi-tenant lives in nobody’s configuration; it lives in what the team knows. Three unremarkable facts, one serious finding — and it exists only in the join.

Post-hoc correlation cannot produce this finding, because no upstream scanner produced an alert to correlate. There is nothing to stitch. The finding is born joined or it is not born.

“Isn’t your collection sequential too?”

Yes — necessarily, and so is everyone’s. The four surfaces are gathered one at a time because access arrives one at a time: a deploy key one day, a cloud role after your security team reviews the printed policy, a database grant after theirs, a conversation with your engineers somewhere in between. There is no such thing as simultaneous collection, for any vendor.

The split in the industry is not about how the inputs arrive. It is about when judgment happens. Detect-then-correlate tools evaluate each surface as they read it — code findings from the code pass, cloud findings from the cloud pass — and only then try to stitch the alert streams. Skulk evaluates nothing until everything collected sits in one linked model. Collect sequentially; judge jointly. An auditor’s documents arrive on different days from different departments, and nobody calls the audit “three separate audits” — the audit is the act of holding them on one desk and finding where they disagree.

You do not have to take the architecture on faith, because the proof is in the artifact you are handed. Open the cross-tenant finding and read its evidence block: a code line, the store’s schema, and your declaration — three surfaces cited on one evidence line, which a per-surface pipeline structurally cannot produce. Or run the logic in reverse: decline a surface, and the ledger names the cross-surface findings that became impossible — not merely that surface’s findings. That subtraction pattern is exactly what joint evaluation predicts, and only what joint evaluation predicts.

Four surfaces, one snapshot, one model

Skulk reads four surfaces — your repository, your cloud control plane, your stores, and your declared intent — into a single persisted snapshot. From that point on, nothing downstream touches a network: the linker that composes the joined model is pure, by rule. Identities are resolved across surfaces, declared agents are matched to collected principals, policies are expanded from definitions captured at collection time, and workload identity is built into one graph with one question — whose permissions does this principal effectively hold? — answered the same way whether the mechanism is an AWS assume-role chain or a GCP service-account impersonation.

The purity rule sounds academic. It is the commercial promise: the same snapshot produces the same findings, every time. Which means that when you re-assess after a quarter of fixes, the diff is real. What changed is what you changed — not scanner drift, not tester variance, not a model that reranked your risks overnight.

The checks are the correlation rules

Because the model is joined before evaluation, the checks themselves are the joins — written down, one by one, readable.

The cross-tenant flagship is a three-way join: a query site with no filter, an index that exists, a declaration that the index is multi-tenant. The exfiltration checks are graph traversals: a path from sensitive data to a model invocation, through however many identity hops it takes — and the whole chain ships as the evidence, hop by hop. The drift family is the sharpest form: join as disagreement. You declared row-level security on; the live catalog says off. You declared this agent runs as this identity; the deployed configuration says otherwise. Where reality disagrees with what you decided, the disagreement is the finding — and no tool that never collected your decision can fire it.

That last clause is the moat, stated plainly. The fourth surface — intent, written down — is collected in a scoped conversation with your engineers. A continuous scanner has no equivalent input, not because its engineers are worse, but because its delivery model never asks.

Triage you can audit

After evaluation, most platforms hand you a risk score. Skulk hands you mechanics — deterministic, and few enough to list:

  • Confidence follows evidence. A finding supported by your declaration carries full severity; one built on observation alone is capped lower. The rule underneath is principled and strict: observations may calibrate severity; declarations may not soften it. Declaring something can create a finding or escalate one. It can never quiet one — so there is no incentive to declare your way to a cleaner report.
  • Escalation is conditional on evidence, not weighted. An exposed policy escalates under internet exposure. An agent’s egress tool escalates when the tool is internet-facing. Conditions you can check, not coefficients you can’t.
  • Compensating controls are credited at their measured strength. The one downgrade that exists — model-invocation logging — ships with its sampling rate, because logging at ten percent is ten percent of a compensation.
  • Related findings co-exist; they are never swallowed. A shared identity fires its own finding, and its downstream consequences fire theirs, each referencing the root. And when the runs-as identity itself is wrong, the findings that depended on it are marked unsound rather than left standing on a false premise.

Then the census closes the loop from the other side: everything that did not join is accounted for. The agent observed but never declared. The store declared but never granted. The file that would not parse. The permission paths that could not be evaluated under the granted scope — stated on the finding itself, in both clouds’ own terms, because the absence of a path is not proof that none exists.

What is deliberately missing

No ML ranking. No aggregate risk score. No fuzzy deduplication. No “potential issue” tier. No suppression logic to tune.

That is not a roadmap gap; it is the design. Deciding what your organization fixes first is triage that belongs to you — it depends on your customers, your contracts, your quarter. What a vendor owes you is not that decision made opaquely on your behalf, but the material to make it well: findings that are born joined, evidence on every line, severity you can recompute by hand, and a re-run that measures your progress instead of remixing it.

One engagement. One report. Every claim traceable — including this one.


Skulk runs two engagements: a design review in the architecture phase, and an automated posture assessment before release, across AWS, Google Cloud, and self-hosted stacks. The scoping conversation costs nothing — support@skulksec.com.

Earlier posts

Two engagements, two moments in the lifecycle. A design review before you build, and an automated posture assessment before you ship. Scoping costs nothing.
support@skulksec.com