A post this week quoting a CISO put a plan many security teams share for 2027 into three words: “Desktops are dirty.” The plan is to move development and AI agent execution off employee laptops into isolated, ephemeral sandboxes, with tightly scoped credentials, controlled network access and a kill switch. We agree with the direction. A sandbox decides where an agent runs and what it can reach on the network. It does not decide, action by action, whether the agent should run this command, read this file, call this MCP tool or send this data to this host. A company needs both layers, and they work best when one policy drives them.
Why the laptop is the problem
Two incidents this month show why. On 21 October, a malicious LiteLLM 1.107.0 was published to PyPI, Docker Hub and GHCR, built from an attacker-controlled repository. LiteLLM’s own notice says the release carried compiled malware that steals credentials and loads remote-code execution tools, that it was on PyPI from about 11:39 to 13:23 UTC, and that on a host with outbound internet access exfiltration should be assumed.
On 29 October, Cursor reported that maliciously modified versions of Anthropic’s Claude Code extension, 2.1.312 and 2.1.313, which Anthropic did not build, were in the Cursor extension marketplace from about 2:27am to 1:35pm Pacific time, and that they attempted to install a persistent SSH backdoor. Microsoft’s analysis says the code ran when the extension loaded, added an attacker’s SSH key, tried to open a reverse SSH tunnel and set up persistence.
Neither needed an agent to choose anything. The package ran when Python started; the extension ran when the editor loaded it. On a developer’s laptop, code like that runs with everything the developer can reach: SSH keys, cloud credentials, tokens. A short-lived sandbox that holds only the credentials one task needs, and lets out only the connections it allows, shrinks what such code can take and how long it can stay. That is the sandbox’s job, and a per-action check does not replace it.
What the sandbox decides, and what it does not
A sandbox works at the level of the operating system and the network: which files the process can touch, which connections it can open, which credentials are inside, and when the whole environment is stopped and thrown away. Credential scoping and the kill switch belong there, and to the identity layer that issues the credentials.
Inside the sandbox, the agent still acts. It runs commands, reads files, calls Model Context Protocol (MCP) tools and fetches pages, with the credentials it was given and to the hosts it is allowed to reach. A sandbox sees connections, not calls. It can let the agent reach a code host; it cannot tell whether this command should run, whether this credential file should be read into the model’s context, whether this MCP server was ever approved, or what this upload to an allowed host carries.
| The question | Sandbox and identity layer | MoorAI |
|---|---|---|
| Where does the agent run, and when is it stopped? | Decides it: the environment, its lifetime, the kill switch. | Runs inside it, on a laptop, a CI runner, a container or a pod. |
| Which credentials can it use? | Decides it: what is issued into the sandbox. | Judges what the agent does with them: a credential read, a secret in a tool argument. |
| Which hosts can it reach? | By connection: addresses, and names where the sandbox supports them. | By call, before it runs: binary, host or address range, port, method and path. |
| Should this command, file read or MCP call run? | Not asked. | Allow, ask or deny, before the tool runs. |
| Was this MCP server, or this tool, approved? | Not asked. | The approved-server list, and tool drift since approval. |
One policy, wherever the agent runs
MoorAI decides each action of an AI agent before it runs, under a policy the console signs, and its records carry no prompt, file or code content. The same engine and policy run where the agent runs:
- On a laptop, in the agent host’s own hooks, such as Claude Code’s PreToolUse hook.
- On a CI runner or in a container, in server mode:
claude -pin CI, the Claude Code GitHub Action, an Agent SDK service in a container. - In a Kubernetes pod or a compose service, as a sidecar in the agent’s network namespace: the MCP gateway, the model proxy, or
moorai-servefor agent loops that are not Claude Code or the Agent SDK, all from theghcr.io/gitayg/moorai-serverimage. - In process, with
@moorai/agent-sdk, which returns the hooks for the Claude Agent SDK’squery().
And the egress rules in that policy, which say which binary may reach which host or address range, port, HTTP method and path, reach the network layer too: as the rules of an egress proxy that a network policy makes the only way out, as the network part of a Windows MXC or macOS Seatbelt sandbox policy, and as the allowed domains of Claude Code’s own sandbox. On macOS, an opt-in, experimental MCP server network sandbox starts each local MCP server under sandbox-exec built from the same rules; with egressDefault: "block" the server’s own connections and DNS reach loopback only. It restricts the network, not files or processes, so it is not full containment.
Set it up in five steps
1. Server mode on the runner or in the container
Server mode is on when the root-owned /etc/moorai/config.json says "mode": "server" or the environment sets MOORAI_MODE=server. The console binding comes from that system file first, then from MOORAI_SERVER_URL, MOORAI_TENANT and MOORAI_INSTALL_TOKEN; MOORAI_SERVICE_ID names the workload. In that mode:
- A repository cannot rebind it. A
MOORAI_*value set in theenvblock of a user, project or local Claude Code settings file is refused and reported as tampering, with a Critical content-free alert. A managed settingsenvblock is trusted. - An ask has no one to answer it, so it is denied with a reason, and one content-free alert records it.
- The actor is the workload. On GitHub Actions without a service id, it is
github:<repository>:<workflow>:<job>, so every run of a job reports as one workload. - It enforces without a token. With no token, the built-in defaults enforce; nothing is reported and no org policy is fetched.
The repository’s examples/server/ has a Dockerfile that registers the hooks in Claude Code’s managed settings (/etc/claude-code/managed-settings.json), which a repository the agent works on cannot switch off, and a GitHub Actions workflow. In a live claude -p run with server mode from the environment, a .env read was denied as a headless ask and the console received content-free reports under the workload’s name; an Agent SDK service and a GitHub Actions run have not yet been watched end to end.
2. Make the egress proxy the only way out
MoorAI’s hook judges egress rules from a call’s text: the command, the WebFetch URL, the MCP arguments. That is a guard for the ordinary case, not a boundary, because a script or a dependency that opens its own socket is never seen. moorai-egress-proxy applies the same rules to the connections a workload actually opens, and reads them only from the verified console policy and the root-owned machine config, never from a flag, the environment or a repository file:
moorai-egress-proxy # 127.0.0.1:8850
HTTPS_PROXY=http://127.0.0.1:8850 HTTP_PROXY=http://127.0.0.1:8850 http_proxy=http://127.0.0.1:8850 <agent>
A network policy makes it the boundary. In Kubernetes, deploy/k8s/moorai-egress.yaml runs the egress proxy, the model proxy and the MCP gateway in their own pod, and the agent pod’s NetworkPolicy allows only that pod and cluster DNS. With Docker Compose, deploy/compose/docker-compose.yml puts the agent on an internal: true network where only the MoorAI containers have a second, outside network. A tool that ignores the proxy variables, such as a raw socket, nc or ssh, connects directly, and the network drops it. TLS is not decrypted, so HTTPS is judged by host and port, and method and path rules apply to plain HTTP.
3. Generate the sandbox’s network policy from the same rules
node cli/sandbox-policy.mjs --target mxc|seatbelt --rules policy.json
The policy goes to standard output. Every rule the target cannot express goes to standard error with its reason, so nothing is dropped silently. Neither target can name a remote host by name. For Windows MXC, only numeric hosts and address ranges become rules, and the default stays deny (more in MoorAI and MXC). For macOS Seatbelt, sandbox-exec names only localhost or *, so egressDefault: "block" denies everything outbound except loopback. Host rules, and the binary, method and path fields, stay with MoorAI’s own check and are reported, not lost.
4. Write Claude Code’s own sandbox settings
moorai-native-settings compile --policy policy.json --host all --platform linux --out ./native
moorai-native-settings check --policy policy.json --host all
For Claude Code, compile writes managed-settings.d/50-moorai.json with the Bash sandbox’s network.allowedDomains and deniedDomains. With egressDefault: "block" it turns the sandbox on with its auto-allow off, so a Bash ask still prompts, sets failIfUnavailable, allows no unsandboxed retry and allows managed domains only. Whatever a domain list cannot carry is listed as unexpressed. compile never writes a system path; you or your MDM place the file. check then reports whether the file on the machine still says the same thing, and exits 3 on drift.
5. Watch that the sandbox stays on
A sandbox protects only the sessions that run inside it. From each enrolled device, MoorAI’s daily content-free heartbeat carries, per agent host, the settings that weaken protection, including sandboxOff, sandboxFullAccess and a bypassPermissions default or session, and the console raises Coverage: agent setting weakened and Coverage: agent active, no MoorAI hook traffic. When Claude Code runs with permission prompts bypassed, a verdict that would ask is denied with a reason, on any enrolled device.
What a security team gets
- One policy for two layers. The egress rules MoorAI judges on each call also write the sandbox’s network policy and Claude Code’s sandbox domains, and whatever a layer cannot express is reported.
- The same decision wherever the agent runs. A laptop, a CI runner, a container and a Kubernetes pod run the same engine under the same signed policy.
- A boundary for code that never asks. With a network policy behind it, the egress proxy judges every connection the workload opens, including those of a dependency.
- Records a SIEM can join. Alerts from server mode and the sidecars carry the workload: the pod, namespace and node from the downward API, and the container id where the runtime exposes it, never content.
Questions security teams ask
If our agents run in sandboxes, why also check each action?
A sandbox decides where the agent runs and which connections it can open. Inside it, the agent still runs commands, reads files, calls MCP tools and sends data to the hosts it is allowed to reach, with the credentials it was given. MoorAI decides each of those actions before it runs, under the same signed policy on a laptop, a CI runner, a container or a Kubernetes pod.
Can MoorAI write our sandbox’s network policy?
For Windows MXC and macOS Seatbelt, from the same egress rules MoorAI judges on each call: sandbox-policy prints the policy and lists every rule the target cannot express, with its reason. Neither can name a remote host by name, so host rules stay with MoorAI’s own check. For Claude Code’s own sandbox, moorai-native-settings writes the allowed and denied domains into managed settings and checks later that the file still says the same.
What happens when an agent in CI would need a human to approve?
In server mode no one is there to answer, so a verdict that would hold for sign-off is denied, with a reason saying this is a headless run, and one content-free alert records it. An org policy or the root-owned system file can choose allow-with-report instead; the environment can only choose deny.
Sources
- LiteLLM. Contained: Oct 21, 2026 LiteLLM release v1.107.0 Compromised, 21 October 2026, updated 23 and 30 October.
- Cursor. Security notice: malicious version of Anthropic.claude-code identified in the Cursor Extension Marketplace, 29 October 2026, updated 30 October.
- Microsoft Security. Inside a malicious Claude Code-branded extension and its persistent SSH backdoor, 30 October 2026.
- MoorAI. MoorAI on GitHub, agent v1.45.0 README: “Server mode (CI, containers, Agent SDK)”, “Server mode: Agent SDK and sidecar”, “Container image and sidecars”, “Egress proxy: egress rules on real connections”, “Sandbox egress policies from the same egress rules”, “One policy, each agent’s own settings files” and coverage integrity.
See also: MoorAI and Microsoft Execution Containers (MXC) and rolling out Claude Code or Codex in a regulated company.