The uncomfortable question
In the past few months, I’ve been hearing different versions of the same question: under what conditions can we safely allow an autonomous system to operate a machine?
It comes up when my colleagues discuss how much autonomy we should give our agents. It comes up when people outside the software industry wonder how to secure their OpenClaw or similar installations. And it’s my own question when I wonder how much of my Kubernetes cluster my agents should control.
Let me be upfront: I don’t have the answer. As with security, the answer depends on the system we’re talking about. There is no one-size-fits-all answer in the cloud native ecosystem. But as someone directly involved in the development of an operating system, and who’s recently gotten heavily invested in software factories, I’d like to share what I think it should look like at this layer of the cake.
By software factory, I mean the machinery that turns a proposed change into a running artifact: source control, review, CI, tests, image builds, signing, release, and deployment.
Root is not an API
I know it feels amazing to give an agent root access to a machine and watch all the impressive things it can do. I recently migrated a home service from a MacBook to a Linux box without downtime, and all it took was giving the agent superuser access.
But just because it can be done doesn’t mean it should be done. Experimentation and production are very different environments, and both come with different expectations. If you give an agent root access and expect a well-written AGENTS.md file to prevent an unrecoverable mistake, you are fooling yourself. The system, however capable it may be, is still nondeterministic, so you cannot guarantee the outcome.
This is not simply about failures. APIs can contain bugs and fail too. It is about reproducibility: knowing what changed and tracking who or what changed it. We should not give agents unrestricted root access to production systems for the same reason we should not give humans unrestricted root access to them. Period.
Containers changed the boundary
In the traditional Linux paradigm, if an application needed something, such as a library, we installed it on the base system. The problem was that the application and the system became tightly coupled: changing the application also meant changing the machine underneath it.
Containers changed that boundary by packaging an application’s userspace dependencies with the application itself. They do not make the host irrelevant; containers still share its kernel and depend on its runtime. But they give us a much cleaner separation of concerns between the application and the system that runs it.
The host’s job can then become much narrower: boot reliably, provide the kernel and runtime capabilities the platform needs, and keep that platform running. The platform, in turn, takes care of the containerized applications.
Immutability is a means to an end
Immutable systems reinforce this boundary. That doesn’t mean the entire system is immutable; that would be useless. Depending on the implementation, selected parts of the filesystem may be mounted read-only or the base system may be treated as an image that is updated as a unit. If the base system needs to change, you define and build a new system image rather than modifying it package by package at runtime.
But preventing drift is not the end goal. The more important property is that the machine moves from one defined state to another. During runtime, an agent can inspect the system, while changes to the base system are expressed in a versioned definition and consumed as a new image. Runtime is no longer where the next state is improvised; it is where a state produced somewhere else is executed.
Will mistakes still slip through? Of course. They do in every software factory: lights on or lights off, agent or human. But when source, build and deployment provenance are retained, we can trace a problem back to the commit and image that introduced it. We have relied on this property in software delivery for a long time; the same principle can apply to pods and to the operating system itself.
At the time of writing, the cloud native ecosystem includes technologies that support image-based, immutable host lifecycles in different ways: bootc, Flatcar Container Linux and Kairos. This is probably the moment when I should acknowledge that I’m a Kairos maintainer. But I’m writing this piece as an Ambassador, so I will keep the argument at the architectural level rather than advocating for one implementation.
This is only the first boundary. An immutable system is not completely immutable, and controlled state transitions do not provide every guarantee an autonomous operator needs. What they give us is something a root shell does not: an explicit path from intent to a reviewed and reproducible system state.
Immutability is not a sandbox
But here comes the tricky part. Agent software does not normally come from the distribution. It may be installed through a language package manager such as npm, shipped as a standalone binary or container image, or integrated into another application you run.
This is why agent harnesses offer sandboxing: to put a stronger boundary around what the agent can do. But not every sandbox is a sealed environment. Local harnesses such as Claude Code or Codex may still be connected to the host through the filesystem, credentials, tools, sockets, network access or permissions granted during a session. The CNCF blog has already explored why sandboxing an agent is not sufficient and how agents can use capabilities we did not anticipate.
An immutable host still gives us an important property: installing, changing or upgrading the system packages that make up the base system is removed from the normal runtime lifecycle. Hadron, a minimal base OS developed by the Kairos team for building image-based systems, takes this further by not providing a package manager at all. The strength of that protection depends on the implementation; immutability alone does not protect the machine from a process with unrestricted host, kernel or disk access. The agent can also still affect every surface where it has write access. The mistake would be to assume that because the base system is protected, the rest of the machine is protected too.
The agent is a workload too
This is where the cloud native separation of concerns becomes useful again. The agent is a workload too, and each session can run in a short-lived execution environment created for the duration of its work and discarded afterwards.
The execution boundary should follow the threat model. A standard pod shares the host kernel and may be appropriate for a narrowly scoped agent, while an agent that runs untrusted code or needs broader capabilities may require a sandboxed pod or VM. The CNCF blog has examined whether a pod is the right deployment unit for an agent in greater depth. For this argument, the important point is that the agent does not need direct access to the host in order to observe it or propose how it should change.
Close the loop without opening the host
But if agents run in isolated workloads, why do we still need the immutable host? In my opinion, the most valuable promise of agent-led systems is that they can help systems self-heal and self-improve. An agent that understands how the system is running can diagnose issues, suggest fixes, improve performance and propose changes to the host system.
These are not the same kind of autonomy. Self-healing means choosing from responses we have already authorized, such as restarting, replacing or rolling back a workload. Self-improvement means proposing a system state that we have not authorized yet, so it must go through the software factory before it becomes real.
Recently, during exploratory QA of Kairos Trusted Boot images, an agent helped trace multi-minute boot stalls and firmware reset loops to incorrect PE section alignment in go-ukify, the library AuroraBoot uses to assemble UKIs. Sections were being aligned to 512-byte boundaries even though the systemd stub required 4 KB alignment. We fixed the library, released go-ukify v0.5.2, updated AuroraBoot and had the agent verify the corrected images, so Kairos and Hadron now pick up the fix.
What the agent should not do is apply those changes through root access or modify a mutable system in place. Starting from the question of autonomy, an immutable system is clearly a better model than a mutable one: the agent can inspect the current state, record its assessment and open a pull request proposing the next state. That proposal can follow the same process as a human-authored change: review, testing, a new image build, signing and release. Only then is the new state consumed by the system itself.
For this to be a real boundary, the agent’s identity must be allowed to propose a change without being allowed to approve or merge it, alter the pipeline, publish or sign the resulting image, or force the machine to consume it. Otherwise, we have only replaced root access with root access plus more steps.
This separation of authority is part of the broader CI/CD threat model covered in Shadow AI in CI/CD. Here, it is what turns an agent’s observation into a proposal for the next host state rather than another path to mutate the current one.
That closes the feedback loop without opening the host. The agent can contribute to the evolution of the system without being allowed to rewrite the system directly.
Trust the boundaries, not the model
Immutability is not sufficient by itself, and neither is workload isolation. Together with a software factory, however, they give us a layered model: protect the base system from runtime changes, isolate the agent according to its capabilities and threat model, and turn any proposed improvement into a reviewed and reproducible state transition.
We do not need to trust the model. We need to trust the boundaries around it and the process through which its proposals become system state.