
As Cursor Cloud Agents become a core part of how engineering teams ship software—offloading tasks from Slack, GitHub, Linear, and the IDE itself—they're quietly becoming a significant point of credential exposure. Each agent boots a fresh Ubuntu VM, clones your repo, and starts running. If it needs to talk to a database, hit an internal API, or install a private package, it needs secrets. The question is: how do those secrets get there safely?
At Infisical, we've been thinking about this a lot. The patterns we're seeing in MCP servers and CI/CD pipelines are showing up in Cloud Agents too—hardcoded credentials, .env files committed alongside environment configs, and secrets baked into snapshots that outlive their usefulness. Here's a better way.
Quick Recap: How Cursor Cloud Agents Work
When you kick off a Cursor Cloud Agent task - whether from the IDE, Slack, or a GitHub webhook - Cursor spins up an isolated Ubuntu VM for that specific task. It restores from a snapshot, clones your repo at the relevant branch, and then runs your environment lifecycle in order:
install- runs once after the snapshot, gets cached. Thinknpm installorpip sync.start- runs on every boot, before the agent begins working. The right place to fetch secrets.
This lifecycle is configured in .cursor/environment.json at the root of your project. Cursor also has a Secrets UI (Settings → Background Agents → Secrets) that injects key-value pairs as encrypted environment variables at runtime.
The problem: the Secrets UI only goes so far. It works for a handful of static values, but it doesn't handle rotation, audit trails, access isolation between team members, or any of the other things you'd expect from a real secrets management workflow. As soon as your agent needs to do anything meaningful (like connect to a database, hit an internal service, install a private registry package), you're going to want something more robust.
The Challenge with Secrets in Cursor Agents
Cursor's cloud agent environment introduces a few specific problems that aren't always obvious:
- Secrets baked into snapshots: If you run
npm installwith an.npmrccontaining an auth token, and then snapshot the disk, that token is now frozen into the image. - Hardcoded values in
environment.json: This file is meant to be committed to your repo, so anything sensitive you put directly in theenvfield is a liability. - No rotation or audit trail: The built-in Secrets UI stores values, but there's no visibility into when they were accessed, by which agent run, or whether they've been rotated recently.
- Long-lived credentials: A token you set in the Secrets UI six months ago is still the same token.
How to Do It Right with Infisical
The cleanest approach: store only your Infisical machine identity credentials in the Cursor Secrets UI, then use those to pull everything else from Infisical at runtime. Your actual secrets never touch Cursor's storage.
1. Set Up a Machine Identity
First, create a machine identity in Infisical scoped specifically to your agent environment - give it access only to the secrets that agent needs, nothing more.
# Authenticate non-interactively using Universal Auth export INFISICAL_TOKEN=$(infisical login \ --method=universal-auth \ --client-id=<identity-client-id> \ --client-secret=<identity-client-secret> \ --silent \ --plain)
Store INFISICAL_CLIENT_ID and INFISICAL_CLIENT_SECRET in the Cursor Secrets UI. These are the only values that live there - everything else comes from Infisical.
2. Option A: infisical run - Inject Secrets Directly into Your Process
The simplest pattern. Use infisical run in your start script to inject secrets as environment variables into whatever process you're launching.
# .cursor/start.sh export INFISICAL_TOKEN=$(infisical login \ --method=universal-auth \ --client-id=$INFISICAL_CLIENT_ID \ --client-secret=$INFISICAL_CLIENT_SECRET \ --silent --plain) infisical run --env=production --projectId=<project-id> -- node server.js
{
"snapshot": "snapshot-...",
"install": "npm install",
"start": "bash .cursor/start.sh"
}
The process launched by infisical run gets all your secrets injected as environment variables. Nothing is written to disk, nothing leaks into the snapshot, and the credentials are fetched fresh on every agent boot.
3. Option B: infisical export - Write Secrets to a File
Some tools expect secrets in a file rather than as environment variables - a .env file, a YAML config, a JSON blob. infisical export handles this.
# Write to .env format infisical export --env=production --projectId=<project-id> --output-file=.env # Write to JSON infisical export --format=json --env=production --projectId=<project-id> --output-file=./config/secrets.json # Write to YAML infisical export --format=yaml --env=production --projectId=<project-id> --output-file=./config/secrets.yaml
A practical start.sh for a Node project that needs a private .npmrc written before dependencies install:
#!/bin/bash # .cursor/start.sh export INFISICAL_TOKEN=$(infisical login \ --method=universal-auth \ --client-id=$INFISICAL_CLIENT_ID \ --client-secret=$INFISICAL_CLIENT_SECRET \ --silent --plain) # Write .npmrc for private registry access infisical export \ --env=production \ --projectId=<project-id> \ --path=/npm-config \ --format=dotenv \ --output-file=.npmrc echo "Secrets ready"
Putting It All Together
Here's what a complete, secure environment.json setup looks like for a typical web project:
{
"snapshot": "snapshot-20250309-xxxxxxxx",
"install": "bash .cursor/install.sh",
"start": "bash .cursor/start.sh"
}
# .cursor/install.sh # Authenticate so we can install private packages export INFISICAL_TOKEN=$(infisical login \ --method=universal-auth \ --client-id=$INFISICAL_CLIENT_ID \ --client-secret=$INFISICAL_CLIENT_SECRET \ --silent --plain) # Write .npmrc for private registry infisical export --env=production --projectId=<project-id> --path=/npm-config --output-file=.npmrc npm install
# .cursor/start.sh export INFISICAL_TOKEN=$(infisical login \ --method=universal-auth \ --client-id=$INFISICAL_CLIENT_ID \ --client-secret=$INFISICAL_CLIENT_SECRET \ --silent --plain) sudo service docker start infisical run --env=production --projectId=<project-id> -- node server.js
The Cursor Secrets UI holds only INFISICAL_CLIENT_ID and INFISICAL_CLIENT_SECRET. Every other secret is fetched fresh on every agent boot, rotatable at any time, and fully auditable.
Isolate Access Per Environment
One more thing worth doing: don't give your agent a machine identity with access to everything. Create a dedicated Infisical identity scoped specifically to the secrets that agent task actually needs.
cursor-agent-dev → access to /dev secrets only cursor-agent-prod → access to /production secrets only, requires approval cursor-agent-ci → access to /ci secrets, read-only
This way, if an agent is ever compromised via prompt injection, which is a real risk with autonomous agents that auto-execute terminal commands, the blast radius is contained to what that identity could access, not your entire secret store.
Stop Baking Secrets Into Your Agents
Cursor Cloud Agents are powerful precisely because they can act autonomously, and that autonomy creates real credential exposure if you're not careful. Baking secrets into snapshots, storing long-lived tokens in the Secrets UI, or hardcoding values in environment.json are all patterns that will eventually cause problems.
The core principle is simple: store as little as possible in Cursor, and use those minimal credentials to fetch everything else from Infisical at runtime. Fresh secrets on every boot, full audit trail, and rotation that doesn't require touching your environment config.
Infisical is available as a fully managed cloud service or self-hosted. Get started in under 5 minutes and make the security foundation of your AI agent workflows as robust as the agents themselves.

