Add Linux build constraint - #270
Conversation
When blfs mount fails (e.g. ACL denies access, config error), the process exits immediately. Previously, the readiness poll loop checked cmd.ProcessState which is always nil until cmd.Wait() is called — so the exit was never detected and we waited the full 30s timeout. Fix: run cmd.Wait() in a goroutine, pipe result into a channel, and check the channel on every poll iteration via non-blocking select. This gives us instant error reporting when blfs exits. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Add sentrylib package with build-time DSN injection via ldflags, runtime env var override, and anonymous mode for OSS/self-hosted - Add sentryRecoveryMiddleware to capture panics to Sentry before gin.Recovery() handles the HTTP 500 response - Parallelize independent init tasks (MergeCABundle, WireGuard, ScaleReset, LoadState) with sync.WaitGroup for faster startup - Move flag.Parse() to top of main, before context creation - Cache BL_ENV to avoid repeated os.Getenv calls in swagger setup - Remove dead mcpServer.Serve() call (unconditionally returns nil) - Extract command execution to startBackgroundCommand() helper - Add SENTRY_DSN build arg to Dockerfile and CI workflows Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Add --disable-sentry CLI flag as an opt-out mechanism - Display a clear startup banner when Sentry is active, informing the user that error reporting is enabled (anonymized) and showing both opt-out methods (flag and env var) - Pass disabled state from flag into sentrylib.Init() Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…nment - Replace custom sentryRecoveryMiddleware with sentrygin.New(Repanic: true) - Add build-time Environment variable via ldflags (prod for main, dev for develop) - Falls back to BL_ENV runtime env var if build-time value is empty - Update Dockerfile and CI workflows to pass SENTRY_ENVIRONMENT Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…erminology Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…d /debug/panic endpoint (dev-only) Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
… comment Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ation metric Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ctions BeforeSend and BeforeSendTransaction hooks now unconditionally remove event.Request (URL, headers, body) and event.User (geo, IP) to ensure no process logs or customer data ever reaches the telemetry sink. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…er/devin/drive-mount-fast-fail
…acheOnClose Added support for opening read-only files with FUSE direct IO to optimize memory usage during bulk reads, and introduced an option to evict the kernel page cache when a file's last handle is closed. Both features are controlled via environment variables for improved performance in specific use cases.
* chore: add smoke-test script for Node-based hub templates (#237) Introduces a new script, node-test.py, that spins up temporary sandboxes for each published Node template and performs various checks to ensure functionality. The script verifies Node version compatibility, checks the availability of public preview URLs, and runs Playwright tests for browser functionality. It allows for configurable expected Node major versions and supports skipping version checks for pre-release images. Co-authored-by: cploujoux <setup@example.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * fix(ci): only treat hub directories as sandboxes The build matrix listed every entry in hub/ as a sandbox, so a script file like node-test.py broke releases. Filter to directories only and move node-test.py back into hub/. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Charles Drappier <cdrappier@blaxel.ai> Co-authored-by: cploujoux <setup@example.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…id URL crash Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(nextjs): replace generic create-next-app scaffold with Blaxel-native Sandbox Lab The default nextjs template just ran create-next-app, giving agents and users a blank Vercel-style starter with no sense of what a Blaxel sandbox actually offers. Replaces it with a Sandbox Lab app that proves the sandbox is real and useful: a live system-stats panel (host, pid, uptime, memory, load, ticking every second), a public terminal that runs a small allowlisted set of real commands, one-click launchers into Codex/Claude/Cursor with a prompt that frames the whole page as an editable template, and docs links into the wider Blaxel platform. Adds AGENTS.md so agents opening the sandbox get oriented automatically instead of only humans reading README.md. Adds a docker-compose entry for local testing, matching the pattern used by sibling templates. Verified: typecheck, build, and the sandbox-check proof script pass both on host and inside a real docker build --platform linux/amd64 run of hub/nextjs (sandbox-api boot, entrypoint, dev server, and the actual app all confirmed working end to end). * fix: address Mendral code review findings - Make findSandboxCommandByCommand match case-insensitively, matching findTerminalBrowserCommand, so 'node -V' or 'PWD' work the same as lowercase input instead of silently falling through to the not-supported rejection. - Replace the arbitrary *8 multiplier on the memory bar with a fixed, explained 512MB ceiling. Comparing RSS to total system/container memory produced a near-invisible bar on a large host or a misleadingly maxed-out bar on a small one; a fixed ceiling for this size of app is honest and explainable. * fix: rate-limit the public /api/run-check endpoint Cursor's security review flagged this correctly: the endpoint executes real subprocesses for any caller with no authentication or abuse controls, so repeated requests could saturate this sandbox's CPU/process capacity (application-layer DoS against a single sandbox instance). Add a simple in-memory guard: only one command runs at a time, plus a 1.5s cooldown between requests. No auth/infra needed since each sandbox is already its own isolated instance. Client now surfaces the resulting 429 message instead of a generic 'Request failed with 429'. Verified: concurrent requests get one 200 + one 429, rapid sequential requests get 429 until the cooldown passes, then succeed again. typecheck/build/sandbox-check still pass. * fix: scope Sandbox Lab to a dedicated onboarder-nextjs template Reverts hub/nextjs back to the original generic create-next-app scaffold. The onboarder's HELLO_WORLD_IMAGE constant in controlplane hardcodes blaxel/nextjs:latest, but that image is also the general, publicly-selectable Next.js template every regular user gets when creating a sandbox for their own project. Shipping the onboarding demo dashboard as its content would have replaced every user's plain Next.js starter with a terminal/stats/agent-launcher UI they didn't ask for. Moves the Sandbox Lab app into a new hub/onboarder-nextjs/ template instead: same Dockerfile pattern, own template.json marked hidden so it never appears in the general template picker, matching the repo's existing convention for platform-managed images (benchmark, vibekit-*). Adds onboarder-nextjs to build.yaml's workflow_dispatch options and a docker-compose entry for local testing, alongside the existing (now reverted) nextjs entry. controlplane's HELLO_WORLD_IMAGE will need to point at blaxel/onboarder-nextjs:latest once this is published \u2014 tracked as a separate follow-up in the other repo, not part of this PR. Verified: docker build --platform linux/amd64 succeeds for both hub/nextjs (reverted, matches origin/main exactly) and the new hub/onboarder-nextjs (full container smoke test: sandbox-api boot, entrypoint, dev server, page renders, /api/sandbox-info returns real process data). typecheck/build/sandbox-check pass from the new app-template path. * fix: README no longer claims to be the default nextjs image Mendral caught this correctly: after the previous commit moved the app into hub/onboarder-nextjs, the README still said it was the default blaxel/nextjs:latest starter. Corrected to describe its actual scope (onboarder-only, hidden platform-managed template). * fix: icon URL in onboarder-nextjs template.json 404s, use a working one Caught by re-checking every field I'd written rather than assuming: blaxel.ai/favicon.ico is 404. Switched to blaxel.ai/logo.png, the same URL hub/app-runner (another hidden platform-managed template) already uses successfully.
* Add a dedicated onboarder-nextjs Sandbox Lab template (#245) * feat(nextjs): replace generic create-next-app scaffold with Blaxel-native Sandbox Lab The default nextjs template just ran create-next-app, giving agents and users a blank Vercel-style starter with no sense of what a Blaxel sandbox actually offers. Replaces it with a Sandbox Lab app that proves the sandbox is real and useful: a live system-stats panel (host, pid, uptime, memory, load, ticking every second), a public terminal that runs a small allowlisted set of real commands, one-click launchers into Codex/Claude/Cursor with a prompt that frames the whole page as an editable template, and docs links into the wider Blaxel platform. Adds AGENTS.md so agents opening the sandbox get oriented automatically instead of only humans reading README.md. Adds a docker-compose entry for local testing, matching the pattern used by sibling templates. Verified: typecheck, build, and the sandbox-check proof script pass both on host and inside a real docker build --platform linux/amd64 run of hub/nextjs (sandbox-api boot, entrypoint, dev server, and the actual app all confirmed working end to end). * fix: address Mendral code review findings - Make findSandboxCommandByCommand match case-insensitively, matching findTerminalBrowserCommand, so 'node -V' or 'PWD' work the same as lowercase input instead of silently falling through to the not-supported rejection. - Replace the arbitrary *8 multiplier on the memory bar with a fixed, explained 512MB ceiling. Comparing RSS to total system/container memory produced a near-invisible bar on a large host or a misleadingly maxed-out bar on a small one; a fixed ceiling for this size of app is honest and explainable. * fix: rate-limit the public /api/run-check endpoint Cursor's security review flagged this correctly: the endpoint executes real subprocesses for any caller with no authentication or abuse controls, so repeated requests could saturate this sandbox's CPU/process capacity (application-layer DoS against a single sandbox instance). Add a simple in-memory guard: only one command runs at a time, plus a 1.5s cooldown between requests. No auth/infra needed since each sandbox is already its own isolated instance. Client now surfaces the resulting 429 message instead of a generic 'Request failed with 429'. Verified: concurrent requests get one 200 + one 429, rapid sequential requests get 429 until the cooldown passes, then succeed again. typecheck/build/sandbox-check still pass. * fix: scope Sandbox Lab to a dedicated onboarder-nextjs template Reverts hub/nextjs back to the original generic create-next-app scaffold. The onboarder's HELLO_WORLD_IMAGE constant in controlplane hardcodes blaxel/nextjs:latest, but that image is also the general, publicly-selectable Next.js template every regular user gets when creating a sandbox for their own project. Shipping the onboarding demo dashboard as its content would have replaced every user's plain Next.js starter with a terminal/stats/agent-launcher UI they didn't ask for. Moves the Sandbox Lab app into a new hub/onboarder-nextjs/ template instead: same Dockerfile pattern, own template.json marked hidden so it never appears in the general template picker, matching the repo's existing convention for platform-managed images (benchmark, vibekit-*). Adds onboarder-nextjs to build.yaml's workflow_dispatch options and a docker-compose entry for local testing, alongside the existing (now reverted) nextjs entry. controlplane's HELLO_WORLD_IMAGE will need to point at blaxel/onboarder-nextjs:latest once this is published \u2014 tracked as a separate follow-up in the other repo, not part of this PR. Verified: docker build --platform linux/amd64 succeeds for both hub/nextjs (reverted, matches origin/main exactly) and the new hub/onboarder-nextjs (full container smoke test: sandbox-api boot, entrypoint, dev server, page renders, /api/sandbox-info returns real process data). typecheck/build/sandbox-check pass from the new app-template path. * fix: README no longer claims to be the default nextjs image Mendral caught this correctly: after the previous commit moved the app into hub/onboarder-nextjs, the README still said it was the default blaxel/nextjs:latest starter. Corrected to describe its actual scope (onboarder-only, hidden platform-managed template). * fix: icon URL in onboarder-nextjs template.json 404s, use a working one Caught by re-checking every field I'd written rather than assuming: blaxel.ai/favicon.ico is 404. Switched to blaxel.ai/logo.png, the same URL hub/app-runner (another hidden platform-managed template) already uses successfully. * fix: sandbox stats panel showed hostname/sandboxName as (none) (#247) Caught this live on Blaxel's real dev sandbox (not locally): Blaxel containers don't set a kernel hostname or the env vars I'd guessed (BL_SANDBOX_NAME, HOSTNAME) — os.hostname() genuinely returns the literal string "(none)" there. The real, correct identifier Blaxel actually sets is BL_NAME (confirmed via env dump on a live dev sandbox: BL_NAME=<sandbox-name>, BL_WORKSPACE, BL_REGION, etc.). Same category of bug as the earlier rssMemoryMb:0 QEMU issue: a panel whose whole point is proving this is a real machine must never show a placeholder value that looks broken. Now prefers BL_NAME/BLAXEL_NAME, never surfaces the literal "(none)" string, and only falls back to os.hostname() for local/Docker dev where it's meaningful. Verified: hot-patched the fix directly onto the still-running dev sandbox (onboarder-nextjs-dev-test) via the filesystem API and confirmed /api/sandbox-info immediately returned the real sandbox name instead of (none). This commit ships the same fix through the normal pipeline (main -> develop -> rebuild -> republish) so the packaged image matches.
…to the environment Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…sses Add a per-mount-path mutex plus an idempotency/conflict check so duplicate or racing POST /drives/mount requests never spawn a second blfs process. UnmountDrive takes the same lock; the mount timeout cleanup uses the internal unlocked variant to avoid self-deadlock. ErrMountPathBusy maps to HTTP 409. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
#268) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The API keeps the privileges it needs (drive FUSE mounts, WireGuard, CA bundle, keep-alive) and drops to BL_SANDBOX_USER for everything it runs on behalf of the user: processes, terminals, the startup command and filesystem operations. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…xample Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…xel-ai/sandbox into develop
Extract the Do method and setfsuid/setfsgid helpers into platform-specific files (do_linux.go / do_other.go) so the identity package compiles on darwin. The Linux-only syscall constants SYS_SETFSUID and SYS_SETFSGID are now gated behind a //go:build linux constraint. On non-Linux platforms, Do simply runs the function without filesystem identity switching, since setfsuid/setfsgid are not available.
| return "", "", fmt.Errorf("failed to create mount directory: %w", err) | ||
| } | ||
| if id := identity.Get(); id != nil { | ||
| if err := os.Chown(mountPath, id.Uid, id.Gid); err != nil { |
There was a problem hiding this comment.
🔒 Agentic Security Review
Severity: HIGH
The new mount handoff path calls os.Chown on a request-supplied mountPath with only minimal validation (".."/length). This allows mounting to pre-existing sensitive paths and changing their ownership to the unprivileged workload UID/GID.
Impact: a compromised workload process can re-own privileged filesystem locations and then modify files that may later execute in privileged flows, enabling privilege escalation inside the sandbox VM.
Reviewed by Cursor Security Reviewer for commit 2479065. Configure here.
There was a problem hiding this comment.
Good catch. The was applied unconditionally — including on pre-existing directories — which could re-own a sensitive path if the subsequent blfs mount failed.
Fixed in 775c4a4: we now check whether the directory existed before MkdirAll and only chown when we actually created it. Pre-existing directories are left untouched.
…ones A request-supplied mountPath targeting a pre-existing system directory (e.g. /usr/local/bin) would have its ownership changed to the workload user. If the subsequent blfs mount then failed, the directory remained re-owned, enabling privilege escalation within the sandbox VM. Fix: check whether the directory exists before MkdirAll and only chown when we actually created it.




Summary
setfsuid/setfsgidhelpers and theDomethod into platform-specific files (do_linux.go/do_other.go) so the identity package compiles on darwinDosimply runs the function without filesystem identity switching since those syscalls don't existFixes darwin (amd64/arm64) build failures caused by
syscall.SYS_SETFSUIDandsyscall.SYS_SETFSGIDbeing Linux-only constants.Related insight: https://app.mendral.com/insights/01KZ2A84VPJ677GYZYCNVNMTW0
Note
Created by Mendral. Tag @mendral-app with feedback or questions.