← Back to blog

Three containers became one process, and the security got better

The original design had three containers — the agent, the SIEM toolset, the internet toolset — talking to each other over HTTP. The shipped design is one process with internal modules. Collapsing them removed an attack surface rather than adding one, and the reason why is a useful correction to an instinct most of us have.

Why it was split in the first place

The reasoning was conventional and, in a different context, would have been right. Separate the components that touch customer telemetry from the components that reach the public internet. Give each one its own container, its own identity, its own network policy. Scale them independently. Segment the blast radius.

That is standard practice, and I did not question it until the deployment model changed.

What it actually cost

Three containers meant three images, three build paths, three deployment steps, and a config surface that had to stay consistent across all of them. Every tool call became an HTTP request to a service sitting on the same host, which means serialisation, a network hop, timeout handling, retry semantics, and a failure mode where a tool call fails for reasons that have nothing to do with the tool.

It also meant authentication between my own components. Which is either a real control or theatre, depending on your threat model — and that question turned out to be the whole argument.

The split boughtThe split cost
Segmentation between internal componentsAn auth surface between components that trust each other anyway
Independent scaling, in principleNothing ever needed to scale independently
Clean conceptual boundariesThree build and deploy paths for one logical unit
—Debugging across process boundaries for synchronous, co-located calls

The correction: which boundary is actually doing the work

In a provider-hosted design — where the agent runs in my cloud and reaches into the customer's tenant — segmentation between my components is a meaningful control, because my infrastructure is the thing standing between the customer's data and the internet.

Once the deployment model moved to running entirely inside the customer's own tenant, that stopped being true. The security boundary that matters is the tenant boundary itself. All investigation data — telemetry, intermediate findings, state, audit records — stays inside the customer's own subscription. The only thing that exits is model inference. In that picture, a network hop between two of my modules on the same host is not a control. It is an additional listening socket, an additional credential, and additional code, all inside the perimeter that actually matters.

The rule. Segmentation between components that fully trust each other and die together is not a security control. It is surface area wearing a security costume.

What collapsing bought

Tool calls became direct function calls: no serialisation, no network failure mode, no auth. One image, one deploy, one process to debug, one place where a stack trace tells the whole story. Startup got simpler, which matters more than it sounds once you are running a container per tenant across an unknown number of tenants.

It also made the dependency footprint smaller, because the HTTP client that had been carrying inter-component traffic went back to doing only what it should: talking to external APIs.

What stayed separate, and why

One component did not get absorbed, and the reason is the test for all of this: it sits in a genuinely different trust domain.

There is a small bootstrap service that runs on my infrastructure, not the customer's. It handles licence validation and delivers the prompt bundle to the agent at runtime, which is the mechanism that keeps the prompts from ever sitting at rest inside a customer-controlled container. That service and the agent do not trust each other by default, they have different lifecycles, they are operated by different parties, and the network between them is genuinely hostile.

So it is a separate service with real authentication, and that authentication is not theatre. The distinction is not "monolith good, services bad" — it is that a process boundary should exist where a trust boundary exists, and nowhere else.

The general version

Microservice instincts are trained on a context: independently scaling components, separate teams, separate deploy cadences, mutually distrusting domains. When none of those conditions hold, the pattern inverts and every boundary you add is pure cost — paid in build complexity, debugging time, and, in a security product, in surface you then have to justify to a buyer who is asking what your opaque container is doing with their data.

Who is writing this

I am Ivan Melekhin. Twenty-five years in cybersecurity, most of the last decade running security operations — building and operating distributed SOC and MSSP teams across Asia-Pacific, with a long detour through OT and maritime environments. This log is the build record for an autonomous SOC investigation agent I started in January 2026, written as the decisions happened rather than tidied up afterwards. I am on LinkedIn if you want to argue with any of it.