@computesdk/gcp
Google Cloud providers for ComputeSDK. Each Google Cloud product is a subpath export:
| Subpath | Export | Product |
|---|---|---|
@computesdk/gcp/cloud-run | cloudRun | Cloud Run Sandboxes |
Cloud Run
import { cloudRun } from '@computesdk/gcp/cloud-run'
Cloud Run Sandboxes are currently in beta and require your GCP project to be allow-listed by the Cloud Run team.
This package supports two modes:
- Remote mode: deploy a gateway Cloud Run service with
npx -p @computesdk/gcp computesdk-cloud-run, then connect usingCLOUD_RUN_SANDBOX_URLandCLOUD_RUN_SANDBOX_SECRET. - Direct mode: run your app inside a Cloud Run service deployed with
--sandbox-launcher, then call the in-container sandbox CLI at/usr/local/gcp/bin/sandbox.
Install
npm install @computesdk/gcp
Setup Remote Gateway
Set your GCP project and region, then run the setup command:
export CLOUD_RUN_PROJECT_ID="your-gcp-project"
export CLOUD_RUN_REGION="us-central1"
npx -p @computesdk/gcp computesdk-cloud-run
The setup command uses gcloud to:
- Build a small ComputeSDK gateway container.
- Deploy it to Cloud Run with
--sandbox-launcherand--no-cpu-throttling. - Configure the gateway for low-latency sandbox execution with 1 CPU, 2Gi memory, concurrency 20 (
CLOUD_RUN_CONCURRENCY), session affinity, CPU boost, and 100 min/max instances. - Allow unauthenticated Cloud Run invocation with
--allow-unauthenticated. - Generate a bearer token for gateway authentication.
- Print the runtime environment variables.
By default, the gateway executes each ComputeSDK command with sandbox do. The ComputeSDK sandbox object is a logical handle used to carry configuration such as environment variables and working directory between calls. Set executionMode: 'stateful' to use sandbox run, sandbox exec, and sandbox delete for persistent sandbox sessions.
Execution mode controls how SDK methods map to the Cloud Run sandbox CLI:
| SDK method | Ephemeral mode (default) | Stateful mode |
|---|---|---|
create() | Creates a local logical handle only | Calls sandbox run --write --detach <id> (an idle, writable session) |
create({ snapshotId }) | — | Calls sandbox run --write --import-tar <snapshot> --detach <id> |
runCommand() | Calls sandbox do -- /bin/sh -c <command> | Calls sandbox exec <id> -- /bin/sh -c <command> |
runCommand(cmd, { onStdout }) | Streams from sandbox do | Streams from sandbox exec |
destroy() | Removes local bookkeeping only | Calls sandbox delete --force <id> |
snapshot.create(id) | — | Calls sandbox tar --file=<snapshot> <id> |
filesystem | Runs each operation with sandbox do | Runs each operation with sandbox exec <id> |
Add the printed values to your app environment:
CLOUD_RUN_SANDBOX_URL=https://computesdk-sandbox-...run.app
CLOUD_RUN_SANDBOX_SECRET=...
Prerequisites:
- Your GCP project is allow-listed for Cloud Run Sandboxes.
gcloudis installed, up to date, and authenticated.- Cloud Build and Cloud Run APIs are enabled for the project.
- Your organization policy allows public Cloud Run invokers, or callers must provide
CLOUD_RUN_AUTH_TOKEN.
Public Gateway Access
The remote gateway is designed to be public at the Cloud Run layer and protected by CLOUD_RUN_SANDBOX_SECRET for sandbox operations. Public Cloud Run access avoids requiring each SDK caller to mint a Google identity token.
The setup command passes --allow-unauthenticated. If your organization blocks public IAM members with constraints/iam.allowedPolicyMemberDomains, the deploy can succeed with an IAM warning but the service will still require Cloud Run IAM auth. In that case, either allow allUsers as roles/run.invoker for this gateway service or set CLOUD_RUN_AUTH_TOKEN to a Google-signed identity token for the gateway URL.
Manual Direct Deploy
Deploy your service with the sandbox launcher enabled:
gcloud beta run deploy my-sandbox-service \
--image=gcr.io/$PROJECT_ID/my-sandbox-service:latest \
--region=$REGION \
--project=$PROJECT_ID \
--sandbox-launcher \
--no-cpu-throttling
The sandbox binary is only available in Cloud Run containers with --sandbox-launcher enabled.
Remote Usage
import { cloudRun } from '@computesdk/gcp/cloud-run'
const compute = cloudRun({
sandboxUrl: process.env.CLOUD_RUN_SANDBOX_URL,
sandboxSecret: process.env.CLOUD_RUN_SANDBOX_SECRET,
})
const sandbox = await compute.instances.create()
const result = await sandbox.runCommand('echo hello')
console.log(result.stdout)
await sandbox.destroy()
Stateful Execution
Remote sandboxes default to ephemeral: every command and filesystem operation
uses a fresh sandbox. Use executionMode: 'stateful' on the provider or on
sandbox.create() whenever files must persist between calls (including a
separate mkdir followed by writeFile). Working-directory changes made by a
shell's cd do not change the starting directory of subsequent commands; set
workdir or pass a command's cwd explicitly.
Stateful sandboxes are local to a gateway instance, routed by session affinity,
and are lost if that instance restarts. Snapshots preserve rootfs state, not
processes or memory, and exclude /tmp and mounted paths. Restoring with
allowEgress: true can fail in the native Cloud Run launcher even when the same
GCS snapshot restores without egress. A timeout does not prove that native
creation stopped; updated gateways attempt bounded deletion of the failed
create's exact ID only when the gateway allocated that ID. Caller-supplied IDs
are never automatically deleted after a failed create: sandbox exec reporting
"not running" does not prove that a native ID is absent. SDK creates without a
name let the gateway allocate the ID. The original create error is still
returned.
import { cloudRun } from '@computesdk/gcp/cloud-run'
const compute = cloudRun({
sandboxUrl: process.env.CLOUD_RUN_SANDBOX_URL,
sandboxSecret: process.env.CLOUD_RUN_SANDBOX_SECRET,
executionMode: 'stateful',
write: true,
})
const sandbox = await compute.instances.create()
await sandbox.runCommand('echo hello > /tmp/hello.txt')
const result = await sandbox.runCommand('cat /tmp/hello.txt')
console.log(result.stdout)
await sandbox.destroy()
Direct Usage
import { cloudRun } from '@computesdk/gcp/cloud-run'
const compute = cloudRun({
persistDir: '/tmp/cloud-run-sandbox',
write: true,
})
const sandbox = await compute.instances.create({
envs: { NODE_ENV: 'production' },
})
const result = await sandbox.runCommand('echo hello')
console.log(result.stdout)
await sandbox.destroy()
Configuration
| Option | Env var | Default | Description |
|---|---|---|---|
sandboxUrl | CLOUD_RUN_SANDBOX_URL | URL of deployed gateway service for remote mode | |
sandboxSecret | CLOUD_RUN_SANDBOX_SECRET | Bearer token for deployed gateway service | |
gatewayAuthToken | CLOUD_RUN_AUTH_TOKEN | Optional Google identity token when Cloud Run IAM auth is enabled | |
sizeGateways | CLOUD_RUN_SANDBOX_URL_<SIZE>, CLOUD_RUN_SANDBOX_SECRET_<SIZE> | Gateway per standard size (xs, sm, md, lg, xl), used for sizeClass creates; secrets default to sandboxSecret | |
executionMode | ephemeral | Use ephemeral for sandbox do or stateful for sandbox run/exec/delete | |
sandboxBinary | CLOUD_RUN_SANDBOX_BINARY | /usr/local/gcp/bin/sandbox | Path to the Cloud Run sandbox CLI |
mode | CLI default | Sandbox CLI mode, local or container | |
allowEgress | false | Allow sandbox network egress | |
rootfs | / | Root filesystem exposed to sandboxes | |
workdir | CLI default | Default working directory | |
template | Cloud Run container template name | ||
persistDir | Host path used for persistent filesystem state | ||
overlayDir | Writable overlay directory | ||
write | false | Allow mounted filesystems to be writable | |
mounts | [] | Bind mounts passed to --mount | |
env | {} | Environment variables for new sandbox sessions | |
globalArgs | [] | Extra CLI global args | |
runArgs | [] | Extra args for sandbox do and sandbox run | |
execArgs | [] | Extra args for sandbox exec in stateful mode |
Notes
Commands use ephemeral sandbox do execution by default. Filesystem writes are only preserved when your Cloud Run sandbox CLI configuration supports host-backed persistence such as --persist-dir. Use executionMode: 'stateful' when you need a detached sandbox session created with sandbox run and reused with sandbox exec.
Sandbox image
Sandboxes use the gateway image as their root filesystem and run as root. The image built by setup includes git, python3, unzip, curl and CA certificates, and keeps the apt package lists so writable sandboxes can apt-get install more. Sandboxes can't switch users, so apt runs its downloads as root and sudo is a shim that runs the command directly. Network access (git clone, apt-get, curl) needs allowEgress: true.
Snapshots
compute.snapshot.create(sandboxId) exports the stateful sandbox's writable rootfs overlay with sandbox tar; compute.sandbox.create({ snapshotId }) starts a new stateful sandbox from it. The source keeps running. Files under /tmp and --mount paths are not included, and running processes and memory are not kept. Snapshots are stored in gs://$CLOUD_RUN_SNAPSHOT_BUCKET/computesdk-snapshots/ when the gateway has CLOUD_RUN_SNAPSHOT_BUCKET set (setup creates the bucket and grants the service's runtime service account roles/storage.objectAdmin), otherwise on the gateway instance that took them.
Gateway instances
A stateful sandbox lives on the gateway instance that created it. The provider replays Cloud Run's session-affinity cookie so follow-up calls reach that instance; affinity is best effort, so keep --session-affinity and spare --concurrency on the service. The gateway deletes stateful sandboxes idle for CLOUD_RUN_SANDBOX_IDLE_TIMEOUT_MS (default one hour, 0 disables; set it at deploy time with the same variable), never while a command is running. Snapshot, restore and live streaming need the current gateway; against a gateway deployed before these features, snapshot calls fail with a redeploy hint and streamed output arrives when the command exits.
Sizes
Cloud Run sandboxes have no CPU or memory controls of their own: they share the --cpu/--memory of the gateway instance they run on. Each size is therefore its own gateway service. Deploy one per size with CLOUD_RUN_SIZE=<size> npx -p @computesdk/gcp computesdk-cloud-run. This deploys computesdk-sandbox-<size> at xs 1 vCPU/1 GiB, sm 1 vCPU/2 GiB, md 2 vCPU/4 GiB, lg 4 vCPU/8 GiB or xl 8 vCPU/16 GiB with one warm instance, and prints CLOUD_RUN_SANDBOX_URL_<SIZE>. compute.sandbox.create({ sizeClass: 'lg' }) (or { vcpus: 4, memoryMiB: 8192 }) then runs on that gateway. The gateway reports its size in /v1/health. A size with no configured gateway, a gateway deployed at a different size, and 2xl (Cloud Run instances top out at 8 vCPU) all fail with a sizeClass: error instead of running another size. Sandboxes on one instance share its CPU and memory, so use a low CLOUD_RUN_CONCURRENCY when each sandbox needs the whole size.
getUrl() is not supported because the current Cloud Run sandbox CLI does not expose per-sandbox ports.
CI with Workload Identity Federation
When Cloud Run IAM auth is enabled, CI needs a Google-signed ID token for the gateway URL. Some federated credentials cannot use gcloud auth print-identity-token directly; use IAM Credentials generateIdToken with a federated access token instead:
ACCESS_TOKEN="$(gcloud auth print-access-token)"
SERVICE_URL="$(gcloud run services describe computesdk-sandbox --region=$CLOUD_RUN_REGION --project=$CLOUD_RUN_PROJECT_ID --format='value(status.url)')"
CLOUD_RUN_AUTH_TOKEN="$(node -e '
const [accessToken, audience, serviceAccount] = process.argv.slice(1)
fetch(`https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${serviceAccount}:generateIdToken`, {
method: "POST",
headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json" },
body: JSON.stringify({ audience, includeEmail: true }),
}).then(r => r.json()).then(j => process.stdout.write(j.token))
' "$ACCESS_TOKEN" "$SERVICE_URL" "$CLOUD_RUN_SERVICE_ACCOUNT")"
The federated principal must be allowed to impersonate CLOUD_RUN_SERVICE_ACCOUNT and mint OpenID tokens for it.