Example repos
Two public repos under https://github.com/parallel-sandbox . Both run locally with docker compose up and both have a README that walks through running them inside a box, tool call by tool call. They are also what we use to test the platform itself.
example-compose-app
https://github.com/parallel-sandbox/example-compose-app
A Node API (port 3000) that queues jobs and a Go worker (port 8080) that completes them, wired together by docker-compose.yml. e2e/ holds a Playwright test that opens the page in a real browser, adds a job, waits for the worker to finish it and saves a screenshot.
In a box this exercises: git clone, docker compose up --build, sandbox_wire for two services, a headed browser on the virtual display, sandbox_shot recording, sandbox_get for the screenshot.
sandbox_start { "services": [{ "name": "api", "port": 3000 }, { "name": "worker", "port": 8080 }] }
sandbox_exec { "id": "<id>", "cmd": "git clone https://github.com/parallel-sandbox/example-compose-app.git" }
sandbox_exec { "id": "<id>", "cmd": "docker compose up -d --build --wait", "cwd": "example-compose-app", "timeoutSec": 600 }
sandbox_wire { "id": "<id>", "service": "api", "mode": "box" }
sandbox_wire { "id": "<id>", "service": "worker", "mode": "box" }
sandbox_exec { "id": "<id>", "cmd": "npm ci && npx playwright install chromium", "cwd": "example-compose-app/e2e", "timeoutSec": 600 }
sandbox_shot { "id": "<id>", "record": "start" }
sandbox_exec { "id": "<id>", "cmd": "DISPLAY=:99 HEADED=1 BASE_URL=http://localhost:3000 npx playwright test", "cwd": "example-compose-app/e2e", "timeoutSec": 300 }
sandbox_shot { "id": "<id>", "record": "stop" }
sandbox_get { "id": "<id>", "path": "example-compose-app/e2e/screenshots/jobs.png" }
sandbox_stop { "id": "<id>" }
The README also shows the other wiring mode: run only the service you changed in the box and point the unchanged one at your own environment with externalBaseUrl and mode: "external".
example-web-with-logs
https://github.com/parallel-sandbox/example-web-with-logs
One static page with the browser log SDK @parallelsandbox/log attached. A button throws an uncaught error three function calls deep. The page is built with esbuild (minified, with source maps), served by nginx, and configured at container start from environment variables so the project's write key never enters the image.
In a box this exercises: secrets at sandbox_start, source map upload, a page on the virtual display, logs_errors returning the error with the stack resolved to src/app.js lines.
sandbox_start { "services": [{ "name": "web", "port": 8080 }], "secrets": ["PSBX_LOG_WRITE_KEY"] }
sandbox_exec { "id": "<id>", "cmd": "git clone https://github.com/parallel-sandbox/example-web-with-logs.git" }
sandbox_exec { "id": "<id>", "cmd": "PSBX_RELEASE=$(git rev-parse --short HEAD) PSBX_LOG_PROJECT=<project id> PSBX_LOG_ENDPOINT=https://log.parallelsandbox.com docker compose up -d --build --wait", "cwd": "example-web-with-logs", "timeoutSec": 600 }
sandbox_wire { "id": "<id>", "service": "web", "mode": "box" }
Upload the source maps from your own machine, not from the box, so your API key never enters it. Same commit, so the same release id:
git clone https://github.com/parallel-sandbox/example-web-with-logs.git && cd example-web-with-logs
npm ci && PSBX_RELEASE=$(git rev-parse --short HEAD) npm run build
PSBX_API_KEY=<api key> PSBX_LOG_PROJECT=<project id> ./scripts/upload-sourcemaps.sh
Back in the box:
sandbox_exec { "id": "<id>", "cmd": "DISPLAY=:99 chromium --no-sandbox --kiosk --window-size=1280,800 --user-data-dir=/tmp/chrome http://localhost:8080/", "background": true }
sandbox_shot { "id": "<id>" }
sandbox_exec { "id": "<id>", "cmd": "DISPLAY=:99 xdotool mousemove 640 300 click 1" }
logs_errors { "project": "<project id>", "since": "10m" }
sandbox_stop { "id": "<id>" }
Using them as templates
Both repos are plain: a docker-compose.yml at the root, one directory per service with its own Dockerfile, tests in their own directory. Any repo shaped like that runs in a box the same way. The only ParallelSandbox-specific parts are the services list at sandbox_start (names and ports) and, if you want browser errors, the log SDK.