Agent Compute Engine: The Credential Never Enters the VM

If your agent holds an API key, one injected sentence can read it. Google's Agent Compute Engine removes that condition. The key never enters the sandbox. A gateway outside the microVM adds it to each outbound request, after it checks who is asking. This essay reads that mechanism and turns it into a pattern you can build on any cloud.

Google Cloud announced Agent Compute Engine on October 22, 2026, at AGNTCon + MCPCon. The post says the service already runs the Gemini agent for work tasks. A customer preview opens "in the coming months." A technical deep dive by Ramanathan Venkatapathy and Rohit Jnagal, both Distinguished Engineers, appeared the same day and gives the mechanism. The service is not open yet, so nothing here was run on it. Every claim below comes from those two posts and the linked Google Cloud documentation. I read them on the day they appeared and checked them again on November 3, 2026.

This site read the open-source sibling in September. Agent Substrate showed how suspended agents share a warm pool of worker pods on Kubernetes. Its egress contract showed a gateway that ignores what an agent claims about a destination. In September that gateway answered 501 to any credential injection rule on the default install. Substrate v0.4.0, released October 8, 2026, moved injection out of its experimental flag. Agent Compute Engine is the managed service where the same ideas run for Gemini today. Google's announcement keeps Substrate on GKE as the choice for teams that want Kubernetes and portability. This essay skips the pool and the contract. It reads five parts that are new here. They are the microVM on an offload card, the identity the VM holds, the credential added at egress, the idle checkpoint, and the lasting disk.


Machine · An agent microVM with an egress airlock

One session as the two posts describe it. A harness outside the VM sends commands. A Cloud Hypervisor microVM forks from an OS template on a Titanium node and gets a session badge from the node. Requests leave over vsock to a local proxy, then through an mTLS tube to the Agent Gateway, where a vault adds the credential as the request leaves. A Hyperdisk holds the workspace, and an idle checkpoint parks memory on a shelf. The flow and the identity rules are from the deep dive. The timing, the backend, and the badge rising from the IPU chip are illustrative.

Hands outside the brain

The deep dive splits every agent into two parts. The harness is the brain. It calls the model and decides the next step. The sandbox is the hands. It runs the code, the shell commands, and the browser that the model asked for. In Agent Compute Engine the harness runs outside the sandbox, and each product team writes and trusts its own harness. The guest only executes explicit commands that the harness sends, and it streams the output back. Each tool call in the harness then maps to one burst of activity in the sandbox, which gives an audit trail.

The model API key belongs to the harness, so it never enters the VM. In most self-hosted setups the loop and the shell share one process tree and one filesystem. The provider key sits in the same environment as the untrusted tool output. The eval sandbox that held the keys had that shape. Moving the loop out of the box is the first half of the design. The second half is what the box is allowed to hold.

A microVM on an offload card

Each session gets its own microVM on the open-source Cloud Hypervisor, with its own guest kernel and root access inside. The announcement calls the boundary hardware-isolated. The deep dive lists what sits under it. The node runs as a stateless appliance with a read-only root filesystem under dm-verity and no persistent local state. Google's Titan chip verifies the firmware and the boot before any workload is scheduled. The Titanium IPU is the offload processor on the node. The private keys for node and workload attestation stay outside the guest, in the IPU, the host, or a remote service. The IPU also mediates every block storage attachment with a grant bound to one instance. So a compromised sandbox, and even a compromised host OS, cannot mount a volume that belongs to another tenant.

The network is the part to copy. The microVMs have no standard virtual network interface, no default gateway, and no direct VPC access. Every packet in or out crosses a virtio-vsock boundary to a local, unprivileged proxy on the node. That proxy forwards to an external proxy, which checks the request scope and applies the egress policy. The guest also has no access to the cloud metadata server. An agent that runs ip route inside the box finds nothing to route to. It can only talk to the proxy, and the proxy only talks to the gateway.

Cloud Hypervisor is a Rust VMM that runs on any Linux host with KVM. The command below boots one, from its snapshot guide. The command has no network flag. That is the shape Agent Compute Engine describes, with a vsock device for the proxy path in place of a network card.

./cloud-hypervisor \
    --api-socket /tmp/cloud-hypervisor.sock \
    --cpus boot=4 \
    --memory size=4G \
    --kernel vmlinux \
    --cmdline "root=/dev/vda1 console=hvc0 rw" \
    --disk path=focal-server-cloudimg-amd64.raw,image_type=raw

From docs/snapshot_restore.md in the Cloud Hypervisor repository, not run here.

What the VM is allowed to hold

The deep dive states the rule in one sentence. Assume an attacker controls everything inside the sandbox, and make sure they find nothing there to use. The example is an email agent that reads a message with hidden text. The text asks it to find API keys on the machine and send them to an address. Most of the time a model ignores that. Sometimes it does not. So the design does not depend on the model. The guest holds no persistent secrets, no OAuth refresh tokens, and no service account keys, in memory or on disk.

What the guest does hold is one identity. At start, the platform gives the microVM a short-lived SPIFFE certificate and identity token, scoped to the session, through a per-VM local listener. SPIFFE is the open standard for workload identity. The identity has one job. It says which sandbox sent a packet. The deep dive is explicit that this identity cannot call a backend API directly if it is taken off the machine. Even the private key for the mTLS tunnel is held outside the guest. The guest presents a certificate it cannot sign with.

There are four identities in total, and only one lives near the sandbox. The user identity authenticates the person's request for the whole task. A delegated identity proves that the user authorized this agent to act for them. Those grants live only in a central authorization service, never on the node. The workload identity is the sandbox's own, described above. A workforce identity lets an agent act as your assistant with its own account and resources. The gateway translates the workload identity into the others, off the node, after it verifies the host, the user, the sandbox, and the session.

The public Agent Identity docs show what the format looks like outside of Agent Compute Engine. A SPIFFE ID reads spiffe://TRUST_DOMAIN/resources/SERVICE/RESOURCE_PATH. The X.509 certificate is valid for 24 hours and is renewed by the platform. Access tokens are bound to that certificate, so a copied token fails from another machine. The docs list Agent Runtime, Gemini Enterprise, and Cloud Run as the services that support it today. Agent Compute Engine is not on that list yet, so its certificate lifetime inside the VM is not published.

You can audit any sandbox for the same property with four commands. Run them from inside the sandbox, as the agent would. Every line should come back empty or refused.

# run inside the sandbox: what an injected instruction would find
env | grep -iE 'key|token|secret|password'
ls -la ~/.config/gcloud ~/.aws ~/.ssh 2>/dev/null
curl -s --max-time 2 -H 'Metadata-Flavor: Google' \
  http://169.254.169.254/computeMetadata/v1/ || echo 'no metadata server'
openssl x509 -in /run/identity/cert.pem -noout -ext subjectAltName -dates

My self-test. The certificate path is illustrative. The metadata endpoint and header are the documented ones for Compute Engine.

The credential is added at egress

Here is the path of one outbound request, as the deep dive gives it. Code inside the sandbox opens a connection. The local proxy on the node intercepts it and binds the node's attestation to it. Then it forwards the request to the Agent Gateway, which is off the node. The gateway validates three things: the user's delegation, the agent's identity, and the node's identity. Then it evaluates the egress policy and the data loss prevention policy for that agent. If all of that passes, it performs what the post calls an in-flight token swap. It inserts the backend credential into the request as the request leaves the gateway. The backend sees a standard request with a verifiable identity that says an agent acted for a specific user. The sandbox never saw the credential.

Two details matter for the pattern. The credential is inserted after the destination is decided, so a forged Host header buys nothing. The earlier egress essay covers why. And the credential is inserted only for the destination the policy allows. A request to an unlisted host gets no token and no route. The figure below shows the same request as each hop sees it.

Fig. 1 · Hop-by-hop header rewriter

One outbound request, GET /drive/v3/files, as each hop sees it. Step through the hops. Toggle a condition to watch which hop refuses and what the backend receives. The hop order and the checks come from the deep dive. Header names beyond the request line, Host, and Authorization are not published, so the figure labels them by role.

The sandbox id, the Drive request, and the order of the five checks are illustrative. The deep dive names the three identities in the triad, the egress and DLP policies, and the token swap. It does not publish header names or the order in which the checks run.

Enterprise connectivity uses the same path. An administrator defines which VPC endpoints, Workspace APIs, or external domains an agent can reach. The gateway enforces it with Private Service Connect and VPC Service Controls. The deep dive says no enterprise credential enters the sandbox for any of it. In your own build, keep the keys outside the sandbox and add them at the egress proxy.

Idle is a signal from the runtime

When Google measured agent sessions, agents spent most of their lives waiting. They wait for the model's next step and they wait for the person. At any moment only a small share of open sessions run code. The post publishes no percentage. Reserving a VM for every open session would charge for all that waiting.

A normal hypervisor cannot tell a guest that works from a guest that waits. Agent Compute Engine solves that above the hypervisor. The sandbox command runtime was built together with the host executor, so the host knows what the agent is doing. The moment a tool call finishes and the loop returns to the model, the sandbox signals that no command or background task is in flight. The host then checkpoints the microVM and marks it idle. An idle session holds zero active resources on the host. The next tool call restores it. The same machinery gives the harness clone, fork, rewind, and rollback. Google DeepMind's Antigravity uses the fork to run parallel experiments from one snapshot, per a quote in the post.

Compare that with Agent Substrate, where the runtime never suspends an actor on its own and leaves idle detection to the layer above. Here the signal comes from inside the sandbox runtime, which is why it can be immediate.

Memory is the other half. Hundreds of VMs on one host run the same base image, the same Python, and the same browser. Agent Compute Engine builds pre-warmed snapshot images with the binaries already initialized. The host maps the snapshot into physical memory with DAX and clones it copy-on-write. Every microVM on the host then shares the same physical pages for the base OS and libraries. Physical memory is spent only on the private pages that each agent changes. Reclaim and compression return freed pages to the host pool.

Cloud Hypervisor exposes both halves to you. Its snapshot guide documents pause, snapshot, and restore. Version 54.0, released October 14, 2026, added a copy-on-write restore mode. Many VMs restored from one snapshot file share the page cache, and each VM's writes stay private. That is the template fork from the machine above, on your own host.

# pause, snapshot, and restore one Cloud Hypervisor VM
./ch-remote --api-socket=/tmp/cloud-hypervisor.sock pause
./ch-remote --api-socket=/tmp/cloud-hypervisor.sock snapshot file:///var/snapshots/s-7f3

# later: restore and resume in one call
./cloud-hypervisor --api-socket /tmp/cloud-hypervisor.sock \
    --restore source_url=file:///var/snapshots/s-7f3,resume=true

# many clones from one template snapshot share the page cache
./cloud-hypervisor --api-socket /tmp/ch-clone-1.sock \
    --restore source_url=file:///var/snapshots/template,memory_restore_mode=copyonwrite

Commands and flags from docs/snapshot_restore.md, with my paths, not run here.

Read the constraints before you depend on it. In copy-on-write mode the snapshot file must stay on disk, unchanged, for the life of every VM restored from it. Truncate it and the guest gets SIGBUS. A snapshot also captures the guest's random number state. Every clone starts with the same /dev/urandom stream until something reseeds it. Cloud Hypervisor's VM Generation ID device changes on every restore, and a kernel built with CONFIG_VMGENID=y reseeds when it sees the change. A program that seeded its own generator keeps the old seed. Reseed it after a restore.

The idle signal is yours to build. In Agent Compute Engine the sandbox runtime raises it. In your build the harness is the part that knows. It sees every tool result arrive. Pause the VM when the command queue is empty and no background process is registered. Resume it before the next command. That is one ch-remote pause after each tool result and one resume before the next.

A disk that outlives the VM

The deep dive tells a two-day story. On Monday an agent installs two Python libraries, writes a script, and saves a chart. On Tuesday the person asks a follow-up. If the agent has to reinstall and rewrite, the person waits and the answer can change. So every session needs a disk that lasts. A full disk per agent would exhaust storage and disk attachment slots long before CPU, because most of those disks would sit empty.

The answer is to sort writes by kind. Scratch data, such as logs, build artifacts, and container-layer churn, goes to a fast, disposable overlay. Read-only data, such as base images, toolchains, and package caches, is mounted once and shared by every sandbox on the host. Only the user's working state and saved artifacts are persisted. Teams can also mount a shared dataset over FUSE on Cloud Storage, with the credentials held on the host side. Each tier is served from Hyperdisk unless the post says otherwise, and it is encrypted at rest and in transit. The IPU grant from the first section is what lets a disk follow one session and no other.

The consequence is that the VM itself is disposable. Memory lives in the checkpoint. Working state lives on the disk. The OS template under them can change between sessions. The figure sorts a Monday session into the tiers and shows what Tuesday finds.

Fig. 2 · Storage tier sorter

Monday's session writes eight things. The shared base supplies two more, the Python runtime and a cached openpyxl wheel. Each lands in a tier by its path: a disposable overlay, a read-only shared volume, the persistent workspace, or memory. Press an event and count what Tuesday finds. The tiers come from the deep dive. The paths, the mount points, and the writes are illustrative.

overlay · disposable

shared volume · read-only

workspace · Hyperdisk

memory · checkpoint only

Survival rules: the overlay and memory survive a checkpoint and nothing else. The shared volume and the workspace survive every event. A new template swaps the shared base and leaves the workspace untouched.

The same shape, three more times

Perplexity open-sourced SPACE on October 15, and it keeps network access and platform-held credentials outside the sandbox. Its red team found no escape from the VM to the host in 108 attempts. On October 22 the Agentic AI Foundation accepted OpenSandbox from Alibaba, and its README reports Firecracker creation at 64 ms at the median. Agent Substrate on GKE now injects credentials at its egress gateway, as its injection guide documents. The actor sends a placeholder header, and the gateway replaces only that header, which differs from the Envoy filter below. SPACE and Substrate keep the credential outside the sandbox, as Agent Compute Engine does. OpenSandbox shares the microVM boundary and the pause and resume.

Build the airlock on your own cloud

The portable pattern has three parts. The sandbox sits on a network with no route out except to one proxy. The proxy runs outside the sandbox, holds the credentials, allows a fixed list of hosts, and inserts the credential on the way out. The sandbox proves who it is with something that is useless anywhere else. The example below builds the first two parts with Docker Compose and Envoy. The third part is the network membership in Compose, and SPIFFE through SPIRE in production.

Compose has an internal flag that creates an externally isolated network. The sandbox joins only that network. The proxy joins it and the default network, so only the proxy can reach the internet.

services:
  sandbox:
    image: agent-sandbox:latest
    networks: [airlock]
    environment:
      GITHUB_API: http://egress:10000
  egress:
    image: envoyproxy/envoy:v1.40-latest
    command: ["envoy", "-c", "/etc/envoy/envoy.yaml"]
    volumes:
      - ./envoy.yaml:/etc/envoy/envoy.yaml:ro
      - ./github_token:/run/secrets/github_token:ro
    networks: [airlock, default]

networks:
  airlock:
    internal: true

My compose file on the documented networks schema. Pick the newest Envoy tag your platform publishes.

Envoy ships a credential injector filter made for exactly this job. It inserts an Authorization header into proxied requests from a secret that only Envoy can read. With overwrite: true, any header the agent sends is replaced. With allow_request_without_credential: false, a missing secret fails closed. The configuration below routes everything to one host and injects a GitHub token from a file.

static_resources:
  listeners:
  - name: airlock
    address: { socket_address: { address: 0.0.0.0, port_value: 10000 } }
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: airlock
          route_config:
            virtual_hosts:
            - name: github
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route: { cluster: github, host_rewrite_literal: api.github.com }
          http_filters:
          - name: envoy.filters.http.credential_injector
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.credential_injector.v3.CredentialInjector
              overwrite: true
              allow_request_without_credential: false
              credential:
                name: envoy.http.injected_credentials.generic
                typed_config:
                  "@type": type.googleapis.com/envoy.extensions.http.injected_credentials.generic.v3.Generic
                  header: Authorization
                  header_value_prefix: "Bearer "
                  credential: { name: github_token }
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
  clusters:
  - name: github
    type: LOGICAL_DNS
    load_assignment:
      cluster_name: github
      endpoints:
      - lb_endpoints:
        - endpoint: { address: { socket_address: { address: api.github.com, port_value: 443 } } }
    transport_socket:
      name: envoy.transport_sockets.tls
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
        sni: api.github.com
        common_tls_context:
          validation_context:
            trusted_ca: { filename: /etc/ssl/certs/ca-certificates.crt }
            match_typed_subject_alt_names:
            - san_type: DNS
              matcher: { exact: api.github.com }
  secrets:
  - name: github_token
    generic_secret:
      secret: { filename: /run/secrets/github_token }

Filter names, type URLs, and the generic secret shape from the Envoy credential injector docs. The listener, route, and cluster are standard Envoy v3. The validation_context checks the GitHub certificate against the system CA bundle and the api.github.com name. Without it, Envoy sends the token to any server that answers. Not run here.

The secret file holds the raw token and nothing else. Envoy strips a trailing newline before it injects. Now run the test from inside the sandbox.

# inside the sandbox: no token anywhere, the proxy adds it
curl -s "$GITHUB_API/user" | head -c 200
env | grep -i token
curl -s --max-time 3 https://api.github.com/ || echo 'no route out'

The self-test, not run here. When the airlock works, the first line returns the user record of the token's owner. The second prints nothing. The third fails.

Walk through what happens. The agent sends a plain HTTP request to egress:10000 with no token. Envoy rewrites the host, opens TLS to api.github.com, checks the certificate, and inserts the header. GitHub answers with the user record for the token's owner. The second command prints nothing, because the sandbox has no token. The third fails, because the internal network has no route to GitHub. An injected instruction that asks the agent to print its keys gets the same empty result. The stat http.airlock.credential_injector.injected counts successful insertions, and failed counts requests where the filter could not insert one.

Add one virtual host per allowed domain, each with its own cluster and its own secret, and refuse everything else. Keep the Envoy config and the secret files on the host, outside any mount the sandbox can see.

Where it breaks

SymptomLikely causeFix
An injected page prints a token from inside the sandboxa secret is still mounted: an environment variable, a file, or the metadata serverrun the self-test above, move the secret to the proxy, and block 169.254.169.254 from the sandbox network
Every request gets 401 from the backendthe secret file is empty or holds the wrong token, and credential_injector.failed risescheck the file on the host, then watch injected rise instead of failed
The agent reaches a host that was never alloweddomains: ["*"] with one cluster is a single-host airlock, and a second route was added without a host matchone virtual host per allowed domain, each with its own cluster and secret, and no catch-all
Clones return the same random valuesthe snapshot captured the guest's random statebuild the guest kernel with CONFIG_VMGENID=y and reseed application generators after a restore
A restored VM dies with SIGBUSthe copy-on-write snapshot file was truncated or edited while a VM used itkeep the file immutable for the life of every VM restored from it
Tuesday's session is missing Monday's librarythe install went to the overlay, not the workspaceinstall into the workspace path or add the package to the template
A stolen identity token works from a laptopthe proxy accepts the token alone, with no binding to the sandbox's network identitybind the token to a certificate the sandbox cannot export, and give it a lifetime of hours

What to verify in any sandbox product

  1. Where does the backend credential live while the agent runs, and can a root shell inside the sandbox print it? Ask for the exact path or variable, then run the self-test.
  2. What identity does the sandbox present outbound, and what can that identity do if it leaves the box? The right answer is "identify the sandbox and nothing else."
  3. Who decides the destination, the agent's Host header or a policy outside the sandbox? The egress essay shows what goes wrong when it is the header.
  4. What marks an agent idle: a timeout, a guess, or a signal from the runtime? And what does a paused session cost?
  5. What survives the end of a session, and what survives a new base image? Ask for the tiers by path.
  6. Is the harness inside or outside the sandbox, and does the sandbox ever hold the model API key?

Agent Compute Engine answers all six in its two posts, and the answers are the mechanisms above. The preview date and the price are not published. You can build the airlock above today on any host that runs Docker.

rg
Rohit Ghumare

CNCF Ambassador and Google Developer Expert. I build agent infrastructure and write about the fundamentals underneath the AI stack. I read the Agent Compute Engine announcement and its technical deep dive on the day they were published, October 22, 2026. I read the Agent Identity, Envoy, Cloud Hypervisor, and Docker Compose documentation beside them. The service is not open yet, so nothing here was run on it.

Related: The Eval Sandbox Held the Keys · Agent Substrate · More posts · X