Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,20 @@ jobs:
go build -ldflags="-s -w"
-o forgevm-agent-linux-amd64 ./cmd/forgevm-agent

- name: Build forgevm (arm64)
run: >
CGO_ENABLED=0 GOOS=linux GOARCH=arm64
go build -ldflags="-s -w -X main.version=${{ github.ref_name }}"
-o forgevm-linux-arm64 ./cmd/forgevm

- name: Build forgevm-agent (arm64)
run: >
CGO_ENABLED=0 GOOS=linux GOARCH=arm64
go build -ldflags="-s -w"
-o forgevm-agent-linux-arm64 ./cmd/forgevm-agent

- name: Generate checksums
run: sha256sum forgevm-linux-amd64 forgevm-agent-linux-amd64 > checksums.txt
run: sha256sum forgevm-linux-amd64 forgevm-agent-linux-amd64 forgevm-linux-arm64 forgevm-agent-linux-arm64 > checksums.txt

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
Expand All @@ -56,4 +68,6 @@ jobs:
files: |
forgevm-linux-amd64
forgevm-agent-linux-amd64
forgevm-linux-arm64
forgevm-agent-linux-arm64
checksums.txt
18 changes: 16 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: build build-agent test lint clean serve dev release-build
.PHONY: build build-agent build-android build-agent-arm64 test lint clean serve dev release-build release-build-all

VERSION ?= $(shell git describe --tags --always 2>/dev/null || echo dev)

Expand All @@ -10,6 +10,14 @@ build:
build-agent:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o bin/forgevm-agent ./cmd/forgevm-agent

# Build for Android/ARM64 (static)
build-android:
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-s -w -X main.version=$(VERSION)" -o dist/forgevm-linux-arm64 ./cmd/forgevm

# Build the guest agent (static, linux/arm64)
build-agent-arm64:
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o bin/forgevm-agent-arm64 ./cmd/forgevm-agent

# Build everything
build-all: build build-agent

Expand Down Expand Up @@ -38,8 +46,14 @@ clean:
lint:
go vet ./...

# Build static release binaries + checksums
# Build static release binaries + checksums (amd64 only)
release-build:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w -X main.version=$(VERSION)" -o forgevm-linux-amd64 ./cmd/forgevm
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o forgevm-agent-linux-amd64 ./cmd/forgevm-agent
sha256sum forgevm-linux-amd64 forgevm-agent-linux-amd64 > checksums.txt

# Build release binaries for all architectures (amd64 + arm64)
release-build-all: release-build
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-s -w -X main.version=$(VERSION)" -o forgevm-linux-arm64 ./cmd/forgevm
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o forgevm-agent-linux-arm64 ./cmd/forgevm-agent
sha256sum forgevm-linux-amd64 forgevm-agent-linux-amd64 forgevm-linux-arm64 forgevm-agent-linux-arm64 > checksums.txt
32 changes: 32 additions & 0 deletions cmd/forgevm/cmd_serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,38 @@ func runServe() error {
Msg("docker provider registered")
}
}
if cfg.Providers.PRoot.Enabled {
timeout, _ := time.ParseDuration(cfg.Providers.PRoot.DefaultTimeout)
if timeout == 0 {
timeout = 60 * time.Second
}
proot := providers.NewPRootProvider(providers.PRootProviderConfig{
RootfsPath: cfg.Providers.PRoot.RootfsPath,
PRootBinary: cfg.Providers.PRoot.PRootBinary,
WorkspaceBase: cfg.Providers.PRoot.WorkspaceBase,
DefaultTimeout: timeout,
MaxSandboxes: cfg.Providers.PRoot.MaxSandboxes,
MaxMemoryMB: cfg.Providers.PRoot.MaxMemoryMB,
MaxDiskMB: cfg.Providers.PRoot.MaxDiskMB,
Languages: cfg.Providers.PRoot.Languages,
}, logger)
registry.Register(proot)
logger.Info().
Str("rootfs", cfg.Providers.PRoot.RootfsPath).
Str("binary", cfg.Providers.PRoot.PRootBinary).
Msg("proot provider registered")
}

// Auto-detect provider if set to "auto"
if cfg.Providers.Default == "auto" {
env := config.DetectEnvironment()
cfg.Providers.Default = config.DefaultProviderForEnv(env)
logger.Info().
Str("environment", env.String()).
Str("provider", cfg.Providers.Default).
Msg("auto-detected default provider")
}

if err := registry.SetDefault(cfg.Providers.Default); err != nil {
logger.Warn().Err(err).Msg("setting default provider")
}
Expand Down
161 changes: 141 additions & 20 deletions images/python-sandbox/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,28 +1,149 @@
FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive

# ── System packages + CLI tools ──
RUN apt-get update && apt-get install -y \
python3 \
python3-pip \
python3-venv \
curl \
wget \
git \
zip \
unzip \
jq \
vim \
nano \
htop \
net-tools \
iputils-ping \
build-essential \
# Core
python3 python3-pip python3-venv python3-dev \
curl wget git zip unzip jq vim nano htop \
net-tools iputils-ping build-essential pkg-config \
# PDF CLI tools
poppler-utils qpdf pdftk-java pandoc \
# OCR
tesseract-ocr tesseract-ocr-eng \
# Image/Media
ffmpeg imagemagick inkscape \
# LibreOffice
libreoffice-calc libreoffice-writer libreoffice-impress libreoffice-draw \
# Java (for tabula-py, pdftk)
default-jre-headless \
# C/C++
gcc g++ \
# SQLite
sqlite3 libsqlite3-dev \
# Graphviz
graphviz libgraphviz-dev \
# Image libs (for Pillow, Wand, sharp)
libmagickwand-dev libfreetype-dev libjpeg-dev libpng-dev libtiff-dev \
libwebp-dev zlib1g-dev libffi-dev libssl-dev \
# Chromium (for Playwright)
chromium-browser \
# Sound
libasound2-dev portaudio19-dev \
&& rm -rf /var/lib/apt/lists/*

# ── Rust/Cargo ──
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"

# ── Node.js 22.x ──
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
&& apt-get install -y nodejs \
&& rm -rf /var/lib/apt/lists/*

RUN pip3 install --no-cache-dir --break-system-packages \
# ── Python packages (--ignore-installed to override deb-managed packages) ──
RUN pip3 install --no-cache-dir --break-system-packages --ignore-installed \
# Data / Science
pandas==3.0.1 \
scipy \
scikit-learn \
scikit-image \
# Visualization
matplotlib \
seaborn \
# Excel / Office
openpyxl==3.1.5 \
xlsxwriter \
python-docx \
python-pptx \
reportlab \
pandas \
numpy
python-pptx==1.0.2 \
odfpy \
# PDF
pypdf==5.9.0 \
pypdfium2==5.6.0 \
pdfplumber==0.11.9 \
pdfminer.six \
pdf2image==1.17.0 \
pdfkit \
pikepdf \
img2pdf \
reportlab==4.4.10 \
camelot-py[cv] \
tabula-py \
# Web / Scraping
flask \
requests \
beautifulsoup4 \
playwright \
grip \
# CV / ML
opencv-python-headless \
mediapipe \
onnxruntime \
flatbuffers \
# Image
Pillow==12.1.1 \
Wand \
imageio \
tifffile \
freetype-py \
# OCR
pytesseract==0.3.13 \
# Markdown / Docs
markitdown==0.1.5 \
markdown \
mkdocs \
mkdocs-material \
mistune \
# Utils
lxml \
sympy \
networkx \
graphviz \
psutil \
cryptography \
sounddevice \
# Misc
Jinja2 \
PyYAML \
click \
tabulate

# Install Playwright browsers
RUN playwright install chromium

# ── Node.js global packages ──
RUN npm install -g \
# Documents
docx@9.6.1 \
pptxgenjs@4.0.1 \
pdf-lib \
pdfjs-dist \
# Mermaid
@mermaid-js/mermaid-cli \
# Markdown
markdown-pdf \
marked \
markdown-toc \
markdownlint-cli \
markdownlint-cli2 \
remark-cli \
# React
react@19 \
react-dom@19 \
react-icons \
# Image
sharp \
# Browser automation
playwright \
# TypeScript
typescript@6 \
ts-node \
tsx \
# Graph
graphviz

# Make global npm packages resolvable by require()
ENV NODE_PATH=/usr/lib/node_modules

WORKDIR /workspace
6 changes: 6 additions & 0 deletions internal/api/routes/providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@ func (p *ProviderRoutes) Detail(w http.ResponseWriter, r *http.Request) {
cfg["type"] = "firecracker"
case *providers.MockProvider:
cfg["type"] = "mock"
case *providers.PRootProvider:
c := v.ProviderConfig()
cfg["rootfs_path"] = c.RootfsPath
cfg["proot_binary"] = c.PRootBinary
cfg["workspace_base"] = c.WorkspaceBase
cfg["type"] = "proot"
default:
cfg["type"] = "other"
}
Expand Down
23 changes: 23 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,19 @@ type ProvidersConfig struct {
Docker DockerConfig `mapstructure:"docker"`
E2B E2BConfig `mapstructure:"e2b"`
Custom CustomConfig `mapstructure:"custom"`
PRoot PRootConfig `mapstructure:"proot"`
}

type PRootConfig struct {
Enabled bool `mapstructure:"enabled"`
RootfsPath string `mapstructure:"rootfs_path"`
PRootBinary string `mapstructure:"proot_binary"`
WorkspaceBase string `mapstructure:"workspace_base"`
DefaultTimeout string `mapstructure:"default_timeout"`
MaxSandboxes int `mapstructure:"max_sandboxes"`
MaxMemoryMB int `mapstructure:"max_memory_mb"`
MaxDiskMB int `mapstructure:"max_disk_mb"`
Languages []string `mapstructure:"languages"`
}

type DockerConfig struct {
Expand Down Expand Up @@ -163,6 +176,16 @@ func setDefaults(v *viper.Viper) {
v.SetDefault("providers.docker.pool_security.workspace_permissions", true)
v.SetDefault("providers.docker.pool_security.hidepid", false)

v.SetDefault("providers.proot.enabled", false)
v.SetDefault("providers.proot.proot_binary", "proot")
v.SetDefault("providers.proot.rootfs_path", "/var/lib/forgevm/rootfs")
v.SetDefault("providers.proot.workspace_base", "/var/lib/forgevm/workspaces")
v.SetDefault("providers.proot.default_timeout", "60s")
v.SetDefault("providers.proot.max_sandboxes", 10)
v.SetDefault("providers.proot.max_memory_mb", 512)
v.SetDefault("providers.proot.max_disk_mb", 1024)
v.SetDefault("providers.proot.languages", []string{"python3", "node", "bash"})

v.SetDefault("defaults.ttl", "30m")
v.SetDefault("defaults.image", "alpine:latest")
v.SetDefault("defaults.memory_mb", 1024)
Expand Down
Loading
Loading