This guide runs workers in a Cursor team pool on Kubernetes, such as Amazon EKS or Google Kubernetes Engine. It covers Cloud Agents started from any Cursor surface, such as the web, the desktop app, Slack, GitHub, or the API. It doesn’t cover the editor’s local agent, which runs on the developer’s computer instead of a worker.Cursor-hosted Cloud Agents run on Cursor’s machines, where you can’t limit the agent’s network to the proxy, so Agent Vault doesn’t work with them.
How it works
When someone starts a Cloud Agent on your pool:- Cursor’s worker controller claims the request and runs your spawn hook
- The spawn hook creates an Agent Vault session for the run, then starts a worker pod with the session token and a Cursor token that only serves this run
- The worker connects to Cursor directly and runs the commands Cursor sends it
- Each command’s requests go through the proxy, which attaches the credentials
This guide uses its own manifests instead of Cursor’s Kubernetes Helm chart, because the chart’s spawn hook can’t create a session for each worker. If you run the chart for this pool, uninstall it before you deploy the controller in Step 5.
cursor-agent-vault
agent-vault-proxy.yaml
worker
agent-vault-ca.pem
agent-vault-env.sh
entrypoint.sh
Dockerfile
controller
Dockerfile
cursor-workers.yaml
cursor-controller.yaml
spawn-worker.sh
Prerequisites
- An Infisical organization where you’re an Agent Vault admin
- An access bundle with a service for each API the agent’s commands call, such as GitHub
- A Kubernetes cluster that enforces NetworkPolicies, such as Amazon EKS with network policy support turned on in the Amazon VPC CNI add-on, or GKE with Dataplane V2
- A container registry your cluster can pull from
kubectl,docker,curl,jq, andopensslon your machine- A Cursor Enterprise plan, with a team pool and a service account API key
- Require Self-Hosted Machines turned on in the Self-Hosted section of the Cursor dashboard, so that no Cloud Agent run goes to Cursor’s machines
Step 1: Set up Agent Vault
Create a proxy
1
In Agent Vault, go to Proxies and select Create Proxy. Enter a Name, then select Create.
2
Copy the proxy token from the Proxy Token dialog. Infisical shows the token only once.
Create a machine identity
Your spawn hook creates sessions as a machine identity. To create the machine identity:1
In your Agent Vault project, go to Access Control > Machine Identities and select Add Machine Identity.
2
On the Create New tab, enter a Name, choose Member in Role, then select Create. Infisical creates the machine identity with Universal Auth and opens its page.
3
On the machine identity’s page, select Universal Auth under Authentication. Copy the Client ID, then select Add Client Secret and copy the client secret.
4
Go to Access Bundles and open the access bundle the agent uses. Select Options > Manage Access, then select Grant Access. Select the machine identity, then select Grant Access.
Step 2: Deploy the proxy
All of your workers share one proxy. Each worker sends the proxy its own session token, so the proxy attaches only the credentials from that worker’s session. The proxy runs as two replicas that use the same token and certificate authority, and the Service spreads connections across them (high availability). Save this manifest asagent-vault-proxy.yaml and apply it to your cluster. It creates the agent-vault namespace and runs the proxy in it:
- Instance URL: if you use EU Cloud or self-host Infisical, replace
https://app.infisical.comwithhttps://eu.infisical.comor the URL of your self-hosted instance - Egress: the proxy can only reach public addresses on ports 443 and 80, so add an egress rule for anything else, such as a self-hosted Infisical instance on a private address or a service at
api.example.com:8443 - DNS: the policy allows DNS lookups through
kube-dnsand NodeLocal DNSCache (node-local-dns), which GKE runs, so change the rule if your cluster’s DNS runs elsewhere
agent-vault-proxy in the agent-vault namespace, under the key INFISICAL_AGENT_VAULT_PROXY_TOKEN. The proxy’s pods start once the Secret exists.
On the Proxies page in Agent Vault, the proxy’s status changes to Healthy once it connects.
Step 3: Build the images
The worker and the controller each need an image. Cursor doesn’t publish either one.Download the proxy’s certificate
The proxy decrypts each HTTPS request to attach the credential, so the worker’s commands have to trust the proxy’s certificate authority. The certificate isn’t secret, so the worker’s image can include it.1
Forward the proxy’s port to your machine:
2
In another terminal, download the certificate:
3
Print the certificate’s fingerprint:Compare the output with the fingerprint in the proxy’s Certificate Authority column on the Proxies page in Agent Vault, where you can hover over the value to see all of it. If the values don’t match, don’t use the file.
Build the worker’s image
The worker and the agent’s commands need different proxy settings. The worker keeps a connection open to Cursor over HTTP/2, which the proxy can’t carry because it only supports HTTP/1.1. However, the agent’s commands have to go through the proxy to get credentials. To satisfy both of these constraints, the worker starts with no proxy settings, and each of the agent’s commands sets them up for itself. The agent runs each command in a bash shell, and bash first runs any script specified by theBASH_ENV variable.
Save this script as worker/agent-vault-env.sh. It reads the session token and sends the command’s requests through the proxy:
The
agent command is itself a bash script, so bash runs this script for the worker too. The first line skips it there, which keeps the worker’s connection to Cursor direct.worker/entrypoint.sh. It points BASH_ENV at the script, sets the certificate file for the worker and the agent’s commands, then starts the worker:
--idle-release-timeout seconds for a follow-up message, then exits. A lower value ends the worker sooner after the last message. Don’t set it to 0, which keeps the worker waiting forever.
Save this Dockerfile as worker/Dockerfile, then build the image from the worker directory and push it to your registry:
- The certificate file:
/etc/agent-vault/ca.pemcombines the proxy’s certificate with the image’s own certificates, becauseSSL_CERT_FILEreplaces the certificates a tool trusts instead of adding to them - The
agentuser: the spawn hook runs the worker as the user ID2000, so the image installs Cursor’s CLI in that user’s home directory - The repository: unlike Cursor-hosted Cloud Agents, workers start with an empty
/workspace, so the agent needs to clone the repository during the run, unless you add a clone to the entrypoint. Either way,gitclones through the proxy, so the GitHub service in your access bundle needs to covergithub.comas well asapi.github.com
Build the controller’s image
The controller runs Cursor’sagent CLI and your spawn hook, which uses kubectl, curl, and jq. Save this Dockerfile as controller/Dockerfile, then build the image from the controller directory and push it to your registry:
<kubectl-version> with your cluster’s Kubernetes version, such as v1.35.0, because kubectl only supports clusters within one minor version of its own. If your nodes use Arm processors, replace amd64 with arm64.
Step 4: Create the workers’ namespace
Save this manifest ascursor-workers.yaml and apply it to your cluster before you deploy the controller in the next step. It creates the cursor-workers namespace that workers run in. It also blocks incoming connections to workers, and their connections to private networks and your cloud’s metadata service.
Step 5: Deploy the worker controller
The controller runs in thecursor namespace, and it starts workers in the cursor-workers namespace. Workers can’t reach the controller or read its credentials.
Create the namespace and the controller
Save this manifest ascursor-controller.yaml and apply it to your cluster:
<controller-image> with the controller’s image and <pool-name> with your pool. With --session-token, the controller gives the spawn hook a Cursor token that only serves one run, so the team’s API key stays in the controller.
Store the controller’s credentials
Store your Cursor service account API key and the machine identity’s client ID and client secret in a Secret namedcursor-worker-controller in the cursor namespace, under the keys CURSOR_API_KEY, INFISICAL_CLIENT_ID, and INFISICAL_CLIENT_SECRET.
Add the spawn hook
The controller runs the spawn hook each time it claims a request. The hook logs in as the machine identity, creates a session with the Create session endpoint, and starts a worker pod. It passes the worker two tokens in a Secret that the pod owns: the Infisical session token, and the Cursor token that the controller got for this run. Kubernetes deletes the Secret with the pod. Save this script asspawn-worker.sh:
<access-bundle-name> with your access bundle’s name and <worker-image> with the worker’s image. If you self-host Infisical, replace https://app.infisical.com with the address of your instance. Size the worker’s resources for the builds and tests your agents run.
Then store the hook in the ConfigMap that the controller mounts:
Succeeded state (or Failed if the worker exited with an error) until you delete it. To delete finished workers, run these commands, or run them on a schedule:
Step 6: Verify it works
Start a Cloud Agent on your pool and ask it to run these commands:- GitHub returns the account that the service’s token belongs to, even though the worker has no GitHub token
- The request reaches
example.comwithout a credential and returns200, or, under Service hosts only, the proxy refuses it with403 - The request reaches
example.comwithout going through the proxy, because this guide only blocks the workers’ connections to private networks and your cloud’s metadata service. If you limited them further, the request fails instead - The environment has no API keys, and the only token is the session token in
HTTPS_PROXY
cursor <pod-name>, where <pod-name> is the name of the worker pod. View the session’s logs to check which requests the agent made. Requests that got a credential have the outcome Brokered.
Your Cloud Agents now run in worker pods with no credentials. Each run gets its own session, and the proxy stops attaching credentials when the session’s
ttl runs out.Security considerations
Credentials outside Agent Vault
Agent Vault protects the credentials that the agent’s commands use, such as tokens for GitHub, your cloud provider, and internal APIs. It doesn’t handle these:- The model’s API key: Cursor calls the model from its own servers, so the key never reaches the worker
- MCP servers that Cursor connects to over HTTP: Cursor connects to HTTP and SSE MCP servers from its own servers, with credentials you give Cursor, so their requests don’t go through the proxy, unlike those of MCP servers that run as a command on the worker
- The worker’s connection to Cursor: the proxy only supports HTTP/1.1, and the worker connects to Cursor over HTTP/2, so the worker reaches Cursor directly
- The worker’s Cursor token: the worker needs a Cursor token to connect to the pool, so the token is on the worker, but it only serves that worker’s run, and the team’s API key stays in the controller
--sync-dashboard-secrets, which passes your Cursor secrets to the worker as environment variables, or with --clone-git-repos or --mint-github-token, which give the worker a GitHub token. Don’t put credentials in the worker’s image either.
Sessions that outlive their workers
Cursor doesn’t tell the controller when a worker exits, so the session keeps working until itsttl runs out. If someone copies the session token during a run, they can use the access bundle’s services through the proxy until then, from any pod that can reach the proxy. Keep the ttl as short as your conversations allow, and if you think a session token leaked, revoke the session in Agent Vault.
Workers’ outgoing connections
The agent’s commands run in the same pod as Cursor’s worker process, which needs a direct connection to Cursor’s hosts,api2.cursor.sh and api2direct.cursor.sh. That connection can’t go through the proxy, even with Cursor’s hosts as Exceptions in the proxy’s traffic policy, because the proxy decrypts every connection and only supports HTTP/1.1, while the worker connects over HTTP/2. This is why only the agent’s commands use the proxy, while the worker connects to Cursor directly.
A standard NetworkPolicy can’t allow hosts by name, so the policy in Step 4 only blocks workers from private networks and your cloud’s metadata service. Workers can still reach any public host. To limit where workers can connect, use a feature that can allow hosts by name, such as Cilium’s toFQDNs or GKE’s FQDN network policies.
Compromised workers
If an agent takes control of its worker pod, it has the session token and the worker’s Cursor token for that run. It can call the access bundle’s services through the proxy and connect to other hosts.The worker has no Kubernetes service account token, so it can’t read the controller’s Secret in the
cursor namespace.Troubleshooting
The spawn hook fails with "CURSOR_AUTH_TOKEN: parameter not set"
The spawn hook fails with "CURSOR_AUTH_TOKEN: parameter not set"
The controller didn’t give the hook a Cursor token for the run. Check that the controller’s command includes
--session-token and doesn’t include --warm-idle, which starts workers before any run and so has no token to give them.The worker's logs show "tlsv1 alert no application protocol"
The worker's logs show "tlsv1 alert no application protocol"
The worker sent its connection to Cursor through the proxy, which doesn’t support HTTP/2. The worker ignores
NO_PROXY, so check that nothing sets HTTPS_PROXY in the worker’s own environment, such as the entrypoint or the Pod’s env, and that the first line of agent-vault-env.sh is unchanged.The worker doesn't connect to Cursor
The worker doesn't connect to Cursor
Check that the worker’s Secret has a
cursor-token key. If you limited where workers can connect, check that your rules allow api2.cursor.sh and api2direct.cursor.sh on port 443, and DNS lookups for both.A command or MCP server reaches an API without a credential
A command or MCP server reaches an API without a credential
The process didn’t start from a bash shell, so it didn’t run
agent-vault-env.sh and doesn’t use the proxy. For an MCP server that runs on the worker, start it through bash, such as with bash -c '<command>'.The worker pod is rejected or stays in ContainerCreating
The worker pod is rejected or stays in ContainerCreating
The
restricted Pod Security Standard rejects pods without the securityContext settings in the spawn hook’s Pod, such as runAsNonRoot and allowPrivilegeEscalation: false, so check that the Pod in your spawn hook still has them. If the pod stays in ContainerCreating, it’s waiting for its Secret, so check the controller’s logs for the error from the spawn hook.The agent's commands fail with "Could not resolve proxy"
The agent's commands fail with "Could not resolve proxy"
The worker couldn’t look up the proxy’s name,
agent-vault-proxy.agent-vault.svc.cluster.local. If your cluster’s domain isn’t cluster.local, change the address in agent-vault-env.sh. If you limited where workers can connect, check that your rules allow DNS lookups for the proxy’s name.The agent's commands fail with a 407 from the proxy
The agent's commands fail with a 407 from the proxy
The request reached the proxy without the session token. Check that the worker’s Secret has a
session-token key, and that the session was created.The agent's commands fail with a 403 from the proxy
The agent's commands fail with a 403 from the proxy
The session ended, or, under Service hosts only, no service in the access bundle covers the host. To let every run reach the host without a credential, add the host to the proxy’s Exceptions. If sessions expire while people are still using their workers, raise the
ttl in the spawn hook.Next steps
Session logs
Record every request an agent makes during a session.
Services
Limit the methods and paths an agent can use.