Overview
Cloud agents such as Cursor Cloud Agents and Devin often need API keys to run and test your application. Teams usually store those keys as environment secrets. Every agent session can then read the raw key, print it, or send it anywhere. With Formal, the key never enters the sandbox:- The runs inside the agent sandbox and authenticates with the agent’s short-lived OIDC token.
- A network rule intercepts requests to the API and forwards them to a .
- A on the Connector injects the key.
- restrict which requests the agent can make. Every request is logged with the agent and the human who started it.
Prerequisites
- A deployed with a hostname that the agent sandbox can reach
- A Cursor Cloud Agent environment or a Devin environment you can edit
- A Formal machine user for your cloud agents
- Permission to create OIDC integrations, resources, network rules, and policies in Formal
- A Stripe API key
1. Store the key on the Connector
Set the key as theSTRIPE_API_KEY environment variable on the Connector.
Remove it from your Cursor and Devin secrets.
2. Create the Stripe resource and Native User
- Control Plane
- Terraform
- Navigate to Resources and click Create Resource
- Set Technology to HTTP, Name to
stripe, Hostname toapi.stripe.com, and Port to443 - Save the resource, then add a TLS configuration and select Verify Full
- Select Authentication, click Add User, and select HTTP Bearer
- Set Label to
stripe-api-key - Choose Static, set the header to
Authorization, and choose Environment Variable - Enter
STRIPE_API_KEYand click Create - Under Default Native User, enter this CEL expression and click Save
Authorization header the agent sends. Without a
default Native User, the Connector forwards requests without a key, and Stripe
returns 401.
Verify: The stripe-api-key Native User appears under Native Users
with type HTTP Bearer and source STRIPE_API_KEY.
3. Make Stripe reachable through the Connector
- Control Plane
- Terraform
- Open your Connector and select Listeners
- Create a listener on an available port, such as
443 - Open the listener, click New Rule, set the type to Resource, and select
stripe
stripe appears under the Connector’s Reachable Resources.
4. Configure the OIDC integration
The Endpoint in the sandbox exchanges the agent’s OIDC token for a Formal machine session. No Formal API key is stored in the sandbox. Go to OIDC Integrations and click Create Integration. Select your provider’s preset, then select the machine user for your cloud agents.- Cursor
- Devin
Replace Keep the preset End-User Email Expression,
your-team-id in the Cursor preset claim condition. You can
also restrict it to one repository:claims.owner_email. Formal
then logs each request with the human who started the agent.integrationoidc_... ID and the machine user’s user_...
ID. To block the machine user from every other Control Plane operation, follow
Restrict Control Plane access.
5. Forward Stripe traffic to the Connector
Create a network rule that intercepts requests toapi.stripe.com from the
cloud agent machine user and forwards them to the Connector. On macOS,
pre_tcp checks the Endpoint identity, so Endpoints logged in as anyone else
leave their traffic alone. Linux ignores pre_tcp, so pre_tls checks the
identity again along with the hostname.
- Control Plane
- Terraform
- Navigate to Network Rules
- Create a rule named
stripe-cloud-agents - Paste the CEL below and replace
<CLOUD_AGENT_MACHINE_USER_ID> - Save the rule and set it to Active
6. Bake the Endpoint into the agent environment
Install the Endpoint when the environment image is built. The sandbox needs outbound access to:api.joinformal.com, for authentication, network rules, and logs- Your Connector hostname, on the listener port from step 3
- The Endpoint package download link, while the image is built
api.stripe.com.
Each setup below downloads the headless Debian package from a link you copy
from the Control Plane. Click Download Formal
Endpoint at the bottom of the sidebar and open Linux. Right-click the
headless package for your architecture and copy the link:
- Ubuntu/Debian (x86, headless) for
amd64 - Ubuntu/Debian (ARM, headless) for
arm64
<FORMAL_ENDPOINT_PACKAGE_URL> below with the copied link and
<INTEGRATION_ID> with the integration ID from step 4. To upgrade the
Endpoint, copy the link again and update your configuration.
Node.js and most Python HTTP clients ignore the system trust store. The
setups below point them at
/etc/ssl/certs/ca-certificates.crt, which
includes the Formal local CA once the Endpoint trusts it. See
Runtime TLS trust
for the variables other clients read.- Cursor
- Devin
Cursor environment builds keep installed files, but not running processes.
The install script runs once per build. A named terminal starts the
Endpoint again in every Cloud Agent run. The Cursor OIDC socket is local
and does not need outbound access.
1
Create the install script
Add
.cursor/install-formal-endpoint.sh:2
Create the runtime script
Add Cursor sets
.cursor/start-formal-endpoint.sh:CURSOR_AGENT_SOCKET on Cloud Agent VMs. Do not hard-code
its path, because self-hosted workers use a different one. The retry
covers the moment before Cursor creates the socket during boot.3
Make the scripts executable
4
Update the environment configuration
Merge these fields into Commit all three files. New Cloud Agents build from the
repository-managed configuration.You can instead give these steps to Cursor’s guided environment setup
under Cloud Agents → Environments. Review the generated
configuration before you activate the build.
.cursor/environment.json:7. Call Stripe without a key
Start a new Cursor Cloud Agent or Devin session. Confirm the Endpoint is signed in and intercepting:formal auth whoami should show the cloud agent machine user, and Transparent
Mode should be enabled. Then call Stripe with no key:
- Open Logs and filter on the
striperesource - Confirm the request shows the cloud agent machine user and the human who started the session
- Confirm the response status is
200 - Run
env | grep -i stripein the sandbox and confirm no key is set
8. (Optional) Restrict Stripe requests with a policy
The Native User injects the key into every request that reaches thestripe
resource. To limit what agents can do with it, add a policy. This one only
allows reads of balances, charges, and customers:
- Control Plane
- Terraform
- Navigate to Policies and click Create Policy
- Set name to
stripe-cloud-agents-read-only - Paste the Rego below and click Create Policy
curl -X POST https://api.stripe.com/v1/refunds
returns the Formal block message.
Troubleshooting
Stripe returns 401
Stripe returns 401
Possible causes:
- The network rule is in Draft or does not match the machine user
- Forward to Connector is not set, so the request never reaches the Connector
- The resource has no default Native User, or
STRIPE_API_KEYis not set on the Connector
stripe resource. If it does not, check the network rule. If
it does, check the Native User and the Connector environment.TLS verification fails
TLS verification fails
Run:Open a new shell so it loads
~/.bashrc, then retry. Do not disable TLS
verification.Requests time out
Requests time out
Confirm the sandbox can reach the Connector hostname on the listener port.
With
fail = "closed", a matched request fails when the Endpoint cannot
reach the Connector.The Endpoint is not signed in
The Endpoint is not signed in
For Cursor, start the Endpoint from
terminals, not install, because
install processes do not survive the build. For Devin, confirm lingering
with loginctl show-user "$(id -un)" and read
journalctl --user -u formal.service. In both cases, confirm the
integration ID has no oidc.formal.ai/ prefix.Next Steps
Native Users
Inject other credential types or fetch them with hooks
Block Agent Auto-Merge
Enforce GitHub policy inside cloud agents
Devin Database Access
Approve Devin database access in Slack
Network Rules
Choose which traffic the Endpoint intercepts