Skip to content

feat(container): self-hostable container image with runtime configuration - #528

Open
svalleru wants to merge 9 commits into
mainfrom
feat/self-host-container
Open

svalleru wants to merge 9 commits into
mainfrom
feat/self-host-container

Conversation

@svalleru

@svalleru svalleru commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Run the Dashboard from one prebuilt container image, configured for each installation when the container starts. The authenticated Next.js layout sends the public domain and sandbox URL to ClientConfigProvider; the terminal and filesystem inspector read them through useClientConfig() on their first render.

Container environment
  ├─ startup validation → Next.js ready
  ├─ PUBLIC_E2B_DOMAIN + PUBLIC_SANDBOX_URL
  │    → server layout → ClientConfigProvider
  │                       ├─ terminal
  │                       └─ filesystem inspector
  └─ E2B_INFRA_API_URL + E2B_DASHBOARD_API_URL + E2B_API_KEY
       → server-side API clients
  • PUBLIC_E2B_DOMAIN and optional PUBLIC_SANDBOX_URL override the legacy names at runtime. Existing NEXT_PUBLIC_* build configuration and the E2B_SANDBOX_URL alias remain supported.
  • Only { domain, sandboxUrl } is serialized for browser consumers. Configuration arrives with the page, so no /api/config endpoint or client fetch/cache is needed.
  • Server-side SDK operations use the same configured domain and sandbox URL. Request headers never select sandbox destinations. Without a sandbox override, the SDK uses domain-based routing; local sandbox proxies need an explicit PUBLIC_SANDBOX_URL.
  • The standalone image runs as a non-root user on port 3001 by default. Bun installs dependencies and checks build configuration; Node builds and serves Next.js.
  • DASHBOARD_COOKIE_SECURE=false supports plain-HTTP installations. Secure cookies remain the production default. Invalid cookie flags and resolved API/sandbox URLs stop server initialization with a named configuration error, even when telemetry is disabled.
  • The container workflow builds and smoke-tests changes affecting the image.

Validation

  • 331 unit and integration tests pass, covering runtime overrides, legacy fallbacks, forged Host/forwarded headers across all server SDK operations, initial provider delivery without fetching, and startup validation with telemetry disabled. Obsolete request-host fallback tests were replaced with regression coverage for configured destinations.
  • TypeScript, Biome checks for changed TypeScript files, and Knip pass.
  • Production Docker build and smoke checks pass, including process failure for invalid PUBLIC_SANDBOX_URL, legacy E2B_SANDBOX_URL, and DASHBOARD_COOKIE_SECURE. Earlier isolated checks verified that one image serves different runtime configurations and does not expose server endpoints or credentials.
  • Prior local-stack verification of the provider flow covered sandbox and template lists, a terminal command, and reading the resulting file through the filesystem inspector.

Deployment notes

  • PUBLIC_ has no special meaning to Next.js; the server explicitly reads and passes these two settings. Restart the container and reload open pages after configuration changes.
  • The sandbox URL must be reachable from both the browser and server. Set PUBLIC_SANDBOX_URL explicitly for a local proxy, such as http://127.0.0.1:3002 when both run on the same machine. There is no request-host fallback.
  • Image builds currently need outbound HTTPS for Google Fonts. The Container check is path-filtered and should not be made required.

@cla-bot cla-bot Bot added the cla-signed label Sep 12, 2026
@cla-bot

cla-bot Bot commented Sep 12, 2026

Copy link
Copy Markdown

Thank you for your pull request and welcome to our community. We could not parse the GitHub identity of the following contributors: Independence Check.
This is most likely caused by a git client misconfiguration; please make sure to:

  1. check if your git client is configured with an email to sign commits git config --list | grep email
  2. If not, set it up using git config --global user.email email@example.com
  3. Make sure that the git commit email is configured in your GitHub account settings, see https://github.com/settings/emails

@cla-bot cla-bot Bot removed the cla-signed label Sep 12, 2026
@svalleru
svalleru marked this pull request as ready for review September 12, 2026 01:58

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

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Comment thread src/core/server/runtime-config.ts Outdated
Comment thread src/configs/cookies.ts Outdated
@svalleru svalleru closed this Sep 14, 2026
@drankou drankou reopened this Sep 15, 2026
@cla-bot

cla-bot Bot commented Sep 15, 2026

Copy link
Copy Markdown

Thank you for your pull request and welcome to our community. We could not parse the GitHub identity of the following contributors: Independence Check.
This is most likely caused by a git client misconfiguration; please make sure to:

  1. check if your git client is configured with an email to sign commits git config --list | grep email
  2. If not, set it up using git config --global user.email email@example.com
  3. Make sure that the git commit email is configured in your GitHub account settings, see https://github.com/settings/emails

@drankou
drankou marked this pull request as draft September 15, 2026 10:55
@e2b-dev e2b-dev deleted a comment from svalleru Sep 15, 2026

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

Findings marked 🟡 are optional suggestions and need no follow-up push.

Comment thread src/core/server/runtime-config.ts Outdated
Comment thread src/core/server/runtime-config.ts Outdated
@cla-bot

cla-bot Bot commented Sep 15, 2026

Copy link
Copy Markdown

Thank you for your pull request and welcome to our community. We could not parse the GitHub identity of the following contributors: Independence Check.
This is most likely caused by a git client misconfiguration; please make sure to:

  1. check if your git client is configured with an email to sign commits git config --list | grep email
  2. If not, set it up using git config --global user.email email@example.com
  3. Make sure that the git commit email is configured in your GitHub account settings, see https://github.com/settings/emails

@svalleru
svalleru marked this pull request as ready for review September 15, 2026 16:35
svalleru and others added 3 commits September 15, 2026 09:37
The sandbox router read the sandbox URL from the environment alone, so an
install that leaves E2B_SANDBOX_URL unset — the runtime-configured case, where
each browser is told the host it reached the dashboard on — sent every
server-side envd call to the build-time domain instead. Killing a terminal's
pty on leaving the page failed every time.

resolveServerSandboxUrl applies the browser's rule to the request the procedure
is serving, and the browser config is now expressed through it so the two
cannot drift.
@svalleru
svalleru force-pushed the feat/self-host-container branch from 8631ec1 to de4b2e0 Compare September 15, 2026 16:40
@cla-bot cla-bot Bot added the cla-signed label Sep 15, 2026

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

I reviewed this PR and didn't find any new bugs. The commit pushed since my last review (8631ec1) substantively addresses the concerns raised in my earlier passes: the header-derived sandbox-origin fallback that trusted X-Forwarded-Host/Proto (the SSRF-adjacent path) was removed entirely in favor of operator-configured values only, resolveSandboxUrl() is now validated eagerly at startup via validateRuntimeConfig() alongside the other URL resolvers, and isSecureCookie() now throws on an unrecognized DASHBOARD_COOKIE_SECURE value instead of silently falling back to secure=true. Because this PR still touches auth-cookie handling, request-origin/URL resolution, and container startup validation, a human look is worthwhile as an additional step before merging.

Extended reasoning...

Overview

Since my previous reviews on this PR, one further commit (8631ec1, "fix(config): validate startup settings and use configured sandbox URLs") landed. I re-ran the full diff against the base and read the current state of src/core/server/runtime-config.ts, src/configs/cookies.ts, src/instrumentation.ts, and src/core/server/api/routers/sandbox.ts directly (not just relying on the resolution metadata) to confirm what changed.

Security risks

All four items I previously flagged across two reviews were substantively fixed in this commit, verified by direct code reading:

  • The entire requestOrigin()/forwardedProtocol()/hostnameOf()/resolveServerSandboxUrl() machinery that trusted X-Forwarded-Host/X-Forwarded-Proto with no allow-list (the SSRF concern) was deleted. resolveBrowserRuntimeConfig() and the sandbox router now call the header-free resolveSandboxUrl(), which only reads operator-set env vars — this also moots the related multi-value X-Forwarded-Host parsing bug, since that code path no longer exists.
  • resolveSandboxUrl() is now called eagerly from a new validateRuntimeConfig(), invoked from src/instrumentation.ts's register() at server startup (with process.exit(1) on failure), closing the gap where a malformed sandbox URL previously surfaced only when a user opened a terminal.
  • isSecureCookie() now explicitly matches 'true'/'false' and throws on anything else, rather than treating any non-'false' string (e.g. '0', 'off') as secure=true; this throw is also wired into validateRuntimeConfig() so a bad DASHBOARD_COOKIE_SECURE fails startup instead of silently reproducing the plain-HTTP login loop the variable exists to fix.
  • New tests in tests/integration/runtime-config-startup.test.ts cover startup rejection for each malformed URL var and for an invalid cookie flag, plus successful startup with a valid configuration — matching the code changes.

I did not find new security issues in this delta; the change is a genuine narrowing of trust (removing a header-trust path) plus fail-fast validation, not new attack surface.

Level of scrutiny

This PR remains large and touches security-relevant code (auth cookie flags, server-side URL/origin resolution used for SDK routing, container startup). That, combined with the significant design change in this latest commit (removing the request-header-based sandbox routing entirely), is exactly the kind of change where a maintainer's sign-off adds value beyond an automated pass, even though the automated review this round found nothing new and confirms the previously raised issues were fixed correctly.

Other factors

The fix commit was authored by drankou, who is a CODEOWNER for this repository (per CODEOWNERS), and the same person resolved the four inline threads I had opened — this is independent-of-author engagement, and my own reading of the code corroborates that the resolutions reflect real fixes rather than administrative closures. No CHANGES_REQUESTED or unresolved third-party objections remain visible in the timeline. Test coverage for the new startup-validation and cookie paths is present and appears to match the implementation.

This review covers commit 8631ec1, which is no longer the latest commit on this pull request; later commits are not covered by it.

Comment thread src/instrumentation.ts

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

i think you should do validation inside env.ts for consistency

Comment thread src/lib/env.ts
Comment on lines +11 to +17
E2B_INFRA_API_URL: z.url().optional(),
E2B_DASHBOARD_API_URL: z.url().optional(),
E2B_SANDBOX_URL: z.url().optional(),

// Read on the server and explicitly passed to the browser by the layout.
PUBLIC_E2B_DOMAIN: z.string().optional(),
PUBLIC_SANDBOX_URL: z.url().optional(),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

let's make them required and just deprecate the old ones instead of fallback

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants