Skip to content

Add Vercel sandbox provider#700

Merged
ColeMurray merged 23 commits into
mainfrom
feat/vercel-managed-base-snapshot
Jun 7, 2026
Merged

Add Vercel sandbox provider#700
ColeMurray merged 23 commits into
mainfrom
feat/vercel-managed-base-snapshot

Conversation

@ColeMurray

@ColeMurray ColeMurray commented Jun 3, 2026

Copy link
Copy Markdown
Owner

Summary

  • add the Vercel Sandbox provider, Worker-compatible REST client, snapshot restore, explicit stop behavior, and repo-image support
  • manage Vercel base-runtime snapshots from Terraform using the checked-out sandbox runtime and Vercel bootstrap source, with a deterministic VERCEL_BASE_SNAPSHOT_NAME wired into the control plane
  • make repo images provider-scoped so Modal and Vercel snapshots cannot be selected across provider boundaries
  • secure the Vercel repo-image callback flow with per-build callback tokens, stored token hashes, provider-session binding, single-use callback consumption, and provider/status-bound ready/failed transitions
  • make repo-image callback routes self-authenticating: Modal uses the existing HMAC before body parsing, while Vercel requires a production-shaped bearer token before parsing and then consumes the D1-backed token/session binding after parsing
  • document Vercel setup, managed base snapshots, runtime source parity, shutdown behavior, CPU/RAM defaults, callback observability, callback security

Notes

The provider intentionally leaves Vercel sandbox resources unset. Vercel therefore uses its default sandbox size, currently observed as 2 vCPU / 4 GB RAM. Resource sizing can be added later by threading a resources.vcpus value through the Vercel create-sandbox request.

vercel_base_snapshot_id remains as a manual override. When it is unset and sandbox_provider = "vercel", Terraform builds the managed base-runtime snapshot from the local checkout as part of the normal sandbox infrastructure apply.

Vercel repo-image build sandboxes do not receive INTERNAL_CALLBACK_SECRET. They receive a random 64-hex per-build callback token whose hash is stored in D1 and consumed only after the callback's build_id and provider_session_id match the registered building row.

The repo-image callback endpoints are public only at the global router layer so provider runtimes can reach them. The route handlers still authenticate callbacks themselves: Modal with the existing internal HMAC, Vercel with the scoped callback token plus the bound Vercel session ID. Successful Vercel repo-image build sessions are stopped after the control plane snapshots the completed build.

Validation

  • npm ci
  • npm run build -w @open-inspect/shared
  • npm test -w @open-inspect/control-plane -- src/sandbox/vercel-base-snapshot.test.ts src/sandbox/providers/vercel-provider.test.ts src/sandbox/vercel-client.test.ts src/sandbox/provider-name.test.ts src/sandbox/lifecycle/manager.test.ts
  • npm test -w @open-inspect/web -- src/lib/sandbox-provider.test.ts
  • npm run build:vercel-base-snapshot -w @open-inspect/control-plane
  • npm run typecheck -w @open-inspect/control-plane
  • npm run lint -w @open-inspect/control-plane
  • npm run build -w @open-inspect/control-plane
  • npm run typecheck -w @open-inspect/web
  • npm run lint -w @open-inspect/web
  • terraform fmt -check -recursive terraform
  • terraform -chdir=terraform/environments/production validate -no-color
  • docs pass for stale Vercel runtime-source and callback wording
  • sub-agent security review of the Vercel callback changes, plus follow-up security review after the router callback auth change
  • npm test -w @open-inspect/control-plane -- src/db/repo-images.test.ts src/routes/repo-images.test.ts src/sandbox/providers/vercel-provider.test.ts
  • npm run test:integration -w @open-inspect/control-plane -- test/integration/repo-images.test.ts
  • uv run --extra dev pytest tests/test_repo_image_callback.py tests/test_entrypoint_build_mode.py -q from packages/sandbox-runtime
  • uv run --extra dev ruff check src/sandbox_runtime/repo_image_callback.py tests/test_repo_image_callback.py from packages/sandbox-runtime
  • git diff --check

Note: local Terraform validation required a temporary darwin_arm64 Cloudflare provider checksum from terraform init; that lock-file change was removed before commit.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds Vercel sandbox backend support: Vercel REST client and client tests, bootstrap and base-snapshot tooling with CI integration, full Vercel SandboxProvider with repo-image build flow, DB migration for provider on repo images, lifecycle wiring and tests, Terraform/CI variable binding and conditional snapshot build, frontend capability updates, and documentation.

Changes

Vercel Sandbox Provider Implementation

Layer / File(s) Summary
Backend names and Env contracts
packages/control-plane/src/sandbox/provider-name.ts, packages/control-plane/src/types.ts
Adds "vercel" to backend names, updates Env with Vercel-related bindings, and exposes supportsRepoImageBackend() to indicate repo-image capability.
Vercel Sandbox REST Client & streaming
packages/control-plane/src/sandbox/vercel-client.ts, packages/control-plane/src/sandbox/vercel-client.test.ts
Worker-compatible Vercel client with NDJSON command streaming/parsing, error wrapping, and tests for create/start/run/snapshot/stop/delete flows.
Vercel runtime bootstrap script
packages/control-plane/src/sandbox/vercel-bootstrap.ts
Exports runtime constants and buildVercelBootstrapScript() used by live sandboxes and CI snapshot bootstraps.
Vercel base snapshot builder & CLI
packages/control-plane/src/sandbox/vercel-base-snapshot.ts, packages/control-plane/src/sandbox/vercel-base-snapshot.test.ts, packages/control-plane/scripts/build-vercel-base-snapshot.ts
Orchestrates sandbox creation, bootstrap execution, snapshot capture/validation/stop, deterministic naming, and provides a CLI that emits snapshot id for CI.
Vercel SandboxProvider and tests
packages/control-plane/src/sandbox/providers/vercel-provider.ts, packages/control-plane/src/sandbox/providers/vercel-provider.test.ts
Full provider: create/restore/snapshot/stop, repo-image build trigger with embedded coordinator, tunnel env handling, SCM env injection, HMAC callback signing, and extensive unit tests.
Repo-images route multi-backend
packages/control-plane/src/routes/repo-images.ts
Refactors repo-image routes to support modal and vercel: backend detection, provider factory, backend-specific trigger/cleanup, unified callback auth, and stores provider on builds.
Control plane wiring & lifecycle updates
packages/control-plane/src/session/durable-object.ts, packages/control-plane/src/sandbox/lifecycle/manager.ts, lifecycle tests
Wires Vercel into SessionDO provider selection, initializes repo-image lookups when supported, and updates lifecycle stop/snapshot logic with canStopProviderSandbox() and matching tests.
Control-plane exports
packages/control-plane/src/sandbox/index.ts
Re-exports Vercel client/provider, base-snapshot and bootstrap helpers, and capability helper.
CLI packaging
packages/control-plane/package.json
Adds build:vercel-base-snapshot script to bundle the CLI snapshot-builder.

DB, Tests, and Migration

Layer / File(s) Summary
Repo-image DB and migration
packages/control-plane/src/db/repo-images.ts, packages/control-plane/src/db/repo-images.test.ts, terraform/d1/migrations/0022_add_repo_image_provider.sql
Adds provider column (default modal), updates store methods to persist/filter by provider, adapts tests, and adds index migration.
Integration tests
packages/control-plane/test/integration/repo-images.test.ts
Extends integration tests to assert provider persistence and provider-filtered latest-ready selection; adds callback-auth negative test.
Lifecycle tests
packages/control-plane/src/sandbox/lifecycle/manager.test.ts
Adds inactivity-timeout scenarios for providers that support explicit stop and mock storage/provider stop behavior.

Terraform and CI/CD Automation

Layer / File(s) Summary
Terraform variables and worker bindings
terraform/environments/production/variables.tf, terraform/environments/production/terraform.tfvars.example, terraform/environments/production/locals.tf, terraform/environments/production/workers-control-plane.tf
Adds Vercel TF inputs (token, project/team, base snapshot, runtime, repo URL/ref, expiration), validation, use_vercel_backend local, and worker bindings/secrets conditional on Vercel backend.
Terraform outputs
terraform/environments/production/outputs.tf
Adds vercel_base_snapshot_id output conditional on Vercel backend and updates verification commands.
GitHub Actions workflow
.github/workflows/terraform.yml
Adds Vercel TF_VARs, increases Apply timeout to 45 minutes, and introduces conditional "Build Vercel base snapshot" step that builds a snapshot and exports its id for TF usage.

Web Frontend and API messages

Layer / File(s) Summary
Public provider support and UI text
packages/web/src/lib/sandbox-provider.ts, packages/web/src/lib/sandbox-provider.test.ts, packages/web/src/components/settings/images-settings.tsx
Adds "vercel" to public provider union, updates supportsRepoImages() to return true for Modal and Vercel, updates tests and settings copy.
Repo-images API messages
packages/web/src/app/api/repo-images/*
Adjusts 501/unsupported-provider error messages to mention SANDBOX_PROVIDER=modal or vercel where applicable.

Documentation

Layer / File(s) Summary
Vercel provider docs & guides
docs/VERCEL_SANDBOX_PROVIDER.md, docs/GETTING_STARTED.md, docs/HOW_IT_WORKS.md, docs/SECRETS.md, docs/IMAGE_PREBUILD.md, terraform/README.md
New Vercel provider doc plus updates to guides and secrets docs explaining Vercel setup, base snapshot flow, CI secrets, and image availability constraints.

Sequence Diagram(s)

sequenceDiagram
  participant GitHubActions as GitHub Actions (terraform.yml)
  participant SnapshotCLI as build-vercel-base-snapshot
  participant SnapshotBuilder as buildVercelBaseSnapshot
  participant VercelClient as VercelSandboxClient
  participant VercelAPI as Vercel Sandbox API
  GitHubActions->>SnapshotCLI: run when SANDBOX_PROVIDER=vercel
  SnapshotCLI->>SnapshotBuilder: invoke with runtime/repo inputs
  SnapshotBuilder->>VercelClient: createSandbox()
  VercelClient->>VercelAPI: POST /sandboxes
  SnapshotBuilder->>VercelClient: runCommandAndWait(bootstrap)
  VercelClient->>VercelAPI: command stream (NDJSON)
  SnapshotBuilder->>VercelClient: snapshotSession()
  VercelClient->>VercelAPI: POST /snapshots
  SnapshotBuilder->>VercelClient: stopSession()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

"🐰
I hopped through CI and sandboxes bright,
Built snapshots by day and tests by night,
Vercel and Modal now share the stage,
Docs and routes updated page by page,
Deploys hum softly — buckle in, delight!"

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/vercel-managed-base-snapshot

@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/control-plane/src/routes/repo-images.ts (1)

84-112: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Authenticate build callbacks before updating D1.

The new Vercel coordinator signs callbacks with Authorization: Bearer <timestamp>.<hmac>, but neither handler validates that header. Anyone who can guess a build_id can mark builds ready/failed, and the success path can cascade into deleting a provider image. Verify the HMAC and timestamp before mutating build state.

Also applies to: 176-195

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/control-plane/src/routes/repo-images.ts` around lines 84 - 112,
Extract and validate the Authorization header in both handleBuildComplete and
the corresponding failure handler (handleBuildFailed) before making any DB
mutations: read the raw request body (before parseJsonBody), parse Authorization
of form "Bearer <timestamp>.<hmac>", ensure timestamp is within an allowed
window (e.g., ±5 minutes), compute an HMAC using the shared coordinator secret
from env (e.g., env.COORDINATOR_SECRET) over the same data the coordinator signs
(timestamp + "." + rawBody or timestamp + "." + build_id depending on
coordinator spec), compare the computed HMAC to the provided one using a
constant-time comparison, and return 401 on missing/invalid signature or expired
timestamp; only proceed to parseJsonBody and update D1 when validation succeeds.
🧹 Nitpick comments (1)
packages/control-plane/src/sandbox/providers/vercel-provider.ts (1)

477-483: ⚡ Quick win

Hoist these default command timeouts into named constants.

The new timeout defaults are being restated inline instead of being defined once and reused, which makes drift between provider commands more likely as this file grows.

As per coding guidelines, "Define each default timeout value exactly once as a named constant and import it everywhere rather than restating literal values" and "Do not use bare timeout variable names; always encode the unit (seconds for Python, milliseconds for TypeScript) in the variable name."

Also applies to: 496-501

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/control-plane/src/sandbox/providers/vercel-provider.ts` around lines
477 - 483, Hoist the literal timeout values used in calls like
this.client.runCommandAndWait({ sessionId, command: "sudo", args: [...],
timeoutMs: 30_000 }) (and the similar call at lines ~496-501) into a named,
exported constant (e.g. VERCEL_PROVIDER_COMMAND_TIMEOUT_MS) and import/use that
constant instead of the magic number; follow the unit-encoding rule in the
constant name (use _MS for TypeScript timeouts). Update all runCommandAndWait
invocations in vercel-provider.ts to reference the new constant and remove the
inline numeric literals so the default timeout is defined exactly once and
reused.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/terraform.yml:
- Around line 312-348: Update the error message in the "Build Vercel base
snapshot" step so it references the actual env var names used elsewhere
(VERCEL_TOKEN and VERCEL_PROJECT_ID) instead of VERCEL_SANDBOX_TOKEN and
VERCEL_SANDBOX_PROJECT_ID; modify the check block that currently echoes
"VERCEL_SANDBOX_TOKEN and VERCEL_SANDBOX_PROJECT_ID are required when
SANDBOX_PROVIDER=vercel" to echo the correct variable names (VERCEL_TOKEN and
VERCEL_PROJECT_ID) so it matches the env keys set and the build script's
requiredEnv calls.

In `@docs/VERCEL_SANDBOX_PROVIDER.md`:
- Line 25: Update the phrasing in the sentence "For GitHub Actions based
deployment, configure the matching repository secrets:" to hyphenate the
compound modifier; change it to "For GitHub Actions-based deployment, configure
the matching repository secrets:" so the compound adjective "GitHub
Actions-based" is clear and grammatically correct.

In `@packages/control-plane/scripts/build-vercel-base-snapshot.ts`:
- Around line 18-25: The script currently allows a missing or invalid --output
value to silently fall back to stdout; modify getArgValue/main so that when
"--output" is present but no following value exists or the following token
starts with "-" (another flag), the script fails fast: detect this condition
after calling getArgValue("--output") in main (or enhance getArgValue to
validate the next token), print a clear error to stderr (including the fact that
--output is missing/invalid) and exit with a non-zero status (e.g., throw or
process.exit(1)) so CI fails instead of continuing.

In `@packages/control-plane/src/sandbox/providers/vercel-provider.ts`:
- Around line 477-485: The remote tunnel-writer invocation using
this.client.runCommandAndWait(...) (with sessionId, VERCEL_PYTHON_BIN and the
script that calls Path.write_text(...)) currently assumes failures will throw;
instead inspect the result returned by runCommandAndWait and fail the launch on
non-zero exit. Modify the caller around runCommandAndWait to capture its return
value (exitCode / status / stdout/stderr fields), check for a non-zero exit code
or error status, log the remote stderr with context, and throw or return an
error so sandbox launch stops when Path.write_text(...) fails.
- Around line 705-721: The happy-path for event "image_build.complete" snapshots
and calls callback but never shuts down the running sandbox, leaving proc alive
until BUILD_TIMEOUT_SECONDS; after snapshot_session() and before returning,
explicitly stop the session by terminating proc (use
proc.send_signal(signal.SIGTERM) then wait with a short timeout and fall back to
proc.kill() and wait) or call the provider's existing cleanup API (e.g.,
stopSandbox/stopSession) to release resources, and ensure any teardown succeeds
before returning from the handler.
- Around line 309-322: The PATH is hard-coded to /vercel/runtimes/node24/bin in
the env construction (Object.assign block) causing mismatched runtime when
this.providerConfig.runtime (or VERCEL_RUNTIME) differs; update the env builders
that set PATH (the block that assigns HOME, NODE_ENV, PATH, etc., and the
similar block around lines 356-377) to interpolate the configured runtime
instead of node24—use this.providerConfig.runtime (or
config.runtime/VERCEL_RUNTIME) to build the runtimePath (e.g.,
`/vercel/runtimes/${runtime}/bin`) and use that value for PATH so the sandbox
runtime and PATH remain consistent.
- Around line 366-376: The sandbox-wide env currently exposes sensitive
Vercel/control-plane secrets (OI_INTERNAL_CALLBACK_SECRET, OI_VERCEL_TOKEN) via
buildBuildEnvVars(), making them visible to repo builds run by
buildCoordinatorScript(); remove these keys from the sandbox-level environment
and instead inject them only into the coordinator/process that performs remote
API callbacks (e.g., pass secrets into the coordinator runtime or a dedicated
callback handler) so repo build env (env=os.environ.copy() used in
buildCoordinatorScript()) never contains OI_INTERNAL_CALLBACK_SECRET or
OI_VERCEL_TOKEN; update buildBuildEnvVars() to omit those two keys (and the same
for the other occurrences called out), and wire secret access into the specific
functions that perform callback/API calls rather than the repo build
environment.

In `@packages/control-plane/src/sandbox/vercel-bootstrap.ts`:
- Line 73: The generated git credential helper script incorrectly references the
shell variable ${VERCEL_PYTHON_BIN} (a build-time TypeScript constant) which is
not defined at runtime; update the script generation so it embeds the actual
Python binary path value instead of the shell variable. Specifically, change the
string written to /usr/local/bin/oi-git-credentials in the code that constructs
that script so the command uses the resolved VERCEL_PYTHON_BIN constant (the
build-time value) and still calls -m
sandbox_runtime.credentials.git_credential_helper "$@" — ensure the resulting
script contains the hardcoded path rather than a ${...} shell variable
reference.

In `@packages/control-plane/src/sandbox/vercel-client.ts`:
- Around line 236-274: The requestText function currently buffers the entire
response body (in requestText) which causes memory exhaustion for NDJSON command
streams; change requestText to consume response.body as a stream (use
response.body.getReader() or a streaming text/NDJSON parser) and process chunks
incrementally, splitting on newlines, parsing each JSON envelope as it arrives,
retaining only the most recent envelope with envelope.type === "command" (or the
"command" field) and discarding prior ones, and if response.ok is false still
read/skips the streamed body then throw VercelSandboxApiError with the captured
status and last error text; ensure headers and error handling logic around
VercelSandboxApiError and httpStatus remain unchanged.

In `@packages/control-plane/src/session/durable-object.ts`:
- Around line 764-766: RepoImageLookup currently only stores provider_image_id
and base_sha which lets SandboxLifecycleManager call the wrong provider after
SANDBOX_PROVIDER changes; update persistence and lookup logic so RepoImageLookup
includes the backend/provider identifier (e.g., provider_name or backend enum)
and ensure code paths that populate or read repoImageLookup (the block guarded
by supportsRepoImageBackend and any callers in SandboxLifecycleManager) filter
by that backend or delete incompatible rows before using the opaque
provider_image_id; also add migration/cleanup logic when reading existing rows
to clear or ignore entries whose stored backend does not match the active
SANDBOX_PROVIDER.

---

Outside diff comments:
In `@packages/control-plane/src/routes/repo-images.ts`:
- Around line 84-112: Extract and validate the Authorization header in both
handleBuildComplete and the corresponding failure handler (handleBuildFailed)
before making any DB mutations: read the raw request body (before
parseJsonBody), parse Authorization of form "Bearer <timestamp>.<hmac>", ensure
timestamp is within an allowed window (e.g., ±5 minutes), compute an HMAC using
the shared coordinator secret from env (e.g., env.COORDINATOR_SECRET) over the
same data the coordinator signs (timestamp + "." + rawBody or timestamp + "." +
build_id depending on coordinator spec), compare the computed HMAC to the
provided one using a constant-time comparison, and return 401 on missing/invalid
signature or expired timestamp; only proceed to parseJsonBody and update D1 when
validation succeeds.

---

Nitpick comments:
In `@packages/control-plane/src/sandbox/providers/vercel-provider.ts`:
- Around line 477-483: Hoist the literal timeout values used in calls like
this.client.runCommandAndWait({ sessionId, command: "sudo", args: [...],
timeoutMs: 30_000 }) (and the similar call at lines ~496-501) into a named,
exported constant (e.g. VERCEL_PROVIDER_COMMAND_TIMEOUT_MS) and import/use that
constant instead of the magic number; follow the unit-encoding rule in the
constant name (use _MS for TypeScript timeouts). Update all runCommandAndWait
invocations in vercel-provider.ts to reference the new constant and remove the
inline numeric literals so the default timeout is defined exactly once and
reused.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7baa04ca-d251-4aad-84cc-602580fa6bf8

📥 Commits

Reviewing files that changed from the base of the PR and between 64482f0 and 0e0ae20.

📒 Files selected for processing (35)
  • .github/workflows/terraform.yml
  • docs/GETTING_STARTED.md
  • docs/HOW_IT_WORKS.md
  • docs/IMAGE_PREBUILD.md
  • docs/SECRETS.md
  • docs/VERCEL_SANDBOX_PROVIDER.md
  • packages/control-plane/package.json
  • packages/control-plane/scripts/build-vercel-base-snapshot.ts
  • packages/control-plane/src/routes/repo-images.ts
  • packages/control-plane/src/sandbox/index.ts
  • packages/control-plane/src/sandbox/lifecycle/manager.test.ts
  • packages/control-plane/src/sandbox/lifecycle/manager.ts
  • packages/control-plane/src/sandbox/provider-name.test.ts
  • packages/control-plane/src/sandbox/provider-name.ts
  • packages/control-plane/src/sandbox/providers/vercel-provider.test.ts
  • packages/control-plane/src/sandbox/providers/vercel-provider.ts
  • packages/control-plane/src/sandbox/vercel-base-snapshot.test.ts
  • packages/control-plane/src/sandbox/vercel-base-snapshot.ts
  • packages/control-plane/src/sandbox/vercel-bootstrap.ts
  • packages/control-plane/src/sandbox/vercel-client.test.ts
  • packages/control-plane/src/sandbox/vercel-client.ts
  • packages/control-plane/src/session/durable-object.ts
  • packages/control-plane/src/types.ts
  • packages/web/src/app/api/repo-images/[owner]/[name]/toggle/route.ts
  • packages/web/src/app/api/repo-images/[owner]/[name]/trigger/route.ts
  • packages/web/src/app/api/repo-images/route.ts
  • packages/web/src/components/settings/images-settings.tsx
  • packages/web/src/lib/sandbox-provider.test.ts
  • packages/web/src/lib/sandbox-provider.ts
  • terraform/README.md
  • terraform/environments/production/locals.tf
  • terraform/environments/production/outputs.tf
  • terraform/environments/production/terraform.tfvars.example
  • terraform/environments/production/variables.tf
  • terraform/environments/production/workers-control-plane.tf

Comment thread .github/workflows/terraform.yml Outdated
Comment thread docs/VERCEL_SANDBOX_PROVIDER.md Outdated
Comment thread packages/control-plane/scripts/build-vercel-base-snapshot.ts
Comment thread packages/control-plane/src/sandbox/providers/vercel-provider.ts
Comment thread packages/control-plane/src/sandbox/providers/vercel-provider.ts Outdated
Comment thread packages/control-plane/src/sandbox/providers/vercel-provider.ts Outdated
Comment thread packages/control-plane/src/sandbox/providers/vercel-provider.ts Outdated
Comment thread packages/control-plane/src/sandbox/vercel-bootstrap.ts Outdated
Comment thread packages/control-plane/src/sandbox/vercel-client.ts
Comment thread packages/control-plane/src/session/durable-object.ts
@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

@ColeMurray

Copy link
Copy Markdown
Owner Author

Addressed the two review-body items in fffcd32:

  • Build callbacks now explicitly verify the internal HMAC token in repo-images.ts before parsing or mutating D1, reusing verifyInternalToken.
  • The default Vercel command timeouts are now named constants: VERCEL_BOOTSTRAP_TIMEOUT_MS and VERCEL_TUNNEL_ENV_WRITE_TIMEOUT_MS.

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

@ColeMurray

Copy link
Copy Markdown
Owner Author

Addressed the Vercel runtime-source parity concern in d9163fd. The managed base snapshot now archives the checked-out local packages/sandbox-runtime package and uploads it into the temporary Vercel sandbox before bootstrap, so the snapshot is built from the same commit/workspace as the deployed control plane. I also removed the VERCEL_RUNTIME_REPO_URL / VERCEL_RUNTIME_REPO_REF Terraform, Worker, workflow, and docs surface entirely, and Vercel fresh sessions now require either a repo image snapshot or the managed base-runtime snapshot.

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/control-plane/src/sandbox/providers/vercel-provider.ts (1)

38-38: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Rename BUILD_TIMEOUT_SECONDS to BUILD_TIMEOUT_MS and store the value in milliseconds.

As per coding guidelines, TypeScript timeout constants should use milliseconds and encode the unit with _MS or timeoutMs. The current constant uses _SECONDS and requires multiplication at the call site (line 244: BUILD_TIMEOUT_SECONDS * 1000), which violates the guideline to "Define each default timeout value exactly once as a named constant and import it everywhere rather than restating literal values."

♻️ Proposed fix
-const BUILD_TIMEOUT_SECONDS = 1800;
+const BUILD_TIMEOUT_MS = 1800 * 1000;
 const VERCEL_TUNNEL_ENV_WRITE_TIMEOUT_MS = 30_000;

Then update line 244:

-        timeoutMs: BUILD_TIMEOUT_SECONDS * 1000,
+        timeoutMs: BUILD_TIMEOUT_MS,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/control-plane/src/sandbox/providers/vercel-provider.ts` at line 38,
Rename the constant BUILD_TIMEOUT_SECONDS to BUILD_TIMEOUT_MS and change its
value to milliseconds (1800 seconds → 1_800_000) so timeouts are defined in ms;
then update all places that multiply BUILD_TIMEOUT_SECONDS by 1000 (e.g., where
BUILD_TIMEOUT_SECONDS * 1000 is used) to use BUILD_TIMEOUT_MS directly, ensuring
any exports/imports or references to BUILD_TIMEOUT_SECONDS are updated to the
new name BUILD_TIMEOUT_MS.

Source: Coding guidelines

🧹 Nitpick comments (1)
packages/control-plane/src/sandbox/vercel-bootstrap.ts (1)

81-83: ⚡ Quick win

Extract the duplicated shellQuote helper to a shared utility module.

The shellQuote function is identical in both vercel-bootstrap.ts (lines 81-83) and vercel-base-snapshot.ts (lines 166-168). Extract it to a shared module (e.g., sandbox/shell-utils.ts) and import it in both files to avoid duplication and maintain a single source of truth.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/control-plane/src/sandbox/vercel-bootstrap.ts` around lines 81 - 83,
Extract the duplicated shellQuote function into a single shared utility (e.g.,
sandbox/shell-utils.ts) and replace the local implementations in
vercel-bootstrap.ts and vercel-base-snapshot.ts with an import of that shared
shellQuote; ensure the exported helper signature remains: function
shellQuote(value: string): string, update both files to import and use
shellQuote instead of their inline copies, and run tests/build to verify no
import/export type issues.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@packages/control-plane/src/sandbox/providers/vercel-provider.ts`:
- Line 38: Rename the constant BUILD_TIMEOUT_SECONDS to BUILD_TIMEOUT_MS and
change its value to milliseconds (1800 seconds → 1_800_000) so timeouts are
defined in ms; then update all places that multiply BUILD_TIMEOUT_SECONDS by
1000 (e.g., where BUILD_TIMEOUT_SECONDS * 1000 is used) to use BUILD_TIMEOUT_MS
directly, ensuring any exports/imports or references to BUILD_TIMEOUT_SECONDS
are updated to the new name BUILD_TIMEOUT_MS.

---

Nitpick comments:
In `@packages/control-plane/src/sandbox/vercel-bootstrap.ts`:
- Around line 81-83: Extract the duplicated shellQuote function into a single
shared utility (e.g., sandbox/shell-utils.ts) and replace the local
implementations in vercel-bootstrap.ts and vercel-base-snapshot.ts with an
import of that shared shellQuote; ensure the exported helper signature remains:
function shellQuote(value: string): string, update both files to import and use
shellQuote instead of their inline copies, and run tests/build to verify no
import/export type issues.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f020d06c-51b2-41e6-8048-c7c3ef03814c

📥 Commits

Reviewing files that changed from the base of the PR and between 35f9d9e and d9163fd.

📒 Files selected for processing (19)
  • .github/workflows/terraform.yml
  • docs/GETTING_STARTED.md
  • docs/VERCEL_SANDBOX_PROVIDER.md
  • packages/control-plane/scripts/build-vercel-base-snapshot.ts
  • packages/control-plane/src/routes/repo-images.ts
  • packages/control-plane/src/sandbox/index.ts
  • packages/control-plane/src/sandbox/providers/vercel-provider.test.ts
  • packages/control-plane/src/sandbox/providers/vercel-provider.ts
  • packages/control-plane/src/sandbox/vercel-base-snapshot.test.ts
  • packages/control-plane/src/sandbox/vercel-base-snapshot.ts
  • packages/control-plane/src/sandbox/vercel-bootstrap.ts
  • packages/control-plane/src/sandbox/vercel-client.test.ts
  • packages/control-plane/src/sandbox/vercel-client.ts
  • packages/control-plane/src/session/durable-object.ts
  • packages/control-plane/src/types.ts
  • terraform/README.md
  • terraform/environments/production/terraform.tfvars.example
  • terraform/environments/production/variables.tf
  • terraform/environments/production/workers-control-plane.tf
💤 Files with no reviewable changes (6)
  • packages/control-plane/src/types.ts
  • terraform/environments/production/workers-control-plane.tf
  • terraform/environments/production/variables.tf
  • terraform/README.md
  • packages/control-plane/src/session/durable-object.ts
  • packages/control-plane/src/routes/repo-images.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/VERCEL_SANDBOX_PROVIDER.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • terraform/environments/production/terraform.tfvars.example
  • .github/workflows/terraform.yml
  • packages/control-plane/src/sandbox/vercel-client.test.ts
  • packages/control-plane/src/sandbox/vercel-base-snapshot.test.ts
  • docs/GETTING_STARTED.md

@ColeMurray

Copy link
Copy Markdown
Owner Author

Moved the Vercel base snapshot build into Terraform in 789190e. The workflow no longer has a separate "Build Vercel base snapshot" step; instead, production Terraform now has a vercel_sandbox_infra module that hashes local packages/sandbox-runtime plus the Vercel builder/bootstrap files, runs a module script to build the managed snapshot from var.project_root, and passes a deterministic VERCEL_BASE_SNAPSHOT_NAME to the Worker. The control plane resolves that name to the latest created Vercel snapshot ID at sandbox creation time, while VERCEL_BASE_SNAPSHOT_ID remains as a manual override that skips the managed build.

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

Comment thread packages/control-plane/src/routes/repo-images.ts Outdated
@ColeMurray

ColeMurray commented Jun 7, 2026

Copy link
Copy Markdown
Owner Author

Performed a dead-code / stale-reference pass after the Terraform-managed Vercel snapshot changes.

Cleanup committed in 66af16e3:

  • Removed the unused Vercel list-snapshot pagination wrapper and cursor field from the control-plane client. Current callers only need the snapshot array and never paginated.
  • Updated provider/tests to consume the simplified snapshot list directly.
  • Removed the redundant Terraform VERCEL_RUNTIME_SOURCE_DIR env var; the build script default already points at packages/sandbox-runtime.
  • Swapped snapshot build source-version precedence so Terraform local source hash wins over GITHUB_SHA when Terraform invokes the script.

Reference scans did not find remaining PR-specific runtime repo/ref plumbing, stale workflow snapshot outputs, or unused VercelListSnapshotsResponse references.

Validation:

  • npm run typecheck -w @open-inspect/control-plane
  • npx vitest run src/sandbox/vercel-base-snapshot.test.ts src/sandbox/vercel-client.test.ts src/sandbox/providers/vercel-provider.test.ts
  • npm run build:vercel-base-snapshot -w @open-inspect/control-plane
  • git diff --check
  • npx prettier --check ...
  • terraform fmt -check ...

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

@ColeMurray ColeMurray merged commit 3ad9c8e into main Jun 7, 2026
18 checks passed
@ColeMurray ColeMurray deleted the feat/vercel-managed-base-snapshot branch June 7, 2026 19:07
ColeMurray added a commit that referenced this pull request Jun 8, 2026
)

## Summary

Groups the Vercel sandbox modules added in #700 into a dedicated
`packages/control-plane/src/sandbox/providers/vercel/` directory and
drops the redundant `vercel-` filename prefix. Previously the Vercel
files were split between the top of `sandbox/` (client, bootstrap,
base-snapshot) and `sandbox/providers/` (the provider).

## Structure

```
src/sandbox/providers/vercel/
├── provider.ts          (was providers/vercel-provider.ts)
├── provider.test.ts
├── client.ts            (was vercel-client.ts)
├── client.test.ts
├── bootstrap.ts         (was vercel-bootstrap.ts)
├── base-snapshot.ts     (was vercel-base-snapshot.ts)
└── base-snapshot.test.ts
```

## Why under `providers/` (not `sandbox/vercel/`)

Keeping the provider under `providers/` preserves that directory as the
home for every provider and establishes a per-provider convention
(`providers/<name>/`) that Modal and Daytona can adopt later, leaving
top-level `sandbox/` for cross-provider concerns (`provider.ts`
interface, `provider-name.ts`, `index.ts`, `lifecycle/`).

## Scope / notes

- Pure move + import-path update. **No exported symbols change**, so the
`sandbox` barrel's public API is identical.
- All 7 files moved via `git mv` (renames detected at 96–100%
similarity, history preserved).
- Import specifiers updated in 5 consumers: the sandbox barrel
(`index.ts`), `routes/repo-images.ts` (+ test, including the `vi.mock`
path string), `session/durable-object.ts`, and
`scripts/build-vercel-base-snapshot.ts`.
- Modal/Daytona providers intentionally left as flat files for now — a
follow-up can nest them into `providers/modal/` and `providers/daytona/`
for full symmetry.
- No changes needed to `package.json`, Terraform, CI, or docs (verified
— no hardcoded module paths; snapshot bundle output stays
`dist/vercel-base-snapshot.js`).

## Validation

- `npm run typecheck -w @open-inspect/control-plane`
- `npm run lint -w @open-inspect/control-plane`
- `npm test -w @open-inspect/control-plane` — 77 files, **1257 tests
passed**
- `npm run build:vercel-base-snapshot -w @open-inspect/control-plane`
- `npm run build -w @open-inspect/control-plane`


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
* Internal reorganization of Vercel sandbox provider and client modules
and related imports. No changes to user-facing functionality or
behavior.
* Updated tests and build/infra reference inputs to align with the new
module layout; test behavior and runtime behavior remain unchanged.
* Re-exports adjusted to reflect the new internal structure without
altering public APIs.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant