# zlayer-agent

Container runtime agent for ZLayer, providing lifecycle management for containers, WebAssembly modules, and jobs.

## Features

The agent supports multiple container runtimes through feature flags:

- **youki** (default on Linux): Linux-native container runtime using libcontainer. Preferred on Linux for performance (no daemon overhead).
- **seatbelt** (default on macOS): Native macOS sandbox runtime using Seatbelt (`sandbox-exec`). Preferred for native Darwin workloads.
- **libkrun-vm** (optional, macOS): Lightweight VM-based runtime using libkrun, used when full Linux compatibility is required on macOS.
- **hcs-runtime** (default on Windows): Native Windows containers via the Host Compute Service (HCS) APIs.
- **wsl** (default on Windows for Linux images): Delegates Linux-image workloads to a WSL2-hosted agent.
- **docker**: Cross-platform fallback using the Docker daemon. Works on Linux, Windows, and macOS.
- **wasm**: WebAssembly runtime using wasmtime for executing WASM workloads with WASI support.

### Windows feature flags

The canonical Windows release build enables both HCS and WSL2 delegation:

```bash
cargo build --release -p zlayer --no-default-features --features hcs-runtime,wsl
```

On Windows, the agent uses a `CompositeRuntime` (`src/runtimes/composite.rs`) that inspects the OCI image manifest's OS field and dispatches each container to the appropriate backend: `HcsRuntime` for Windows images and `Wsl2DelegateRuntime` for Linux images. Docker remains available as a fallback when neither native runtime is suitable.

## Installation

Add to your `Cargo.toml`:

```toml
[dependencies]
zlayer-agent = { path = "../zlayer-agent" }

# Or with specific features
zlayer-agent = { path = "../zlayer-agent", features = ["docker", "wasm"] }
```

## Runtime Selection

### Automatic Selection

Use `RuntimeConfig::Auto` to automatically select the best available runtime:

```rust
use zlayer_agent::{RuntimeConfig, create_runtime};

let runtime = create_runtime(RuntimeConfig::Auto).await?;
```

Selection logic:
- **Linux**: Prefers youki (libcontainer) if available, falls back to Docker.
- **macOS**: Prefers the Seatbelt sandbox runtime for native Darwin workloads, uses the libkrun VM runtime when full Linux compatibility is required, and falls back to Docker.
- **Windows**: Uses the `CompositeRuntime` to dispatch per image: `HcsRuntime` for Windows containers and `Wsl2DelegateRuntime` (requires the `wsl` feature) for Linux containers. Docker remains available as a fallback. Activated via `--features hcs-runtime,wsl`.
- **WASM artifacts**: Use `RuntimeConfig::Wasm` explicitly (auto-detected from the OCI manifest where applicable).

### Runtime matrix

| Runtime | Platform | Default? |
|---------|----------|----------|
| youki | Linux | Yes (Linux) |
| seatbelt | macOS | Yes (macOS) |
| libkrun VM | macOS | Optional |
| HCS | Windows | Yes (Windows containers) |
| WSL2 delegate | Windows + `wsl` feature | Yes (Linux containers on Windows) |
| composite | Windows | Auto-selected |
| docker | All | Fallback |
| wasm | All | Auto-detected from OCI manifest |

### Explicit Selection

```rust
use zlayer_agent::{RuntimeConfig, YoukiConfig, create_runtime};

// Use youki runtime
let youki = create_runtime(RuntimeConfig::Youki(YoukiConfig::default())).await?;

// Use Docker runtime
#[cfg(feature = "docker")]
let docker = create_runtime(RuntimeConfig::Docker).await?;

// Use WASM runtime
#[cfg(feature = "wasm")]
{
    use zlayer_agent::WasmConfig;
    let wasm = create_runtime(RuntimeConfig::Wasm(WasmConfig::default())).await?;
}
```

## WebAssembly Runtime Support

The `wasm` feature enables running WebAssembly modules with WASI support as an alternative to traditional containers.

### Enabling WASM Support

```toml
[dependencies]
zlayer-agent = { path = "../zlayer-agent", features = ["wasm"] }
```

### WASM Runtime Configuration

```rust
use zlayer_agent::{WasmConfig, WasmRuntime};
use std::path::PathBuf;
use std::time::Duration;

let config = WasmConfig {
    // Directory for caching WASM modules
    cache_dir: PathBuf::from("/var/lib/zlayer/wasm"),

    // Enable epoch-based interruption for timeouts
    enable_epochs: true,

    // Instructions before yield check (for cooperative scheduling)
    epoch_deadline: 1_000_000,

    // Maximum execution time for WASM modules
    max_execution_time: Duration::from_secs(3600),
};

let runtime = WasmRuntime::new(config).await?;
```

### WASI Support

The WASM runtime supports WASI Preview 1 (wasip1) with the following capabilities:

- Environment variables
- Command-line arguments
- Stdout/stderr output
- Exit codes via `proc_exit`

### WASM vs Container Runtime Comparison

| Feature | Container (youki/seatbelt/libkrun/HCS/WSL2/Docker) | WASM |
|---------|----------------------------------------------------|------|
| Isolation | Process/cgroup, Seatbelt sandbox, VM, or HCS silo | In-process sandbox |
| Startup time | 100ms+ | <10ms |
| Memory overhead | Higher (full rootfs) | Lower (just module) |
| `exec` support | Yes | No |
| Cgroup stats | Yes (where the host supports it) | No (stub values) |
| Filesystem mounts | Yes | Limited |
| Network | Full | Limited |
| Platform | Linux (youki), macOS (seatbelt/libkrun), Windows (HCS + WSL2 delegate), Docker fallback on all | Cross-platform |

### WASM Limitations

- **No `exec` support**: WASM modules are single-process and don't support executing additional commands
- **No cgroup stats**: WASM runs in-process without kernel isolation, so resource stats return stub values
- **Limited filesystem**: Currently only environment variables are passed; filesystem mounts are not yet supported
- **Single entry point**: WASM modules must export `_start` or `main` function

### Example: Running a WASM Workload

```rust
use zlayer_agent::{ContainerId, Runtime, WasmConfig, WasmRuntime};
use zlayer_spec::ServiceSpec;
use std::time::Duration;

async fn run_wasm_module(spec: &ServiceSpec) -> Result<i32, Box<dyn std::error::Error>> {
    // Create WASM runtime
    let runtime = WasmRuntime::new(WasmConfig::default()).await?;

    let id = ContainerId {
        service: "my-wasm-service".to_string(),
        replica: 1,
    };

    // Pull WASM artifact from registry
    runtime.pull_image(&spec.image.name.to_string()).await?;

    // Create instance
    runtime.create_container(&id, spec).await?;

    // Start execution
    runtime.start_container(&id).await?;

    // Wait for completion
    let exit_code = runtime.wait_container(&id).await?;

    // Cleanup
    runtime.remove_container(&id).await?;

    Ok(exit_code)
}
```

## Service Manager

The `ServiceManager` provides higher-level orchestration for services:

```rust
use zlayer_agent::{ServiceManager, RuntimeConfig};
use zlayer_spec::DeploymentSpec;

// Load deployment spec
let spec: DeploymentSpec = serde_yaml::from_str(yaml)?;

// Create service manager with auto-selected runtime
let manager = ServiceManager::new(RuntimeConfig::Auto).await?;

// Deploy services
for (name, service_spec) in &spec.services {
    manager.deploy_service(name, service_spec).await?;
}
```

## Testing

### Running All Tests

```bash
# Unit tests
cargo test -p zlayer-agent

# With Docker runtime tests
cargo test -p zlayer-agent --features docker

# With WASM runtime tests
cargo test -p zlayer-agent --features wasm

# All features
cargo test -p zlayer-agent --all-features
```

### WASM-Specific Tests

```bash
cargo test -p zlayer-agent --features wasm -- --nocapture
```

The WASM tests use inline WAT (WebAssembly Text Format) modules compiled at test time, so no external WASM binaries are required.

## Module Structure

- `runtime.rs`: Abstract `Runtime` trait and `ContainerState` types
- `runtimes/`: Runtime implementations
  - `youki.rs`: Linux-native runtime using libcontainer
  - `seatbelt.rs`: macOS Seatbelt sandbox runtime
  - `libkrun.rs`: macOS libkrun VM runtime for full Linux compatibility (feature: `libkrun-vm`)
  - `hcs.rs`: Windows Host Compute Service runtime for Windows containers (feature: `hcs-runtime`)
  - `wsl2_delegate.rs`: Windows runtime that delegates Linux containers to a WSL2-hosted agent (feature: `wsl`)
  - `composite.rs`: `CompositeRuntime` that inspects the OCI image OS and dispatches to `HcsRuntime` or `Wsl2DelegateRuntime` on Windows
  - `docker.rs`: Docker daemon runtime (feature: `docker`)
  - `wasm.rs`: WebAssembly runtime using wasmtime (feature: `wasm`)
- `service.rs`: `ServiceManager` for higher-level orchestration
- `container_supervisor.rs`: Container health monitoring and restart logic
- `job.rs`: Job execution support for run-to-completion workloads
- `cron_scheduler.rs`: Cron-based job scheduling
- `health.rs`: Health check implementations
- `init.rs`: Init action orchestration
- `env.rs`: Environment variable resolution
- `bundle.rs`: OCI bundle creation for youki

## Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `ZLAYER_WASM_CACHE_DIR` | WASM module cache directory | `/var/lib/zlayer/wasm` |
| `ZLAYER_RUNTIME_DIR` | Runtime state directory | `/var/run/zlayer` |
| `ZLAYER_ROOT_DIR` | Root directory for bundles | `/var/lib/zlayer` |

## License

Apache-2.0
