Declarative YAML Specs
Instead of writing Rust code, you can define runs as YAML files and execute them with voidbox run --file <path>. The parser requires api_version: v1; other values are rejected.
Kinds
Four top-level spec kinds are supported:
agent— a single agent run inside one isolated VM.pipeline— multiple agent boxes composed into explicit stages.workflow— command-oriented steps executed in a shared sandbox.sandbox— a bare VM definition used for interactive or shell-oriented flows.
Every spec shares the same top-level envelope: api_version, kind, name, an optional sandbox: block, an optional llm: block, and an optional observe: block for OTLP config. The rest of the structure is kind-specific.
Agent
A single agent spec declares the sandbox, LLM provider, prompt, and skills:
api_version: v1
kind: agent
name: hn_researcher
sandbox:
mode: auto
memory_mb: 1024
vcpus: 1
network: true
llm:
provider: claude
agent:
prompt: >
Analyze current HackerNews top stories and produce
a tactical briefing for AI engineering teams.
skills:
- 'file:examples/hackernews/skills/hackernews-api.md'
timeout_secs: 600
Pipeline
A pipeline spec chains boxes with explicit stage ordering. Stages can be sequential (type: box) or parallel (type: fan_out):
api_version: v1
kind: pipeline
name: research
sandbox:
mode: auto
memory_mb: 512
network: true
pipeline:
boxes:
- name: researcher
prompt: Find 3 recent HN posts about Rust.
skills: ['agent:claude-code']
timeout_secs: 300
- name: analyst
prompt: Extract key technical facts.
skills: ['agent:claude-code']
- name: writer
prompt: Turn facts into an executive summary.
skills: ['agent:claude-code']
stages:
- type: box
name: researcher
- type: fan_out
boxes: [analyst, writer]
Fan-out outputs are merged into a JSON array written to the next stage’s /workspace/input.json (see Pipeline Composition).
Per-box sandbox overrides (memory_mb, vcpus, network, env, mounts, snapshot) can be placed inside each PipelineBoxSpec under sandbox: to override the top-level defaults for that box only.
Workflow
A workflow is a shell-style DAG of commands sharing a single sandbox. No LLM required.
api_version: v1
kind: workflow
name: quick-workflow
sandbox:
mode: mock
memory_mb: 256
network: false
workflow:
steps:
- name: fetch
run:
program: echo
args: ['hello from workflow']
- name: transform
depends_on: [fetch]
run:
program: tr
args: ['a-z', 'A-Z']
stdin_from: fetch
output_step: transform
Sandbox
A bare VM that boots and waits for interactive attach (voidbox shell). No agent loop runs.
api_version: v1
kind: sandbox
name: claude-sandbox
sandbox:
memory_mb: 2048
network: true
llm:
provider: claude-personal
Running with the CLI
voidbox run --file examples/hackernews/hackernews_agent.yaml
voidbox run --file examples/specs/pipeline.yaml
The subcommand is run, the flag is --file (no short alias, no positional). For interactive agent or sandbox specs, use voidbox shell --file <path> instead.
Field reference
sandbox:
| Field | Values / default | Notes |
|---|---|---|
mode | auto (default), mock, local | Selects the backend. No kvm / vz strings — the backend is picked by host OS. |
memory_mb | integer | Guest memory. |
vcpus | integer | Guest vCPUs. |
network | bool | Enables the NAT’d guest network. |
env | map of string→string | Guest environment variables. |
mounts | list of { host, guest, mode: ro|rw } | Host directory mounts. |
kernel | path | Explicit kernel image. |
initramfs | path | Explicit initramfs image. |
image | OCI ref | Base image used as the guest rootfs (e.g. python:3.12). |
guest_image | OCI ref, or "" to disable auto-pull | Kernel + initramfs OCI bundle. |
snapshot | path or hash prefix | Restore from a snapshot instead of cold-booting. |
llm:
| Field | Values / default | Notes |
|---|---|---|
provider | claude, claude-personal, codex, ollama, custom | Unknown values fall back to claude. lm-studio is Rust-API-only today; not addressable from YAML. |
model | string | Provider-dependent. Ollama defaults to qwen3-coder:7b if unset. |
base_url | URL | For ollama and custom. custom defaults to http://<guest-host-gateway>:11434. |
api_key_env | env var name | For custom: the host-side env var the CLI reads to inject the API key into the guest. |
agent:
| Field | Values / default | Notes |
|---|---|---|
prompt | string (required) | Agent prompt. |
skills | list of skill entries | See “Skills” below. |
timeout_secs | integer | Per-box timeout. |
output_file | guest path | Agent’s structured output destination inside the guest. |
mode | task (default), service, interactive | service runs as a long-lived box; interactive waits for PTY attach. |
messaging | { enabled, provider_bridge } | Opt-in cross-box messaging. |
Skills
Each entry in skills: is either a <type>:<value> string or an OCI image object:
| Form | Example |
|---|---|
agent:<runtime> | agent:claude-code |
file:<host-path> | file:skills/research-method.md |
remote:<url> | remote:https://example.com/skill.md |
cli:<binary> | cli:jq |
mcp:<server-name> | mcp:filesystem |
| OCI object | { image: ghcr.io/voidbox/skill-jq, mount: /skills/jq } |
Inline and programmatic MCP skills exist in the Rust API but are not expressible from YAML.
Stage types
| Form | Meaning |
|---|---|
{ type: box, name: <box> } | Run <box> sequentially. |
{ type: fan_out, boxes: [...] } | Run listed boxes in parallel. |
Environment overrides
Override LLM settings at runtime without editing the spec. Env vars take precedence over spec-level llm.* values.
| Env var | Overrides |
|---|---|
VOIDBOX_LLM_PROVIDER | llm.provider |
VOIDBOX_LLM_MODEL | llm.model |
VOIDBOX_LLM_BASE_URL | llm.base_url |
VOIDBOX_LLM_API_KEY_ENV | llm.api_key_env |
VOIDBOX_LLM_PROVIDER=ollama VOIDBOX_LLM_MODEL=phi4-mini \
voidbox run --file examples/specs/pipeline.yaml
Next
Learn how to compose pipelines in Rust, or set up local LLMs with Ollama.