Skip to content
Runtime

How to use AWS from a sandbox without storing keys

In IAM, trust https://withruntime.com/oidc. Then exchange the sandbox's identity token for a role with AssumeRoleWithWebIdentity.

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-token

A 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-identity

Or 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 sub can contain a colon, so org:<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_TOKEN gets 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).

Start

Terminalnpx withruntime sandboxes run -- runtime sandboxes identity-token --audience sts.amazonaws.com

A new account gets 100 free hours of a 1 GB machine in its first 30 days, with no card required.

Sources

Facts on this page were checked on 26 October 2026.

100 free hoursin your first 30 days.

of a 1 GB machine

  • No credit card
  • Four sandboxes at once, 2 vCPU and 4 GiB each
  • Then prepaid credit from $10, no plan fee
Start free, no card
Start free, no card