From b97f07203392379434142663132f51455931ded4 Mon Sep 17 00:00:00 2001 From: Adam Docolomansky Date: Thu, 29 Jan 2026 13:54:54 +0100 Subject: [PATCH 01/11] feat: add e2b_lite --- .github/workflows/build-lite-binaries.yml | 102 ++ E2B-LITE-DESIGN.md | 407 ++++++ .../api/internal/orchestrator/lifecycle.go | 15 +- scripts/e2b-lite-setup.sh | 1226 +++++++++++++++++ tests/test_e2b_lite.py | 125 ++ 5 files changed, 1871 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/build-lite-binaries.yml create mode 100644 E2B-LITE-DESIGN.md create mode 100755 scripts/e2b-lite-setup.sh create mode 100644 tests/test_e2b_lite.py diff --git a/.github/workflows/build-lite-binaries.yml b/.github/workflows/build-lite-binaries.yml new file mode 100644 index 0000000000..ea9d00d2cb --- /dev/null +++ b/.github/workflows/build-lite-binaries.yml @@ -0,0 +1,102 @@ +name: Build E2B Lite Binaries + +on: + release: + types: [published] + workflow_dispatch: + inputs: + tag: + description: 'Tag to build (e.g., v1.0.0)' + required: true + type: string + +permissions: + contents: write + +jobs: + build: + name: Build E2B Lite Binaries + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.tag || github.ref }} + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.24' + cache: true + + - name: Get version + id: version + run: | + if [ -n "${{ github.event.inputs.tag }}" ]; then + VERSION="${{ github.event.inputs.tag }}" + else + VERSION="${GITHUB_REF#refs/tags/}" + fi + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Building version: $VERSION" + + - name: Get commit SHA + id: commit + run: | + COMMIT_SHA=$(git rev-parse --short HEAD) + echo "sha=$COMMIT_SHA" >> $GITHUB_OUTPUT + echo "Commit SHA: $COMMIT_SHA" + + - name: Build API binary + run: | + cd packages/api + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ../../dist/api-linux-amd64 \ + -ldflags "-X=main.commitSHA=${{ steps.commit.outputs.sha }}" . + chmod +x ../../dist/api-linux-amd64 + + - name: Build Orchestrator binary + run: | + cd packages/orchestrator + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ../../dist/orchestrator-linux-amd64 \ + -ldflags "-X=main.commitSHA=${{ steps.commit.outputs.sha }}" . + chmod +x ../../dist/orchestrator-linux-amd64 + + - name: Build Client-Proxy binary + run: | + cd packages/client-proxy + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ../../dist/client-proxy-linux-amd64 \ + -ldflags "-X=main.commitSHA=${{ steps.commit.outputs.sha }}" . + chmod +x ../../dist/client-proxy-linux-amd64 + + - name: Build Envd binary + run: | + cd packages/envd + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ../../dist/envd-linux-amd64 \ + -ldflags "-X=main.commitSHA=${{ steps.commit.outputs.sha }}" . + chmod +x ../../dist/envd-linux-amd64 + + - name: Create checksums + run: | + cd dist + sha256sum * > checksums.txt + cat checksums.txt + + - name: Upload binaries to release + if: github.event_name == 'release' + uses: softprops/action-gh-release@v2 + with: + files: | + dist/api-linux-amd64 + dist/orchestrator-linux-amd64 + dist/client-proxy-linux-amd64 + dist/envd-linux-amd64 + dist/checksums.txt + + - name: Upload artifacts (workflow_dispatch) + if: github.event_name == 'workflow_dispatch' + uses: actions/upload-artifact@v4 + with: + name: e2b-lite-binaries-${{ steps.version.outputs.version }} + path: dist/ + retention-days: 30 diff --git a/E2B-LITE-DESIGN.md b/E2B-LITE-DESIGN.md new file mode 100644 index 0000000000..5f8488b051 --- /dev/null +++ b/E2B-LITE-DESIGN.md @@ -0,0 +1,407 @@ +# E2B Lite + +**Version:** 2.0 +**Last Updated:** 2026-01-28 +**Status:** Working + +## Overview + +E2B Lite enables developers to run E2B sandboxes locally on bare metal Linux servers. It uses the same E2B Python/JS SDK with minimal configuration changes. + +### What Works + +- Full sandbox lifecycle (create, execute, pause, resume, kill) +- Command execution in sandboxes +- Filesystem operations (read, write, list) +- Python SDK compatibility +- Local template storage + +### Requirements + +| Requirement | Minimum | Notes | +|-------------|---------|-------| +| **OS** | Linux (Ubuntu 24.04 recommended) | Bare metal or nested KVM | +| **Kernel** | 5.10+ to run, 6.8+ to build templates | `uname -r` to check | +| **CPU** | x86_64 with KVM support | `lscpu | grep -i kvm` | +| **RAM** | 4 GB | More for concurrent sandboxes | +| **Disk** | 20 GB SSD | For templates and snapshots | + +--- + +## Quick Start + +### 1. Clone and Setup + +```bash +git clone https://github.com/e2b-dev/infra.git +cd infra + +# Full setup (installs deps, builds binaries, starts infra, seeds DB) +./scripts/e2b-lite-setup.sh + +# Or skip dependency installation if already have Docker/Go/Node +./scripts/e2b-lite-setup.sh --no-deps +``` + +### 2. Start Services + +```bash +# Start all services (foreground, Ctrl+C to stop) +./scripts/services/start-all.sh + +# Or start in background +./scripts/services/start-all.sh --bg +``` + +### 3. Test + +```bash +pip install e2b +python scripts/test-e2b-lite.py +``` + +--- + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Python/JS SDK │ +└─────────────────────────────────────────────────────────────────┘ + │ │ + ▼ ▼ +┌───────────────────────────────┐ ┌─────────────────────────────┐ +│ API Server │ │ Client-Proxy │ +│ (Port 80) │ │ (Port 3002) │ +│ │ │ │ +│ - Sandbox management │ │ - Routes to orchestrator │ +│ - Authentication │ │ - Proxies envd traffic │ +│ - Template metadata │ │ │ +└───────────────────────────────┘ └─────────────────────────────┘ + │ │ + ▼ ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ Orchestrator │ +│ (Port 5008) │ +│ │ +│ - Firecracker VM management │ +│ - Template building │ +│ - Snapshot/restore │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ Firecracker microVMs │ +│ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ Sandbox 1 │ │ Sandbox 2 │ │ Sandbox N │ │ +│ │ (envd) │ │ (envd) │ │ (envd) │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### Services + +| Service | Port | Description | +|---------|------|-------------| +| **API** | 80 | REST API for sandbox management | +| **Client-Proxy** | 3002 | HTTP proxy for envd (in-VM daemon) | +| **Orchestrator** | 5008 | gRPC server for Firecracker orchestration | + +### Infrastructure (Docker) + +| Service | Port | Description | +|---------|------|-------------| +| PostgreSQL | 5432 | Primary database | +| Redis | 6379 | Caching and state | +| Loki | 3100 | Log aggregation | +| Grafana | 53000 | Dashboards | +| OTEL Collector | 4317 | Telemetry | + +--- + +## SDK Usage + +**Important:** The SDK requires both `api_url` and `sandbox_url` parameters. + +```python +from e2b import Sandbox + +# Create sandbox +sandbox = Sandbox.create( + template="7d5fxy9c5orhtppj0hdz", # Template ID from database + api_url="http://localhost:80", # API server (NOT port 3000!) + sandbox_url="http://localhost:3002", # Client-proxy for envd + api_key="e2b_53ae1fed82754c17ad8077fbc8bcdd90", + timeout=120, +) + +# Run command +result = sandbox.commands.run("echo 'Hello from E2B!'") +print(result.stdout) + +# File operations +sandbox.files.write("/tmp/test.txt", "Hello World") +content = sandbox.files.read("/tmp/test.txt") +files = sandbox.files.list("/tmp") + +# Cleanup +sandbox.kill() +``` + +### Environment Variables (Alternative) + +```bash +export E2B_API_KEY="e2b_53ae1fed82754c17ad8077fbc8bcdd90" +# Note: E2B_ENVD_API_URL does NOT work - must use sandbox_url parameter +``` + +--- + +## Credentials + +From `packages/local-dev/seed-local-database.go`: + +| Credential | Value | +|------------|-------| +| **API Key** | `e2b_53ae1fed82754c17ad8077fbc8bcdd90` | +| **Access Token** | `sk_e2b_89215020937a4c989cde33d7bc647715` | +| **Team ID** | `0b8a3ded-4489-4722-afd1-1d82e64ec2d5` | +| **User ID** | `89215020-937a-4c98-9cde-33d7bc647715` | + +--- + +## Setup Script Details + +`scripts/e2b-lite-setup.sh` performs these steps: + +1. **Install dependencies** (Docker, Go, Node.js, build tools) +2. **Load kernel modules** (NBD with nbds_max=128, TUN) +3. **Allocate HugePages** (2048 pages = 4GB) +4. **Download artifacts** (kernel vmlinux-6.1.158, Firecracker v1.12.1) +5. **Build binaries** (envd, API, orchestrator, client-proxy) +6. **Create envd symlink** (`bin/envd` → `bin/debug/envd`) +7. **Install npm dependencies** (in `packages/shared/scripts`) +8. **Start Docker infrastructure** (PostgreSQL, Redis, Loki, etc.) +9. **Run database migrations** +10. **Seed database** (creates user, team, API keys) +11. **Build base template** (if kernel 6.8+) +12. **Create service scripts** (`scripts/services/start-*.sh`) + +### Options + +```bash +./scripts/e2b-lite-setup.sh # Full setup +./scripts/e2b-lite-setup.sh --no-deps # Skip dependency installation +./scripts/e2b-lite-setup.sh --deps-only # Only install dependencies +./scripts/e2b-lite-setup.sh --no-template # Skip template building +``` + +--- + +## Service Scripts + +Created by setup script in `scripts/services/`: + +| Script | Description | +|--------|-------------| +| `start-all.sh` | Start all services (foreground) | +| `start-all.sh --bg` | Start all services (background) | +| `start-api.sh` | Start API server only | +| `start-orchestrator.sh` | Start orchestrator only | +| `start-client-proxy.sh` | Start client-proxy only | + +### Manual Service Management + +```bash +# Start in background +./scripts/services/start-all.sh --bg + +# Check status +ps aux | grep -E 'bin/(api|orchestrator|client-proxy)' + +# View logs +tail -f /tmp/e2b-api.log +tail -f /tmp/e2b-orchestrator.log +tail -f /tmp/e2b-client-proxy.log + +# Stop all +pkill -f 'bin/(api|orchestrator|client-proxy)' +``` + +--- + +## Template Management + +### Check Existing Template + +```bash +# Query database for template ID +docker exec local-dev-postgres-1 psql -U postgres -c "SELECT id FROM envs;" +``` + +### Build New Template (requires kernel 6.8+) + +```bash +export STORAGE_PROVIDER=Local +export LOCAL_TEMPLATE_STORAGE_BASE_PATH=./packages/orchestrator/tmp/local-template-storage +export HOST_ENVD_PATH=./packages/envd/bin/envd +export HOST_KERNELS_DIR=./packages/fc-kernels +export FIRECRACKER_VERSIONS_DIR=./packages/fc-versions/builds +export POSTGRES_CONNECTION_STRING="postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable" + +go run packages/orchestrator/cmd/build-template/main.go \ + -template base \ + -build $(uuidgen) \ + -storage ./packages/orchestrator/tmp \ + -kernel vmlinux-6.1.158 \ + -firecracker v1.12.1_717921c \ + -vcpu 2 \ + -memory 512 \ + -disk 1024 +``` + +--- + +## Troubleshooting + +### "The sandbox was not found" when running commands + +**Cause:** SDK can't reach envd via client-proxy. + +**Fix:** Ensure client-proxy is running and `sandbox_url` is set: +```python +sandbox = Sandbox.create( + ... + sandbox_url="http://localhost:3002", # Required! +) +``` + +### Connection refused on port 3000 + +**Cause:** API listens on port 80, not 3000. + +**Fix:** Use `api_url="http://localhost:80"` + +### Sandbox created but commands timeout + +**Cause:** Client-proxy not running. + +**Fix:** Start client-proxy: +```bash +./scripts/services/start-client-proxy.sh +``` + +### Template build fails with "fsopen failed" + +**Cause:** Kernel < 6.8 doesn't support new overlayfs syscalls. + +**Fix:** Upgrade to kernel 6.8+ (Ubuntu 24.04) or use prebuilt template. + +### "Cannot allocate memory" or OOM + +**Cause:** Insufficient HugePages or RAM. + +**Fix:** +```bash +# Allocate more HugePages +echo 2048 | sudo tee /proc/sys/vm/nr_hugepages + +# Check current allocation +grep HugePages /proc/meminfo +``` + +### NBD module issues + +**Fix:** +```bash +# Unload and reload with more devices +sudo rmmod nbd +sudo modprobe nbd nbds_max=128 + +# Verify +ls /dev/nbd* | wc -l # Should show 128+ +``` + +--- + +## Directory Structure + +``` +infra/ +├── scripts/ +│ ├── e2b-lite-setup.sh # Main setup script +│ ├── test-e2b-lite.py # Python test script +│ └── services/ +│ ├── start-all.sh # Start all services +│ ├── start-api.sh # Start API +│ ├── start-orchestrator.sh # Start orchestrator +│ └── start-client-proxy.sh # Start client-proxy +├── packages/ +│ ├── api/bin/api # API binary +│ ├── orchestrator/bin/orchestrator +│ ├── client-proxy/bin/client-proxy +│ ├── envd/bin/envd # In-VM daemon +│ ├── fc-kernels/ # Linux kernels +│ │ └── vmlinux-6.1.158/vmlinux.bin +│ ├── fc-versions/builds/ # Firecracker binaries +│ │ └── v1.12.1_717921c/firecracker +│ └── local-dev/ +│ ├── docker-compose.yaml # Infrastructure stack +│ └── seed-local-database.go # DB seeding +└── tmp/ # Runtime data +``` + +--- + +## API Reference + +### Create Sandbox + +```bash +curl -X POST http://localhost:80/sandboxes \ + -H "Content-Type: application/json" \ + -H "X-API-Key: e2b_53ae1fed82754c17ad8077fbc8bcdd90" \ + -d '{"templateID": "7d5fxy9c5orhtppj0hdz", "timeout": 60}' +``` + +### List Sandboxes + +```bash +curl http://localhost:80/sandboxes \ + -H "X-API-Key: e2b_53ae1fed82754c17ad8077fbc8bcdd90" +``` + +### Delete Sandbox + +```bash +curl -X DELETE http://localhost:80/sandboxes/{sandboxId} \ + -H "X-API-Key: e2b_53ae1fed82754c17ad8077fbc8bcdd90" +``` + +--- + +## Differences from Cloud E2B + +| Feature | Cloud E2B | E2B Lite | +|---------|-----------|----------| +| API URL | `https://api.e2b.dev` | `http://localhost:80` | +| Sandbox URL | Automatic | `http://localhost:3002` | +| Templates | Cloud storage | Local filesystem | +| Scaling | Multi-node | Single node | +| Auth | Full team/user | Seeded credentials | + +--- + +## E2B CLI + +E2B CLI (`npx @e2b/cli`) local setup support: +- CLI uses SDK's ConnectionConfig which supports environment variables +- Set these environment variables for local E2B Lite: + ```bash + export E2B_API_URL="http://localhost:80" + export E2B_SANDBOX_URL="http://localhost:3002" + export E2B_ACCESS_TOKEN="sk_e2b_89215020937a4c989cde33d7bc647715" + export E2B_API_KEY="e2b_53ae1fed82754c17ad8077fbc8bcdd90" + ``` +- Then use CLI normally: `npx @e2b/cli template list` diff --git a/packages/api/internal/orchestrator/lifecycle.go b/packages/api/internal/orchestrator/lifecycle.go index 3b83fadfd4..8d30c90303 100644 --- a/packages/api/internal/orchestrator/lifecycle.go +++ b/packages/api/internal/orchestrator/lifecycle.go @@ -7,6 +7,7 @@ import ( "go.uber.org/zap" "github.com/e2b-dev/infra/packages/api/internal/sandbox" + "github.com/e2b-dev/infra/packages/shared/pkg/env" "github.com/e2b-dev/infra/packages/shared/pkg/logger" e2bcatalog "github.com/e2b-dev/infra/packages/shared/pkg/sandbox-catalog" ) @@ -19,15 +20,21 @@ func (o *Orchestrator) addSandboxToRoutingTable(ctx context.Context, sandbox san return } - // Only add to routing table if the node is managed by Nomad - // For remote cluster nodes we are using gPRC metadata for routing registration instead - if !node.IsNomadManaged() { + // Only add to routing table if the node is managed by Nomad or in local mode + // For remote cluster nodes we are using gRPC metadata for routing registration instead + if !node.IsNomadManaged() && !env.IsLocal() { return } + // For local mode, cluster nodes have empty IPAddress, so use localhost + orchestratorIP := node.IPAddress + if orchestratorIP == "" && env.IsLocal() { + orchestratorIP = "localhost" + } + info := e2bcatalog.SandboxInfo{ OrchestratorID: node.Metadata().ServiceInstanceID, - OrchestratorIP: node.IPAddress, + OrchestratorIP: orchestratorIP, ExecutionID: sandbox.ExecutionID, StartedAt: sandbox.StartTime, diff --git a/scripts/e2b-lite-setup.sh b/scripts/e2b-lite-setup.sh new file mode 100755 index 0000000000..7fd552577c --- /dev/null +++ b/scripts/e2b-lite-setup.sh @@ -0,0 +1,1226 @@ +#!/bin/bash +# +# E2B Lite Setup Script +# +# This script sets up a complete E2B Lite environment for local development. +# It handles prerequisites, downloads artifacts, builds binaries, starts +# infrastructure, and optionally builds the base template. +# +# Usage: +# ./scripts/e2b-lite-setup.sh # Full setup (requires sudo) +# ./scripts/e2b-lite-setup.sh --deps-only # Only install system dependencies +# ./scripts/e2b-lite-setup.sh --no-deps # Skip dependency installation +# ./scripts/e2b-lite-setup.sh --no-template # Skip template building +# ./scripts/e2b-lite-setup.sh --prebuilt # Download pre-built binaries (faster) +# ./scripts/e2b-lite-setup.sh --prebuilt --version v1.0.0 # Specific version +# +# Requirements: +# - Linux with KVM support (bare metal recommended) +# - Root/sudo access +# - Internet connection +# + +set -euo pipefail + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Configuration +KERNEL_VERSION="${KERNEL_VERSION:-vmlinux-6.1.158}" +FC_VERSION="${FC_VERSION:-v1.12.1_717921c}" + +# Paths (relative to repo root) +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +FC_VERSIONS_DIR="$REPO_ROOT/packages/fc-versions/builds" +KERNELS_DIR="$REPO_ROOT/packages/fc-kernels" +ENVD_DIR="$REPO_ROOT/packages/envd" +API_DIR="$REPO_ROOT/packages/api" +ORCHESTRATOR_DIR="$REPO_ROOT/packages/orchestrator" +CLIENT_PROXY_DIR="$REPO_ROOT/packages/client-proxy" +SHARED_SCRIPTS_DIR="$REPO_ROOT/packages/shared/scripts" +LOCAL_DEV_DIR="$REPO_ROOT/packages/local-dev" +TMP_DIR="$REPO_ROOT/tmp" + +# Download URLs +KERNEL_URL="https://storage.googleapis.com/e2b-prod-public-builds/kernels/${KERNEL_VERSION}/vmlinux.bin" +FC_URL="https://github.com/e2b-dev/fc-versions/releases/download/${FC_VERSION}/firecracker" + +# Default credentials (from seed-local-database.go) +API_KEY="e2b_53ae1fed82754c17ad8077fbc8bcdd90" +ACCESS_TOKEN="sk_e2b_89215020937a4c989cde33d7bc647715" + +# Parse arguments +INSTALL_DEPS=true +BUILD_TEMPLATE=true +DEPS_ONLY=false +USE_PREBUILT=false +PREBUILT_VERSION="latest" + +while [[ $# -gt 0 ]]; do + case $1 in + --no-deps) + INSTALL_DEPS=false + shift + ;; + --deps-only) + DEPS_ONLY=true + shift + ;; + --no-template) + BUILD_TEMPLATE=false + shift + ;; + --prebuilt) + USE_PREBUILT=true + shift + ;; + --version) + PREBUILT_VERSION="$2" + shift 2 + ;; + --help|-h) + echo "Usage: $0 [options]" + echo "" + echo "Options:" + echo " --no-deps Skip system dependency installation" + echo " --deps-only Only install dependencies, then exit" + echo " --no-template Skip template building" + echo " --prebuilt Download pre-built binaries instead of compiling" + echo " --version VERSION Specify version for pre-built binaries (default: latest)" + echo " --help Show this help" + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +echo "========================================" +echo " E2B Lite Setup" +echo "========================================" +echo "" + +# ----------------------------------------------------------------------------- +# Fix git safe directory (needed when repo is rsync'd/copied) +# ----------------------------------------------------------------------------- +if ! git -C "$REPO_ROOT" status &>/dev/null; then + echo "Fixing git safe directory..." + git config --global --add safe.directory "$REPO_ROOT" 2>/dev/null || true +fi + +# ----------------------------------------------------------------------------- +# Check if running as root for certain operations +# ----------------------------------------------------------------------------- +check_sudo() { + if [[ $EUID -ne 0 ]]; then + if ! sudo -n true 2>/dev/null; then + echo -e "${YELLOW}Some operations require sudo. You may be prompted for password.${NC}" + fi + fi +} + +# ----------------------------------------------------------------------------- +# Install system dependencies +# ----------------------------------------------------------------------------- +install_dependencies() { + echo -e "${BLUE}Installing system dependencies...${NC}" + echo "" + + # Detect package manager + if command -v apt-get &> /dev/null; then + PKG_MANAGER="apt" + else + echo -e "${RED}Error: Only apt-based systems (Ubuntu/Debian) are currently supported${NC}" + exit 1 + fi + + # Update package list + echo " Updating package list..." + sudo apt-get update -qq + + # Install Docker if not present + if ! command -v docker &> /dev/null; then + echo " Installing Docker..." + sudo install -m 0755 -d /etc/apt/keyrings + sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc + sudo chmod a+r /etc/apt/keyrings/docker.asc + + echo "Types: deb +URIs: https://download.docker.com/linux/ubuntu +Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") +Components: stable +Signed-By: /etc/apt/keyrings/docker.asc" | sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null + + sudo apt-get update -qq + sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin + echo -e " ${GREEN}✓${NC} Docker installed" + else + echo -e " ${GREEN}✓${NC} Docker already installed" + fi + + # Install Go if not present + if ! command -v go &> /dev/null; then + echo " Installing Go via snap..." + sudo snap install --classic go + echo -e " ${GREEN}✓${NC} Go installed" + else + echo -e " ${GREEN}✓${NC} Go already installed ($(go version | grep -oP '\d+\.\d+' | head -1))" + fi + + # Install Node.js if not present (needed for template building) + if ! command -v node &> /dev/null; then + echo " Installing Node.js..." + # Install via NodeSource for recent version + curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - + sudo apt-get install -y nodejs + echo -e " ${GREEN}✓${NC} Node.js installed" + else + echo -e " ${GREEN}✓${NC} Node.js already installed ($(node --version))" + fi + + # Install build tools and other dependencies + echo " Installing build tools..." + sudo apt-get install -y build-essential make ca-certificates curl git net-tools + echo -e " ${GREEN}✓${NC} Build tools installed" + + echo "" +} + +# ----------------------------------------------------------------------------- +# Check prerequisites +# ----------------------------------------------------------------------------- +check_prerequisites() { + echo "Checking prerequisites..." + + # Check OS + if [[ "$OSTYPE" != "linux-gnu"* ]]; then + echo -e "${RED}Error: E2B Lite requires Linux. Detected: $OSTYPE${NC}" + exit 1 + fi + echo -e " ${GREEN}✓${NC} Linux detected" + + # Check kernel version + KERNEL_MAJOR=$(uname -r | cut -d. -f1) + KERNEL_MINOR=$(uname -r | cut -d. -f2) + if [ "$KERNEL_MAJOR" -lt 5 ] || ([ "$KERNEL_MAJOR" -eq 5 ] && [ "$KERNEL_MINOR" -lt 10 ]); then + echo -e "${RED}Error: Kernel $(uname -r) is too old. Minimum required: 5.10${NC}" + exit 1 + fi + echo -e " ${GREEN}✓${NC} Kernel $(uname -r)" + + # Check for kernel 6.8+ (needed for building templates) + if [ "$KERNEL_MAJOR" -lt 6 ] || ([ "$KERNEL_MAJOR" -eq 6 ] && [ "$KERNEL_MINOR" -lt 8 ]); then + echo -e " ${YELLOW}!${NC} Kernel < 6.8: You can run sandboxes but cannot build custom templates" + BUILD_TEMPLATE=false + else + echo -e " ${GREEN}✓${NC} Kernel 6.8+: Full support (running + building templates)" + fi + + # Check KVM + if [[ ! -e /dev/kvm ]]; then + echo -e "${RED}Error: /dev/kvm not found. KVM is required.${NC}" + echo " Enable KVM: sudo modprobe kvm_intel (or kvm_amd)" + exit 1 + fi + if [[ ! -r /dev/kvm ]] || [[ ! -w /dev/kvm ]]; then + echo -e "${YELLOW}Warning: No read/write access to /dev/kvm${NC}" + echo " Fix: sudo usermod -aG kvm \$USER && newgrp kvm" + echo " Or run with sudo" + fi + echo -e " ${GREEN}✓${NC} KVM available" + + # Check Docker + if ! command -v docker &> /dev/null; then + echo -e "${RED}Error: Docker not found. Run with --deps-only first or install manually.${NC}" + exit 1 + fi + if ! docker info &> /dev/null; then + echo -e "${RED}Error: Docker daemon not running or no permission${NC}" + echo " Start Docker: sudo systemctl start docker" + echo " Or add to group: sudo usermod -aG docker \$USER && newgrp docker" + exit 1 + fi + echo -e " ${GREEN}✓${NC} Docker available" + + # Check Go + if ! command -v go &> /dev/null; then + echo -e "${RED}Error: Go not found. Run with --deps-only first or install manually.${NC}" + exit 1 + fi + GO_VERSION=$(go version | grep -oP '\d+\.\d+' | head -1) + echo -e " ${GREEN}✓${NC} Go $GO_VERSION" + + # Check Node.js (optional, for template building) + if command -v node &> /dev/null; then + echo -e " ${GREEN}✓${NC} Node.js $(node --version)" + else + echo -e " ${YELLOW}!${NC} Node.js not found (needed for template building)" + fi + + echo "" +} + +# ----------------------------------------------------------------------------- +# Setup kernel modules +# ----------------------------------------------------------------------------- +setup_kernel_modules() { + echo "Setting up kernel modules..." + + # NBD module with sufficient devices + if ! lsmod | grep -q "^nbd "; then + echo " Loading NBD module..." + if ! sudo modprobe nbd nbds_max=128 2>/dev/null; then + echo -e "${YELLOW}Warning: Failed to load NBD module. You may need to install it.${NC}" + else + echo -e " ${GREEN}✓${NC} NBD module loaded (nbds_max=128)" + fi + else + # Check if we have enough NBD devices + NBD_COUNT=$(ls -1 /dev/nbd* 2>/dev/null | wc -l) + if [ "$NBD_COUNT" -lt 64 ]; then + echo " Reloading NBD module with more devices..." + sudo rmmod nbd 2>/dev/null || true + sudo modprobe nbd nbds_max=128 + fi + echo -e " ${GREEN}✓${NC} NBD module loaded" + fi + + # TUN module + if ! lsmod | grep -q "^tun "; then + echo " Loading TUN module..." + sudo modprobe tun 2>/dev/null || echo -e "${YELLOW}Warning: Failed to load TUN module${NC}" + fi + echo -e " ${GREEN}✓${NC} TUN module" + + echo "" +} + +# ----------------------------------------------------------------------------- +# Setup HugePages +# ----------------------------------------------------------------------------- +setup_hugepages() { + echo "Setting up HugePages..." + + HUGEPAGES_TOTAL=$(cat /proc/sys/vm/nr_hugepages 2>/dev/null || echo 0) + HUGEPAGES_NEEDED=2048 # 2048 * 2MB = 4GB reserved for HugePages + + if [ "$HUGEPAGES_TOTAL" -lt "$HUGEPAGES_NEEDED" ]; then + echo " Allocating HugePages ($HUGEPAGES_NEEDED pages = $((HUGEPAGES_NEEDED * 2))MB)..." + if echo "$HUGEPAGES_NEEDED" | sudo tee /proc/sys/vm/nr_hugepages > /dev/null 2>&1; then + # Make it persistent across reboots + if ! grep -q "vm.nr_hugepages" /etc/sysctl.conf 2>/dev/null; then + echo "vm.nr_hugepages=$HUGEPAGES_NEEDED" | sudo tee -a /etc/sysctl.conf > /dev/null + echo -e " ${GREEN}✓${NC} HugePages configured (persistent)" + else + sudo sed -i "s/vm.nr_hugepages=.*/vm.nr_hugepages=$HUGEPAGES_NEEDED/" /etc/sysctl.conf + echo -e " ${GREEN}✓${NC} HugePages allocated" + fi + else + echo -e "${YELLOW}Warning: Failed to allocate HugePages. Template building may fail.${NC}" + echo " Manual fix: echo $HUGEPAGES_NEEDED | sudo tee /proc/sys/vm/nr_hugepages" + fi + else + echo -e " ${GREEN}✓${NC} HugePages already configured ($HUGEPAGES_TOTAL pages)" + fi + + echo "" +} + +# ----------------------------------------------------------------------------- +# Create directory structure +# ----------------------------------------------------------------------------- +create_directories() { + echo "Creating directory structure..." + + mkdir -p "$FC_VERSIONS_DIR/$FC_VERSION" + mkdir -p "$KERNELS_DIR/$KERNEL_VERSION" + mkdir -p "$TMP_DIR/templates" + mkdir -p "$TMP_DIR/orchestrator" + mkdir -p "$TMP_DIR/sandbox" + mkdir -p "$TMP_DIR/sandbox-cache" + mkdir -p "$TMP_DIR/snapshot-cache" + mkdir -p "$ORCHESTRATOR_DIR/tmp/local-template-storage" + mkdir -p "$ORCHESTRATOR_DIR/tmp/sandbox-cache-dir" + mkdir -p "$ORCHESTRATOR_DIR/tmp/snapshot-cache" + + echo -e " ${GREEN}✓${NC} Directories created" + echo "" +} + +# ----------------------------------------------------------------------------- +# Download artifacts +# ----------------------------------------------------------------------------- +download_artifacts() { + echo "Downloading artifacts..." + + # Download kernel + KERNEL_PATH="$KERNELS_DIR/$KERNEL_VERSION/vmlinux.bin" + if [[ -f "$KERNEL_PATH" ]]; then + echo -e " ${GREEN}✓${NC} Kernel $KERNEL_VERSION already exists" + else + echo " Downloading kernel $KERNEL_VERSION..." + if curl -fsSL "$KERNEL_URL" -o "$KERNEL_PATH"; then + chmod 644 "$KERNEL_PATH" + echo -e " ${GREEN}✓${NC} Kernel downloaded" + else + echo -e "${RED}Error: Failed to download kernel${NC}" + echo " URL: $KERNEL_URL" + exit 1 + fi + fi + + # Download Firecracker + FC_PATH="$FC_VERSIONS_DIR/$FC_VERSION/firecracker" + if [[ -f "$FC_PATH" ]]; then + echo -e " ${GREEN}✓${NC} Firecracker $FC_VERSION already exists" + else + echo " Downloading Firecracker $FC_VERSION..." + if curl -fsSL "$FC_URL" -o "$FC_PATH"; then + chmod +x "$FC_PATH" + echo -e " ${GREEN}✓${NC} Firecracker downloaded" + else + echo -e "${RED}Error: Failed to download Firecracker${NC}" + echo " URL: $FC_URL" + exit 1 + fi + fi + + echo "" +} + +# ----------------------------------------------------------------------------- +# Download pre-built binaries +# ----------------------------------------------------------------------------- +download_prebuilt_binaries() { + echo "Downloading pre-built binaries..." + + GITHUB_REPO="e2b-dev/infra" + + # Determine version to download + if [[ "$PREBUILT_VERSION" == "latest" ]]; then + echo " Fetching latest release..." + RELEASE_URL="https://api.github.com/repos/$GITHUB_REPO/releases/latest" + RELEASE_INFO=$(curl -fsSL "$RELEASE_URL" 2>/dev/null) + if [[ -z "$RELEASE_INFO" ]]; then + echo -e "${RED}Error: Failed to fetch latest release info${NC}" + echo " Falling back to building from source..." + build_binaries + return + fi + VERSION=$(echo "$RELEASE_INFO" | grep -oP '"tag_name":\s*"\K[^"]+' | head -1) + if [[ -z "$VERSION" ]]; then + echo -e "${YELLOW}Warning: No releases found, falling back to building from source${NC}" + build_binaries + return + fi + else + VERSION="$PREBUILT_VERSION" + fi + + echo " Version: $VERSION" + + # Download URLs + BASE_URL="https://github.com/$GITHUB_REPO/releases/download/$VERSION" + + # Create bin directories + mkdir -p "$API_DIR/bin" + mkdir -p "$ORCHESTRATOR_DIR/bin" + mkdir -p "$CLIENT_PROXY_DIR/bin" + mkdir -p "$ENVD_DIR/bin" + + # Download API + API_PATH="$API_DIR/bin/api" + if [[ -f "$API_PATH" ]]; then + echo -e " ${GREEN}✓${NC} API already exists" + else + echo " Downloading API..." + if curl -fsSL "$BASE_URL/api-linux-amd64" -o "$API_PATH"; then + chmod +x "$API_PATH" + echo -e " ${GREEN}✓${NC} API downloaded" + else + echo -e "${YELLOW}Warning: Failed to download API, will build from source${NC}" + BUILD_API=true + fi + fi + + # Download Orchestrator + ORCH_PATH="$ORCHESTRATOR_DIR/bin/orchestrator" + if [[ -f "$ORCH_PATH" ]]; then + echo -e " ${GREEN}✓${NC} Orchestrator already exists" + else + echo " Downloading Orchestrator..." + if curl -fsSL "$BASE_URL/orchestrator-linux-amd64" -o "$ORCH_PATH"; then + chmod +x "$ORCH_PATH" + echo -e " ${GREEN}✓${NC} Orchestrator downloaded" + else + echo -e "${YELLOW}Warning: Failed to download Orchestrator, will build from source${NC}" + BUILD_ORCH=true + fi + fi + + # Download Client-Proxy + PROXY_PATH="$CLIENT_PROXY_DIR/bin/client-proxy" + if [[ -f "$PROXY_PATH" ]]; then + echo -e " ${GREEN}✓${NC} Client-Proxy already exists" + else + echo " Downloading Client-Proxy..." + if curl -fsSL "$BASE_URL/client-proxy-linux-amd64" -o "$PROXY_PATH"; then + chmod +x "$PROXY_PATH" + echo -e " ${GREEN}✓${NC} Client-Proxy downloaded" + else + echo -e "${YELLOW}Warning: Failed to download Client-Proxy, will build from source${NC}" + BUILD_PROXY=true + fi + fi + + # Download Envd + ENVD_PATH="$ENVD_DIR/bin/envd" + if [[ -f "$ENVD_PATH" ]]; then + echo -e " ${GREEN}✓${NC} envd already exists" + else + echo " Downloading envd..." + if curl -fsSL "$BASE_URL/envd-linux-amd64" -o "$ENVD_PATH"; then + chmod +x "$ENVD_PATH" + echo -e " ${GREEN}✓${NC} envd downloaded" + else + echo -e "${YELLOW}Warning: Failed to download envd, will build from source${NC}" + BUILD_ENVD=true + fi + fi + + # Build any that failed to download + if [[ "$BUILD_API" == "true" ]] || [[ "$BUILD_ORCH" == "true" ]] || \ + [[ "$BUILD_PROXY" == "true" ]] || [[ "$BUILD_ENVD" == "true" ]]; then + echo "" + echo "Building missing binaries from source..." + + if [[ "$BUILD_ENVD" == "true" ]]; then + echo " Building envd..." + make -C "$ENVD_DIR" build > /dev/null 2>&1 || echo -e "${RED}Failed to build envd${NC}" + fi + + if [[ "$BUILD_API" == "true" ]]; then + echo " Building API..." + make -C "$API_DIR" build > /dev/null 2>&1 || echo -e "${RED}Failed to build API${NC}" + fi + + if [[ "$BUILD_ORCH" == "true" ]]; then + echo " Building Orchestrator..." + make -C "$ORCHESTRATOR_DIR" build-debug > /dev/null 2>&1 || echo -e "${RED}Failed to build Orchestrator${NC}" + fi + + if [[ "$BUILD_PROXY" == "true" ]]; then + echo " Building Client-Proxy..." + make -C "$CLIENT_PROXY_DIR" build > /dev/null 2>&1 || echo -e "${RED}Failed to build Client-Proxy${NC}" + fi + fi + + echo "" +} + +# ----------------------------------------------------------------------------- +# Build all binaries +# ----------------------------------------------------------------------------- +build_binaries() { + echo "Building binaries..." + + # Build envd + ENVD_DEBUG_PATH="$ENVD_DIR/bin/debug/envd" + ENVD_PATH="$ENVD_DIR/bin/envd" + if [[ -f "$ENVD_DEBUG_PATH" ]]; then + echo -e " ${GREEN}✓${NC} envd already built" + else + echo " Building envd..." + if make -C "$ENVD_DIR" build-debug > /dev/null 2>&1; then + echo -e " ${GREEN}✓${NC} envd built" + else + echo -e "${RED}Error: Failed to build envd${NC}" + exit 1 + fi + fi + + # Create symlink for envd + if [[ ! -L "$ENVD_PATH" ]] && [[ ! -f "$ENVD_PATH" ]]; then + ln -s "$ENVD_DEBUG_PATH" "$ENVD_PATH" + echo -e " ${GREEN}✓${NC} envd symlink created" + fi + + # Build API + API_PATH="$API_DIR/bin/api" + if [[ -f "$API_PATH" ]]; then + echo -e " ${GREEN}✓${NC} API already built" + else + echo " Building API..." + if make -C "$API_DIR" build > /dev/null 2>&1; then + echo -e " ${GREEN}✓${NC} API built" + else + echo -e "${RED}Error: Failed to build API${NC}" + exit 1 + fi + fi + + # Build Orchestrator + ORCH_PATH="$ORCHESTRATOR_DIR/bin/orchestrator" + if [[ -f "$ORCH_PATH" ]]; then + echo -e " ${GREEN}✓${NC} Orchestrator already built" + else + echo " Building Orchestrator..." + if make -C "$ORCHESTRATOR_DIR" build-debug > /dev/null 2>&1; then + echo -e " ${GREEN}✓${NC} Orchestrator built" + else + echo -e "${RED}Error: Failed to build Orchestrator${NC}" + exit 1 + fi + fi + + # Build Client-Proxy + PROXY_PATH="$CLIENT_PROXY_DIR/bin/client-proxy" + if [[ -f "$PROXY_PATH" ]]; then + echo -e " ${GREEN}✓${NC} Client-Proxy already built" + else + echo " Building Client-Proxy..." + if make -C "$CLIENT_PROXY_DIR" build > /dev/null 2>&1; then + echo -e " ${GREEN}✓${NC} Client-Proxy built" + else + echo -e "${RED}Error: Failed to build Client-Proxy${NC}" + exit 1 + fi + fi + + echo "" +} + +# ----------------------------------------------------------------------------- +# Setup npm dependencies for template building +# ----------------------------------------------------------------------------- +setup_npm_dependencies() { + if ! command -v npm &> /dev/null; then + echo -e "${YELLOW}Skipping npm dependencies (npm not found)${NC}" + return + fi + + echo "Setting up npm dependencies..." + + if [[ -d "$SHARED_SCRIPTS_DIR" ]]; then + if [[ ! -d "$SHARED_SCRIPTS_DIR/node_modules" ]]; then + echo " Installing npm packages in shared/scripts..." + (cd "$SHARED_SCRIPTS_DIR" && npm install --silent) || { + echo -e "${YELLOW}Warning: Failed to install npm packages${NC}" + } + fi + echo -e " ${GREEN}✓${NC} npm dependencies ready" + fi + + echo "" +} + +# ----------------------------------------------------------------------------- +# Start Docker infrastructure +# ----------------------------------------------------------------------------- +start_infrastructure() { + echo "Starting Docker infrastructure..." + + # Use full docker-compose with all services + COMPOSE_FILE="$LOCAL_DEV_DIR/docker-compose.yaml" + + if [[ ! -f "$COMPOSE_FILE" ]]; then + echo -e "${RED}Error: docker-compose.yaml not found at $COMPOSE_FILE${NC}" + exit 1 + fi + + # Check if containers are already running + if docker ps --format '{{.Names}}' | grep -q "local-dev-postgres"; then + echo -e " ${GREEN}✓${NC} Infrastructure already running" + else + echo " Starting containers..." + docker compose -f "$COMPOSE_FILE" up -d + + # Wait for PostgreSQL to be ready + echo " Waiting for PostgreSQL..." + for i in {1..30}; do + if docker exec local-dev-postgres-1 pg_isready -U postgres > /dev/null 2>&1; then + break + fi + sleep 1 + done + echo -e " ${GREEN}✓${NC} Infrastructure started" + fi + + echo "" +} + +# ----------------------------------------------------------------------------- +# Run database migrations +# ----------------------------------------------------------------------------- +run_migrations() { + echo "Running database migrations..." + + export POSTGRES_CONNECTION_STRING="postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable" + + if make -C "$REPO_ROOT/packages/db" migrate-local > /dev/null 2>&1; then + echo -e " ${GREEN}✓${NC} Migrations applied" + else + echo -e "${YELLOW}Warning: Migration may have failed or already applied${NC}" + fi + + echo "" +} + +# ----------------------------------------------------------------------------- +# Seed database +# ----------------------------------------------------------------------------- +seed_database() { + echo "Seeding database..." + + export POSTGRES_CONNECTION_STRING="postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable" + + # Check if already seeded by looking for the team + TEAM_EXISTS=$(docker exec local-dev-postgres-1 psql -U postgres -tAc "SELECT COUNT(*) FROM teams WHERE id='0b8a3ded-4489-4722-afd1-1d82e64ec2d5';" 2>/dev/null || echo "0") + + if [[ "$TEAM_EXISTS" == "1" ]]; then + echo -e " ${GREEN}✓${NC} Database already seeded" + else + echo " Running seed script..." + (cd "$LOCAL_DEV_DIR" && go run seed-local-database.go) > /dev/null 2>&1 || { + echo -e "${YELLOW}Warning: Seeding may have failed${NC}" + } + echo -e " ${GREEN}✓${NC} Database seeded" + fi + + echo "" +} + +# ----------------------------------------------------------------------------- +# Build base template +# ----------------------------------------------------------------------------- + +# Generate a 20-character lowercase alphanumeric template ID (like e2b production) +generate_template_id() { + # Use head -c to read finite bytes first (avoids SIGPIPE with pipefail) + head -c 500 /dev/urandom | tr -dc 'a-z0-9' | head -c 20 + echo # Add newline +} + +build_base_template() { + if [[ "$BUILD_TEMPLATE" != "true" ]]; then + echo -e "${YELLOW}Skipping template build (--no-template or kernel < 6.8)${NC}" + echo "" + return + fi + + echo "Building base template..." + + # Check if template already exists in database + EXISTING_TEMPLATE=$(docker exec local-dev-postgres-1 psql -U postgres -tAc "SELECT id FROM envs LIMIT 1;" 2>/dev/null | tr -d ' ' || echo "") + if [[ -n "$EXISTING_TEMPLATE" ]]; then + echo -e " ${GREEN}✓${NC} Template already exists in database: $EXISTING_TEMPLATE" + echo "" + return + fi + + # Check if template files exist but aren't registered + TEMPLATE_STORAGE="$ORCHESTRATOR_DIR/tmp/local-template-storage" + EXISTING_BUILD=$(ls -1 "$TEMPLATE_STORAGE" 2>/dev/null | head -1) + + if [[ -n "$EXISTING_BUILD" ]]; then + echo " Found existing template files, registering in database..." + TEMPLATE_ID=$(generate_template_id) + BUILD_ID="$EXISTING_BUILD" + register_template "$TEMPLATE_ID" "$BUILD_ID" + echo -e " ${GREEN}✓${NC} Template registered: $TEMPLATE_ID" + echo "" + return + fi + + # Set environment for template building + export STORAGE_PROVIDER=Local + export LOCAL_TEMPLATE_STORAGE_BASE_PATH="$TEMPLATE_STORAGE" + export HOST_ENVD_PATH="$ENVD_DIR/bin/envd" + export HOST_KERNELS_DIR="$KERNELS_DIR" + export FIRECRACKER_VERSIONS_DIR="$FC_VERSIONS_DIR" + export POSTGRES_CONNECTION_STRING="postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable" + + # Generate IDs + TEMPLATE_ID=$(generate_template_id) + BUILD_ID=$(cat /proc/sys/kernel/random/uuid) + + echo " Template ID: $TEMPLATE_ID" + echo " Build ID: $BUILD_ID" + echo " Building template (this may take a few minutes)..." + + if go run "$ORCHESTRATOR_DIR/cmd/build-template/main.go" \ + -template "$TEMPLATE_ID" \ + -build "$BUILD_ID" \ + -storage "$ORCHESTRATOR_DIR/tmp" \ + -kernel "$KERNEL_VERSION" \ + -firecracker "$FC_VERSION" \ + -vcpu 2 \ + -memory 512 \ + -disk 1024 > /tmp/template-build.log 2>&1; then + echo -e " ${GREEN}✓${NC} Template built successfully" + + # Register template in database + register_template "$TEMPLATE_ID" "$BUILD_ID" + echo -e " ${GREEN}✓${NC} Template registered in database" + else + echo -e "${YELLOW}Warning: Template build failed. Check /tmp/template-build.log${NC}" + echo " You can build it manually later with:" + echo " make -C packages/shared/scripts local-build-base-template" + fi + + echo "" +} + +# Register template in the database +register_template() { + local TEMPLATE_ID="$1" + local BUILD_ID="$2" + local TEAM_ID="0b8a3ded-4489-4722-afd1-1d82e64ec2d5" + + # Insert into envs table + docker exec local-dev-postgres-1 psql -U postgres -c " + INSERT INTO public.envs (id, team_id, public, updated_at) + VALUES ('$TEMPLATE_ID', '$TEAM_ID', true, NOW()) + ON CONFLICT (id) DO NOTHING; + " > /dev/null 2>&1 + + # Insert into env_builds table (status must be 'uploaded' for API to find it) + # Note: total_disk_size_mb and envd_version are required by the API + docker exec local-dev-postgres-1 psql -U postgres -c " + INSERT INTO public.env_builds (id, env_id, status, vcpu, ram_mb, free_disk_size_mb, total_disk_size_mb, kernel_version, firecracker_version, envd_version, cluster_node_id, created_at, updated_at, finished_at) + VALUES ('$BUILD_ID', '$TEMPLATE_ID', 'uploaded', 2, 512, 1024, 512, '$KERNEL_VERSION', '$FC_VERSION', '0.2.0', 'local', NOW(), NOW(), NOW()) + ON CONFLICT (id) DO NOTHING; + " > /dev/null 2>&1 + + # Insert into env_build_assignments table (links build to template with 'default' tag) + docker exec local-dev-postgres-1 psql -U postgres -c " + INSERT INTO public.env_build_assignments (env_id, build_id, tag, source, created_at) + VALUES ('$TEMPLATE_ID', '$BUILD_ID', 'default', 'setup', NOW()) + ON CONFLICT DO NOTHING; + " > /dev/null 2>&1 +} + +# ----------------------------------------------------------------------------- +# Create service start scripts +# ----------------------------------------------------------------------------- +create_start_scripts() { + echo "Creating service start scripts..." + + # Create scripts directory + mkdir -p "$REPO_ROOT/scripts/services" + + # API start script + cat > "$REPO_ROOT/scripts/services/start-api.sh" << 'SCRIPT' +#!/bin/bash +cd "$(dirname "$0")/../.." || exit 1 +REPO_ROOT="$(pwd)" + +cd packages/api || exit 1 + +NODE_ID=$(hostname) \ +LOKI_URL="localhost:3100" \ +DNS_PORT=9953 \ +ENVIRONMENT=local \ +LOGS_COLLECTOR_ADDRESS=http://localhost:30006 \ +OTEL_COLLECTOR_GRPC_ENDPOINT=localhost:4317 \ +POSTGRES_CONNECTION_STRING="postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable" \ +REDIS_URL=localhost:6379 \ +SANDBOX_ACCESS_TOKEN_HASH_SEED="--sandbox-access-token-hash-seed--" \ +LOCAL_CLUSTER_ENDPOINT=localhost:3001 \ +LOCAL_CLUSTER_TOKEN="--edge-secret--" \ +./bin/api +SCRIPT + chmod +x "$REPO_ROOT/scripts/services/start-api.sh" + + # Orchestrator start script + cat > "$REPO_ROOT/scripts/services/start-orchestrator.sh" << 'SCRIPT' +#!/bin/bash +cd "$(dirname "$0")/../.." || exit 1 +REPO_ROOT="$(pwd)" + +cd packages/orchestrator || exit 1 + +# Get absolute path to orchestrator directory +ORCH_DIR="$(pwd)" + +NODE_ID=$(hostname) \ +LOKI_URL="localhost:3100" \ +ARTIFACTS_REGISTRY_PROVIDER=Local \ +ENVIRONMENT=local \ +FIRECRACKER_VERSIONS_DIR=../fc-versions/builds \ +HOST_ENVD_PATH=../envd/bin/envd \ +HOST_KERNELS_DIR=../fc-kernels \ +LOCAL_TEMPLATE_STORAGE_BASE_PATH=./tmp/local-template-storage \ +LOGS_COLLECTOR_ADDRESS=http://localhost:30006 \ +ORCHESTRATOR_BASE_PATH=./tmp/ \ +ORCHESTRATOR_LOCK_PATH=./tmp/.lock \ +ORCHESTRATOR_SERVICES=orchestrator,template-manager \ +OTEL_COLLECTOR_GRPC_ENDPOINT=localhost:4317 \ +REDIS_URL=localhost:6379 \ +SANDBOX_CACHE_DIR=./tmp/sandbox-cache-dir \ +SANDBOX_DIR="${ORCH_DIR}/tmp/sandbox" \ +SNAPSHOT_CACHE_DIR=./tmp/snapshot-cache \ +STORAGE_PROVIDER=Local \ +./bin/orchestrator +SCRIPT + chmod +x "$REPO_ROOT/scripts/services/start-orchestrator.sh" + + # Client-Proxy start script + cat > "$REPO_ROOT/scripts/services/start-client-proxy.sh" << 'SCRIPT' +#!/bin/bash +cd "$(dirname "$0")/../.." || exit 1 +REPO_ROOT="$(pwd)" + +cd packages/client-proxy || exit 1 + +NODE_ID=$(hostname) \ +EDGE_SECRET="--edge-secret--" \ +EDGE_URL="http://localhost:80" \ +ENVIRONMENT=local \ +LOGS_COLLECTOR_ADDRESS="http://localhost:30006" \ +LOKI_URL="http://localhost:3100" \ +NODE_IP="127.0.0.1" \ +REDIS_URL="localhost:6379" \ +SD_EDGE_PROVIDER=STATIC \ +SD_EDGE_STATIC="127.0.0.1" \ +SD_ORCHESTRATOR_PROVIDER=STATIC \ +SD_ORCHESTRATOR_STATIC="127.0.0.1" \ +SKIP_ORCHESTRATOR_READINESS_CHECK=true \ +OTEL_COLLECTOR_GRPC_ENDPOINT=localhost:4317 \ +./bin/client-proxy +SCRIPT + chmod +x "$REPO_ROOT/scripts/services/start-client-proxy.sh" + + # All-in-one start script + cat > "$REPO_ROOT/scripts/services/start-all.sh" << 'SCRIPT' +#!/bin/bash +# +# Start all E2B Lite services +# +# Usage: +# ./scripts/services/start-all.sh # Start in foreground (Ctrl+C to stop) +# ./scripts/services/start-all.sh --bg # Start in background +# + +cd "$(dirname "$0")/../.." || exit 1 +REPO_ROOT="$(pwd)" + +BACKGROUND=false +if [[ "$1" == "--bg" ]]; then + BACKGROUND=true +fi + +echo "Starting E2B Lite services..." + +if [[ "$BACKGROUND" == "true" ]]; then + # Background mode + nohup "$REPO_ROOT/scripts/services/start-api.sh" > /tmp/e2b-api.log 2>&1 & + echo " API started (PID: $!, log: /tmp/e2b-api.log)" + + nohup "$REPO_ROOT/scripts/services/start-orchestrator.sh" > /tmp/e2b-orchestrator.log 2>&1 & + echo " Orchestrator started (PID: $!, log: /tmp/e2b-orchestrator.log)" + + sleep 2 # Wait for orchestrator to initialize + + nohup "$REPO_ROOT/scripts/services/start-client-proxy.sh" > /tmp/e2b-client-proxy.log 2>&1 & + echo " Client-Proxy started (PID: $!, log: /tmp/e2b-client-proxy.log)" + + echo "" + echo "All services started in background." + echo "Check status: ps aux | grep -E 'api|orchestrator|client-proxy'" + echo "Stop all: pkill -f 'bin/(api|orchestrator|client-proxy)'" +else + # Foreground mode with trap + cleanup() { + echo "" + echo "Stopping services..." + pkill -f 'bin/api' 2>/dev/null + pkill -f 'bin/orchestrator' 2>/dev/null + pkill -f 'bin/client-proxy' 2>/dev/null + exit 0 + } + trap cleanup SIGINT SIGTERM + + "$REPO_ROOT/scripts/services/start-api.sh" & + API_PID=$! + echo " API started (PID: $API_PID)" + + "$REPO_ROOT/scripts/services/start-orchestrator.sh" & + ORCH_PID=$! + echo " Orchestrator started (PID: $ORCH_PID)" + + sleep 2 + + "$REPO_ROOT/scripts/services/start-client-proxy.sh" & + PROXY_PID=$! + echo " Client-Proxy started (PID: $PROXY_PID)" + + echo "" + echo "All services running. Press Ctrl+C to stop." + wait +fi +SCRIPT + chmod +x "$REPO_ROOT/scripts/services/start-all.sh" + + echo -e " ${GREEN}✓${NC} Service scripts created" + echo "" +} + +# ----------------------------------------------------------------------------- +# Create test script +# ----------------------------------------------------------------------------- +create_test_script() { + echo "Creating test script..." + + cat > "$REPO_ROOT/scripts/test-e2b-lite.py" << 'SCRIPT' +#!/usr/bin/env python3 +""" +E2B Lite Test Script + +Tests basic sandbox functionality: creation, commands, filesystem. + +Usage: + pip install e2b + python scripts/test-e2b-lite.py +""" + +import os +import subprocess +import sys + +try: + from e2b import Sandbox +except ImportError: + print("Error: e2b package not installed") + print("Install with: pip install e2b") + sys.exit(1) + + +def get_template_id_from_db(): + """Query PostgreSQL for the template ID.""" + try: + result = subprocess.run( + ["docker", "exec", "local-dev-postgres-1", "psql", "-U", "postgres", "-tAc", "SELECT id FROM envs LIMIT 1;"], + capture_output=True, + text=True, + timeout=10, + ) + if result.returncode == 0 and result.stdout.strip(): + return result.stdout.strip() + except Exception: + pass + return None + + +# Configuration +API_KEY = os.environ.get("E2B_API_KEY", "e2b_53ae1fed82754c17ad8077fbc8bcdd90") +API_URL = os.environ.get("E2B_API_URL", "http://localhost:80") +SANDBOX_URL = os.environ.get("E2B_SANDBOX_URL", "http://localhost:3002") + +# Get template ID: from env var, from database, or fail +TEMPLATE_ID = os.environ.get("E2B_TEMPLATE_ID") +if not TEMPLATE_ID: + TEMPLATE_ID = get_template_id_from_db() + +if not TEMPLATE_ID: + print("=" * 50) + print(" E2B Lite Test - ERROR") + print("=" * 50) + print() + print("No template found!") + print() + print("Either:") + print(" 1. Set E2B_TEMPLATE_ID environment variable") + print(" 2. Build a template: ./scripts/e2b-lite-setup.sh") + print() + print("To check database:") + print(" docker exec local-dev-postgres-1 psql -U postgres -c 'SELECT id FROM envs;'") + sys.exit(1) + +print("=" * 50) +print(" E2B Lite Test") +print("=" * 50) +print() +print(f"API URL: {API_URL}") +print(f"Sandbox URL: {SANDBOX_URL}") +print(f"Template: {TEMPLATE_ID}") +print() + +try: + print("1. Creating sandbox...") + sandbox = Sandbox.create( + template=TEMPLATE_ID, + api_url=API_URL, + sandbox_url=SANDBOX_URL, + timeout=120, + api_key=API_KEY, + ) + print(f" ✓ Sandbox ID: {sandbox.sandbox_id}") + print() + + print("2. Running command...") + result = sandbox.commands.run("echo 'Hello from E2B Lite!' && uname -a", user="root") + print(f" ✓ Output: {result.stdout.strip()}") + print() + + print("3. Writing file via command...") + sandbox.commands.run("echo 'Hello World from E2B!' > /tmp/test.txt", user="root") + print(" ✓ Written /tmp/test.txt") + print() + + print("4. Reading file via command...") + result = sandbox.commands.run("cat /tmp/test.txt", user="root") + print(f" ✓ Content: {result.stdout.strip()}") + print() + + print("5. Listing directory via command...") + result = sandbox.commands.run("ls /tmp | head -5", user="root") + print(f" ✓ Files: {result.stdout.strip()}") + print() + + print("6. Running Python...") + result = sandbox.commands.run("python3 -c \"print(2+2)\"", user="root") + print(f" ✓ 2+2 = {result.stdout.strip()}") + print() + + sandbox.kill() + print("=" * 50) + print(" All tests passed!") + print("=" * 50) + +except Exception as e: + print(f"\n❌ Error: {e}") + print("\nTroubleshooting:") + print(" 1. Ensure all services are running:") + print(" ./scripts/services/start-all.sh") + print(" 2. Check service logs:") + print(" tail -f /tmp/e2b-*.log") + print(" 3. Verify template exists in database:") + print(" docker exec local-dev-postgres-1 psql -U postgres -c 'SELECT id FROM envs;'") + sys.exit(1) +SCRIPT + chmod +x "$REPO_ROOT/scripts/test-e2b-lite.py" + + echo -e " ${GREEN}✓${NC} Test script created" + echo "" +} + +# ----------------------------------------------------------------------------- +# Summary +# ----------------------------------------------------------------------------- +print_summary() { + # Get template ID from database + TEMPLATE_ID=$(docker exec local-dev-postgres-1 psql -U postgres -tAc "SELECT id FROM envs LIMIT 1;" 2>/dev/null | tr -d ' ' || echo "") + + echo "========================================" + echo -e " ${GREEN}E2B Lite Setup Complete!${NC}" + echo "========================================" + echo "" + echo -e "${BLUE}Credentials:${NC}" + echo " API Key: $API_KEY" + echo " Access Token: $ACCESS_TOKEN" + if [[ -n "$TEMPLATE_ID" ]]; then + echo " Template ID: $TEMPLATE_ID" + fi + echo "" + echo -e "${BLUE}Services:${NC}" + echo " API: http://localhost:80" + echo " Client-Proxy: http://localhost:3002 (envd)" + echo " Orchestrator: localhost:5008 (gRPC)" + echo "" + echo -e "${BLUE}Quick Start:${NC}" + echo "" + echo " 1. Start all services:" + echo " ./scripts/services/start-all.sh" + echo "" + echo " 2. Or start in background:" + echo " ./scripts/services/start-all.sh --bg" + echo "" + echo " 3. Test with Python SDK:" + echo " python3 -m venv e2b_venv" + echo " source e2b_venv/bin/activate" + echo " pip install e2b" + echo " python scripts/test-e2b-lite.py" + echo "" + echo -e "${BLUE}Python SDK Usage:${NC}" + echo "" + echo " from e2b import Sandbox" + echo "" + echo " sandbox = Sandbox.create(" + echo " template=\"${TEMPLATE_ID:-}\"," + echo " api_url=\"http://localhost:80\"," + echo " sandbox_url=\"http://localhost:3002\"," + echo " api_key=\"$API_KEY\"," + echo " )" + echo " result = sandbox.commands.run(\"echo hello\", user=\"root\")" + echo " print(result.stdout)" + echo " sandbox.kill()" + echo "" + echo -e "${BLUE}Environment Variables:${NC}" + echo "" + echo " # For SDK" + echo " export E2B_API_KEY=\"$API_KEY\"" + echo "" + echo " # For CLI (npx @e2b/cli)" + echo " export E2B_API_URL=\"http://localhost:80\"" + echo " export E2B_SANDBOX_URL=\"http://localhost:3002\"" + echo " export E2B_ACCESS_TOKEN=\"$ACCESS_TOKEN\"" + echo " export E2B_API_KEY=\"$API_KEY\"" + echo "" + echo -e "${BLUE}CLI Usage:${NC}" + echo "" + echo " # Set environment variables first (see above), then:" + echo " npx @e2b/cli template list" + echo " npx @e2b/cli sandbox list" + echo "" + echo "For more details, see E2B-LITE-DESIGN.md" +} + +# ============================================================================= +# Main execution +# ============================================================================= + +check_sudo + +# Install dependencies if requested +if [[ "$INSTALL_DEPS" == "true" ]]; then + install_dependencies +fi + +# Exit if deps-only mode +if [[ "$DEPS_ONLY" == "true" ]]; then + echo "Dependencies installed. Run again without --deps-only for full setup." + exit 0 +fi + +# Run all setup steps +check_prerequisites +setup_kernel_modules +setup_hugepages +create_directories +download_artifacts + +# Build or download binaries +if [[ "$USE_PREBUILT" == "true" ]]; then + download_prebuilt_binaries +else + build_binaries +fi + +setup_npm_dependencies +start_infrastructure +run_migrations +seed_database +build_base_template +create_start_scripts +create_test_script + +print_summary diff --git a/tests/test_e2b_lite.py b/tests/test_e2b_lite.py new file mode 100644 index 0000000000..67068a3a15 --- /dev/null +++ b/tests/test_e2b_lite.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +""" +E2B Lite Test Script + +Tests basic sandbox functionality: creation, commands, filesystem. + +Usage: + pip install e2b + python tests/test_e2b_lite.py +""" + +import os +import subprocess +import sys + +try: + from e2b import Sandbox +except ImportError: + print("Error: e2b package not installed") + print("Install with: pip install e2b") + sys.exit(1) + + +def get_template_id_from_db(): + """Query PostgreSQL for the template ID.""" + try: + result = subprocess.run( + ["docker", "exec", "local-dev-postgres-1", "psql", "-U", "postgres", "-tAc", "SELECT id FROM envs LIMIT 1;"], + capture_output=True, + text=True, + timeout=10, + ) + if result.returncode == 0 and result.stdout.strip(): + return result.stdout.strip() + except Exception: + pass + return None + + +# Configuration +API_KEY = os.environ.get("E2B_API_KEY", "e2b_53ae1fed82754c17ad8077fbc8bcdd90") +API_URL = os.environ.get("E2B_API_URL", "http://localhost:80") +SANDBOX_URL = os.environ.get("E2B_SANDBOX_URL", "http://localhost:3002") + +# Get template ID: from env var, from database, or fail +TEMPLATE_ID = os.environ.get("E2B_TEMPLATE_ID") +if not TEMPLATE_ID: + TEMPLATE_ID = get_template_id_from_db() + +if not TEMPLATE_ID: + print("=" * 50) + print(" E2B Lite Test - ERROR") + print("=" * 50) + print() + print("No template found!") + print() + print("Either:") + print(" 1. Set E2B_TEMPLATE_ID environment variable") + print(" 2. Build a template: ./scripts/e2b-lite-setup.sh") + print() + print("To check database:") + print(" docker exec local-dev-postgres-1 psql -U postgres -c 'SELECT id FROM envs;'") + sys.exit(1) + +print("=" * 50) +print(" E2B Lite Test") +print("=" * 50) +print() +print(f"API URL: {API_URL}") +print(f"Sandbox URL: {SANDBOX_URL}") +print(f"Template: {TEMPLATE_ID}") +print() + +try: + print("1. Creating sandbox...") + sandbox = Sandbox.create( + template=TEMPLATE_ID, + api_url=API_URL, + sandbox_url=SANDBOX_URL, + timeout=120, + api_key=API_KEY, + ) + print(f" ✓ Sandbox ID: {sandbox.sandbox_id}") + print() + + print("2. Running command...") + result = sandbox.commands.run("echo 'Hello from E2B Lite!' && uname -a", user="root") + print(f" ✓ Output: {result.stdout.strip()}") + print() + + print("3. Writing file via command...") + sandbox.commands.run("echo 'Hello World from E2B!' > /tmp/test.txt", user="root") + print(" ✓ Written /tmp/test.txt") + print() + + print("4. Reading file via command...") + result = sandbox.commands.run("cat /tmp/test.txt", user="root") + print(f" ✓ Content: {result.stdout.strip()}") + print() + + print("5. Listing directory via command...") + result = sandbox.commands.run("ls /tmp | head -5", user="root") + print(f" ✓ Files: {result.stdout.strip()}") + print() + + print("6. Running Python...") + result = sandbox.commands.run("python3 -c \"print(2+2)\"", user="root") + print(f" ✓ 2+2 = {result.stdout.strip()}") + print() + + sandbox.kill() + print("=" * 50) + print(" All tests passed!") + print("=" * 50) + +except Exception as e: + print(f"\n❌ Error: {e}") + print("\nTroubleshooting:") + print(" 1. Ensure all services are running:") + print(" ./scripts/services/start-all.sh") + print(" 2. Check service logs:") + print(" tail -f /tmp/e2b-*.log") + print(" 3. Verify template exists in database:") + print(" docker exec local-dev-postgres-1 psql -U postgres -c 'SELECT id FROM envs;'") + sys.exit(1) From 4f83b5d0100766a2b117be6816ad289497c3eec5 Mon Sep 17 00:00:00 2001 From: Adam Docolomansky Date: Thu, 29 Jan 2026 14:31:25 +0100 Subject: [PATCH 02/11] fix: adapt to orchestrator cmd changes --- scripts/e2b-lite-setup.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/e2b-lite-setup.sh b/scripts/e2b-lite-setup.sh index 7fd552577c..f515d7284b 100755 --- a/scripts/e2b-lite-setup.sh +++ b/scripts/e2b-lite-setup.sh @@ -742,10 +742,13 @@ build_base_template() { # Set environment for template building export STORAGE_PROVIDER=Local + export ARTIFACTS_REGISTRY_PROVIDER=Local export LOCAL_TEMPLATE_STORAGE_BASE_PATH="$TEMPLATE_STORAGE" export HOST_ENVD_PATH="$ENVD_DIR/bin/envd" export HOST_KERNELS_DIR="$KERNELS_DIR" export FIRECRACKER_VERSIONS_DIR="$FC_VERSIONS_DIR" + export ORCHESTRATOR_BASE_PATH="$ORCHESTRATOR_DIR/tmp" + export SANDBOX_DIR="$ORCHESTRATOR_DIR/tmp/sandbox" export POSTGRES_CONNECTION_STRING="postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable" # Generate IDs @@ -756,9 +759,9 @@ build_base_template() { echo " Build ID: $BUILD_ID" echo " Building template (this may take a few minutes)..." - if go run "$ORCHESTRATOR_DIR/cmd/build-template/main.go" \ + if go run "$ORCHESTRATOR_DIR/cmd/create-build/main.go" \ -template "$TEMPLATE_ID" \ - -build "$BUILD_ID" \ + -to-build "$BUILD_ID" \ -storage "$ORCHESTRATOR_DIR/tmp" \ -kernel "$KERNEL_VERSION" \ -firecracker "$FC_VERSION" \ From 88fb1d8237505ac0ecbcfb1498f42b07b5d45387 Mon Sep 17 00:00:00 2001 From: Adam Docolomansky Date: Thu, 29 Jan 2026 15:20:17 +0100 Subject: [PATCH 03/11] fix: simplify template builds --- scripts/e2b-lite-setup.sh | 72 ++++++++++++++------------------------- 1 file changed, 25 insertions(+), 47 deletions(-) diff --git a/scripts/e2b-lite-setup.sh b/scripts/e2b-lite-setup.sh index f515d7284b..6ba0fb0198 100755 --- a/scripts/e2b-lite-setup.sh +++ b/scripts/e2b-lite-setup.sh @@ -348,8 +348,12 @@ create_directories() { mkdir -p "$TMP_DIR/sandbox-cache" mkdir -p "$TMP_DIR/snapshot-cache" mkdir -p "$ORCHESTRATOR_DIR/tmp/local-template-storage" - mkdir -p "$ORCHESTRATOR_DIR/tmp/sandbox-cache-dir" + mkdir -p "$ORCHESTRATOR_DIR/tmp/sandbox" mkdir -p "$ORCHESTRATOR_DIR/tmp/snapshot-cache" + mkdir -p "$ORCHESTRATOR_DIR/tmp/orchestrator/sandbox" + mkdir -p "$ORCHESTRATOR_DIR/tmp/orchestrator/template" + mkdir -p "$ORCHESTRATOR_DIR/tmp/orchestrator/build" + mkdir -p "$ORCHESTRATOR_DIR/tmp/orchestrator/build-templates" echo -e " ${GREEN}✓${NC} Directories created" echo "" @@ -358,41 +362,16 @@ create_directories() { # ----------------------------------------------------------------------------- # Download artifacts # ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +# Download artifacts +# Note: When using create-build with -storage flag, kernel and firecracker +# are downloaded automatically to $storage/kernels and $storage/fc-versions. +# This function is kept for backwards compatibility but can be skipped. +# ----------------------------------------------------------------------------- download_artifacts() { echo "Downloading artifacts..." - - # Download kernel - KERNEL_PATH="$KERNELS_DIR/$KERNEL_VERSION/vmlinux.bin" - if [[ -f "$KERNEL_PATH" ]]; then - echo -e " ${GREEN}✓${NC} Kernel $KERNEL_VERSION already exists" - else - echo " Downloading kernel $KERNEL_VERSION..." - if curl -fsSL "$KERNEL_URL" -o "$KERNEL_PATH"; then - chmod 644 "$KERNEL_PATH" - echo -e " ${GREEN}✓${NC} Kernel downloaded" - else - echo -e "${RED}Error: Failed to download kernel${NC}" - echo " URL: $KERNEL_URL" - exit 1 - fi - fi - - # Download Firecracker - FC_PATH="$FC_VERSIONS_DIR/$FC_VERSION/firecracker" - if [[ -f "$FC_PATH" ]]; then - echo -e " ${GREEN}✓${NC} Firecracker $FC_VERSION already exists" - else - echo " Downloading Firecracker $FC_VERSION..." - if curl -fsSL "$FC_URL" -o "$FC_PATH"; then - chmod +x "$FC_PATH" - echo -e " ${GREEN}✓${NC} Firecracker downloaded" - else - echo -e "${RED}Error: Failed to download Firecracker${NC}" - echo " URL: $FC_URL" - exit 1 - fi - fi - + echo " Note: create-build tool will download kernel and firecracker automatically" + echo " to the storage directory when building templates." echo "" } @@ -741,15 +720,12 @@ build_base_template() { fi # Set environment for template building - export STORAGE_PROVIDER=Local - export ARTIFACTS_REGISTRY_PROVIDER=Local - export LOCAL_TEMPLATE_STORAGE_BASE_PATH="$TEMPLATE_STORAGE" + # The create-build tool with -storage flag handles most setup automatically. + # We only need to set paths for envd and the template storage location (to match orchestrator runtime) + # and let it download/setup kernel and firecracker to its expected paths. export HOST_ENVD_PATH="$ENVD_DIR/bin/envd" - export HOST_KERNELS_DIR="$KERNELS_DIR" - export FIRECRACKER_VERSIONS_DIR="$FC_VERSIONS_DIR" - export ORCHESTRATOR_BASE_PATH="$ORCHESTRATOR_DIR/tmp" - export SANDBOX_DIR="$ORCHESTRATOR_DIR/tmp/sandbox" - export POSTGRES_CONNECTION_STRING="postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable" + # Override template storage to match what orchestrator runtime expects + export LOCAL_TEMPLATE_STORAGE_BASE_PATH="$TEMPLATE_STORAGE" # Generate IDs TEMPLATE_ID=$(generate_template_id) @@ -767,7 +743,8 @@ build_base_template() { -firecracker "$FC_VERSION" \ -vcpu 2 \ -memory 512 \ - -disk 1024 > /tmp/template-build.log 2>&1; then + -disk 1024 \ + -v > /tmp/template-build.log 2>&1; then echo -e " ${GREEN}✓${NC} Template built successfully" # Register template in database @@ -858,19 +835,20 @@ NODE_ID=$(hostname) \ LOKI_URL="localhost:3100" \ ARTIFACTS_REGISTRY_PROVIDER=Local \ ENVIRONMENT=local \ -FIRECRACKER_VERSIONS_DIR=../fc-versions/builds \ +FIRECRACKER_VERSIONS_DIR=./tmp/fc-versions \ HOST_ENVD_PATH=../envd/bin/envd \ -HOST_KERNELS_DIR=../fc-kernels \ +HOST_KERNELS_DIR=./tmp/kernels \ LOCAL_TEMPLATE_STORAGE_BASE_PATH=./tmp/local-template-storage \ LOGS_COLLECTOR_ADDRESS=http://localhost:30006 \ -ORCHESTRATOR_BASE_PATH=./tmp/ \ +ORCHESTRATOR_BASE_PATH=./tmp/orchestrator \ ORCHESTRATOR_LOCK_PATH=./tmp/.lock \ ORCHESTRATOR_SERVICES=orchestrator,template-manager \ OTEL_COLLECTOR_GRPC_ENDPOINT=localhost:4317 \ REDIS_URL=localhost:6379 \ -SANDBOX_CACHE_DIR=./tmp/sandbox-cache-dir \ +SANDBOX_CACHE_DIR=./tmp/orchestrator/sandbox \ SANDBOX_DIR="${ORCH_DIR}/tmp/sandbox" \ SNAPSHOT_CACHE_DIR=./tmp/snapshot-cache \ +TEMPLATE_CACHE_DIR=./tmp/orchestrator/template \ STORAGE_PROVIDER=Local \ ./bin/orchestrator SCRIPT From ae74b5602dce1c035b603966b005be8faad8aef8 Mon Sep 17 00:00:00 2001 From: Adam Docolomansky Date: Thu, 29 Jan 2026 15:35:25 +0100 Subject: [PATCH 04/11] fix: envd build --- scripts/e2b-lite-setup.sh | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/scripts/e2b-lite-setup.sh b/scripts/e2b-lite-setup.sh index 6ba0fb0198..345435ebe5 100755 --- a/scripts/e2b-lite-setup.sh +++ b/scripts/e2b-lite-setup.sh @@ -511,14 +511,15 @@ download_prebuilt_binaries() { build_binaries() { echo "Building binaries..." - # Build envd - ENVD_DEBUG_PATH="$ENVD_DIR/bin/debug/envd" + # Build envd - MUST use regular build (not build-debug) for static linking + # The debug build uses CGO_ENABLED=1 which produces a dynamically linked binary + # that won't work inside the minimal Firecracker VM ENVD_PATH="$ENVD_DIR/bin/envd" - if [[ -f "$ENVD_DEBUG_PATH" ]]; then + if [[ -f "$ENVD_PATH" ]]; then echo -e " ${GREEN}✓${NC} envd already built" else - echo " Building envd..." - if make -C "$ENVD_DIR" build-debug > /dev/null 2>&1; then + echo " Building envd (statically linked for VM)..." + if make -C "$ENVD_DIR" build > /dev/null 2>&1; then echo -e " ${GREEN}✓${NC} envd built" else echo -e "${RED}Error: Failed to build envd${NC}" @@ -526,12 +527,6 @@ build_binaries() { fi fi - # Create symlink for envd - if [[ ! -L "$ENVD_PATH" ]] && [[ ! -f "$ENVD_PATH" ]]; then - ln -s "$ENVD_DEBUG_PATH" "$ENVD_PATH" - echo -e " ${GREEN}✓${NC} envd symlink created" - fi - # Build API API_PATH="$API_DIR/bin/api" if [[ -f "$API_PATH" ]]; then From b742e82e46e5195d1933eab3516fae58653b424d Mon Sep 17 00:00:00 2001 From: Adam Docolomansky Date: Tue, 3 Feb 2026 17:22:24 +0100 Subject: [PATCH 05/11] chore: update self-host docs --- E2B-LITE-DESIGN.md | 124 ++++++++++++++------------------------------- self-host.md | 40 ++++++++++++--- 2 files changed, 73 insertions(+), 91 deletions(-) diff --git a/E2B-LITE-DESIGN.md b/E2B-LITE-DESIGN.md index 5f8488b051..f227cf4e79 100644 --- a/E2B-LITE-DESIGN.md +++ b/E2B-LITE-DESIGN.md @@ -1,7 +1,7 @@ # E2B Lite -**Version:** 2.0 -**Last Updated:** 2026-01-28 +**Version:** 2.1 +**Last Updated:** 2026-02-03 **Status:** Working ## Overview @@ -36,10 +36,16 @@ E2B Lite enables developers to run E2B sandboxes locally on bare metal Linux ser git clone https://github.com/e2b-dev/infra.git cd infra -# Full setup (installs deps, builds binaries, starts infra, seeds DB) +# Check if your system meets requirements +./scripts/e2b-lite-setup.sh --check-req + +# Full setup with clean progress UI ./scripts/e2b-lite-setup.sh -# Or skip dependency installation if already have Docker/Go/Node +# Or with verbose output (shows all apt, build logs, etc.) +./scripts/e2b-lite-setup.sh --verbose + +# Skip dependency installation if already have Docker/Go/Node ./scripts/e2b-lite-setup.sh --no-deps ``` @@ -149,13 +155,6 @@ files = sandbox.files.list("/tmp") sandbox.kill() ``` -### Environment Variables (Alternative) - -```bash -export E2B_API_KEY="e2b_53ae1fed82754c17ad8077fbc8bcdd90" -# Note: E2B_ENVD_API_URL does NOT work - must use sandbox_url parameter -``` - --- ## Credentials @@ -176,25 +175,26 @@ From `packages/local-dev/seed-local-database.go`: `scripts/e2b-lite-setup.sh` performs these steps: 1. **Install dependencies** (Docker, Go, Node.js, build tools) -2. **Load kernel modules** (NBD with nbds_max=128, TUN) -3. **Allocate HugePages** (2048 pages = 4GB) -4. **Download artifacts** (kernel vmlinux-6.1.158, Firecracker v1.12.1) -5. **Build binaries** (envd, API, orchestrator, client-proxy) -6. **Create envd symlink** (`bin/envd` → `bin/debug/envd`) -7. **Install npm dependencies** (in `packages/shared/scripts`) -8. **Start Docker infrastructure** (PostgreSQL, Redis, Loki, etc.) -9. **Run database migrations** -10. **Seed database** (creates user, team, API keys) -11. **Build base template** (if kernel 6.8+) -12. **Create service scripts** (`scripts/services/start-*.sh`) +2. **Check prerequisites** (OS, kernel, KVM, Docker, Go) +3. **Setup system** (load kernel modules, allocate HugePages, create directories) +4. **Build binaries** (envd, API, orchestrator, client-proxy) +5. **Install npm dependencies** (in `packages/shared/scripts`) +6. **Start Docker infrastructure** (PostgreSQL, Redis, Loki, etc.) +7. **Configure database** (run migrations, seed data) +8. **Build base template** (if kernel 6.8+) +9. **Create service scripts** (`scripts/services/start-*.sh`) ### Options ```bash -./scripts/e2b-lite-setup.sh # Full setup +./scripts/e2b-lite-setup.sh # Full setup with clean progress UI +./scripts/e2b-lite-setup.sh --verbose # Show detailed output (apt, build logs) +./scripts/e2b-lite-setup.sh --check-req # Only check if system meets requirements ./scripts/e2b-lite-setup.sh --no-deps # Skip dependency installation ./scripts/e2b-lite-setup.sh --deps-only # Only install dependencies ./scripts/e2b-lite-setup.sh --no-template # Skip template building +./scripts/e2b-lite-setup.sh --prebuilt # Download pre-built binaries (faster) +./scripts/e2b-lite-setup.sh --prebuilt --version v1.0.0 # Specific version ``` --- @@ -263,68 +263,6 @@ go run packages/orchestrator/cmd/build-template/main.go \ --- -## Troubleshooting - -### "The sandbox was not found" when running commands - -**Cause:** SDK can't reach envd via client-proxy. - -**Fix:** Ensure client-proxy is running and `sandbox_url` is set: -```python -sandbox = Sandbox.create( - ... - sandbox_url="http://localhost:3002", # Required! -) -``` - -### Connection refused on port 3000 - -**Cause:** API listens on port 80, not 3000. - -**Fix:** Use `api_url="http://localhost:80"` - -### Sandbox created but commands timeout - -**Cause:** Client-proxy not running. - -**Fix:** Start client-proxy: -```bash -./scripts/services/start-client-proxy.sh -``` - -### Template build fails with "fsopen failed" - -**Cause:** Kernel < 6.8 doesn't support new overlayfs syscalls. - -**Fix:** Upgrade to kernel 6.8+ (Ubuntu 24.04) or use prebuilt template. - -### "Cannot allocate memory" or OOM - -**Cause:** Insufficient HugePages or RAM. - -**Fix:** -```bash -# Allocate more HugePages -echo 2048 | sudo tee /proc/sys/vm/nr_hugepages - -# Check current allocation -grep HugePages /proc/meminfo -``` - -### NBD module issues - -**Fix:** -```bash -# Unload and reload with more devices -sudo rmmod nbd -sudo modprobe nbd nbds_max=128 - -# Verify -ls /dev/nbd* | wc -l # Should show 128+ -``` - ---- - ## Directory Structure ``` @@ -393,6 +331,22 @@ curl -X DELETE http://localhost:80/sandboxes/{sandboxId} \ --- +## Coming Soon + +Planned improvements for E2B Lite: + +- [ ] **Pre-built binaries** - Download binaries instead of compiling (faster install) +- [ ] **Pre-built templates** - Common templates (Python, Node.js, Go) ready to use +- [ ] **GitHub release workflow** - Auto-build binaries and templates with each release +- [ ] **One-liner install** - `curl -fsSL https://e2b.dev/install-lite | bash` +- [ ] **Auto-update** - Version check and update mechanism +- [ ] **More package managers** - Support for dnf (Fedora/RHEL), pacman (Arch), zypper (openSUSE) +- [ ] **macOS support** - Nested virtualization via Apple Hypervisor Framework +- [ ] **Lite mode** - Strip unnecessary components, reduce metrics collection overhead +- [ ] **Move to cloud** - Simple migration tool from local E2B Lite to E2B Cloud/Enterprise + +--- + ## E2B CLI E2B CLI (`npx @e2b/cli`) local setup support: diff --git a/self-host.md b/self-host.md index 34f5e9748b..2216ac3084 100644 --- a/self-host.md +++ b/self-host.md @@ -1,8 +1,36 @@ -# Self-hosting E2B on Google Cloud +# Self-hosting E2B -## Prerequisites +## E2B Lite (Local PC) -**Tools** +For local development or single-machine deployments, use **E2B Lite** - a streamlined setup that runs everything on your Linux machine. + +```bash +git clone https://github.com/e2b-dev/infra.git +cd infra + +# Check if your system meets requirements +./scripts/e2b-lite-setup.sh --check-req + +# Full setup (installs Docker, Go, Node.js if needed) +./scripts/e2b-lite-setup.sh + +# Or with verbose output +./scripts/e2b-lite-setup.sh --verbose +``` + +**Requirements:** Linux with KVM support (Ubuntu 24.04 recommended), 4GB+ RAM, 20GB disk. + +The setup script installs all dependencies, builds binaries, starts Docker infrastructure, and creates a base template. See [E2B-LITE-DESIGN.md](E2B-LITE-DESIGN.md) for details. + +--- + +## E2B on Google Cloud (Production) + +For production deployments with multi-node support, deploy E2B on GCP using Terraform and Nomad. + +### Prerequisites + +#### Tools - [Packer](https://developer.hashicorp.com/packer/tutorials/docker-get-started/get-started-install-cli#installing-packer) - Used for building the disk image of the orchestrator client and server @@ -31,20 +59,20 @@ - [Docker](https://docs.docker.com/engine/install/) -**Accounts** +#### Accounts - Cloudflare account - Domain on Cloudflare - GCP account + project - PostgreSQL database (Supabase's DB only supported for now) -**Optional** +#### Optional Recommended for monitoring and logging - Grafana Account & Stack (see Step 15 for detailed notes) - Posthog Account -## Steps +### Steps Check if you can use config for terraform state management From 7ccdc0add202d8bf91c7b9f5060b66875d15ce25 Mon Sep 17 00:00:00 2001 From: Adam Docolomansky Date: Tue, 3 Feb 2026 17:22:40 +0100 Subject: [PATCH 06/11] chore: split to clean UI + verbose setup --- scripts/e2b-lite-setup.sh | 787 +++++++++++++++++++++++--------------- 1 file changed, 488 insertions(+), 299 deletions(-) diff --git a/scripts/e2b-lite-setup.sh b/scripts/e2b-lite-setup.sh index 345435ebe5..3d3db88ffe 100755 --- a/scripts/e2b-lite-setup.sh +++ b/scripts/e2b-lite-setup.sh @@ -7,7 +7,9 @@ # infrastructure, and optionally builds the base template. # # Usage: -# ./scripts/e2b-lite-setup.sh # Full setup (requires sudo) +# ./scripts/e2b-lite-setup.sh # Full setup with clean progress UI +# ./scripts/e2b-lite-setup.sh --verbose # Full setup with detailed output +# ./scripts/e2b-lite-setup.sh --check-req # Only check requirements # ./scripts/e2b-lite-setup.sh --deps-only # Only install system dependencies # ./scripts/e2b-lite-setup.sh --no-deps # Skip dependency installation # ./scripts/e2b-lite-setup.sh --no-template # Skip template building @@ -27,8 +29,14 @@ RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' +DIM='\033[2m' +BOLD='\033[1m' NC='\033[0m' # No Color +# Spinner characters +SPINNER_CHARS='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏' +SPINNER_PID="" + # Configuration KERNEL_VERSION="${KERNEL_VERSION:-vmlinux-6.1.158}" FC_VERSION="${FC_VERSION:-v1.12.1_717921c}" @@ -61,9 +69,19 @@ BUILD_TEMPLATE=true DEPS_ONLY=false USE_PREBUILT=false PREBUILT_VERSION="latest" +VERBOSE=false +CHECK_REQ_ONLY=false while [[ $# -gt 0 ]]; do case $1 in + --verbose|-v) + VERBOSE=true + shift + ;; + --check-req) + CHECK_REQ_ONLY=true + shift + ;; --no-deps) INSTALL_DEPS=false shift @@ -88,6 +106,8 @@ while [[ $# -gt 0 ]]; do echo "Usage: $0 [options]" echo "" echo "Options:" + echo " --verbose, -v Show detailed output (apt, build logs, etc.)" + echo " --check-req Only check if system meets requirements" echo " --no-deps Skip system dependency installation" echo " --deps-only Only install dependencies, then exit" echo " --no-template Skip template building" @@ -103,10 +123,151 @@ while [[ $# -gt 0 ]]; do esac done -echo "========================================" -echo " E2B Lite Setup" -echo "========================================" -echo "" +# ----------------------------------------------------------------------------- +# Progress UI Functions +# ----------------------------------------------------------------------------- + +# Start spinner with message +start_spinner() { + local msg="$1" + if [[ "$VERBOSE" == "true" ]]; then + echo -e "${BLUE}$msg${NC}" + return + fi + + # Save cursor position and print message + printf " %s " "$msg" + + # Start spinner in background + ( + i=0 + while true; do + printf "\r %s ${SPINNER_CHARS:i++%${#SPINNER_CHARS}:1} " "$msg" + sleep 0.1 + done + ) & + SPINNER_PID=$! + disown $SPINNER_PID 2>/dev/null || true +} + +# Stop spinner with success +stop_spinner_success() { + local msg="${1:-}" + if [[ -n "$SPINNER_PID" ]]; then + kill $SPINNER_PID 2>/dev/null || true + wait $SPINNER_PID 2>/dev/null || true + SPINNER_PID="" + fi + if [[ "$VERBOSE" != "true" ]]; then + if [[ -n "$msg" ]]; then + printf "\r ${GREEN}✓${NC} %s\n" "$msg" + else + printf "\r ${GREEN}✓${NC}\n" + fi + fi +} + +# Stop spinner with failure +stop_spinner_fail() { + local msg="${1:-}" + if [[ -n "$SPINNER_PID" ]]; then + kill $SPINNER_PID 2>/dev/null || true + wait $SPINNER_PID 2>/dev/null || true + SPINNER_PID="" + fi + if [[ "$VERBOSE" != "true" ]]; then + if [[ -n "$msg" ]]; then + printf "\r ${RED}✗${NC} %s\n" "$msg" + else + printf "\r ${RED}✗${NC}\n" + fi + fi +} + +# Stop spinner with warning +stop_spinner_warn() { + local msg="${1:-}" + if [[ -n "$SPINNER_PID" ]]; then + kill $SPINNER_PID 2>/dev/null || true + wait $SPINNER_PID 2>/dev/null || true + SPINNER_PID="" + fi + if [[ "$VERBOSE" != "true" ]]; then + if [[ -n "$msg" ]]; then + printf "\r ${YELLOW}!${NC} %s\n" "$msg" + else + printf "\r ${YELLOW}!${NC}\n" + fi + fi +} + +# Run command with optional output suppression +run_cmd() { + local log_file="/tmp/e2b-setup-$$.log" + if [[ "$VERBOSE" == "true" ]]; then + "$@" + else + if "$@" >> "$log_file" 2>&1; then + return 0 + else + local exit_code=$? + echo "" + echo -e "${RED}Command failed. Last 20 lines of output:${NC}" + tail -20 "$log_file" 2>/dev/null || true + return $exit_code + fi + fi +} + +# Print step header +print_step() { + local step_num="$1" + local total="$2" + local msg="$3" + echo "" + if [[ "$VERBOSE" == "true" ]]; then + echo -e "${BLUE}[$step_num/$total] $msg${NC}" + else + echo -e "${BOLD}[$step_num/$total]${NC} $msg" + fi +} + +# Print success line (for check results) +print_ok() { + echo -e " ${GREEN}✓${NC} $1" +} + +# Print warning line +print_warn() { + echo -e " ${YELLOW}!${NC} $1" +} + +# Print error line +print_err() { + echo -e " ${RED}✗${NC} $1" +} + +# Cleanup on exit +cleanup_spinner() { + if [[ -n "$SPINNER_PID" ]]; then + kill $SPINNER_PID 2>/dev/null || true + fi +} +trap cleanup_spinner EXIT + +# Print banner +if [[ "$CHECK_REQ_ONLY" == "true" ]]; then + echo "" + echo -e "${BOLD}E2B Lite - Requirements Check${NC}" + echo "" +else + echo "" + echo -e "${BOLD}E2B Lite Setup${NC}" + if [[ "$VERBOSE" != "true" ]]; then + echo -e "${DIM}Use --verbose for detailed output${NC}" + fi + echo "" +fi # ----------------------------------------------------------------------------- # Fix git safe directory (needed when repo is rsync'd/copied) @@ -131,214 +292,263 @@ check_sudo() { # Install system dependencies # ----------------------------------------------------------------------------- install_dependencies() { - echo -e "${BLUE}Installing system dependencies...${NC}" - echo "" - # Detect package manager - if command -v apt-get &> /dev/null; then - PKG_MANAGER="apt" - else - echo -e "${RED}Error: Only apt-based systems (Ubuntu/Debian) are currently supported${NC}" + if ! command -v apt-get &> /dev/null; then + print_err "Only apt-based systems (Ubuntu/Debian) are currently supported" exit 1 fi # Update package list - echo " Updating package list..." - sudo apt-get update -qq + start_spinner "Updating package list" + if run_cmd sudo apt-get update -qq; then + stop_spinner_success "Package list updated" + else + stop_spinner_fail "Failed to update package list" + exit 1 + fi # Install Docker if not present if ! command -v docker &> /dev/null; then - echo " Installing Docker..." - sudo install -m 0755 -d /etc/apt/keyrings - sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc - sudo chmod a+r /etc/apt/keyrings/docker.asc + start_spinner "Installing Docker" + if run_cmd sudo install -m 0755 -d /etc/apt/keyrings && \ + run_cmd sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc && \ + run_cmd sudo chmod a+r /etc/apt/keyrings/docker.asc; then - echo "Types: deb + echo "Types: deb URIs: https://download.docker.com/linux/ubuntu Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") Components: stable -Signed-By: /etc/apt/keyrings/docker.asc" | sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null +Signed-By: /etc/apt/keyrings/docker.asc" | sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null 2>&1 - sudo apt-get update -qq - sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin - echo -e " ${GREEN}✓${NC} Docker installed" + if run_cmd sudo apt-get update -qq && \ + run_cmd sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin; then + stop_spinner_success "Docker installed" + else + stop_spinner_fail "Failed to install Docker" + exit 1 + fi + else + stop_spinner_fail "Failed to setup Docker repository" + exit 1 + fi else - echo -e " ${GREEN}✓${NC} Docker already installed" + print_ok "Docker already installed" fi # Install Go if not present if ! command -v go &> /dev/null; then - echo " Installing Go via snap..." - sudo snap install --classic go - echo -e " ${GREEN}✓${NC} Go installed" + start_spinner "Installing Go" + if run_cmd sudo snap install --classic go; then + stop_spinner_success "Go installed" + else + stop_spinner_fail "Failed to install Go" + exit 1 + fi else - echo -e " ${GREEN}✓${NC} Go already installed ($(go version | grep -oP '\d+\.\d+' | head -1))" + print_ok "Go already installed ($(go version | grep -oP '\d+\.\d+' | head -1))" fi # Install Node.js if not present (needed for template building) if ! command -v node &> /dev/null; then - echo " Installing Node.js..." - # Install via NodeSource for recent version - curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - - sudo apt-get install -y nodejs - echo -e " ${GREEN}✓${NC} Node.js installed" + start_spinner "Installing Node.js" + if curl -fsSL https://deb.nodesource.com/setup_22.x 2>/dev/null | sudo -E bash - > /dev/null 2>&1 && \ + run_cmd sudo apt-get install -y nodejs; then + stop_spinner_success "Node.js installed" + else + stop_spinner_fail "Failed to install Node.js" + exit 1 + fi else - echo -e " ${GREEN}✓${NC} Node.js already installed ($(node --version))" + print_ok "Node.js already installed ($(node --version))" fi # Install build tools and other dependencies - echo " Installing build tools..." - sudo apt-get install -y build-essential make ca-certificates curl git net-tools - echo -e " ${GREEN}✓${NC} Build tools installed" - - echo "" + start_spinner "Installing build tools" + if run_cmd sudo apt-get install -y build-essential make ca-certificates curl git net-tools; then + stop_spinner_success "Build tools installed" + else + stop_spinner_fail "Failed to install build tools" + exit 1 + fi } # ----------------------------------------------------------------------------- # Check prerequisites # ----------------------------------------------------------------------------- check_prerequisites() { - echo "Checking prerequisites..." + local has_errors=false + local has_warnings=false # Check OS if [[ "$OSTYPE" != "linux-gnu"* ]]; then - echo -e "${RED}Error: E2B Lite requires Linux. Detected: $OSTYPE${NC}" - exit 1 + print_err "E2B Lite requires Linux. Detected: $OSTYPE" + has_errors=true + else + print_ok "Linux detected" fi - echo -e " ${GREEN}✓${NC} Linux detected" # Check kernel version KERNEL_MAJOR=$(uname -r | cut -d. -f1) KERNEL_MINOR=$(uname -r | cut -d. -f2) if [ "$KERNEL_MAJOR" -lt 5 ] || ([ "$KERNEL_MAJOR" -eq 5 ] && [ "$KERNEL_MINOR" -lt 10 ]); then - echo -e "${RED}Error: Kernel $(uname -r) is too old. Minimum required: 5.10${NC}" - exit 1 + print_err "Kernel $(uname -r) is too old. Minimum required: 5.10" + has_errors=true + else + print_ok "Kernel $(uname -r)" fi - echo -e " ${GREEN}✓${NC} Kernel $(uname -r)" # Check for kernel 6.8+ (needed for building templates) if [ "$KERNEL_MAJOR" -lt 6 ] || ([ "$KERNEL_MAJOR" -eq 6 ] && [ "$KERNEL_MINOR" -lt 8 ]); then - echo -e " ${YELLOW}!${NC} Kernel < 6.8: You can run sandboxes but cannot build custom templates" + print_warn "Kernel < 6.8: You can run sandboxes but cannot build custom templates" BUILD_TEMPLATE=false + has_warnings=true else - echo -e " ${GREEN}✓${NC} Kernel 6.8+: Full support (running + building templates)" + print_ok "Kernel 6.8+: Full support (running + building templates)" fi # Check KVM if [[ ! -e /dev/kvm ]]; then - echo -e "${RED}Error: /dev/kvm not found. KVM is required.${NC}" - echo " Enable KVM: sudo modprobe kvm_intel (or kvm_amd)" - exit 1 - fi - if [[ ! -r /dev/kvm ]] || [[ ! -w /dev/kvm ]]; then - echo -e "${YELLOW}Warning: No read/write access to /dev/kvm${NC}" - echo " Fix: sudo usermod -aG kvm \$USER && newgrp kvm" - echo " Or run with sudo" + print_err "/dev/kvm not found. KVM is required" + echo " Enable KVM: sudo modprobe kvm_intel (or kvm_amd)" + has_errors=true + elif [[ ! -r /dev/kvm ]] || [[ ! -w /dev/kvm ]]; then + print_warn "No read/write access to /dev/kvm" + echo " Fix: sudo usermod -aG kvm \$USER && newgrp kvm" + has_warnings=true + else + print_ok "KVM available" fi - echo -e " ${GREEN}✓${NC} KVM available" # Check Docker if ! command -v docker &> /dev/null; then - echo -e "${RED}Error: Docker not found. Run with --deps-only first or install manually.${NC}" - exit 1 - fi - if ! docker info &> /dev/null; then - echo -e "${RED}Error: Docker daemon not running or no permission${NC}" - echo " Start Docker: sudo systemctl start docker" - echo " Or add to group: sudo usermod -aG docker \$USER && newgrp docker" - exit 1 + if [[ "$CHECK_REQ_ONLY" == "true" ]]; then + print_err "Docker not found" + else + print_err "Docker not found. Run with --deps-only first or install manually" + fi + has_errors=true + elif ! docker info &> /dev/null 2>&1; then + print_err "Docker daemon not running or no permission" + echo " Start Docker: sudo systemctl start docker" + echo " Or add to group: sudo usermod -aG docker \$USER && newgrp docker" + has_errors=true + else + print_ok "Docker available" fi - echo -e " ${GREEN}✓${NC} Docker available" # Check Go if ! command -v go &> /dev/null; then - echo -e "${RED}Error: Go not found. Run with --deps-only first or install manually.${NC}" - exit 1 + if [[ "$CHECK_REQ_ONLY" == "true" ]]; then + print_err "Go not found" + else + print_err "Go not found. Run with --deps-only first or install manually" + fi + has_errors=true + else + GO_VERSION=$(go version | grep -oP '\d+\.\d+' | head -1) + print_ok "Go $GO_VERSION" fi - GO_VERSION=$(go version | grep -oP '\d+\.\d+' | head -1) - echo -e " ${GREEN}✓${NC} Go $GO_VERSION" # Check Node.js (optional, for template building) if command -v node &> /dev/null; then - echo -e " ${GREEN}✓${NC} Node.js $(node --version)" + print_ok "Node.js $(node --version)" else - echo -e " ${YELLOW}!${NC} Node.js not found (needed for template building)" + print_warn "Node.js not found (needed for template building)" + has_warnings=true fi - echo "" + # Return appropriate exit code for check-req mode + if [[ "$CHECK_REQ_ONLY" == "true" ]]; then + echo "" + if [[ "$has_errors" == "true" ]]; then + echo -e "${RED}Some requirements are not met.${NC}" + echo "Install missing dependencies with: ./scripts/e2b-lite-setup.sh --deps-only" + exit 1 + elif [[ "$has_warnings" == "true" ]]; then + echo -e "${YELLOW}System is ready with some limitations.${NC}" + exit 0 + else + echo -e "${GREEN}All requirements met. System is ready for E2B Lite.${NC}" + exit 0 + fi + fi + + # For non-check-req mode, exit on errors + if [[ "$has_errors" == "true" ]]; then + exit 1 + fi } # ----------------------------------------------------------------------------- # Setup kernel modules # ----------------------------------------------------------------------------- setup_kernel_modules() { - echo "Setting up kernel modules..." - # NBD module with sufficient devices if ! lsmod | grep -q "^nbd "; then - echo " Loading NBD module..." - if ! sudo modprobe nbd nbds_max=128 2>/dev/null; then - echo -e "${YELLOW}Warning: Failed to load NBD module. You may need to install it.${NC}" + start_spinner "Loading NBD module" + if sudo modprobe nbd nbds_max=128 2>/dev/null; then + stop_spinner_success "NBD module loaded (nbds_max=128)" else - echo -e " ${GREEN}✓${NC} NBD module loaded (nbds_max=128)" + stop_spinner_warn "Failed to load NBD module (may need to install)" fi else # Check if we have enough NBD devices NBD_COUNT=$(ls -1 /dev/nbd* 2>/dev/null | wc -l) if [ "$NBD_COUNT" -lt 64 ]; then - echo " Reloading NBD module with more devices..." + start_spinner "Reloading NBD module with more devices" sudo rmmod nbd 2>/dev/null || true sudo modprobe nbd nbds_max=128 + stop_spinner_success "NBD module reloaded" + else + print_ok "NBD module loaded" fi - echo -e " ${GREEN}✓${NC} NBD module loaded" fi # TUN module if ! lsmod | grep -q "^tun "; then - echo " Loading TUN module..." - sudo modprobe tun 2>/dev/null || echo -e "${YELLOW}Warning: Failed to load TUN module${NC}" + start_spinner "Loading TUN module" + if sudo modprobe tun 2>/dev/null; then + stop_spinner_success "TUN module loaded" + else + stop_spinner_warn "Failed to load TUN module" + fi + else + print_ok "TUN module loaded" fi - echo -e " ${GREEN}✓${NC} TUN module" - - echo "" } # ----------------------------------------------------------------------------- # Setup HugePages # ----------------------------------------------------------------------------- setup_hugepages() { - echo "Setting up HugePages..." - HUGEPAGES_TOTAL=$(cat /proc/sys/vm/nr_hugepages 2>/dev/null || echo 0) HUGEPAGES_NEEDED=2048 # 2048 * 2MB = 4GB reserved for HugePages if [ "$HUGEPAGES_TOTAL" -lt "$HUGEPAGES_NEEDED" ]; then - echo " Allocating HugePages ($HUGEPAGES_NEEDED pages = $((HUGEPAGES_NEEDED * 2))MB)..." + start_spinner "Allocating HugePages (4GB)" if echo "$HUGEPAGES_NEEDED" | sudo tee /proc/sys/vm/nr_hugepages > /dev/null 2>&1; then # Make it persistent across reboots if ! grep -q "vm.nr_hugepages" /etc/sysctl.conf 2>/dev/null; then echo "vm.nr_hugepages=$HUGEPAGES_NEEDED" | sudo tee -a /etc/sysctl.conf > /dev/null - echo -e " ${GREEN}✓${NC} HugePages configured (persistent)" + stop_spinner_success "HugePages configured (persistent)" else sudo sed -i "s/vm.nr_hugepages=.*/vm.nr_hugepages=$HUGEPAGES_NEEDED/" /etc/sysctl.conf - echo -e " ${GREEN}✓${NC} HugePages allocated" + stop_spinner_success "HugePages allocated" fi else - echo -e "${YELLOW}Warning: Failed to allocate HugePages. Template building may fail.${NC}" - echo " Manual fix: echo $HUGEPAGES_NEEDED | sudo tee /proc/sys/vm/nr_hugepages" + stop_spinner_warn "Failed to allocate HugePages" + echo " Manual fix: echo $HUGEPAGES_NEEDED | sudo tee /proc/sys/vm/nr_hugepages" fi else - echo -e " ${GREEN}✓${NC} HugePages already configured ($HUGEPAGES_TOTAL pages)" + print_ok "HugePages already configured ($HUGEPAGES_TOTAL pages)" fi - - echo "" } # ----------------------------------------------------------------------------- # Create directory structure # ----------------------------------------------------------------------------- create_directories() { - echo "Creating directory structure..." + start_spinner "Creating directory structure" mkdir -p "$FC_VERSIONS_DIR/$FC_VERSION" mkdir -p "$KERNELS_DIR/$KERNEL_VERSION" @@ -355,13 +565,9 @@ create_directories() { mkdir -p "$ORCHESTRATOR_DIR/tmp/orchestrator/build" mkdir -p "$ORCHESTRATOR_DIR/tmp/orchestrator/build-templates" - echo -e " ${GREEN}✓${NC} Directories created" - echo "" + stop_spinner_success "Directories created" } -# ----------------------------------------------------------------------------- -# Download artifacts -# ----------------------------------------------------------------------------- # ----------------------------------------------------------------------------- # Download artifacts # Note: When using create-build with -storage flag, kernel and firecracker @@ -369,43 +575,42 @@ create_directories() { # This function is kept for backwards compatibility but can be skipped. # ----------------------------------------------------------------------------- download_artifacts() { - echo "Downloading artifacts..." - echo " Note: create-build tool will download kernel and firecracker automatically" - echo " to the storage directory when building templates." - echo "" + if [[ "$VERBOSE" == "true" ]]; then + echo "Note: create-build tool will download kernel and firecracker automatically" + fi } # ----------------------------------------------------------------------------- # Download pre-built binaries # ----------------------------------------------------------------------------- download_prebuilt_binaries() { - echo "Downloading pre-built binaries..." - GITHUB_REPO="e2b-dev/infra" + BUILD_API=false + BUILD_ORCH=false + BUILD_PROXY=false + BUILD_ENVD=false # Determine version to download if [[ "$PREBUILT_VERSION" == "latest" ]]; then - echo " Fetching latest release..." + start_spinner "Fetching latest release info" RELEASE_URL="https://api.github.com/repos/$GITHUB_REPO/releases/latest" RELEASE_INFO=$(curl -fsSL "$RELEASE_URL" 2>/dev/null) if [[ -z "$RELEASE_INFO" ]]; then - echo -e "${RED}Error: Failed to fetch latest release info${NC}" - echo " Falling back to building from source..." + stop_spinner_warn "Failed to fetch release info, building from source" build_binaries return fi VERSION=$(echo "$RELEASE_INFO" | grep -oP '"tag_name":\s*"\K[^"]+' | head -1) if [[ -z "$VERSION" ]]; then - echo -e "${YELLOW}Warning: No releases found, falling back to building from source${NC}" + stop_spinner_warn "No releases found, building from source" build_binaries return fi + stop_spinner_success "Found version $VERSION" else VERSION="$PREBUILT_VERSION" fi - echo " Version: $VERSION" - # Download URLs BASE_URL="https://github.com/$GITHUB_REPO/releases/download/$VERSION" @@ -418,14 +623,14 @@ download_prebuilt_binaries() { # Download API API_PATH="$API_DIR/bin/api" if [[ -f "$API_PATH" ]]; then - echo -e " ${GREEN}✓${NC} API already exists" + print_ok "API already exists" else - echo " Downloading API..." - if curl -fsSL "$BASE_URL/api-linux-amd64" -o "$API_PATH"; then + start_spinner "Downloading API" + if curl -fsSL "$BASE_URL/api-linux-amd64" -o "$API_PATH" 2>/dev/null; then chmod +x "$API_PATH" - echo -e " ${GREEN}✓${NC} API downloaded" + stop_spinner_success "API downloaded" else - echo -e "${YELLOW}Warning: Failed to download API, will build from source${NC}" + stop_spinner_warn "Failed to download API" BUILD_API=true fi fi @@ -433,14 +638,14 @@ download_prebuilt_binaries() { # Download Orchestrator ORCH_PATH="$ORCHESTRATOR_DIR/bin/orchestrator" if [[ -f "$ORCH_PATH" ]]; then - echo -e " ${GREEN}✓${NC} Orchestrator already exists" + print_ok "Orchestrator already exists" else - echo " Downloading Orchestrator..." - if curl -fsSL "$BASE_URL/orchestrator-linux-amd64" -o "$ORCH_PATH"; then + start_spinner "Downloading Orchestrator" + if curl -fsSL "$BASE_URL/orchestrator-linux-amd64" -o "$ORCH_PATH" 2>/dev/null; then chmod +x "$ORCH_PATH" - echo -e " ${GREEN}✓${NC} Orchestrator downloaded" + stop_spinner_success "Orchestrator downloaded" else - echo -e "${YELLOW}Warning: Failed to download Orchestrator, will build from source${NC}" + stop_spinner_warn "Failed to download Orchestrator" BUILD_ORCH=true fi fi @@ -448,14 +653,14 @@ download_prebuilt_binaries() { # Download Client-Proxy PROXY_PATH="$CLIENT_PROXY_DIR/bin/client-proxy" if [[ -f "$PROXY_PATH" ]]; then - echo -e " ${GREEN}✓${NC} Client-Proxy already exists" + print_ok "Client-Proxy already exists" else - echo " Downloading Client-Proxy..." - if curl -fsSL "$BASE_URL/client-proxy-linux-amd64" -o "$PROXY_PATH"; then + start_spinner "Downloading Client-Proxy" + if curl -fsSL "$BASE_URL/client-proxy-linux-amd64" -o "$PROXY_PATH" 2>/dev/null; then chmod +x "$PROXY_PATH" - echo -e " ${GREEN}✓${NC} Client-Proxy downloaded" + stop_spinner_success "Client-Proxy downloaded" else - echo -e "${YELLOW}Warning: Failed to download Client-Proxy, will build from source${NC}" + stop_spinner_warn "Failed to download Client-Proxy" BUILD_PROXY=true fi fi @@ -463,14 +668,14 @@ download_prebuilt_binaries() { # Download Envd ENVD_PATH="$ENVD_DIR/bin/envd" if [[ -f "$ENVD_PATH" ]]; then - echo -e " ${GREEN}✓${NC} envd already exists" + print_ok "envd already exists" else - echo " Downloading envd..." - if curl -fsSL "$BASE_URL/envd-linux-amd64" -o "$ENVD_PATH"; then + start_spinner "Downloading envd" + if curl -fsSL "$BASE_URL/envd-linux-amd64" -o "$ENVD_PATH" 2>/dev/null; then chmod +x "$ENVD_PATH" - echo -e " ${GREEN}✓${NC} envd downloaded" + stop_spinner_success "envd downloaded" else - echo -e "${YELLOW}Warning: Failed to download envd, will build from source${NC}" + stop_spinner_warn "Failed to download envd" BUILD_ENVD=true fi fi @@ -478,51 +683,61 @@ download_prebuilt_binaries() { # Build any that failed to download if [[ "$BUILD_API" == "true" ]] || [[ "$BUILD_ORCH" == "true" ]] || \ [[ "$BUILD_PROXY" == "true" ]] || [[ "$BUILD_ENVD" == "true" ]]; then - echo "" - echo "Building missing binaries from source..." if [[ "$BUILD_ENVD" == "true" ]]; then - echo " Building envd..." - make -C "$ENVD_DIR" build > /dev/null 2>&1 || echo -e "${RED}Failed to build envd${NC}" + start_spinner "Building envd from source" + if run_cmd make -C "$ENVD_DIR" build; then + stop_spinner_success "envd built" + else + stop_spinner_fail "Failed to build envd" + fi fi if [[ "$BUILD_API" == "true" ]]; then - echo " Building API..." - make -C "$API_DIR" build > /dev/null 2>&1 || echo -e "${RED}Failed to build API${NC}" + start_spinner "Building API from source" + if run_cmd make -C "$API_DIR" build; then + stop_spinner_success "API built" + else + stop_spinner_fail "Failed to build API" + fi fi if [[ "$BUILD_ORCH" == "true" ]]; then - echo " Building Orchestrator..." - make -C "$ORCHESTRATOR_DIR" build-debug > /dev/null 2>&1 || echo -e "${RED}Failed to build Orchestrator${NC}" + start_spinner "Building Orchestrator from source" + if run_cmd make -C "$ORCHESTRATOR_DIR" build-debug; then + stop_spinner_success "Orchestrator built" + else + stop_spinner_fail "Failed to build Orchestrator" + fi fi if [[ "$BUILD_PROXY" == "true" ]]; then - echo " Building Client-Proxy..." - make -C "$CLIENT_PROXY_DIR" build > /dev/null 2>&1 || echo -e "${RED}Failed to build Client-Proxy${NC}" + start_spinner "Building Client-Proxy from source" + if run_cmd make -C "$CLIENT_PROXY_DIR" build; then + stop_spinner_success "Client-Proxy built" + else + stop_spinner_fail "Failed to build Client-Proxy" + fi fi fi - - echo "" } # ----------------------------------------------------------------------------- # Build all binaries # ----------------------------------------------------------------------------- build_binaries() { - echo "Building binaries..." - # Build envd - MUST use regular build (not build-debug) for static linking # The debug build uses CGO_ENABLED=1 which produces a dynamically linked binary # that won't work inside the minimal Firecracker VM ENVD_PATH="$ENVD_DIR/bin/envd" if [[ -f "$ENVD_PATH" ]]; then - echo -e " ${GREEN}✓${NC} envd already built" + print_ok "envd already built" else - echo " Building envd (statically linked for VM)..." - if make -C "$ENVD_DIR" build > /dev/null 2>&1; then - echo -e " ${GREEN}✓${NC} envd built" + start_spinner "Building envd" + if run_cmd make -C "$ENVD_DIR" build; then + stop_spinner_success "envd built" else - echo -e "${RED}Error: Failed to build envd${NC}" + stop_spinner_fail "Failed to build envd" exit 1 fi fi @@ -530,13 +745,13 @@ build_binaries() { # Build API API_PATH="$API_DIR/bin/api" if [[ -f "$API_PATH" ]]; then - echo -e " ${GREEN}✓${NC} API already built" + print_ok "API already built" else - echo " Building API..." - if make -C "$API_DIR" build > /dev/null 2>&1; then - echo -e " ${GREEN}✓${NC} API built" + start_spinner "Building API" + if run_cmd make -C "$API_DIR" build; then + stop_spinner_success "API built" else - echo -e "${RED}Error: Failed to build API${NC}" + stop_spinner_fail "Failed to build API" exit 1 fi fi @@ -544,13 +759,13 @@ build_binaries() { # Build Orchestrator ORCH_PATH="$ORCHESTRATOR_DIR/bin/orchestrator" if [[ -f "$ORCH_PATH" ]]; then - echo -e " ${GREEN}✓${NC} Orchestrator already built" + print_ok "Orchestrator already built" else - echo " Building Orchestrator..." - if make -C "$ORCHESTRATOR_DIR" build-debug > /dev/null 2>&1; then - echo -e " ${GREEN}✓${NC} Orchestrator built" + start_spinner "Building Orchestrator" + if run_cmd make -C "$ORCHESTRATOR_DIR" build-debug; then + stop_spinner_success "Orchestrator built" else - echo -e "${RED}Error: Failed to build Orchestrator${NC}" + stop_spinner_fail "Failed to build Orchestrator" exit 1 fi fi @@ -558,18 +773,16 @@ build_binaries() { # Build Client-Proxy PROXY_PATH="$CLIENT_PROXY_DIR/bin/client-proxy" if [[ -f "$PROXY_PATH" ]]; then - echo -e " ${GREEN}✓${NC} Client-Proxy already built" + print_ok "Client-Proxy already built" else - echo " Building Client-Proxy..." - if make -C "$CLIENT_PROXY_DIR" build > /dev/null 2>&1; then - echo -e " ${GREEN}✓${NC} Client-Proxy built" + start_spinner "Building Client-Proxy" + if run_cmd make -C "$CLIENT_PROXY_DIR" build; then + stop_spinner_success "Client-Proxy built" else - echo -e "${RED}Error: Failed to build Client-Proxy${NC}" + stop_spinner_fail "Failed to build Client-Proxy" exit 1 fi fi - - echo "" } # ----------------------------------------------------------------------------- @@ -577,99 +790,90 @@ build_binaries() { # ----------------------------------------------------------------------------- setup_npm_dependencies() { if ! command -v npm &> /dev/null; then - echo -e "${YELLOW}Skipping npm dependencies (npm not found)${NC}" + print_warn "Skipping npm dependencies (npm not found)" return fi - echo "Setting up npm dependencies..." - if [[ -d "$SHARED_SCRIPTS_DIR" ]]; then if [[ ! -d "$SHARED_SCRIPTS_DIR/node_modules" ]]; then - echo " Installing npm packages in shared/scripts..." - (cd "$SHARED_SCRIPTS_DIR" && npm install --silent) || { - echo -e "${YELLOW}Warning: Failed to install npm packages${NC}" - } + start_spinner "Installing npm packages" + if (cd "$SHARED_SCRIPTS_DIR" && run_cmd npm install --silent); then + stop_spinner_success "npm dependencies installed" + else + stop_spinner_warn "Failed to install npm packages" + fi + else + print_ok "npm dependencies ready" fi - echo -e " ${GREEN}✓${NC} npm dependencies ready" fi - - echo "" } # ----------------------------------------------------------------------------- # Start Docker infrastructure # ----------------------------------------------------------------------------- start_infrastructure() { - echo "Starting Docker infrastructure..." - # Use full docker-compose with all services COMPOSE_FILE="$LOCAL_DEV_DIR/docker-compose.yaml" if [[ ! -f "$COMPOSE_FILE" ]]; then - echo -e "${RED}Error: docker-compose.yaml not found at $COMPOSE_FILE${NC}" + print_err "docker-compose.yaml not found at $COMPOSE_FILE" exit 1 fi # Check if containers are already running if docker ps --format '{{.Names}}' | grep -q "local-dev-postgres"; then - echo -e " ${GREEN}✓${NC} Infrastructure already running" + print_ok "Infrastructure already running" else - echo " Starting containers..." - docker compose -f "$COMPOSE_FILE" up -d - - # Wait for PostgreSQL to be ready - echo " Waiting for PostgreSQL..." - for i in {1..30}; do - if docker exec local-dev-postgres-1 pg_isready -U postgres > /dev/null 2>&1; then - break - fi - sleep 1 - done - echo -e " ${GREEN}✓${NC} Infrastructure started" + start_spinner "Starting Docker containers" + if run_cmd docker compose -f "$COMPOSE_FILE" up -d; then + # Wait for PostgreSQL to be ready + for i in {1..30}; do + if docker exec local-dev-postgres-1 pg_isready -U postgres > /dev/null 2>&1; then + break + fi + sleep 1 + done + stop_spinner_success "Infrastructure started" + else + stop_spinner_fail "Failed to start infrastructure" + exit 1 + fi fi - - echo "" } # ----------------------------------------------------------------------------- # Run database migrations # ----------------------------------------------------------------------------- run_migrations() { - echo "Running database migrations..." - export POSTGRES_CONNECTION_STRING="postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable" - if make -C "$REPO_ROOT/packages/db" migrate-local > /dev/null 2>&1; then - echo -e " ${GREEN}✓${NC} Migrations applied" + start_spinner "Running database migrations" + if run_cmd make -C "$REPO_ROOT/packages/db" migrate-local; then + stop_spinner_success "Migrations applied" else - echo -e "${YELLOW}Warning: Migration may have failed or already applied${NC}" + stop_spinner_warn "Migrations may have failed or already applied" fi - - echo "" } # ----------------------------------------------------------------------------- # Seed database # ----------------------------------------------------------------------------- seed_database() { - echo "Seeding database..." - export POSTGRES_CONNECTION_STRING="postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable" # Check if already seeded by looking for the team TEAM_EXISTS=$(docker exec local-dev-postgres-1 psql -U postgres -tAc "SELECT COUNT(*) FROM teams WHERE id='0b8a3ded-4489-4722-afd1-1d82e64ec2d5';" 2>/dev/null || echo "0") if [[ "$TEAM_EXISTS" == "1" ]]; then - echo -e " ${GREEN}✓${NC} Database already seeded" + print_ok "Database already seeded" else - echo " Running seed script..." - (cd "$LOCAL_DEV_DIR" && go run seed-local-database.go) > /dev/null 2>&1 || { - echo -e "${YELLOW}Warning: Seeding may have failed${NC}" - } - echo -e " ${GREEN}✓${NC} Database seeded" + start_spinner "Seeding database" + if (cd "$LOCAL_DEV_DIR" && run_cmd go run seed-local-database.go); then + stop_spinner_success "Database seeded" + else + stop_spinner_warn "Seeding may have failed" + fi fi - - echo "" } # ----------------------------------------------------------------------------- @@ -685,18 +889,14 @@ generate_template_id() { build_base_template() { if [[ "$BUILD_TEMPLATE" != "true" ]]; then - echo -e "${YELLOW}Skipping template build (--no-template or kernel < 6.8)${NC}" - echo "" + print_warn "Skipping template build (--no-template or kernel < 6.8)" return fi - echo "Building base template..." - # Check if template already exists in database EXISTING_TEMPLATE=$(docker exec local-dev-postgres-1 psql -U postgres -tAc "SELECT id FROM envs LIMIT 1;" 2>/dev/null | tr -d ' ' || echo "") if [[ -n "$EXISTING_TEMPLATE" ]]; then - echo -e " ${GREEN}✓${NC} Template already exists in database: $EXISTING_TEMPLATE" - echo "" + print_ok "Template already exists: $EXISTING_TEMPLATE" return fi @@ -705,30 +905,29 @@ build_base_template() { EXISTING_BUILD=$(ls -1 "$TEMPLATE_STORAGE" 2>/dev/null | head -1) if [[ -n "$EXISTING_BUILD" ]]; then - echo " Found existing template files, registering in database..." + start_spinner "Registering existing template" TEMPLATE_ID=$(generate_template_id) BUILD_ID="$EXISTING_BUILD" register_template "$TEMPLATE_ID" "$BUILD_ID" - echo -e " ${GREEN}✓${NC} Template registered: $TEMPLATE_ID" - echo "" + stop_spinner_success "Template registered: $TEMPLATE_ID" return fi # Set environment for template building - # The create-build tool with -storage flag handles most setup automatically. - # We only need to set paths for envd and the template storage location (to match orchestrator runtime) - # and let it download/setup kernel and firecracker to its expected paths. export HOST_ENVD_PATH="$ENVD_DIR/bin/envd" - # Override template storage to match what orchestrator runtime expects export LOCAL_TEMPLATE_STORAGE_BASE_PATH="$TEMPLATE_STORAGE" # Generate IDs TEMPLATE_ID=$(generate_template_id) BUILD_ID=$(cat /proc/sys/kernel/random/uuid) - echo " Template ID: $TEMPLATE_ID" - echo " Build ID: $BUILD_ID" - echo " Building template (this may take a few minutes)..." + start_spinner "Building base template (this may take a few minutes)" + + if [[ "$VERBOSE" == "true" ]]; then + echo "" + echo " Template ID: $TEMPLATE_ID" + echo " Build ID: $BUILD_ID" + fi if go run "$ORCHESTRATOR_DIR/cmd/create-build/main.go" \ -template "$TEMPLATE_ID" \ @@ -740,18 +939,17 @@ build_base_template() { -memory 512 \ -disk 1024 \ -v > /tmp/template-build.log 2>&1; then - echo -e " ${GREEN}✓${NC} Template built successfully" + stop_spinner_success "Template built: $TEMPLATE_ID" # Register template in database register_template "$TEMPLATE_ID" "$BUILD_ID" - echo -e " ${GREEN}✓${NC} Template registered in database" + print_ok "Template registered in database" else - echo -e "${YELLOW}Warning: Template build failed. Check /tmp/template-build.log${NC}" - echo " You can build it manually later with:" - echo " make -C packages/shared/scripts local-build-base-template" + stop_spinner_warn "Template build failed" + echo " Check /tmp/template-build.log for details" + echo " You can build it manually later with:" + echo " make -C packages/shared/scripts local-build-base-template" fi - - echo "" } # Register template in the database @@ -787,7 +985,7 @@ register_template() { # Create service start scripts # ----------------------------------------------------------------------------- create_start_scripts() { - echo "Creating service start scripts..." + start_spinner "Creating service scripts" # Create scripts directory mkdir -p "$REPO_ROOT/scripts/services" @@ -946,15 +1144,14 @@ fi SCRIPT chmod +x "$REPO_ROOT/scripts/services/start-all.sh" - echo -e " ${GREEN}✓${NC} Service scripts created" - echo "" + stop_spinner_success "Service scripts created" } # ----------------------------------------------------------------------------- # Create test script # ----------------------------------------------------------------------------- create_test_script() { - echo "Creating test script..." + start_spinner "Creating test script" cat > "$REPO_ROOT/scripts/test-e2b-lite.py" << 'SCRIPT' #!/usr/bin/env python3 @@ -1085,8 +1282,7 @@ except Exception as e: SCRIPT chmod +x "$REPO_ROOT/scripts/test-e2b-lite.py" - echo -e " ${GREEN}✓${NC} Test script created" - echo "" + stop_spinner_success "Test script created" } # ----------------------------------------------------------------------------- @@ -1096,106 +1292,99 @@ print_summary() { # Get template ID from database TEMPLATE_ID=$(docker exec local-dev-postgres-1 psql -U postgres -tAc "SELECT id FROM envs LIMIT 1;" 2>/dev/null | tr -d ' ' || echo "") - echo "========================================" - echo -e " ${GREEN}E2B Lite Setup Complete!${NC}" - echo "========================================" - echo "" - echo -e "${BLUE}Credentials:${NC}" - echo " API Key: $API_KEY" - echo " Access Token: $ACCESS_TOKEN" - if [[ -n "$TEMPLATE_ID" ]]; then - echo " Template ID: $TEMPLATE_ID" - fi echo "" - echo -e "${BLUE}Services:${NC}" - echo " API: http://localhost:80" - echo " Client-Proxy: http://localhost:3002 (envd)" - echo " Orchestrator: localhost:5008 (gRPC)" + echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "${GREEN} E2B Lite Setup Complete!${NC}" + echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo "" - echo -e "${BLUE}Quick Start:${NC}" + echo -e "${BOLD}Next Steps:${NC}" echo "" echo " 1. Start all services:" - echo " ./scripts/services/start-all.sh" - echo "" - echo " 2. Or start in background:" - echo " ./scripts/services/start-all.sh --bg" - echo "" - echo " 3. Test with Python SDK:" - echo " python3 -m venv e2b_venv" - echo " source e2b_venv/bin/activate" - echo " pip install e2b" - echo " python scripts/test-e2b-lite.py" + echo -e " ${DIM}./scripts/services/start-all.sh${NC}" echo "" - echo -e "${BLUE}Python SDK Usage:${NC}" + echo " 2. Test with Python SDK:" + echo -e " ${DIM}pip install e2b${NC}" + echo -e " ${DIM}python scripts/test-e2b-lite.py${NC}" echo "" - echo " from e2b import Sandbox" + echo -e "${BOLD}Quick Reference:${NC}" echo "" - echo " sandbox = Sandbox.create(" - echo " template=\"${TEMPLATE_ID:-}\"," - echo " api_url=\"http://localhost:80\"," - echo " sandbox_url=\"http://localhost:3002\"," - echo " api_key=\"$API_KEY\"," - echo " )" - echo " result = sandbox.commands.run(\"echo hello\", user=\"root\")" - echo " print(result.stdout)" - echo " sandbox.kill()" - echo "" - echo -e "${BLUE}Environment Variables:${NC}" - echo "" - echo " # For SDK" - echo " export E2B_API_KEY=\"$API_KEY\"" - echo "" - echo " # For CLI (npx @e2b/cli)" - echo " export E2B_API_URL=\"http://localhost:80\"" - echo " export E2B_SANDBOX_URL=\"http://localhost:3002\"" - echo " export E2B_ACCESS_TOKEN=\"$ACCESS_TOKEN\"" - echo " export E2B_API_KEY=\"$API_KEY\"" - echo "" - echo -e "${BLUE}CLI Usage:${NC}" + echo " API URL: http://localhost:80" + echo " Sandbox URL: http://localhost:3002" + echo " API Key: $API_KEY" + if [[ -n "$TEMPLATE_ID" ]]; then + echo " Template ID: $TEMPLATE_ID" + fi echo "" - echo " # Set environment variables first (see above), then:" - echo " npx @e2b/cli template list" - echo " npx @e2b/cli sandbox list" + echo -e "${DIM}For detailed usage, see E2B-LITE-DESIGN.md${NC}" echo "" - echo "For more details, see E2B-LITE-DESIGN.md" } # ============================================================================= # Main execution # ============================================================================= +# Count total steps for progress display +TOTAL_STEPS=10 +CURRENT_STEP=0 + +next_step() { + CURRENT_STEP=$((CURRENT_STEP + 1)) + print_step "$CURRENT_STEP" "$TOTAL_STEPS" "$1" +} + +# Handle --check-req mode +if [[ "$CHECK_REQ_ONLY" == "true" ]]; then + check_prerequisites + exit 0 +fi + check_sudo # Install dependencies if requested if [[ "$INSTALL_DEPS" == "true" ]]; then + next_step "Installing dependencies" install_dependencies fi # Exit if deps-only mode if [[ "$DEPS_ONLY" == "true" ]]; then - echo "Dependencies installed. Run again without --deps-only for full setup." + echo "" + echo -e "${GREEN}Dependencies installed.${NC}" + echo "Run again without --deps-only for full setup." exit 0 fi # Run all setup steps +next_step "Checking prerequisites" check_prerequisites + +next_step "Setting up system" setup_kernel_modules setup_hugepages create_directories download_artifacts -# Build or download binaries +next_step "Building binaries" if [[ "$USE_PREBUILT" == "true" ]]; then download_prebuilt_binaries else build_binaries fi +next_step "Setting up npm dependencies" setup_npm_dependencies + +next_step "Starting infrastructure" start_infrastructure + +next_step "Configuring database" run_migrations seed_database + +next_step "Building template" build_base_template + +next_step "Creating scripts" create_start_scripts create_test_script From d0e34c5f62ed471ed0c1ebf9e3bfb55f3538590b Mon Sep 17 00:00:00 2001 From: Adam Docolomansky Date: Tue, 3 Feb 2026 17:57:03 +0100 Subject: [PATCH 07/11] feat(e2b-lite): improve installation UX with clean progress UI - Add --verbose flag for detailed output (apt, build logs, etc.) - Add --check-req flag to check requirements without installing - Add clean progress UI with spinners in non-verbose mode - Consolidate output to 1-2 lines per step (8 steps total) - Fix spinner race condition leaving artifacts on screen - Change start-all.sh to run in background by default (--fg for foreground) - Update summary with copy-paste export commands and CLI usage - Add "Coming Soon" section to E2B-LITE-DESIGN.md - Update documentation to reflect new options Co-Authored-By: Claude Opus 4.5 --- E2B-LITE-DESIGN.md | 14 +- scripts/e2b-lite-setup.sh | 522 +++++++++++++++++++++++++------------- 2 files changed, 352 insertions(+), 184 deletions(-) diff --git a/E2B-LITE-DESIGN.md b/E2B-LITE-DESIGN.md index f227cf4e79..62ae6d1bf7 100644 --- a/E2B-LITE-DESIGN.md +++ b/E2B-LITE-DESIGN.md @@ -52,11 +52,11 @@ cd infra ### 2. Start Services ```bash -# Start all services (foreground, Ctrl+C to stop) +# Start all services in background (default) ./scripts/services/start-all.sh -# Or start in background -./scripts/services/start-all.sh --bg +# Or start in foreground (Ctrl+C to stop) +./scripts/services/start-all.sh --fg ``` ### 3. Test @@ -205,8 +205,8 @@ Created by setup script in `scripts/services/`: | Script | Description | |--------|-------------| -| `start-all.sh` | Start all services (foreground) | -| `start-all.sh --bg` | Start all services (background) | +| `start-all.sh` | Start all services (background) | +| `start-all.sh --fg` | Start all services (foreground) | | `start-api.sh` | Start API server only | | `start-orchestrator.sh` | Start orchestrator only | | `start-client-proxy.sh` | Start client-proxy only | @@ -214,8 +214,8 @@ Created by setup script in `scripts/services/`: ### Manual Service Management ```bash -# Start in background -./scripts/services/start-all.sh --bg +# Start in background (default) +./scripts/services/start-all.sh # Check status ps aux | grep -E 'bin/(api|orchestrator|client-proxy)' diff --git a/scripts/e2b-lite-setup.sh b/scripts/e2b-lite-setup.sh index 3d3db88ffe..7942716b19 100755 --- a/scripts/e2b-lite-setup.sh +++ b/scripts/e2b-lite-setup.sh @@ -135,11 +135,12 @@ start_spinner() { return fi - # Save cursor position and print message + # Print initial message printf " %s " "$msg" - # Start spinner in background + # Start spinner in background (don't disown so wait works) ( + trap 'exit 0' TERM INT i=0 while true; do printf "\r %s ${SPINNER_CHARS:i++%${#SPINNER_CHARS}:1} " "$msg" @@ -147,22 +148,28 @@ start_spinner() { done ) & SPINNER_PID=$! - disown $SPINNER_PID 2>/dev/null || true } -# Stop spinner with success -stop_spinner_success() { - local msg="${1:-}" +# Helper to kill spinner cleanly +kill_spinner() { if [[ -n "$SPINNER_PID" ]]; then kill $SPINNER_PID 2>/dev/null || true wait $SPINNER_PID 2>/dev/null || true SPINNER_PID="" + # Clear the entire line + printf "\r\033[2K" fi +} + +# Stop spinner with success +stop_spinner_success() { + local msg="${1:-}" + kill_spinner if [[ "$VERBOSE" != "true" ]]; then if [[ -n "$msg" ]]; then - printf "\r ${GREEN}✓${NC} %s\n" "$msg" + printf " ${GREEN}✓${NC} %s\n" "$msg" else - printf "\r ${GREEN}✓${NC}\n" + printf " ${GREEN}✓${NC}\n" fi fi } @@ -170,16 +177,12 @@ stop_spinner_success() { # Stop spinner with failure stop_spinner_fail() { local msg="${1:-}" - if [[ -n "$SPINNER_PID" ]]; then - kill $SPINNER_PID 2>/dev/null || true - wait $SPINNER_PID 2>/dev/null || true - SPINNER_PID="" - fi + kill_spinner if [[ "$VERBOSE" != "true" ]]; then if [[ -n "$msg" ]]; then - printf "\r ${RED}✗${NC} %s\n" "$msg" + printf " ${RED}✗${NC} %s\n" "$msg" else - printf "\r ${RED}✗${NC}\n" + printf " ${RED}✗${NC}\n" fi fi } @@ -187,16 +190,12 @@ stop_spinner_fail() { # Stop spinner with warning stop_spinner_warn() { local msg="${1:-}" - if [[ -n "$SPINNER_PID" ]]; then - kill $SPINNER_PID 2>/dev/null || true - wait $SPINNER_PID 2>/dev/null || true - SPINNER_PID="" - fi + kill_spinner if [[ "$VERBOSE" != "true" ]]; then if [[ -n "$msg" ]]; then - printf "\r ${YELLOW}!${NC} %s\n" "$msg" + printf " ${YELLOW}!${NC} %s\n" "$msg" else - printf "\r ${YELLOW}!${NC}\n" + printf " ${YELLOW}!${NC}\n" fi fi } @@ -251,6 +250,8 @@ print_err() { cleanup_spinner() { if [[ -n "$SPINNER_PID" ]]; then kill $SPINNER_PID 2>/dev/null || true + wait $SPINNER_PID 2>/dev/null || true + printf "\r\033[2K" fi } trap cleanup_spinner EXIT @@ -298,10 +299,20 @@ install_dependencies() { exit 1 fi + local installed_items=() + + if [[ "$VERBOSE" != "true" ]]; then + start_spinner "Installing dependencies" + fi + # Update package list - start_spinner "Updating package list" + if [[ "$VERBOSE" == "true" ]]; then + start_spinner "Updating package list" + fi if run_cmd sudo apt-get update -qq; then - stop_spinner_success "Package list updated" + if [[ "$VERBOSE" == "true" ]]; then + stop_spinner_success "Package list updated" + fi else stop_spinner_fail "Failed to update package list" exit 1 @@ -309,7 +320,9 @@ install_dependencies() { # Install Docker if not present if ! command -v docker &> /dev/null; then - start_spinner "Installing Docker" + if [[ "$VERBOSE" == "true" ]]; then + start_spinner "Installing Docker" + fi if run_cmd sudo install -m 0755 -d /etc/apt/keyrings && \ run_cmd sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc && \ run_cmd sudo chmod a+r /etc/apt/keyrings/docker.asc; then @@ -322,7 +335,10 @@ Signed-By: /etc/apt/keyrings/docker.asc" | sudo tee /etc/apt/sources.list.d/dock if run_cmd sudo apt-get update -qq && \ run_cmd sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin; then - stop_spinner_success "Docker installed" + if [[ "$VERBOSE" == "true" ]]; then + stop_spinner_success "Docker installed" + fi + installed_items+=("Docker") else stop_spinner_fail "Failed to install Docker" exit 1 @@ -331,45 +347,63 @@ Signed-By: /etc/apt/keyrings/docker.asc" | sudo tee /etc/apt/sources.list.d/dock stop_spinner_fail "Failed to setup Docker repository" exit 1 fi - else - print_ok "Docker already installed" fi # Install Go if not present if ! command -v go &> /dev/null; then - start_spinner "Installing Go" + if [[ "$VERBOSE" == "true" ]]; then + start_spinner "Installing Go" + fi if run_cmd sudo snap install --classic go; then - stop_spinner_success "Go installed" + if [[ "$VERBOSE" == "true" ]]; then + stop_spinner_success "Go installed" + fi + installed_items+=("Go") else stop_spinner_fail "Failed to install Go" exit 1 fi - else - print_ok "Go already installed ($(go version | grep -oP '\d+\.\d+' | head -1))" fi # Install Node.js if not present (needed for template building) if ! command -v node &> /dev/null; then - start_spinner "Installing Node.js" + if [[ "$VERBOSE" == "true" ]]; then + start_spinner "Installing Node.js" + fi if curl -fsSL https://deb.nodesource.com/setup_22.x 2>/dev/null | sudo -E bash - > /dev/null 2>&1 && \ run_cmd sudo apt-get install -y nodejs; then - stop_spinner_success "Node.js installed" + if [[ "$VERBOSE" == "true" ]]; then + stop_spinner_success "Node.js installed" + fi + installed_items+=("Node.js") else stop_spinner_fail "Failed to install Node.js" exit 1 fi - else - print_ok "Node.js already installed ($(node --version))" fi # Install build tools and other dependencies - start_spinner "Installing build tools" + if [[ "$VERBOSE" == "true" ]]; then + start_spinner "Installing build tools" + fi if run_cmd sudo apt-get install -y build-essential make ca-certificates curl git net-tools; then - stop_spinner_success "Build tools installed" + if [[ "$VERBOSE" == "true" ]]; then + stop_spinner_success "Build tools installed" + fi + installed_items+=("build-tools") else stop_spinner_fail "Failed to install build tools" exit 1 fi + + # Summary for non-verbose mode + if [[ "$VERBOSE" != "true" ]]; then + if [[ ${#installed_items[@]} -gt 0 ]]; then + stop_spinner_success "Installed: ${installed_items[*]}" + else + stop_spinner_success "All dependencies already installed" + fi + fi } # ----------------------------------------------------------------------------- @@ -378,82 +412,116 @@ Signed-By: /etc/apt/keyrings/docker.asc" | sudo tee /etc/apt/sources.list.d/dock check_prerequisites() { local has_errors=false local has_warnings=false + local warning_msg="" # Check OS if [[ "$OSTYPE" != "linux-gnu"* ]]; then - print_err "E2B Lite requires Linux. Detected: $OSTYPE" + if [[ "$VERBOSE" == "true" ]] || [[ "$CHECK_REQ_ONLY" == "true" ]]; then + print_err "E2B Lite requires Linux. Detected: $OSTYPE" + fi has_errors=true else - print_ok "Linux detected" + if [[ "$VERBOSE" == "true" ]] || [[ "$CHECK_REQ_ONLY" == "true" ]]; then + print_ok "Linux detected" + fi fi # Check kernel version KERNEL_MAJOR=$(uname -r | cut -d. -f1) KERNEL_MINOR=$(uname -r | cut -d. -f2) if [ "$KERNEL_MAJOR" -lt 5 ] || ([ "$KERNEL_MAJOR" -eq 5 ] && [ "$KERNEL_MINOR" -lt 10 ]); then - print_err "Kernel $(uname -r) is too old. Minimum required: 5.10" + if [[ "$VERBOSE" == "true" ]] || [[ "$CHECK_REQ_ONLY" == "true" ]]; then + print_err "Kernel $(uname -r) is too old. Minimum required: 5.10" + fi has_errors=true else - print_ok "Kernel $(uname -r)" + if [[ "$VERBOSE" == "true" ]] || [[ "$CHECK_REQ_ONLY" == "true" ]]; then + print_ok "Kernel $(uname -r)" + fi fi # Check for kernel 6.8+ (needed for building templates) if [ "$KERNEL_MAJOR" -lt 6 ] || ([ "$KERNEL_MAJOR" -eq 6 ] && [ "$KERNEL_MINOR" -lt 8 ]); then - print_warn "Kernel < 6.8: You can run sandboxes but cannot build custom templates" + if [[ "$VERBOSE" == "true" ]] || [[ "$CHECK_REQ_ONLY" == "true" ]]; then + print_warn "Kernel < 6.8: You can run sandboxes but cannot build custom templates" + fi BUILD_TEMPLATE=false has_warnings=true + warning_msg="kernel < 6.8" else - print_ok "Kernel 6.8+: Full support (running + building templates)" + if [[ "$VERBOSE" == "true" ]] || [[ "$CHECK_REQ_ONLY" == "true" ]]; then + print_ok "Kernel 6.8+: Full support (running + building templates)" + fi fi # Check KVM if [[ ! -e /dev/kvm ]]; then - print_err "/dev/kvm not found. KVM is required" - echo " Enable KVM: sudo modprobe kvm_intel (or kvm_amd)" + if [[ "$VERBOSE" == "true" ]] || [[ "$CHECK_REQ_ONLY" == "true" ]]; then + print_err "/dev/kvm not found. KVM is required" + echo " Enable KVM: sudo modprobe kvm_intel (or kvm_amd)" + fi has_errors=true elif [[ ! -r /dev/kvm ]] || [[ ! -w /dev/kvm ]]; then - print_warn "No read/write access to /dev/kvm" - echo " Fix: sudo usermod -aG kvm \$USER && newgrp kvm" + if [[ "$VERBOSE" == "true" ]] || [[ "$CHECK_REQ_ONLY" == "true" ]]; then + print_warn "No read/write access to /dev/kvm" + echo " Fix: sudo usermod -aG kvm \$USER && newgrp kvm" + fi has_warnings=true else - print_ok "KVM available" + if [[ "$VERBOSE" == "true" ]] || [[ "$CHECK_REQ_ONLY" == "true" ]]; then + print_ok "KVM available" + fi fi # Check Docker if ! command -v docker &> /dev/null; then - if [[ "$CHECK_REQ_ONLY" == "true" ]]; then - print_err "Docker not found" - else - print_err "Docker not found. Run with --deps-only first or install manually" + if [[ "$VERBOSE" == "true" ]] || [[ "$CHECK_REQ_ONLY" == "true" ]]; then + if [[ "$CHECK_REQ_ONLY" == "true" ]]; then + print_err "Docker not found" + else + print_err "Docker not found. Run with --deps-only first or install manually" + fi fi has_errors=true elif ! docker info &> /dev/null 2>&1; then - print_err "Docker daemon not running or no permission" - echo " Start Docker: sudo systemctl start docker" - echo " Or add to group: sudo usermod -aG docker \$USER && newgrp docker" + if [[ "$VERBOSE" == "true" ]] || [[ "$CHECK_REQ_ONLY" == "true" ]]; then + print_err "Docker daemon not running or no permission" + echo " Start Docker: sudo systemctl start docker" + echo " Or add to group: sudo usermod -aG docker \$USER && newgrp docker" + fi has_errors=true else - print_ok "Docker available" + if [[ "$VERBOSE" == "true" ]] || [[ "$CHECK_REQ_ONLY" == "true" ]]; then + print_ok "Docker available" + fi fi # Check Go if ! command -v go &> /dev/null; then - if [[ "$CHECK_REQ_ONLY" == "true" ]]; then - print_err "Go not found" - else - print_err "Go not found. Run with --deps-only first or install manually" + if [[ "$VERBOSE" == "true" ]] || [[ "$CHECK_REQ_ONLY" == "true" ]]; then + if [[ "$CHECK_REQ_ONLY" == "true" ]]; then + print_err "Go not found" + else + print_err "Go not found. Run with --deps-only first or install manually" + fi fi has_errors=true else GO_VERSION=$(go version | grep -oP '\d+\.\d+' | head -1) - print_ok "Go $GO_VERSION" + if [[ "$VERBOSE" == "true" ]] || [[ "$CHECK_REQ_ONLY" == "true" ]]; then + print_ok "Go $GO_VERSION" + fi fi # Check Node.js (optional, for template building) if command -v node &> /dev/null; then - print_ok "Node.js $(node --version)" + if [[ "$VERBOSE" == "true" ]] || [[ "$CHECK_REQ_ONLY" == "true" ]]; then + print_ok "Node.js $(node --version)" + fi else - print_warn "Node.js not found (needed for template building)" + if [[ "$VERBOSE" == "true" ]] || [[ "$CHECK_REQ_ONLY" == "true" ]]; then + print_warn "Node.js not found (needed for template building)" + fi has_warnings=true fi @@ -473,6 +541,18 @@ check_prerequisites() { fi fi + # Summary for non-verbose mode + if [[ "$VERBOSE" != "true" ]]; then + if [[ "$has_errors" == "true" ]]; then + print_err "Prerequisites check failed" + exit 1 + elif [[ "$has_warnings" == "true" ]]; then + print_warn "System ready ($warning_msg)" + else + print_ok "All prerequisites met" + fi + fi + # For non-check-req mode, exit on errors if [[ "$has_errors" == "true" ]]; then exit 1 @@ -480,75 +560,93 @@ check_prerequisites() { } # ----------------------------------------------------------------------------- -# Setup kernel modules +# Setup system (kernel modules, hugepages, directories) # ----------------------------------------------------------------------------- -setup_kernel_modules() { +setup_system() { + local config_items=() + + if [[ "$VERBOSE" != "true" ]]; then + start_spinner "Configuring system" + fi + # NBD module with sufficient devices if ! lsmod | grep -q "^nbd "; then - start_spinner "Loading NBD module" + if [[ "$VERBOSE" == "true" ]]; then + start_spinner "Loading NBD module" + fi if sudo modprobe nbd nbds_max=128 2>/dev/null; then - stop_spinner_success "NBD module loaded (nbds_max=128)" + if [[ "$VERBOSE" == "true" ]]; then + stop_spinner_success "NBD module loaded (nbds_max=128)" + fi + config_items+=("NBD") else - stop_spinner_warn "Failed to load NBD module (may need to install)" + if [[ "$VERBOSE" == "true" ]]; then + stop_spinner_warn "Failed to load NBD module (may need to install)" + fi fi else # Check if we have enough NBD devices NBD_COUNT=$(ls -1 /dev/nbd* 2>/dev/null | wc -l) if [ "$NBD_COUNT" -lt 64 ]; then - start_spinner "Reloading NBD module with more devices" + if [[ "$VERBOSE" == "true" ]]; then + start_spinner "Reloading NBD module with more devices" + fi sudo rmmod nbd 2>/dev/null || true sudo modprobe nbd nbds_max=128 - stop_spinner_success "NBD module reloaded" - else - print_ok "NBD module loaded" + if [[ "$VERBOSE" == "true" ]]; then + stop_spinner_success "NBD module reloaded" + fi + config_items+=("NBD") fi fi # TUN module if ! lsmod | grep -q "^tun "; then - start_spinner "Loading TUN module" + if [[ "$VERBOSE" == "true" ]]; then + start_spinner "Loading TUN module" + fi if sudo modprobe tun 2>/dev/null; then - stop_spinner_success "TUN module loaded" + if [[ "$VERBOSE" == "true" ]]; then + stop_spinner_success "TUN module loaded" + fi + config_items+=("TUN") else - stop_spinner_warn "Failed to load TUN module" + if [[ "$VERBOSE" == "true" ]]; then + stop_spinner_warn "Failed to load TUN module" + fi fi - else - print_ok "TUN module loaded" fi -} -# ----------------------------------------------------------------------------- -# Setup HugePages -# ----------------------------------------------------------------------------- -setup_hugepages() { + # HugePages HUGEPAGES_TOTAL=$(cat /proc/sys/vm/nr_hugepages 2>/dev/null || echo 0) HUGEPAGES_NEEDED=2048 # 2048 * 2MB = 4GB reserved for HugePages if [ "$HUGEPAGES_TOTAL" -lt "$HUGEPAGES_NEEDED" ]; then - start_spinner "Allocating HugePages (4GB)" + if [[ "$VERBOSE" == "true" ]]; then + start_spinner "Allocating HugePages (4GB)" + fi if echo "$HUGEPAGES_NEEDED" | sudo tee /proc/sys/vm/nr_hugepages > /dev/null 2>&1; then - # Make it persistent across reboots if ! grep -q "vm.nr_hugepages" /etc/sysctl.conf 2>/dev/null; then echo "vm.nr_hugepages=$HUGEPAGES_NEEDED" | sudo tee -a /etc/sysctl.conf > /dev/null - stop_spinner_success "HugePages configured (persistent)" else sudo sed -i "s/vm.nr_hugepages=.*/vm.nr_hugepages=$HUGEPAGES_NEEDED/" /etc/sysctl.conf - stop_spinner_success "HugePages allocated" fi + if [[ "$VERBOSE" == "true" ]]; then + stop_spinner_success "HugePages configured (persistent)" + fi + config_items+=("HugePages") else - stop_spinner_warn "Failed to allocate HugePages" - echo " Manual fix: echo $HUGEPAGES_NEEDED | sudo tee /proc/sys/vm/nr_hugepages" + if [[ "$VERBOSE" == "true" ]]; then + stop_spinner_warn "Failed to allocate HugePages" + echo " Manual fix: echo $HUGEPAGES_NEEDED | sudo tee /proc/sys/vm/nr_hugepages" + fi fi - else - print_ok "HugePages already configured ($HUGEPAGES_TOTAL pages)" fi -} -# ----------------------------------------------------------------------------- -# Create directory structure -# ----------------------------------------------------------------------------- -create_directories() { - start_spinner "Creating directory structure" + # Create directories + if [[ "$VERBOSE" == "true" ]]; then + start_spinner "Creating directory structure" + fi mkdir -p "$FC_VERSIONS_DIR/$FC_VERSION" mkdir -p "$KERNELS_DIR/$KERNEL_VERSION" @@ -565,7 +663,15 @@ create_directories() { mkdir -p "$ORCHESTRATOR_DIR/tmp/orchestrator/build" mkdir -p "$ORCHESTRATOR_DIR/tmp/orchestrator/build-templates" - stop_spinner_success "Directories created" + if [[ "$VERBOSE" == "true" ]]; then + stop_spinner_success "Directories created" + fi + config_items+=("directories") + + # Summary for non-verbose mode + if [[ "$VERBOSE" != "true" ]]; then + stop_spinner_success "System configured (${config_items[*]})" + fi } # ----------------------------------------------------------------------------- @@ -726,16 +832,26 @@ download_prebuilt_binaries() { # Build all binaries # ----------------------------------------------------------------------------- build_binaries() { + local built_items=() + local existing_items=() + + if [[ "$VERBOSE" != "true" ]]; then + start_spinner "Building binaries" + fi + # Build envd - MUST use regular build (not build-debug) for static linking - # The debug build uses CGO_ENABLED=1 which produces a dynamically linked binary - # that won't work inside the minimal Firecracker VM ENVD_PATH="$ENVD_DIR/bin/envd" if [[ -f "$ENVD_PATH" ]]; then - print_ok "envd already built" + existing_items+=("envd") else - start_spinner "Building envd" + if [[ "$VERBOSE" == "true" ]]; then + start_spinner "Building envd" + fi if run_cmd make -C "$ENVD_DIR" build; then - stop_spinner_success "envd built" + if [[ "$VERBOSE" == "true" ]]; then + stop_spinner_success "envd built" + fi + built_items+=("envd") else stop_spinner_fail "Failed to build envd" exit 1 @@ -745,11 +861,16 @@ build_binaries() { # Build API API_PATH="$API_DIR/bin/api" if [[ -f "$API_PATH" ]]; then - print_ok "API already built" + existing_items+=("API") else - start_spinner "Building API" + if [[ "$VERBOSE" == "true" ]]; then + start_spinner "Building API" + fi if run_cmd make -C "$API_DIR" build; then - stop_spinner_success "API built" + if [[ "$VERBOSE" == "true" ]]; then + stop_spinner_success "API built" + fi + built_items+=("API") else stop_spinner_fail "Failed to build API" exit 1 @@ -759,11 +880,16 @@ build_binaries() { # Build Orchestrator ORCH_PATH="$ORCHESTRATOR_DIR/bin/orchestrator" if [[ -f "$ORCH_PATH" ]]; then - print_ok "Orchestrator already built" + existing_items+=("Orchestrator") else - start_spinner "Building Orchestrator" + if [[ "$VERBOSE" == "true" ]]; then + start_spinner "Building Orchestrator" + fi if run_cmd make -C "$ORCHESTRATOR_DIR" build-debug; then - stop_spinner_success "Orchestrator built" + if [[ "$VERBOSE" == "true" ]]; then + stop_spinner_success "Orchestrator built" + fi + built_items+=("Orchestrator") else stop_spinner_fail "Failed to build Orchestrator" exit 1 @@ -773,16 +899,30 @@ build_binaries() { # Build Client-Proxy PROXY_PATH="$CLIENT_PROXY_DIR/bin/client-proxy" if [[ -f "$PROXY_PATH" ]]; then - print_ok "Client-Proxy already built" + existing_items+=("Client-Proxy") else - start_spinner "Building Client-Proxy" + if [[ "$VERBOSE" == "true" ]]; then + start_spinner "Building Client-Proxy" + fi if run_cmd make -C "$CLIENT_PROXY_DIR" build; then - stop_spinner_success "Client-Proxy built" + if [[ "$VERBOSE" == "true" ]]; then + stop_spinner_success "Client-Proxy built" + fi + built_items+=("Client-Proxy") else stop_spinner_fail "Failed to build Client-Proxy" exit 1 fi fi + + # Summary for non-verbose mode + if [[ "$VERBOSE" != "true" ]]; then + if [[ ${#built_items[@]} -gt 0 ]]; then + stop_spinner_success "Built: ${built_items[*]}" + else + stop_spinner_success "All binaries already built" + fi + fi } # ----------------------------------------------------------------------------- @@ -790,7 +930,9 @@ build_binaries() { # ----------------------------------------------------------------------------- setup_npm_dependencies() { if ! command -v npm &> /dev/null; then - print_warn "Skipping npm dependencies (npm not found)" + if [[ "$VERBOSE" == "true" ]]; then + print_warn "Skipping npm dependencies (npm not found)" + fi return fi @@ -803,7 +945,11 @@ setup_npm_dependencies() { stop_spinner_warn "Failed to install npm packages" fi else - print_ok "npm dependencies ready" + if [[ "$VERBOSE" == "true" ]]; then + print_ok "npm dependencies ready" + else + print_ok "npm dependencies ready" + fi fi fi } @@ -812,7 +958,6 @@ setup_npm_dependencies() { # Start Docker infrastructure # ----------------------------------------------------------------------------- start_infrastructure() { - # Use full docker-compose with all services COMPOSE_FILE="$LOCAL_DEV_DIR/docker-compose.yaml" if [[ ! -f "$COMPOSE_FILE" ]]; then @@ -820,20 +965,18 @@ start_infrastructure() { exit 1 fi - # Check if containers are already running if docker ps --format '{{.Names}}' | grep -q "local-dev-postgres"; then print_ok "Infrastructure already running" else - start_spinner "Starting Docker containers" + start_spinner "Starting Docker infrastructure" if run_cmd docker compose -f "$COMPOSE_FILE" up -d; then - # Wait for PostgreSQL to be ready for i in {1..30}; do if docker exec local-dev-postgres-1 pg_isready -U postgres > /dev/null 2>&1; then break fi sleep 1 done - stop_spinner_success "Infrastructure started" + stop_spinner_success "Docker infrastructure started" else stop_spinner_fail "Failed to start infrastructure" exit 1 @@ -842,36 +985,61 @@ start_infrastructure() { } # ----------------------------------------------------------------------------- -# Run database migrations +# Configure database (migrations + seeding) # ----------------------------------------------------------------------------- -run_migrations() { +configure_database() { export POSTGRES_CONNECTION_STRING="postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable" - start_spinner "Running database migrations" + local db_actions=() + + if [[ "$VERBOSE" != "true" ]]; then + start_spinner "Configuring database" + fi + + # Run migrations + if [[ "$VERBOSE" == "true" ]]; then + start_spinner "Running database migrations" + fi if run_cmd make -C "$REPO_ROOT/packages/db" migrate-local; then - stop_spinner_success "Migrations applied" + if [[ "$VERBOSE" == "true" ]]; then + stop_spinner_success "Migrations applied" + fi + db_actions+=("migrations") else - stop_spinner_warn "Migrations may have failed or already applied" + if [[ "$VERBOSE" == "true" ]]; then + stop_spinner_warn "Migrations may have failed or already applied" + fi fi -} - -# ----------------------------------------------------------------------------- -# Seed database -# ----------------------------------------------------------------------------- -seed_database() { - export POSTGRES_CONNECTION_STRING="postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable" - # Check if already seeded by looking for the team + # Check if already seeded TEAM_EXISTS=$(docker exec local-dev-postgres-1 psql -U postgres -tAc "SELECT COUNT(*) FROM teams WHERE id='0b8a3ded-4489-4722-afd1-1d82e64ec2d5';" 2>/dev/null || echo "0") if [[ "$TEAM_EXISTS" == "1" ]]; then - print_ok "Database already seeded" + if [[ "$VERBOSE" == "true" ]]; then + print_ok "Database already seeded" + fi else - start_spinner "Seeding database" + if [[ "$VERBOSE" == "true" ]]; then + start_spinner "Seeding database" + fi if (cd "$LOCAL_DEV_DIR" && run_cmd go run seed-local-database.go); then - stop_spinner_success "Database seeded" + if [[ "$VERBOSE" == "true" ]]; then + stop_spinner_success "Database seeded" + fi + db_actions+=("seeded") + else + if [[ "$VERBOSE" == "true" ]]; then + stop_spinner_warn "Seeding may have failed" + fi + fi + fi + + # Summary for non-verbose mode + if [[ "$VERBOSE" != "true" ]]; then + if [[ ${#db_actions[@]} -gt 0 ]]; then + stop_spinner_success "Database configured (${db_actions[*]})" else - stop_spinner_warn "Seeding may have failed" + stop_spinner_success "Database already configured" fi fi } @@ -1080,38 +1248,21 @@ SCRIPT # Start all E2B Lite services # # Usage: -# ./scripts/services/start-all.sh # Start in foreground (Ctrl+C to stop) -# ./scripts/services/start-all.sh --bg # Start in background +# ./scripts/services/start-all.sh # Start in background (default) +# ./scripts/services/start-all.sh --fg # Start in foreground (Ctrl+C to stop) # cd "$(dirname "$0")/../.." || exit 1 REPO_ROOT="$(pwd)" -BACKGROUND=false -if [[ "$1" == "--bg" ]]; then - BACKGROUND=true +FOREGROUND=false +if [[ "$1" == "--fg" ]]; then + FOREGROUND=true fi echo "Starting E2B Lite services..." -if [[ "$BACKGROUND" == "true" ]]; then - # Background mode - nohup "$REPO_ROOT/scripts/services/start-api.sh" > /tmp/e2b-api.log 2>&1 & - echo " API started (PID: $!, log: /tmp/e2b-api.log)" - - nohup "$REPO_ROOT/scripts/services/start-orchestrator.sh" > /tmp/e2b-orchestrator.log 2>&1 & - echo " Orchestrator started (PID: $!, log: /tmp/e2b-orchestrator.log)" - - sleep 2 # Wait for orchestrator to initialize - - nohup "$REPO_ROOT/scripts/services/start-client-proxy.sh" > /tmp/e2b-client-proxy.log 2>&1 & - echo " Client-Proxy started (PID: $!, log: /tmp/e2b-client-proxy.log)" - - echo "" - echo "All services started in background." - echo "Check status: ps aux | grep -E 'api|orchestrator|client-proxy'" - echo "Stop all: pkill -f 'bin/(api|orchestrator|client-proxy)'" -else +if [[ "$FOREGROUND" == "true" ]]; then # Foreground mode with trap cleanup() { echo "" @@ -1140,6 +1291,23 @@ else echo "" echo "All services running. Press Ctrl+C to stop." wait +else + # Background mode (default) + nohup "$REPO_ROOT/scripts/services/start-api.sh" > /tmp/e2b-api.log 2>&1 & + echo " API started (PID: $!, log: /tmp/e2b-api.log)" + + nohup "$REPO_ROOT/scripts/services/start-orchestrator.sh" > /tmp/e2b-orchestrator.log 2>&1 & + echo " Orchestrator started (PID: $!, log: /tmp/e2b-orchestrator.log)" + + sleep 2 # Wait for orchestrator to initialize + + nohup "$REPO_ROOT/scripts/services/start-client-proxy.sh" > /tmp/e2b-client-proxy.log 2>&1 & + echo " Client-Proxy started (PID: $!, log: /tmp/e2b-client-proxy.log)" + + echo "" + echo "All services started in background." + echo "Check status: ps aux | grep -E 'api|orchestrator|client-proxy'" + echo "Stop all: pkill -f 'bin/(api|orchestrator|client-proxy)'" fi SCRIPT chmod +x "$REPO_ROOT/scripts/services/start-all.sh" @@ -1300,21 +1468,26 @@ print_summary() { echo -e "${BOLD}Next Steps:${NC}" echo "" echo " 1. Start all services:" - echo -e " ${DIM}./scripts/services/start-all.sh${NC}" + echo " ./scripts/services/start-all.sh" echo "" echo " 2. Test with Python SDK:" - echo -e " ${DIM}pip install e2b${NC}" - echo -e " ${DIM}python scripts/test-e2b-lite.py${NC}" + echo " pip install e2b && python scripts/test-e2b-lite.py" echo "" - echo -e "${BOLD}Quick Reference:${NC}" + echo -e "${BOLD}Environment Variables (copy & paste):${NC}" echo "" - echo " API URL: http://localhost:80" - echo " Sandbox URL: http://localhost:3002" - echo " API Key: $API_KEY" + echo "export E2B_API_KEY=\"$API_KEY\"" + echo "export E2B_API_URL=\"http://localhost:80\"" + echo "export E2B_SANDBOX_URL=\"http://localhost:3002\"" + echo "export E2B_ACCESS_TOKEN=\"$ACCESS_TOKEN\"" if [[ -n "$TEMPLATE_ID" ]]; then - echo " Template ID: $TEMPLATE_ID" + echo "export E2B_TEMPLATE_ID=\"$TEMPLATE_ID\"" fi echo "" + echo -e "${BOLD}CLI Usage:${NC}" + echo "" + echo " npx @e2b/cli template list" + echo " npx @e2b/cli sandbox list" + echo "" echo -e "${DIM}For detailed usage, see E2B-LITE-DESIGN.md${NC}" echo "" } @@ -1324,7 +1497,7 @@ print_summary() { # ============================================================================= # Count total steps for progress display -TOTAL_STEPS=10 +TOTAL_STEPS=8 CURRENT_STEP=0 next_step() { @@ -1358,10 +1531,8 @@ fi next_step "Checking prerequisites" check_prerequisites -next_step "Setting up system" -setup_kernel_modules -setup_hugepages -create_directories +next_step "Configuring system" +setup_system download_artifacts next_step "Building binaries" @@ -1371,15 +1542,12 @@ else build_binaries fi -next_step "Setting up npm dependencies" -setup_npm_dependencies - next_step "Starting infrastructure" start_infrastructure +setup_npm_dependencies next_step "Configuring database" -run_migrations -seed_database +configure_database next_step "Building template" build_base_template From d93a172bab583b9055c13927f8d3e85975695164 Mon Sep 17 00:00:00 2001 From: Adam Docolomansky Date: Tue, 3 Feb 2026 18:44:38 +0100 Subject: [PATCH 08/11] fix: remove duplicate npm condition branch Co-Authored-By: Claude Opus 4.5 --- scripts/e2b-lite-setup.sh | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/scripts/e2b-lite-setup.sh b/scripts/e2b-lite-setup.sh index 7942716b19..aa3a6dd51b 100755 --- a/scripts/e2b-lite-setup.sh +++ b/scripts/e2b-lite-setup.sh @@ -945,11 +945,7 @@ setup_npm_dependencies() { stop_spinner_warn "Failed to install npm packages" fi else - if [[ "$VERBOSE" == "true" ]]; then - print_ok "npm dependencies ready" - else - print_ok "npm dependencies ready" - fi + print_ok "npm dependencies ready" fi fi } From adcc333f0c73b4d448fe2473c6d8920e04237709 Mon Sep 17 00:00:00 2001 From: Adam Docolomansky Date: Tue, 3 Feb 2026 18:50:08 +0100 Subject: [PATCH 09/11] chore: remove untested e2b-lite binaries workflow The workflow for pre-building E2B Lite binaries is not yet tested. The --prebuilt flag in the setup script will gracefully fall back to building from source when releases don't exist. Co-Authored-By: Claude Opus 4.5 --- .github/workflows/build-lite-binaries.yml | 102 ---------------------- 1 file changed, 102 deletions(-) delete mode 100644 .github/workflows/build-lite-binaries.yml diff --git a/.github/workflows/build-lite-binaries.yml b/.github/workflows/build-lite-binaries.yml deleted file mode 100644 index ea9d00d2cb..0000000000 --- a/.github/workflows/build-lite-binaries.yml +++ /dev/null @@ -1,102 +0,0 @@ -name: Build E2B Lite Binaries - -on: - release: - types: [published] - workflow_dispatch: - inputs: - tag: - description: 'Tag to build (e.g., v1.0.0)' - required: true - type: string - -permissions: - contents: write - -jobs: - build: - name: Build E2B Lite Binaries - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - ref: ${{ github.event.inputs.tag || github.ref }} - - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: '1.24' - cache: true - - - name: Get version - id: version - run: | - if [ -n "${{ github.event.inputs.tag }}" ]; then - VERSION="${{ github.event.inputs.tag }}" - else - VERSION="${GITHUB_REF#refs/tags/}" - fi - echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "Building version: $VERSION" - - - name: Get commit SHA - id: commit - run: | - COMMIT_SHA=$(git rev-parse --short HEAD) - echo "sha=$COMMIT_SHA" >> $GITHUB_OUTPUT - echo "Commit SHA: $COMMIT_SHA" - - - name: Build API binary - run: | - cd packages/api - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ../../dist/api-linux-amd64 \ - -ldflags "-X=main.commitSHA=${{ steps.commit.outputs.sha }}" . - chmod +x ../../dist/api-linux-amd64 - - - name: Build Orchestrator binary - run: | - cd packages/orchestrator - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ../../dist/orchestrator-linux-amd64 \ - -ldflags "-X=main.commitSHA=${{ steps.commit.outputs.sha }}" . - chmod +x ../../dist/orchestrator-linux-amd64 - - - name: Build Client-Proxy binary - run: | - cd packages/client-proxy - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ../../dist/client-proxy-linux-amd64 \ - -ldflags "-X=main.commitSHA=${{ steps.commit.outputs.sha }}" . - chmod +x ../../dist/client-proxy-linux-amd64 - - - name: Build Envd binary - run: | - cd packages/envd - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ../../dist/envd-linux-amd64 \ - -ldflags "-X=main.commitSHA=${{ steps.commit.outputs.sha }}" . - chmod +x ../../dist/envd-linux-amd64 - - - name: Create checksums - run: | - cd dist - sha256sum * > checksums.txt - cat checksums.txt - - - name: Upload binaries to release - if: github.event_name == 'release' - uses: softprops/action-gh-release@v2 - with: - files: | - dist/api-linux-amd64 - dist/orchestrator-linux-amd64 - dist/client-proxy-linux-amd64 - dist/envd-linux-amd64 - dist/checksums.txt - - - name: Upload artifacts (workflow_dispatch) - if: github.event_name == 'workflow_dispatch' - uses: actions/upload-artifact@v4 - with: - name: e2b-lite-binaries-${{ steps.version.outputs.version }} - path: dist/ - retention-days: 30 From 31a552beebd9154f20218768b999223815221a9e Mon Sep 17 00:00:00 2001 From: Adam Docolomansky Date: Tue, 3 Feb 2026 19:02:35 +0100 Subject: [PATCH 10/11] chore: add venv prompt to summary --- scripts/e2b-lite-setup.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/e2b-lite-setup.sh b/scripts/e2b-lite-setup.sh index aa3a6dd51b..e5bc68269e 100755 --- a/scripts/e2b-lite-setup.sh +++ b/scripts/e2b-lite-setup.sh @@ -1467,6 +1467,8 @@ print_summary() { echo " ./scripts/services/start-all.sh" echo "" echo " 2. Test with Python SDK:" + echo " python3 -m venv e2b_lite_venv" + echo " source e2b_lite_venv/bin/activate" echo " pip install e2b && python scripts/test-e2b-lite.py" echo "" echo -e "${BOLD}Environment Variables (copy & paste):${NC}" From b29fca0ff36576f5971c4d5c06d543313b9913e2 Mon Sep 17 00:00:00 2001 From: Adam Docolomansky Date: Thu, 5 Feb 2026 19:10:27 +0100 Subject: [PATCH 11/11] fix: total_disk_size discrepancy --- scripts/e2b-lite-setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/e2b-lite-setup.sh b/scripts/e2b-lite-setup.sh index e5bc68269e..24306bab56 100755 --- a/scripts/e2b-lite-setup.sh +++ b/scripts/e2b-lite-setup.sh @@ -1133,7 +1133,7 @@ register_template() { # Note: total_disk_size_mb and envd_version are required by the API docker exec local-dev-postgres-1 psql -U postgres -c " INSERT INTO public.env_builds (id, env_id, status, vcpu, ram_mb, free_disk_size_mb, total_disk_size_mb, kernel_version, firecracker_version, envd_version, cluster_node_id, created_at, updated_at, finished_at) - VALUES ('$BUILD_ID', '$TEMPLATE_ID', 'uploaded', 2, 512, 1024, 512, '$KERNEL_VERSION', '$FC_VERSION', '0.2.0', 'local', NOW(), NOW(), NOW()) + VALUES ('$BUILD_ID', '$TEMPLATE_ID', 'uploaded', 2, 512, 1024, 1024, '$KERNEL_VERSION', '$FC_VERSION', '0.2.0', 'local', NOW(), NOW(), NOW()) ON CONFLICT (id) DO NOTHING; " > /dev/null 2>&1