On Runtime, a sandbox proves its identity with a signed token, so no AWS key is ever put into it. Runtime signs a short-lived OpenID Connect token that names the sandbox, your organization and its image. AWS STS exchanges that token for temporary credentials for a role you choose. It works like GitHub Actions' OIDC tokens. It needs no Runtime API key inside the sandbox, and it is free (identity tokens). A leaked token expires in 10 minutes by default. The role's trust policy decides which sandboxes may use it at all.
1. Trust Runtime in IAM, once
Add an OpenID Connect identity provider in IAM with provider URL
https://withruntime.com/oidc and audience sts.amazonaws.com. Then create a
role whose trust policy names that provider. Use your AWS account ID and your
Runtime organization ID (runtime whoami shows the organization):
JSON{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::123456789012:oidc-provider/withruntime.com/oidc" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "withruntime.com/oidc:aud": "sts.amazonaws.com" }, "StringLike": { "withruntime.com/oidc:sub": "org:<your-org-id>:image:api-worker:*" } } } ]}That condition admits only sandboxes in your organization that are made from
the image api-worker. To admit every sandbox in the organization, use
org:<your-org-id>:*.
2. Get a token in the sandbox
TypeScriptimport { writeFile } from "node:fs/promises";import { Sandbox } from "withruntime";// Runs inside the sandbox.const { token, subject } = await Sandbox.identityToken({ audience: "sts.amazonaws.com", lifetimeSeconds: 3600,});await writeFile("/tmp/aws-token", token);console.log(subject); // org:<org-id>:image:<image>:sandbox:<sandbox-id>Pythonfrom withruntime import Sandbox# Runs inside the sandbox.token = Sandbox.identity_token("sts.amazonaws.com", lifetime_seconds=3600)with open("/tmp/aws-token", "w") as file: file.write(token)Terminalruntime sandboxes identity-token --audience sts.amazonaws.com --lifetime 3600 > /tmp/aws-tokenA new sandbox on the current default image also has
RUNTIME_ID_TOKEN_REQUEST_URL and RUNTIME_ID_TOKEN_REQUEST_TOKEN set. So curl
can request a token in the same way as in GitHub Actions.
3. Let the AWS SDKs pick it up
Set the standard variables to point at the file. The AWS CLI and every AWS SDK
then call AssumeRoleWithWebIdentity on their own:
Terminalexport AWS_ROLE_ARN=arn:aws:iam::123456789012:role/runtime-sandboxexport AWS_WEB_IDENTITY_TOKEN_FILE=/tmp/aws-tokenaws sts get-caller-identityOr run the whole job from your own server. Pass the role in the command's environment. The environment holds no secret:
TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create({ image: "api-worker", timeoutSeconds: 1800 });await sbx.exec("pip install --quiet boto3", { check: true, timeoutMs: 300_000 });const run = await sbx.exec( "runtime sandboxes identity-token --audience sts.amazonaws.com --lifetime 3600 > /tmp/aws-token && " + 'python3 -c \'import boto3; print(boto3.client("sts").get_caller_identity()["Arn"])\'', { env: { AWS_ROLE_ARN: "arn:aws:iam::123456789012:role/runtime-sandbox", AWS_WEB_IDENTITY_TOKEN_FILE: "/tmp/aws-token", AWS_REGION: "us-east-1", }, check: true, },);console.log(run.stdout); // arn:aws:sts::123456789012:assumed-role/runtime-sandbox/...Pythonfrom withruntime import Sandboxwith Sandbox.create(image="api-worker", timeout_seconds=1800) as sbx: sbx.exec("pip install --quiet boto3", check=True, timeout_ms=300_000) run = sbx.exec( "runtime sandboxes identity-token --audience sts.amazonaws.com --lifetime 3600 > /tmp/aws-token && " "python3 -c 'import boto3; print(boto3.client(\"sts\").get_caller_identity()[\"Arn\"])'", env={ "AWS_ROLE_ARN": "arn:aws:iam::123456789012:role/runtime-sandbox", "AWS_WEB_IDENTITY_TOKEN_FILE": "/tmp/aws-token", "AWS_REGION": "us-east-1", }, check=True, ) print(run.stdout)The token and the session
| What | Value |
|---|---|
Issuer (iss) |
https://withruntime.com/oidc |
Subject (sub) |
org:<org-id>:image:<image>:sandbox:<sandbox-id> |
Audience (aud) |
What you asked for; sts.amazonaws.com for AWS |
| Other claims | org_id, sandbox_id, sandbox_name, image, image_id, image_digest, funding, region |
| Signature | RS256; keys at https://withruntime.com/oidc/jwks |
| Token lifetime | 10 minutes by default; 60 to 3600 seconds with lifetimeSeconds or --lifetime |
| AWS session | One hour by default; DurationSeconds from 900 up to the role's maximum, which is 1 to 12 hours (AWS) |
| Price | Free |
Runtime publishes a new signing key one hour before it uses it. It keeps an old key published for two hours after the switch. So a verifier that caches the keys never misses one.
Mistakes and how Runtime handles them
- A trust policy that admits too much. No part of
subcan contain a colon, soorg:<org-id>:image:<name>:*matches only sandboxes made from that image. Narrow each role to the image that needs it. - A job longer than the token. A token lasts at most an hour. Write the file again before it expires for work that runs longer.
- Copying the request token out.
RUNTIME_ID_TOKEN_REQUEST_TOKENgets tokens only for this sandbox, only while it runs, and stops when the sandbox stops or pauses. Treat it as a password all the same. - Asking from outside a sandbox. Tokens are for sandboxes: code outside
one has nothing to ask with, and no API key can get a token. The SDK call
fails there with
identity_unavailable. - A network allow list. STS is an ordinary HTTPS host; allow
sts.amazonaws.com(or your region's STS host) when you narrow a sandbox's rules (the network).
Related
- Mount an S3 bucket as a directory, with the key kept out of the sandbox.
- Verify a webhook signature, for events going the other way.
- Run untrusted LLM code with no cloud credentials in reach.
Start
Terminalnpx withruntime sandboxes run -- runtime sandboxes identity-token --audience sts.amazonaws.comA new account gets 100 free hours of a 1 GB machine in its first 30 days, with no card required.
Sources
- AWS STS API Reference, AssumeRoleWithWebIdentity: https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html, read 26 October 2026.
Facts on this page were checked on 26 October 2026.