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
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ AENV_INSTALL_SUDO ?= sudo
TEST_SCRIPTS_DIR := ./scripts/tests

# Runner for tests that require AENV's network and namespace capabilities.
CAPABILITY_RUNNER := CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="$(CURDIR)/scripts/run-with-capabilities.sh"
# The target triple is derived from uname -m so the runner works on both
# x86_64 and aarch64 hosts without manual configuration.
HOST_TRIPLE := $(shell uname -m)-unknown-linux-gnu
HOST_TRIPLE_UPPER := $(shell echo $(HOST_TRIPLE) | tr '[:lower:]-' '[:upper:]_')
CAPABILITY_RUNNER := CARGO_TARGET_$(HOST_TRIPLE_UPPER)_RUNNER="$(CURDIR)/scripts/run-with-capabilities.sh"
AENV_TEST_STATE_ID ?= $(if $(GITHUB_RUN_ID),$(GITHUB_RUN_ID)-$(GITHUB_RUN_ATTEMPT),local-$$(id -u))
AENV_TEST_STATE_DIR ?= /tmp/aenv-test-$(AENV_TEST_STATE_ID)
AENV_TEST_DEPS_PATH ?= $(if $(AENV_DEPS_PATH),$(AENV_DEPS_PATH),/var/lib/aenv/deps)
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ AgentENV (AENV) is a platform for running agent environments at scale, powering

## 📋 Prerequisites

- **Linux kernel 6.8+**; the install script additionally requires **Ubuntu 24.04** (see *Quick Start* below for installation options)
- **Linux kernel 6.8+** on x86_64 or aarch64; the install script requires **Ubuntu 24.04 (x86_64)**
- ARM (aarch64) servers are supported via source build — see [ARM Quick Start](docs/src/getting-started/quickstart-arm.md)
- `/dev/kvm` access for Firecracker microVM execution

If your server does not support standard KVM, see the [PVM deployment guide](https://kvcache-ai.github.io/AgentENV/dev/deployment/pvm.html) before installing.
Expand Down Expand Up @@ -73,6 +74,11 @@ docker run -d --privileged -v /dev:/dev -p 8000:8000 ghcr.io/kvcache-ai/aenv-ser

The server is accessible at `http://127.0.0.1:8000` by default.

*Option C — ARM (aarch64) source build*

ARM servers (AWS Graviton, Ampere Altra, Kunpeng) require building from source.
See the [ARM Quick Start](docs/src/getting-started/quickstart-arm.md) for full instructions.

**2. Install the aenv CLI** *(skip if you used Option A in step 1)*

Install separately if you used the Docker method above, or if you are running
Expand Down Expand Up @@ -104,6 +110,7 @@ aenv start ubuntu # starts a sandbox and attaches an interactive shel

For Docker Compose / Kubernetes cluster deployment and build-from-source instructions,
see 📖 [Deployment](https://kvcache-ai.github.io/AgentENV/latest/deployment/manual-compile.html).
For ARM (aarch64) source-build installation, see 📖 [ARM Quick Start](docs/src/getting-started/quickstart-arm.md).

---

Expand Down
11 changes: 11 additions & 0 deletions config/deps_manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
version = "1.15.1-patch-v1"
url = "https://pub-4ee15c400f554ab7a9eac3f5bc8f53de.r2.dev/firecracker-{version}-{arch}.tgz"

# ARM (aarch64) uses the kvcache-ai/firecracker release because the patched
# R2 build is only published for x86_64.
[firecracker.kvm.arch.aarch64]
version = "1.15.1-patch-v1"
url = "https://github.com/kvcache-ai/firecracker/releases/download/aenv-deps/firecracker-{version}-{arch}.tgz"

[firecracker.pvm]
version = "v1.17.0-next.1"
url = "https://github.com/kvcache-ai/firecracker-next/releases/download/{version}/firecracker-next-{version}-{arch}.tgz"
Expand All @@ -10,6 +16,11 @@ url = "https://github.com/kvcache-ai/firecracker-next/releases/download/{version
version = "vmlinux-6.1.175"
url = "https://pub-4ee15c400f554ab7a9eac3f5bc8f53de.r2.dev/{version}"

# ARM kernel is published in the same kvcache-ai/firecracker release.
[kernel.kvm.arch.aarch64]
version = "vmlinux-6.1.175"
url = "https://github.com/kvcache-ai/firecracker/releases/download/aenv-deps/{version}-{arch}"

[kernel.pvm]
version = "6.12.33-pvm"
url = "https://github.com/kvcache-ai/linux/releases/download/pvm-kernel-6.12.33/vmlinux-guest-6.12.33-pvm"
Expand Down
3 changes: 2 additions & 1 deletion docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
# Getting Started

- [Overview](./getting-started/overview.md)
- [Quick Start](./getting-started/quickstart.md)
- [Quick Start (x86_64)](./getting-started/quickstart.md)
- [Quick Start (ARM)](./getting-started/quickstart-arm.md)
- [On-Demand Loading](./getting-started/on-demand-loading.md)
- [aenv CLI Reference](./getting-started/aenv-cli.md)

Expand Down
174 changes: 174 additions & 0 deletions docs/src/getting-started/quickstart-arm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# ARM (aarch64) Quick Start

Build and run AgentENV from source on ARM servers (AWS Graviton, Ampere Altra,
Kunpeng, etc.). Pre-built binaries are not yet published for aarch64; follow the
source-build steps below.

## Prerequisites

- Linux aarch64 host with KVM enabled (`/dev/kvm`)
- Rust toolchain (stable)
- Docker with Buildx (for building the tools drive)
- ~25 GB disk, ~8 GB RAM

### Ubuntu / Debian

```bash
sudo apt-get install -y \
gcc g++ make protobuf-compiler libprotobuf-dev \
clang libclang-dev dpkg jq e2fsprogs \
iptables iproute2 umoci zstd curl libaio1t64
```

### openEuler / CentOS / RHEL

```bash
sudo dnf install -y \
gcc gcc-c++ make protobuf-compiler protobuf-devel \
clang clang-devel dpkg jq e2fsprogs \
iptables iproute umoci zstd curl libaio
```

> **ublk**: enabled by default (overlaybd snapshots + on-demand loading).
> Works on Ubuntu/Debian ARM out of the box. openEuler kernels ship with
> `CONFIG_BLK_DEV_UBLK` disabled — if you need full functionality on
> openEuler, rebuild the host kernel with ublk enabled.

## 1. Install Rust

```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source "$HOME/.cargo/env"
```

If behind a firewall, download the script manually and use a mirror:

```bash
curl -o rustup-init.sh https://sh.rustup.rs
chmod +x rustup-init.sh
RUSTUP_UPDATE_ROOT=https://mirrors.aliyun.com/rustup/rustup \
RUSTUP_DIST_SERVER=https://mirrors.aliyun.com/rustup \
./rustup-init.sh -y
source "$HOME/.cargo/env"
```

## 2. Build the ARM tools drive

The pre-built tools drive image is currently amd64-only. Build the ARM variant:

```bash
cd tools-image
make build ARCH=arm64
# output: out/tools-0.1.0-arm64.ext4
cd ..
```

## 3. Build AgentENV

```bash
ulimit -n 65536
make release
```

The first build takes 20–40 minutes (`librocksdb-sys` compiles a large C++
codebase). On memory-constrained hosts use `CARGO_BUILD_JOBS=2 make release`.

## 4. Configure

Create an ARM configuration:

```bash
cp config/default.toml config/arm.toml
```

Edit `config/arm.toml`:

```toml
# Path to the ARM tools drive built in step 2.
[tools]
drive_path = "/path/to/AgentENV/tools-image/out/tools-0.1.0-arm64.ext4"
version = "0.1.0"

# ARM uses MMIO, not PCI.
[firecracker]
boot_args = "console=ttyS0 reboot=k panic=1 init=/init"
```

> **ublk (overlaybd storage)**: ublk is enabled by default and works on ARM hosts
> where the kernel has `CONFIG_BLK_DEV_UBLK` (Ubuntu ARM, Debian ARM, etc.).
> If your host kernel lacks ublk (e.g. openEuler default), disable it:
> ```toml
> [ublk]
> enabled = false
> ```

## 5. First-run setup

```bash
# One-time host provisioning (KVM group, network sysctls).
sudo ./target/release/server \
--config config/arm.toml \
--setup-host \
--runtime-user "$USER" \
--runtime-group "$(id -gn)"

# Download runtime dependencies (Firecracker aarch64, kernel, overlaybd, regctl).
./target/release/server --config config/arm.toml --setup-only
```

If the server cannot reach GitHub, download these manually and place them at the
expected paths:

| Asset | Destination |
|-------|-------------|
| [firecracker-1.15.1-patch-v1-aarch64.tgz](https://github.com/kvcache-ai/firecracker/releases/download/aenv-deps/firecracker-1.15.1-patch-v1-aarch64.tgz) | Extract to `/var/lib/aenv/deps/firecracker/1.15.1-patch-v1/firecracker` |
| [vmlinux-6.1.175-aarch64](https://github.com/kvcache-ai/firecracker/releases/download/aenv-deps/vmlinux-6.1.175-aarch64) | `/var/lib/aenv/deps/kernel/vmlinux-6.1.175/aarch64/vmlinux.bin` |
| [regctl-linux-arm64](https://github.com/regclient/regclient/releases/download/v0.11.5/regctl-linux-arm64) | `/var/lib/aenv/deps/regctl/v0.11.5/regctl` (chmod +x) |
| [overlaybd aarch64 .deb](https://github.com/containerd/overlaybd/releases/tag/v1.0.18) | Extract with `dpkg-deb -x`, copy `opt/overlaybd/` to `/var/lib/aenv/deps/overlaybd/` |

## 6. Start the server

```bash
sudo -E env API_ADDR=0.0.0.0:8000 \
./target/release/server --config config/arm.toml
```

Verify:

```bash
curl http://127.0.0.1:8000/health
# HTTP 204 No Content
```

## Troubleshooting

### `/dev/kvm` not accessible

```bash
sudo modprobe kvm
sudo usermod -aG kvm "$USER"
# re-login
ls -l /dev/kvm
```

### `unsupported Linux distribution`

openEuler / CentOS / RHEL hosts may need the distro-recognition patch
(see `src/setup/packages.rs`). Ensure you are on the `feat/arm-support` branch.

### Too many open files

```bash
ulimit -n 65536
```

### protoc errors

```bash
sudo dnf install -y protobuf-compiler protobuf-devel # RPM
sudo apt-get install -y protobuf-compiler # DEB
```

### Firecracker pool prime timed out

Harmless warning on first start. The pool warms lazily on first sandbox creation.
30 changes: 27 additions & 3 deletions src/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,28 @@ struct SetupDependencyManifest {
#[derive(Debug, Deserialize)]
struct ManifestDownload {
version: String,
/// Per-architecture overrides (see `config/deps_manifest.toml`).
/// When resolving a path for the current host architecture, the
/// override's `version` replaces the top-level default.
#[serde(default)]
arch: std::collections::HashMap<String, ArchManifestDownload>,
}

#[derive(Debug, Clone, Deserialize)]
struct ArchManifestDownload {
version: String,
}

impl ManifestDownload {
/// Return the effective version for the current host architecture.
/// Checks for an arch-specific override in the manifest first; falls
/// back to the top-level `version`.
fn version_for_host_arch(&self) -> &str {
self.arch
.get(std::env::consts::ARCH)
.map(|a| a.version.as_str())
.unwrap_or(&self.version)
}
}

#[derive(Debug, Deserialize)]
Expand Down Expand Up @@ -594,7 +616,7 @@ impl AppConfig {
.firecracker
.version
.as_deref()
.unwrap_or(&self.manifest_firecracker().version);
.unwrap_or_else(|| self.manifest_firecracker().version_for_host_arch());
self.deps_path
.join("firecracker")
.join(version)
Expand All @@ -608,10 +630,12 @@ impl AppConfig {
.kernel
.version
.as_deref()
.unwrap_or(&self.manifest_kernel().version);
.unwrap_or_else(|| self.manifest_kernel().version_for_host_arch());
let arch = std::env::consts::ARCH;
self.deps_path
.join("kernel")
.join(version)
.join(arch)
.join("vmlinux.bin")
Comment on lines +638 to 639

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[test · medium]
This changes the managed kernel path to include the architecture, but the existing managed_dependency_paths_remain_implicit_and_resolve_from_deps_path test in this file still expects kernel/kernel-test/vmlinux.bin. The test will now fail on every supported host. Update its expected path to include std::env::consts::ARCH (and ideally add coverage confirming the architecture-specific layout).

Comment on lines +638 to 639

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[test · medium]
This changes the resolved default path, but the existing managed_dependency_paths_remain_implicit_and_resolve_from_deps_path test still expects kernel/kernel-test/vmlinux.bin, so the test suite will fail on every architecture. Update that assertion to include std::env::consts::ARCH (and consider an architecture-aware path test).

Suggestion:

Suggested change
.join(arch)
.join("vmlinux.bin")
.join(std::env::consts::ARCH)
.join("vmlinux.bin")

Comment on lines 637 to 639

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[test · medium]
Adding the architecture directory changes every implicit kernel path, but this file's existing tests still assert kernel/<version>/vmlinux.bin (for example, managed_dependency_paths_remain_implicit_and_resolve_from_deps_path and both mode assertions in managed_dependency_paths_select_the_active_mode_versions). Those tests will now fail on all hosts. Update the expected paths to include std::env::consts::ARCH, and preferably add coverage for the architecture-specific manifest-version selection.

})
}
Expand Down Expand Up @@ -659,7 +683,7 @@ impl AppConfig {
.firecracker
.version
.as_deref()
.unwrap_or(&self.manifest_firecracker().version);
.unwrap_or_else(|| self.manifest_firecracker().version_for_host_arch());
let path = self
.deps_path
.join("firecracker")
Expand Down
Loading
Loading