Skip to content

Add Linux build constraint - #270

Open
mendral-app[bot] wants to merge 41 commits into
mainfrom
mendral/add-linux-build-constraint
Open

Add Linux build constraint#270
mendral-app[bot] wants to merge 41 commits into
mainfrom
mendral/add-linux-build-constraint

Conversation

@mendral-app

@mendral-app mendral-app Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Extract setfsuid/setfsgid helpers and the Do method into platform-specific files (do_linux.go / do_other.go) so the identity package compiles on darwin
  • On non-Linux platforms, Do simply runs the function without filesystem identity switching since those syscalls don't exist

Fixes darwin (amd64/arm64) build failures caused by syscall.SYS_SETFSUID and syscall.SYS_SETFSGID being Linux-only constants.

Related insight: https://app.mendral.com/insights/01KZ2A84VPJ677GYZYCNVNMTW0


Note

Created by Mendral. Tag @mendral-app with feedback or questions.

drappier-charles and others added 30 commits June 13, 2026 01:32
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>
…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>
Joffref and others added 10 commits July 24, 2026 16:37
…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>
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.
@mendral-app
mendral-app Bot requested a review from a team August 2, 2026 22:45
@mendral-app
mendral-app Bot marked this pull request as ready for review August 2, 2026 22:58

@cursor cursor 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.

Stale comment

Completed agentic security review for the current head. Posting one net-new deduplicated finding.

Open in Web View Automation 

Sent by Cursor Security Agent: Security Reviewer

Comment thread sandbox-api/src/handler/drive/mount.go Outdated
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 {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 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.

Fix in Cursor Fix in Web

Reviewed by Cursor Security Reviewer for commit 2479065. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.
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.

4 participants