Skip to content

Latest commit

 

History

History

README.md

Cheatcode sandbox image (Daytona)

The project sandbox image for the Daytona backend. Daytona injects its own daemon (host-mounted, PID 1) and overrides the image ENTRYPOINT, so this image does not bake a sandbox daemon. Headed Chromium's Xvfb display and code-server are started on demand after sandbox creation via toolbox sessions running scripts under scripts/, not by the entrypoint.

Sandboxes are created with user: "node" (see ProjectSandbox.createSandbox) so the runtime user matches /workspace + the baked Next/Expo templates under /home/node.

Build and publish

The normal path is the protected Build Sandbox Snapshot GitHub workflow. Dispatch it from main and enter BUILD_SNAPSHOT. It publishes an immutable candidate named cheatcode-sandbox-viewer-bundle-<12-character-commit-sha>-<workflow-run-id> and refuses any pre-existing or surviving candidate name. If Daytona returns its exact transient 30-second processing timeout, the workflow may remove only the unused failed candidate created by that same publish attempt before retrying the run-scoped name. Candidate ID, creation time, OCI digest, region, resources, error shape, and lastUsedAt are revalidated immediately before deletion. Active, previously used, pre-existing, or ambiguous snapshots fail closed and are never deleted or replaced.

Promotion is a separate reviewed source change: update the agent-worker DAYTONA_SANDBOX_SNAPSHOT var to the candidate name, then use the protected database migration/backend release path. Keeping publication and promotion separate preserves the currently running snapshot as an immediate rollback target. After promotion, each user's first workspace operation waits for active work to drain, deletes only the stale canonical container, and recreates it on the same isolated persistent-volume subpath. Project files and user-installed skills therefore survive runtime promotion. Ambiguous ownership, snapshot labels, duplicate identities, or storage mounts fail closed.

The protected production release preflight independently installs the same checksum-pinned Daytona CLI and scans every paginated snapshot-list page. It accepts the configured name only when exactly one active snapshot has the reviewed region and 2-vCPU/4-GiB/10-GiB resources, provider identifiers and image reference have canonical shapes, validation was not skipped, and no provider error is present. The source commit encoded in the immutable name must be an ancestor of the exact release, with no later change anywhere under this sandbox image directory. The same preflight requires the configured shared workspace volume to exist exactly once in the snapshot's organization and to be ready without a provider error. Daytona's current FUSE/object- store volume contract exposes no fixed region or capacity field; target region is enforced by the snapshot and every canonical sandbox, while storage scales through the provider object store. The preflight also scans the complete sandbox inventory and rejects duplicate live canonical label identities, proving recovery does not depend on a replacement retaining the historical physical sandbox name.

Local build check

Docker can validate the AMD64 image locally without publishing it:

docker build --platform=linux/amd64 \
  --build-context default_skills=skills \
  -t cheatcode-sandbox:<immutable-tag> \
  infra/containers/sandbox

Do not authenticate a laptop to Daytona or publish a snapshot manually. Dispatch the protected Build Sandbox Snapshot workflow from main with BUILD_SNAPSHOT, review its emitted immutable name, and commit that name to the agent-worker DAYTONA_SANDBOX_SNAPSHOT var. The authoritative current default is committed in apps/agent-worker/wrangler.jsonc.

Use an immutable tag, not :latest (rejected) and not a digest (digest pinning is currently broken for Daytona pushed-image references). The Dockerfile base image is still pinned by OCI digest. Snapshot names are immutable in Daytona; publish each image update under a new snapshot name and move DAYTONA_SANDBOX_SNAPSHOT forward. Resources are baked into the snapshot; rebuild/re-push to change them (or build per-tier snapshots).

When updating code-server, an Open VSX extension, or the Daytona CLI, update its exact version and SHA-256 together. The remote build fails closed when downloaded bytes do not match the reviewed checksum. create-next-app and the Expo template tarball are also version-pinned so a rebuild cannot silently switch template generations. The Node base image uses an OCI digest and apt resolves both the main and security repositories from the reviewed DEBIAN_SNAPSHOT; update those pins deliberately to take security patches.

The package manager, source generators, document/data runtime, and the Parquet Viewer runtime overlay have their own checked-in npm locks under package-manager/, app-generators/, doc-runtime/, and extension-overrides/parquet-viewer/. They are installed with npm ci; no npx or mutable generator resolution runs during the image build. Document-runtime versions are owned by its reviewed manifest and lock; they are intentionally absent from the Worker workspace catalog. Its uuid override keeps ExcelJS on the patched UUID implementation while preserving the public CommonJS v4 API that ExcelJS consumes. The Next and Expo scaffolds likewise use reviewed manifests and pnpm locks under app-templates/. Their locked packages are installed into immutable sandbox-local runtimes in the image. Project creation copies only source and config to the persistent Daytona volume; dependency trees and generated compiler caches stay on the sandbox's local filesystem, avoiding slow, partial writes to object-store FUSE. Additional dependencies use a project-scoped local modules directory and can be restored from the reviewed package store after a sandbox replacement. Expo uses an exact expo-template-default tarball with a reviewed SHA-256 rather than the mutable default alias. These locks prevent a snapshot rebuild from resolving a different dependency tree while the application source stays unchanged.

Open VSX currently publishes Parquet Viewer 3.1.0 with vulnerable Thrift and WebSocket runtimes. The image keeps the extension feature but replaces those two runtime packages with the exact, lockfile-pinned versions in extension-overrides/parquet-viewer/ and fails the build if the resulting Parquet reader cannot load. Remove this overlay only after a pinned extension release ships equivalent or newer patched dependencies.

The browser driver pins playwright-core both to satisfy Stagehand's optional compatibility peer and to install a matching Chromium artifact. Stagehand 3 runs against that explicit Chromium path through CDP rather than launching an unversioned system browser. The image omits Playwright's unused headless-shell and FFmpeg artifacts because the product launches headed Chromium directly. Stagehand currently resolves @ai-sdk/provider-utils 3.0.29. That release contains the bounded JSON-response reader that Vercel shipped in 3.0.28. GitHub's current GHSA-866g-f22w-33x8 range nevertheless marks every 3.x version through 3.0.97 affected, so npm audit reports the resulting low-severity transitive paths. The driver independently caps every non-streaming provider response and restricts Node fetches to the exact selected Anthropic, Google, or OpenAI API hostname, containing both that resource-consumption surface and the related download-URL SSRF advisory. Chromium navigation remains inside the isolated sandbox and does not use this provider transport. Keep the exception visible and reassess it with each Stagehand/AI SDK release; do not apply npm's suggested breaking Stagehand downgrade. Static checks fail on moderate-or-higher findings across every sandbox lock without hiding this low-severity report.

Snapshot publication builds and scans the exact local AMD64 image before pushing it to Daytona. Trivy fails on every fixable medium-or-higher vulnerability and every high-or-critical embedded secret; Debian findings without an available package fix remain visible in the full report but cannot block a rebuild indefinitely.

Trivy's current fixable image report has one path-specific metadata mismatch, not an executable vulnerable package. It treats VS Code's built-in extension manifest at lib/vscode/extensions/npm/package.json as the npm CLI because the extension is also named npm. .trivyignore.yaml suppresses only that exact package URL and image path, records the rationale, and expires the exception on October 15, 2026. Re-verify the path after every code-server upgrade; never replace the entry with a broad vulnerability-ID ignore.

The browser driver is a privileged trust boundary inside the otherwise user-programmable sandbox. Project commands run as node; only the immutable launcher may be started through the narrow sudo rule, and that launcher drops to the separate cheatcode-browser Unix user. The Agent Worker sends the request-scoped model key and driver bearer token once over the Daytona session's stdin. They are never command arguments, environment variables, workspace files, or persisted process metadata. The driver runs from its mode-0700 home with core dumps disabled, deletes provider-key environment names defensively, expires after 55 minutes, and requires both the bearer token and run ID. Worker calls reach it through a short-lived Daytona-signed port URL; arbitrary workspace code does not receive that URL or either credential. Preserve this boundary when changing the driver launch path.

Skills and workspace metadata

The repository's top-level skills/ directory is the only source for curated default skills. The snapshot build mounts it as the default_skills BuildKit context and copies the same reviewed files to /home/node/.cheatcode/default-skills/, making the active playbooks inspectable without maintaining a second copy. Default skills are immutable snapshot data. User-authored metadata remains canonical in Postgres, while its versioned package is canonical in R2 and mirrored completely to /workspace/.cheatcode/skills/<slug>/ for editing. The package may contain bounded source, schemas, references, templates, and common binary assets; dependency folders, virtual environments, caches, locks, and build output remain sandbox-local.

Cheatcode does not deploy or synchronize generated user apps. The curated catalog therefore has no deploy skill, deploy runtime, or app-side Composio bridge. Connected-app skills invoke the existing request-scoped Composio tools for explicit user actions only.

Regenerate JavaScript locks from their owning directories with the repository's pinned package-manager versions:

cd infra/containers/sandbox/package-manager
npm install --package-lock-only --ignore-scripts --no-audit --no-fund

cd ../app-generators
npm install --package-lock-only --ignore-scripts --no-audit --no-fund

cd ../doc-runtime
npm install --package-lock-only --ignore-scripts --no-audit --no-fund

cd ../extension-overrides/parquet-viewer
npm install --package-lock-only --ignore-scripts --no-audit --no-fund

cd ../app-templates/next
pnpm install --lockfile-only --ignore-scripts --ignore-workspace

cd ../expo
pnpm install --lockfile-only --ignore-scripts --ignore-workspace

Static checks audit the root graph plus all sandbox npm locks and both independent template locks. Run the template audits from the repository root with pnpm --dir <template-directory> --ignore-workspace audit; omitting --ignore-workspace audits the root workspace instead of the selected template.

The Next scaffold follows the web workspace's exact Next, React, React DOM, Tailwind, Biome, and TypeScript pins. The Expo scaffold instead follows its exact Expo SDK 57 compatibility matrix; do not force the web workspace's React patch into that manifest without first upgrading and validating the Expo SDK as a unit. Their local overrides keep Next's PostCSS and Expo's transitive Xcode UUID implementation on patched versions; Xcode's CommonJS v4() call remains compatible with the pinned UUID 11 release.

Python top-level dependencies live in requirements.in; requirements.txt is the Python 3.11 / Linux AMD64 lock and includes hashes for every resolved dependency. Regenerate it from this directory with the exact command recorded in its header. The image installs with --require-hashes and --only-binary=:all:. Audit the resolved lock before publishing an image:

uvx --from pip-audit==2.10.1 pip-audit -r requirements.txt --disable-pip

The image bakes code-server plus document and data viewers used by the Cheatcode computer Files surface. The product embeds this as a controlled file/document viewer and must never route the Browser tab or an empty computer state to the generic code-server Welcome UI. Generated deliverables can also be rendered through the product file-preview API: PPTX/DOCX/XLSX and other Office files are converted with LibreOffice, PDFs and images render inline, and code/data files use the Files surface.