Skip to content
Closed
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
24 changes: 24 additions & 0 deletions config/default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,30 @@ peer_discovery_refresh_interval_secs = 5
# Timeout for custom extension HTTP calls, in milliseconds.
# timeout_ms = 5000

[template_build]
# Build-context upload settings backing the E2B SDK's COPY support
# (GET /templates/{templateID}/files/{hash} plus the returned upload URL).
# Maximum accepted size for one uploaded build-context archive, in MiB.
# files_max_upload_mib = 1024
# Maximum size one build-context archive may expand to once decompressed, in MiB.
# files_max_context_mib = 4096
# Cap on the combined on-disk size of all build-context archives one build spec
# may reference, in MiB.
# files_max_build_context_mib = 4096
# How long an issued upload URL stays valid, in seconds.
# files_url_ttl_secs = 3600
# How long one build-context upload request may run before the server gives up
# and responds 408, in seconds.
# files_upload_timeout_secs = 300
# Optional external base URL used when building upload URLs. Defaults to
# "http://{Host header}" of the upload-link request, which matches
# direct-node and bundled-gateway deployments.
# Any TLS-terminated, gateway-fronted, or multi-hop deployment MUST set this to
# the external origin clients reach: the fallback derives the URL from the
# request Host header with plain http, and that upload URL carries a bearer
# token in its query string.
# public_base_url = "https://agentenv.example.com"

[cluster]
# Shared gRPC endpoint for cluster-level services such as scheduler heartbeat
# reporting and P2P peer discovery (e.g. "http://127.0.0.1:9090").
Expand Down
28 changes: 28 additions & 0 deletions docs/src/integration/e2b.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,34 @@ sandbox.beta_pause()
sandbox.kill()
```

### Template builds

The SDK's template builder works against AgentENV, including Dockerfiles with `COPY`:

```python
import asyncio
from e2b import AsyncTemplate, Template

template = Template(file_context_path=".").from_dockerfile(
"""
FROM ubuntu:24.04
COPY requirements.txt /opt/app/requirements.txt
RUN apt-get update && apt-get install -y python3
"""
)
asyncio.run(AsyncTemplate.build(template=template, alias="my-template"))
```

How `COPY` works: for each `COPY` instruction the SDK requests an upload link (`GET /templates/{templateID}/files/{hash}`), `PUT`s a tar archive of the matching context files to the returned bearer upload URL, and references the archive by `filesHash` when it starts the build. AgentENV stores the archives in the snapshot repository (shared across nodes) and extracts them inside the build sandbox.

Requirements and behavior notes:

- The base image must provide `/bin/bash` (already required for `RUN` steps) and `tar` for `COPY` steps.
- Copied files are owned by `root:root` like Docker's `COPY` default. `COPY --chown=user:group` resolves names against the image's own `/etc/passwd` and `/etc/group` and applies only to the files the copy creates; an unknown user fails the build.
- Write directory destinations with a trailing slash (`COPY app.py /opt/`). Docker's special case of copying a single file onto an existing directory named without a trailing slash (`COPY app.py /opt`) is not supported and fails the build with a clear error.
- Rebuilding an existing alias is not currently supported. Use a new alias (or remove the existing template first) until the alias subsystem is refactored.
- Any TLS-terminated, gateway-fronted, or multi-hop deployment must set `template_build.public_base_url` (or `AENV_TEMPLATE_BUILD_PUBLIC_BASE_URL`) to the external origin clients reach, for example `https://agentenv.example.com`. When it is unset, the upload URL is derived from the request `Host` header with plain `http`, and because that URL carries a bearer token in its query string the upload either fails against an HTTPS-only listener or sends the token and the whole build context in cleartext. Direct-node and bundled-gateway deployments can keep the default.

## E2B CLI

AgentENV is compatible with the E2B CLI, but we recommend using the
Expand Down
27 changes: 23 additions & 4 deletions scripts/tests/e2e/e2b_python_sdk_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import importlib.metadata
import os
import time
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Callable, TypeVar

from e2b import Sandbox, Template
Expand Down Expand Up @@ -56,21 +58,32 @@ def main() -> int:
workdir = f"/tmp/{template_name}"
derived_workdir = f"/tmp/{derived_template_name}"
build_marker = f"sdk-build-marker-{time.time_ns()}"
copy_marker = f"sdk-copy-marker-{time.time_ns()}"
add_marker = f"sdk-add-marker-{time.time_ns()}"
derived_marker = f"sdk-from-template-marker-{time.time_ns()}"
startup_marker = f"sdk-startup-marker-{time.time_ns()}"

build_context = TemporaryDirectory(prefix="agentenv-e2b-build-context-")
build_info = None
derived_build_info = None
sandbox = None
derived_sandbox = None

try:
context_path = Path(build_context.name)
(context_path / "copy-source.txt").write_text(copy_marker, encoding="utf-8")
(context_path / "add-source.txt").write_text(add_marker, encoding="utf-8")

log(f"building template {template_name} from {base_image}")
template = (
Template()
.from_image(base_image)
.run_cmd(f"mkdir -p {workdir}")
.set_workdir(workdir)
Template(file_context_path=context_path)
.from_dockerfile(
f"""FROM {base_image}
WORKDIR {workdir}
COPY copy-source.txt copied.txt
ADD add-source.txt added.txt
"""
)
.set_envs({"AENV_E2B_SDK_MARKER": build_marker})
.run_cmd("printf '%s' \"$AENV_E2B_SDK_MARKER\" > marker.txt")
.run_cmd("pwd > workdir.txt")
Expand Down Expand Up @@ -130,6 +143,8 @@ def read_build_artifacts():
"test -n \"$pid_line\"; "
"printf 'marker=' && cat marker.txt && "
"printf '\\nworkdir=' && cat workdir.txt && "
"printf '\\ncopy=' && cat copied.txt && "
"printf '\\nadd=' && cat added.txt && "
"printf '\\nstartup=' && cat startup-ready.txt && "
"printf '\\nprocess=%s' \"$pid_line\"",
cwd=workdir,
Expand All @@ -141,6 +156,8 @@ def read_build_artifacts():
require(result.exit_code == 0, f"command exited with {result.exit_code}")
require(f"marker={build_marker}" in result.stdout, "build marker file did not match")
require(f"workdir={workdir}" in result.stdout, "WORKDIR build step was not preserved")
require(f"copy={copy_marker}" in result.stdout, "COPY artifact did not match")
require(f"add={add_marker}" in result.stdout, "ADD artifact did not match")
require(
f"startup={startup_marker}" in result.stdout,
"startup ready marker file did not match",
Expand Down Expand Up @@ -304,6 +321,8 @@ def read_derived_build_artifacts():
except Exception as error: # noqa: BLE001 - best-effort cleanup.
log(f"cleanup template delete failed: {error}")

build_context.cleanup()


if __name__ == "__main__":
try:
Expand Down
4 changes: 2 additions & 2 deletions scripts/tests/e2e/suites/09_e2b_compat.sh
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,13 @@ if python3 -c 'import e2b' >/dev/null 2>&1; then
log "Running: e2b Python SDK compatibility (${sdk_script})"
if command -v timeout >/dev/null 2>&1; then
if sdk_output=$(timeout "${sdk_timeout}" python3 "$sdk_script" 2>&1); then
_pass "e2b Python SDK template build, startCmd/readyCmd, sandbox lifecycle, and commands"
_pass "e2b Python SDK fresh COPY/ADD template build and sandbox lifecycle"
else
log "e2b Python SDK output: ${sdk_output:0:1200}"
_fail "e2b Python SDK compatibility" "exit 0" "non-zero"
fi
elif sdk_output=$(python3 "$sdk_script" 2>&1); then
_pass "e2b Python SDK template build, startCmd/readyCmd, sandbox lifecycle, and commands"
_pass "e2b Python SDK fresh COPY/ADD template build and sandbox lifecycle"
else
log "e2b Python SDK output: ${sdk_output:0:1200}"
_fail "e2b Python SDK compatibility" "exit 0" "non-zero"
Expand Down
Loading
Loading