Sandboxes v2 (Beta)
What a Sandboxes v2 sandbox is, its two delivery modes, versions, environments, the claim lifecycle and limits.
A Sandboxes v2 sandbox is a live, isolated copy of your application: running on its own https URLs, seeded with known data, with a working login for every role your tests need. You describe it once in a cofactor.sandbox.yaml; after that, anyone (a person, a test run, a coding agent) can claim a fresh copy in seconds, use it, and throw it away.
Sandboxes are fast to claim because they are restored, not rebuilt: installing, building and seeding happen once per version and environment, are captured in a ready snapshot, and every claim restores its own copy of it.
Sandboxes v2 is enabled per organization. If a command answers "Sandboxes v2 is not enabled for this organization", ask us to turn it on.
Setting one up? Copy this prompt and give it to your agent. It checks the CLI and your sign-in, reads the sandbox guide, asks you how you want it set up, then builds and proves a sandbox:
01Authentication in native flows
Each native flow run gets its own copy. When its Login node needs a persona, Cofactor invokes the copy’s private authentication script and installs the returned app session. Matching persona names let existing flows run without editing their Login nodes; no session is shared between tests. This does not automatically configure authentication for external Playwright suites.
Provider sign-in tests start without an app session and exercise the app’s login UI and callback. See Google OAuth and third-party login for callback configuration, the default identity and Beta rollout limits.
02How it relates to Sandboxes (Beta)
Sandboxes (Beta) are built from templates you author inside Cofactor (artifacts, a checklist, template actions) and provisioned as instances. Sandboxes v2 is a separate model:
| Sandboxes (Beta) | Sandboxes v2 | |
|---|---|---|
| Defined by | A template authored in Cofactor | One cofactor.sandbox.yaml, in your repository or pushed with your image |
| Belongs to | A template | An application |
| Code comes from | A linked GitHub repository | A connected GitHub repository, or images you push (no repository access) |
| Getting one | Provision an instance | Claim a copy of a ready snapshot |
| CLI | cofactor sandbox create, provision, logs… | cofactor sandbox validate, push, connect, claim, run… |
| Built-in guide | cofactor sandbox guide | cofactor sandbox guide v2 |
Both share the cofactor sandbox command group. The Sandbox CLI page lists the v2 commands.
03Delivery: where the code comes from
Every sandbox belongs to a Cofactor application. How the application's code reaches its sandboxes is its own setting, and an application has exactly one:
| GitHub | Pushed images | |
|---|---|---|
| Set up with | cofactor sandbox connect --application A --repo owner/name [--dir D] | cofactor sandbox push --application A <image> (the first push sets it up) |
| Cofactor's access | Your repository, through the GitHub App | None: you build and push the image |
| The definition | At the repository root or a definition directory, read at each commit | Pushed with each image |
| Building | Cofactor runs your setup and lifecycle.build | Already built into the image |
| A version is | A commit | A push |
| Pull request previews | Automatic, one per pull request | Push with a tag per pull request |
| Guide | Sandboxes from GitHub | Sandboxes from a pushed image |
An application connected to GitHub refuses pushed images. A monorepo can connect one application per definition directory.
04Versions and tags
- GitHub: each commit is a version. A claim without
--atcopies the default branch's current ready snapshot of the environment, which Cofactor rebuilds at the head every 6 hours while the environment is in use (claimed in the last two weeks), so it can be up to 6 hours behind. A snapshot more than 12 hours old counts as stale, and the claim builds the head instead.--at <sha>claims a specific commit (the full 40-character SHA). - Pushed images: each push is a version, with its own id. A claim without
--atuses the newest ready push.--attakes a push id or a tag. - Tags (pushed images): a push can carry up to 10, each letters, digits,
.,_and-, at most 64 characters, starting with a letter or digit. A tag names the newest ready push that carries it, so pushing--tag nightlyagain moves the tag; older pushes stay claimable by their id.
05Environments
An environment (a "flavor") is the same sandbox, same code, started with different data and config: default, free-plan, beta-features. Every definition has a default environment, and it's what a claim gets when it doesn't name one. An environment can extend another, running its parent's seeds first.
Each environment in the definition becomes a sandbox environment of the application, alongside its other environments. Each persona in the definition becomes one of the application's personas, matched by name (case doesn't matter), so a test run on a sandbox environment signs in with that sandbox's own logins, with no mapping to set up.
This sync only adds:
- an environment the definition no longer declares stays on the application until you delete it, and claims of it fail, naming the missing environment;
- if the application already has an environment with that name, the new one is named
<name> (sandbox); - the name, variables and default flag you give a synced environment are kept.
A test run can target a sandbox environment like any other environment of the application, and runs on its own copy.
06The claim lifecycle
- Snapshot. A claim needs the ready snapshot for its version and environment. If there isn't one yet, it's built first, once, however many claims are waiting on it: (GitHub) the nearest layer is restored and the outstanding build steps run;
prepareruns; the environment's seeds run in order; the sandbox is captured with its services running. - Restore. A copy of the ready snapshot is restored. Nothing is built or seeded; the copy starts where the snapshot left off, data included.
- Prepare.
lifecycle.prepareruns on the copy. This is where it becomes this sandbox: its own public URLs, freshly generated persona passwords and, with emulation declared, a fresh emulator configuration. - Ready. The sandbox reports its URLs, persona logins and seed outputs. A copy is never reused.
cofactor sandbox show breaks the time to ready down into these phases (snapshot, restore, prepare).
Failures. A failure while building a ready snapshot (a build step, prepare or a seed) fails the claims that were waiting on that build. A claim made after the failure builds the snapshot again. So a passing problem clears with the next claim, but a seed or build bug that fails every time keeps failing until you fix it and push or commit again. See When a seed fails.
Statuses. A sandbox is claiming, ready, failed or destroyed. A push is uploading, importing, ready or failed.
How long a sandbox lasts
- A sandbox you claim has a two-hour lease, shown as
expiresbycofactor sandbox show. It isn't extended. Destroy it as soon as you're done (cofactor sandbox destroy <id>), and claim a new one for more time. - A pull request preview is kept alive by each push to the pull request, for up to two weeks, and is destroyed when the pull request closes.
07Logins
Each persona gets a freshly generated password in every sandbox. lifecycle.prepare receives them as COFACTOR_PERSONAS and must make each login work in the app. People and agents read them with cofactor sandbox show <id> (or cofactor sandbox preview for a pull request). They never appear in a pull request comment.
08In the app
The Sandboxes page has a v2 view, with:
- Applications: each application's delivery and latest version, and its environments, each with a readiness state (
ready,warming,staleornone) and a Claim button; - Live sandboxes: application, environment, version, status, who claimed it, and when it expires, with links to its URLs, its logins, and Destroy;
- Pushes (pushed-image applications): each push's tags, status and any import error, with a button to claim that version.
A sandbox's detail page shows its status and timing, URLs, persona logins, seed outputs and expiry, and runs actions from a form built from each action's input schema.
09API keys
A scoped API key needs the sandbox scope's matching action for each Sandboxes v2 call: read, publish (push), create (claim), run (actions), delete (destroy) and edit (connect a repository). sandbox:read includes persona logins. A CI job that pushes images needs sandbox:read, sandbox:publish and property:read. See API Keys and Sandbox CLI → API key scopes.
10Limits
| Limit | Value |
|---|---|
Ports per sandbox, implicit emulation and object-storage included | 8 |
resources.vcpus | 1–32 |
resources.memory_mib | 1024–65536 |
| Definition size | 256 KB |
| Action timeout | 1–1800 seconds (default 60) |
| Service health | healthy within 5 minutes of starting |
| Image platform (pushed) | linux/amd64, glibc, with bash |
| One image layer (pushed) | 5 GB |
| Tags per push | 10 |
| Layers reused by a re-push | those uploaded in the last 30 days |
| Claimed sandbox lease | 2 hours |
| Object storage providers per application | 1 |
11Reference
- Sandbox definition: every field of
cofactor.sandbox.yaml - Sandbox CLI: every
cofactor sandboxv2 command - Seeds and actions
- Ports and params
- Object storage
- Emulation