Connect a cloud
Steps an agent follows to link the user's AWS, Google Cloud or Azure account to Cua Spaces with no stored key, or Hetzner and Modal with an API token sealed to their devices, set a budget and a largest machine, and verify it.
Link your AWS, Google Cloud or Azure account once, with no stored key, and Spaces can run there from every device and agent on your Cua account. This page is written for the coding agent that does it for you. The Cua Spaces app gives you a prompt like this one to paste into your agent:
Connect my AWS account to Cua Spaces: region us-west-2, $200 monthly budget, machines up to 4 vCPU and 16 GB. Follow
cua cloud connect aws --helpand https://cua.ai/docs/spaces/guides/connect-a-cloud untilcua cloud status awssays connected.
How every run ends#
cua cloud connect never prompts and is safe to run again. Each run ends
with these fields (--json prints them as one object):
| Field | What it holds |
|---|---|
status | connected, needs-trust, checking, waiting-for-approval or failed |
next.run | The exact command you run next |
next.open | A page the person opens (with next.paste, a command they paste there) |
next.why | What the step is for, in plain words |
verify | The command that confirms it: cua cloud status aws |
checks | What the check found (name, ok, detail) |
The exit code says whose turn it is:
| Exit code | Meaning |
|---|---|
0 | Connected |
10 | The next step is yours: run next.run |
11 | The next step is a person's: send them next.open (and next.paste) |
1 | Error: read next.why |
AWS#
AWS is linked without any key. Cua signs 15-minute tokens for the person's
Cua account, and their AWS account trusts those tokens through one
CloudFormation stack, cua-spaces. The stack holds an OIDC provider and a
role that can create, stop and delete only machines tagged
cua-managed=true. It holds no keys, and Cua never stores AWS credentials.
1. Ask the person for the limits#
Ask for three things, unless the prompt already gave them:
- the region (for example
us-west-2); - a monthly budget in US dollars (required: Cua refuses new machines once it is reached, and stopping and deleting always works);
- the largest machine, as vCPUs and GB (for example 4 vCPU and 16 GB).
Then run:
cua cloud connect aws --oidc --region us-west-2 --budget 200 --max-size 4x162. The person creates the trust#
The status is needs-trust, and the exit code is 11. Send the person
next.open and next.paste, and tell them:
- open the link while signed in to their AWS account (it opens AWS CloudShell);
- paste the command there and press Enter. It creates the
cua-spacesstack, which takes about a minute; - reply with their 12-digit AWS account id once the stack shows
CREATE_COMPLETE. The id is in the top right of the AWS console.
If the link is a CloudFormation quick-create page instead, they tick "I acknowledge that AWS CloudFormation might create IAM resources with custom names" and press Create stack.
3. Check it#
Run the command in next.run. It adds the account id and --check:
cua cloud connect aws --oidc --region us-west-2 --budget 200 --max-size 4x16 --account-id 123456789012 --checkThe check gets a check token from Cua, exchanges it with AWS STS and dry-runs a machine. Nothing is created, and nothing is billed.
connected(exit code 0): done. Go to step 4.needs-trustagain: the stack is missing, still being created, or was made in another AWS account. Show the personnext.openagain, and confirm the account id.waiting-for-approval(exit code 11): because an agent made the link, a person approves it in the Cua Spaces app before machines can be created. Ask them to approve it.failed: readchecksandnext.why, fix what they say, and run the check again.
4. Verify#
cua cloud status awsThe AWS line says connected. The line under it shows the budget, what was
spent this month, the largest machine, and "Signs in through Cua". Spaces can
now go there:
cua spaces create --on awsChange the limits later#
Run the same command with the new values. You can lower a budget or a size
right away. When an agent raises one, the change waits for the person's
approval (waiting-for-approval), and the old limit stays in force until
then.
Approve what an agent asked for#
The person approves in the Cua Spaces app, or in their own terminal:
cua cloud approve aws
cua cloud approve aws --denycua cloud status aws names the agent and what it asked for. Denying a raise
keeps the limits in force; denying a link the person never approved
disconnects it. An agent cannot approve: the command refuses in an agent's
shell, and no MCP server offers it.
What it costs#
Cua estimates what its machines cost from each one's list price and how long it ran. The cloud's own bill is the final word. To see the month so far by Space, by agent, by day or by cloud:
cua cloud cost
cua cloud cost --by agent
cua cloud cost --cloud aws --by day --since 2026-10-01 --format csvThe CSV starts with the header key,usd,hours,machines. When a cloud has a
budget, Cua tells the person once each month when its spend passes half the
budget, then 80%, then all of it. The note shows in the Cua Spaces app and in
cua cloud audit. At the full budget, new machines are refused until the
next month; stopping and deleting still work.
Already connected?#
A cloud linked before this, through the AWS CLI, gcloud, a Hetzner token or a
Modal profile, keeps working as it is. There is nothing to do after an
update: the link, its region, its budget, the machines Cua made there and
the cleanup of them all carry over. cua cloud status aws shows one line
offering the switch:
cua cloud connect aws --oidcThe switch keeps the region, budget, largest machine and lifetime the link
had. The AWS CLI sign-in stays in force until the check passes. Switching to
a different AWS account is refused while Cua still has machines in the old
one, so none are left out of reach. Running cua cloud connect aws again
without --oidc, for example to change the budget, keeps an OIDC link as it
is.
Hetzner and Modal#
Hetzner and Modal have no OIDC, so they use an API token. Cua seals the token to the person's devices: the device you run on encrypts it with the account's pool key, and only devices of the person's Cua account can open it. Cua's servers keep only the ciphertext. Any of the person's devices then opens it in memory when it calls the cloud. No tool returns it, not to you and not to any other agent.
1. Ask the person for the limits and the token#
Ask for the location (Hetzner: for example fsn1), a monthly budget in US
dollars and the largest machine. Then ask for the token:
- Hetzner: a Read & Write API token. In the Hetzner Cloud console, open the project, then Security, API tokens, Generate API token.
- Modal: run
modal token new, and give the token id and secret as one line,<token id>:<token secret>.
Pipe the token in on standard input. Never print it, put it in a command line or write it to a file:
cua cloud connect hetzner --token-stdin --region fsn1 --budget 50 --max-size 4x8cua cloud connect modal --token-stdin --budget 50The command checks the token first: it reads the project and dry-runs a create. Nothing is created and nothing is billed. Only a token that passes is sealed.
connected(exit code 0): done.waiting-for-approval(exit code 11): because an agent gave the token, a person approves it in the Cua Spaces app first. Machines are refused until then.failed: readchecksandnext.why. A read-only Hetzner token fails thepermissionscheck; ask for a Read & Write one.
2. Verify#
cua cloud status hetznerThe line under Hetzner names the devices that can open the token ("can open it: ..."). At least one of them must be online for Cua to make machines there while the person is away.
On the person's other devices#
They do not paste the token again. On another device of the same Cua account, link the account's sealed token:
cua cloud connect hetzner --sealed --region fsn1 --budget 50Sealed or kept on this device#
Without --sealed or --no-sealed, the token is sealed when this device has
the account's pool keys (the cua of the Cua Spaces app, signed in) and a
budget is given, or when the link is sealed already. Otherwise the Hetzner
token is kept in this device's Cua credential store, as in earlier
versions.
--sealedinsists on sealing. Without a token on standard input, it seals the token this device already has (the saved one,HCLOUD_TOKEN, the hcloud CLI's, or for ModalMODAL_TOKEN_IDandMODAL_TOKEN_SECRETor the~/.modal.tomlprofile). This is how a link from an earlier version moves to a sealed token.--no-sealedkeeps the Hetzner token on this device only.
Replace or revoke the token#
- Replace it: run the connect command again with the new token on standard input. It is sealed and swapped in for every device.
- Revoke it: delete the token in the Hetzner Cloud console (or with
modal token), or runcua cloud disconnect hetzner, which deletes the sealed token on Cua's servers.
When the person removes a device from their Cua account, the account's pool key moves to a new generation. The next device that uses the token seals it again under the new key, so the removed device cannot open it any more. To be sure, replace the token as well.
Google Cloud#
Google Cloud is linked without any key too. The person's project trusts
Cua's tokens through a workload identity pool named cua. Its provider
accepts only tokens for their Cua account, and one custom role, cuaSpaces,
holds the 15 Compute Engine permissions Cua uses. There is no service account
and no key.
1. Ask for the limits and the project#
Ask for the project id (for example my-project), the region, the monthly
budget and the largest machine. Then run:
cua cloud connect gcp --oidc --project my-project --region us-central1 --budget 200 --max-size 4x162. The person creates the trust#
The status is needs-trust. Send the person next.open and next.paste:
next.openopens Google Cloud Shell with Cua's trust script,cua-trust.sh. They sign in with an account that can manage IAM in the project.- They paste
next.paste(bash cua-trust.sh --project my-project --account <their Cua account id> --issuer-host oidc.cua.ai) and press Enter. It is safe to run again. - They reply with the project number the script prints (it is also on the project's dashboard).
When you run on a machine that is signed in to gcloud with rights to manage
IAM in the project, next.run is the same script instead
(cua cloud trust gcp | bash -s -- …), and the exit code is 10. Run it only
when the person asked you to set up the trust.
3. Check it#
Run next.run. It adds the project number and --check:
cua cloud connect gcp --oidc --project my-project --region us-central1 --budget 200 --max-size 4x16 --project-number 123456789012 --checkThe check gets a check token from Cua, exchanges it at Google's Security
Token Service and checks the role's permissions with testIamPermissions.
Nothing is created. The statuses mean the same as for AWS. needs-trust
again means the pool is missing, still being set up, or trusts another Cua
account.
4. Verify#
cua cloud status gcpAzure#
Azure is always linked without a key. A user-assigned managed identity,
cua-spaces, trusts Cua's tokens for the person's Cua account through a
federated credential. It holds two built-in roles, Virtual Machine Contributor
and Network Contributor, on one resource group only (cua-spaces unless
you say otherwise). Everything Cua creates stays in that group.
1. Ask for the limits#
Ask for the region (for example westus2), the monthly budget and the
largest machine. Then run:
cua cloud connect azure --region westus2 --budget 200 --max-size 4x162. The person creates the trust#
The status is needs-trust. Send the person next.open, a Deploy to Azure
link, and tell them:
- sign in to Azure, pick the subscription, and create (or pick) the resource
group
cua-spacesin the region; - enter their Cua account id in Cua Account (
next.whyhas it) and press Review + create. They need Owner or User Access Administrator on the group, because the template assigns roles; - reply with the deployment's outputs
subscriptionId,tenantIdandclientId(Deployments, cua-spaces, Outputs).
When you run on a machine signed in to the az CLI, next.run deploys the
same template with az deployment group create and prints those outputs.
Run it only when the person asked you to set up the trust.
3. Check it#
Run next.run. It adds the three ids and --check:
cua cloud connect azure --region westus2 --budget 200 --max-size 4x16 --subscription 00000000-0000-0000-0000-000000000000 --tenant 11111111-1111-1111-1111-111111111111 --client-id 22222222-2222-2222-2222-222222222222 --checkThe check gets a check token from Cua, exchanges it with Microsoft Entra, reads the resource group and its permissions, and validates a machine deployment. Nothing is created.
4. Verify#
cua cloud status azureOther clouds#
Hetzner and Modal link with a token sealed to the person's devices (see
Hetzner and Modal), or with their own sign-in on this
device, and Google Cloud too if the person prefers gcloud on this device to
a link without a key. The budget and largest machine work the same way:
cua cloud connect gcp --project my-project --region us-central1 --budget 100 --max-size 4x16
cua cloud connect modal --environment spaces --budget 50The second command links Modal with the Modal CLI's sign-in on this device
(~/.modal.toml) instead of a sealed token.
What to ask the person, and what not to#
- Ask for: the region, the monthly budget, the largest machine, the AWS account id, the Google Cloud project id and number, the Azure ids the template prints, the Hetzner or Modal API token, and an approval in the Cua Spaces app when the status says so.
- Never ask for access keys, service account keys, client secrets or a password. These links use none.
- Never print, log or store a Hetzner or Modal token yourself. Pipe it to
cua cloud connect --token-stdinand nothing else. - Never set up the trust yourself with the person's cloud credentials (the CloudFormation stack, the trust script, the Azure template) unless they ask you to. It is theirs to create and delete.
Remove it#
cua cloud disconnect awsDisconnecting stops new tokens at once. A person who also wants to remove
the trust deletes it in the cloud: the cua-spaces stack in CloudFormation,
the cua workload identity pool in Google Cloud, or the cua-spaces managed
identity in Azure. Cua never deletes IAM resources.