Skip to content

Blueprint Structure

A blueprint is a directory containing a manifest file, one or more task files, and an optional lockfile.


Directory layout

my-blueprint/
├── manifest.yml        # required: metadata, variables, task order
├── variables.yml       # optional: base variable definitions
├── alloy.lock.yml      # optional: resolved toolchain URLs and checksums (commit this)
├── 00-system.yml       # task file 1
├── 10-toolchain.yml    # task file 2
├── 20-environment.yml  # task file 3
└── alloy.state.yml     # auto-generated by provisioner — DO NOT commit

The numbering convention for task files (00-, 10-, 20-) is a recommendation, not a requirement. The explicit run_order: in manifest.yml controls execution order regardless of filename.


manifest.yml

The manifest is the entry point. The provisioner reads it first.

name: ARM Cortex-M Development Environment # required
version: 1.0.0 # required
description: ARM GCC toolchain for Cortex-M embedded development.

variables: # optional
  ARM_TOOLCHAIN_DEST: /opt/arm-none-eabi
  DEV_USERNAME: vagrant

toolchains: # optional: catalog references
  - ref: toolchain.arm-gnu.arm-none-eabi@stable
    alias: ARM_TOOLCHAIN # injects ARM_TOOLCHAIN_URL and ARM_TOOLCHAIN_SHA

required_env: # optional: validate env vars before provisioning
  - name: GITLAB_TOKEN
    description: Token for private artifact registry

supported_hosts: # optional: document supported platforms
  - linux/amd64
  - linux/arm64

run_order: # required: explicit execution order
  - 00-system.yml
  - 10-toolchain.yml
  - 20-environment.yml

Manifest fields

Field Required Description
name Yes Human-readable environment name
version Yes Semantic version (e.g. 1.2.0)
description No Short description
variables No Key-value pairs; available as {{.Vars.NAME}} in task files
toolchains No Catalog refs with optional aliases
required_env No Env vars that must be set before provisioning
supported_hosts No Documentation of supported os/arch platforms
run_order Yes Ordered list of task files to execute

Task files

Task files are flat YAML lists. Each item is a task with a name, an action, and action-specific fields.

- name: Install build tools
  action: apt_install
  packages:
    - git
    - cmake
    - ninja-build
    - python3

- name: Download ARM GCC toolchain
  action: unarchive_from_url
  dest: "{{.Vars.ARM_TOOLCHAIN_DEST}}"
  creates: "{{.Vars.ARM_TOOLCHAIN_DEST}}/bin/arm-none-eabi-gcc"
  per_arch:
    amd64:
      url: https://example.com/arm-gcc-13-x86_64.tar.xz
      sha256: "aaa111bbb222..."
    arm64:
      url: https://example.com/arm-gcc-13-aarch64.tar.xz
      sha256: "ccc333ddd444..."

- name: Configure PATH
  action: write_env_file
  file: /etc/profile.d/10-arm-toolchain.sh
  content: |
    export PATH=$PATH:{{.Vars.ARM_TOOLCHAIN_DEST}}/bin
  owner: root:root
  mode: "0644"

Common task fields

Field Description
name Required. Human-readable task identifier.
action Required. The operation to perform (see below).
creates Path to check; if it exists, skip the task. The fastest idempotency mechanism.
always_run If true, bypass all idempotency checks and always execute.
skip_if Shell expression; if it exits 0, skip the task entirely. Takes precedence over always_run.
run_as Run as this user. Use {{.Vars.DEV_USERNAME}}; auto-detected from sudo context.
owner Set file ownership after execution (e.g. {{.Vars.DEV_USERNAME}}:{{.Vars.DEV_USERNAME}}).
mode Set file permissions after execution (e.g. 0755 or u+x).
per_arch Architecture-specific overrides for url, sha256, command, or source.

Architecture overrides (per_arch)

Use per_arch to handle amd64 and arm64 differences without conditional logic:

- name: Install toolchain
  action: unarchive_from_url
  dest: /opt/my-toolchain
  creates: /opt/my-toolchain/bin/my-gcc
  per_arch:
    amd64:
      url: https://example.com/toolchain-x86_64.tar.gz
      sha256: "abc123..."
    arm64:
      url: https://example.com/toolchain-aarch64.tar.gz
      sha256: "def456..."

The provisioner detects the host architecture and merges the matching block before executing the task.

Common actions

Action What it does
apt_install Install packages via apt-get (only installs missing packages)
run_command Execute a shell command
get_file Download a file with SHA256 verification
unarchive_from_url Download and extract an archive
unarchive_from_ref Extract a toolchain resolved from the lockfile
write_env_file Write a file (env script, config, toolchain file)
udev_rules Install a udev rules file and reload udev
python_package Install a Python package via pip
rust_package Install a Rust package via cargo

Full action reference: Blueprint Syntax


alloy.lock.yml

The lockfile pins catalog toolchain references to exact download URLs and SHA256 checksums. Generate it with:

alloy-host resolve        # if using alloy-host
resolved_at: "2026-01-15T10:00:00Z"
host: linux/amd64
toolchains:
  toolchain.arm-gnu.arm-none-eabi@stable:
    url: https://api.alloy-it.io/toolchains/arm-gnu/arm-none-eabi/stable/arm-gnu-13.3.tar.xz
    sha256: "abc123def456..."
    method: archive

Always commit the lockfile. It ensures every developer, CI runner, and Docker build uses byte-for-byte identical toolchains.


alloy.state.yml

The provisioner creates alloy.state.yml after each run. It records a fingerprint of every completed task so re-runs can skip unchanged work.

Do not commit alloy.state.yml. Add it to .gitignore:

alloy.state.yml
.env

Next steps