Aves
Aves SDE is an embedded firmware development environment. It bundles a library of packages, a reproducible build system, and a CLI that bootstraps its own toolchain.
There are no installed dependencies on the host system — every build runs inside a hermetic toolchain provided by nix-portable, which the CLI fetches automatically on first use.
What’s it for
The Aves SDE is intended to grow into a complete embedded SDE: a Rust kernel for nRF52-class MCUs, plus the supporting packages — bootloaders, vendor SoftDevices, board definitions, build helpers — needed to develop, flash, and ship firmware.
This documentation covers the build system and CLI as they exist today. Start with installation, then follow the quickstart to build the example bootloader.
Status
Alpha / pre-release.
The build pipeline works end-to-end for one example package — the Adafruit nRF52 UF2 bootloader. Most kernel and infrastructure packages are not yet committed.
What’s stable
- The CLI build pipeline (fetch sources, sandbox, build, emit artifacts).
- The TOML manifest format (workspace, project, package, board).
- Build reproducibility under a pinned
nixpkgsrevision.
What’s not yet stable
- The set of
board.tomlfields the build pipeline actually consumes (most are ahead-of-time declarations for upcoming consumers). - Per-dependency build overrides in
aves_manifest.toml. - The flashing workflow (
aves flash).
See the roadmap for what’s planned next.
Installation
Quick install
curl -fsSL https://sh.avesde.com | sh
Linux and macOS are supported.
The install script downloads the latest aves binary, verifies its
checksum, and writes it to ~/.local/bin/aves. Make sure
~/.local/bin is on your PATH:
export PATH="$HOME/.local/bin:$PATH"
From source
If you’d rather build from source, clone the repo and use Cargo:
git clone https://git.paximi.com/samuel/aves
cd aves
cargo build --release --manifest-path aves-cli/Cargo.toml
cp target/release/aves-cli ~/.local/bin/aves
First run
On the first build, the CLI will prompt to download nix-portable
into ~/.local/share/aves/nix/<version>/. The download is cached and
reused across every project on the machine.
Source clones for each package are also kept outside the project tree,
under ~/.local/share/aves/src/<pkg>/, so a single clone of an
upstream repository can be reused across workspaces.
Quickstart
Three TOML files describe an Aves build:
| File | Scope | Purpose |
|---|---|---|
aves.toml | Workspace | Pins nixpkgs, sets policy, picks a runtime. |
aves_manifest.toml | Project | Picks the board and the packages to build. |
packages/<name>/package.toml | Package | Defines a single buildable component. |
Plus board files at boards/<name>.toml.
1. Edit the manifest
aves_manifest.toml:
[nixpkgs]
channel = "nixos-25.05"
[project]
name = "my-device"
board = "feather_nrf52840_express"
[packages.nrf52-adafruit-uf2-bootloader]
version = "0.10.0"
2. Build
aves build
You’ll see something like:
fetch nrf52-adafruit-uf2-bootloader@0.10.0
toolchain gcc-arm-embedded, gnumake, python3, adafruit-nrfutil
build feather_nrf52840_express
✓ build/nrf52-adafruit-uf2-bootloader/feather_nrf52840_express/bootloader.uf2
3. Find the output
Artifacts land at:
build/<package>/<board>/*.{uf2,hex,zip}
The exact extensions depend on the package’s [build] outputs globs —
see Packages.
What happened
- The CLI read
aves.tomlto resolve the pinnednixpkgsrevision and thenix-portableruntime. - It read
aves_manifest.tomlto learn the board (feather_nrf52840_express) and the package list (just one bootloader). - For each package, it cloned the source into
~/.local/share/aves/src/<pkg>/, evaluated the[build] inputsas Nix expressions, opened a sandbox, and ran the[build] commandinside it with{board}substituted from the manifest. - Files matching
[build] outputsglobs were copied intobuild/<package>/<board>/.
Because both the toolchain and the package versions are pinned, the same workspace produces byte-identical artifacts on any machine.
Workspace (aves.toml)
aves.toml is the workspace-wide configuration. Every package built in
the workspace sees the same aves.toml; project-specific concerns
belong in aves_manifest.toml instead.
Example
[nixpkgs]
channel = "nixos-25.05"
rev = "<lock-hash>"
[policy]
allow_unfree = true
allow_insecure = true
[nix-portable]
runtime = "proot"
Sections
[nixpkgs]
Pins the nixpkgs revision used to evaluate every package’s [build] inputs. Pinning is what makes builds reproducible across machines and
across time.
| Key | Type | Required | Description |
|---|---|---|---|
channel | string | yes | Nixpkgs channel name, e.g. "nixos-25.05". |
rev | string | no | Specific commit hash. Required for full lock. |
[policy]
Workspace-wide gates for permissive build flags. Each gate is checked
when a package’s [build] requests the matching flag.
| Key | Type | Default | Description |
|---|---|---|---|
allow_unfree | bool | false | Permit packages whose inputs include unfree licenses. |
allow_insecure | bool | false | Permit packages whose inputs include insecure-marked deps. |
[nix-portable]
Configures the nix-portable backend.
| Key | Type | Default | Description |
|---|---|---|---|
runtime | string | (auto) | One of proot, bwrap. Pins the sandbox runtime. |
Project manifest (aves_manifest.toml)
The project manifest declares what to build: which board the project targets, which packages to pull in, and which versions to pin.
Example
[nixpkgs]
channel = "nixos-25.05"
[project]
name = "my-device"
board = "feather_nrf52840_express"
[packages.nrf52-adafruit-uf2-bootloader]
version = "0.10.0"
Sections
[nixpkgs]
Same shape as in aves.toml. Anything set here
overrides the workspace pin for this project.
[project]
| Key | Type | Required | Description |
|---|---|---|---|
name | string | yes | Human-readable project name. |
board | string | yes | Board key — see Boards. |
The board field is resolved against project-local boards/
first, then the global registry at ~/.local/share/aves/boards/.
[packages.<name>]
One table per package the project depends on. The table key is the
package name (matching the name field in the package’s own
package.toml).
| Key | Type | Required | Description |
|---|---|---|---|
version | string | yes | Pinned version. Must exactly match a release. |
Per-package overrides (variables passed into the build, source revisions, etc.) are on the roadmap.
Packages (package.toml)
A package is a single buildable component — a bootloader, a
SoftDevice, a flashing helper, the kernel. Each package lives at
packages/<name>/package.toml.
The build recipe is parameterized by the manifest’s board, so one package recipe builds for every compatible target.
Example
[package]
name = "nrf52-adafruit-uf2-bootloader"
category = "bootloader"
version = "0.10.0"
[supports]
socs = ["nrf52840"]
[source]
git = "https://github.com/adafruit/Adafruit_nRF52_Bootloader.git"
rev = "6180d8a26b8ca4c494158e4c5e9ca183f6801826"
submodules = "recursive"
[build]
inputs = [
"gcc-arm-embedded",
"gnumake",
"python3.withPackages (ps: [ ps.intelhex ])",
"adafruit-nrfutil",
]
command = ["make", "BOARD={board}", "all"]
allow_unfree = true
outputs = ["_build/build-{board}/*.{uf2,hex,zip}"]
Sections
[package]
| Key | Type | Required | Description |
|---|---|---|---|
name | string | yes | Package name, must match its directory. |
category | string | yes | One of bootloader, kernel, softdevice, tool, library. |
version | string | yes | Semver string. Treated as opaque by the build. |
[supports]
Compatibility declarations. The build will refuse to run if the manifest’s board doesn’t match.
| Key | Type | Required | Description |
|---|---|---|---|
socs | string[] | no | List of compatible SoC keys, e.g. ["nrf52840"]. |
platforms | string[] | no | List of compatible platform keys. |
[source]
Where to clone the upstream source.
| Key | Type | Required | Description |
|---|---|---|---|
git | string | yes | Upstream repository URL. |
rev | string | yes | Pinned commit hash. |
submodules | string | no | One of none, shallow, recursive. |
[build]
How to actually build the package.
| Key | Type | Required | Description |
|---|---|---|---|
inputs | string[] | yes | Nix expressions evaluated under with pkgs;. |
command | string[] | yes | argv for the build command. {board} is substituted. |
outputs | string[] | yes | Globs of files to copy out of the sandbox. {board} is substituted. |
allow_unfree | bool | no | Required if any input is unfree. Gated by workspace [policy]. |
inputs accepts bare attribute names (gcc-arm-embedded) and
function calls (python3.withPackages (ps: [ ps.intelhex ])).
{board} placeholders in command and outputs are substituted from
the project manifest’s board field.
Boards (board.toml)
A board file describes a piece of hardware: SoC, memory map, peripherals, bootloader region, USB family ID, and so on.
Project-local boards live alongside the manifest at
boards/<name>.toml. Shared boards can be installed into the global
registry at ~/.local/share/aves/boards/. The manifest’s board field
is resolved against project-local boards first, then the global
registry.
Example
name = "feather_nrf52840_express"
soc = "nrf52840"
platform = "nrf5"
[memory.flash]
origin = 0x00000000
length = 0x00100000
# … peripherals, bootloader region, USB family ID, etc.
Top-level fields
| Key | Type | Required | Description |
|---|---|---|---|
name | string | yes | Board key. Must match the manifest’s [project] board. |
soc | string | yes | SoC key, e.g. "nrf52840". Used by [supports] socs. |
platform | string | yes | Platform family, e.g. "nrf5". |
Sections
[memory.<region>]
Memory region declarations. Multiple regions are supported (flash,
ram, softdevice, …).
| Key | Type | Required | Description |
|---|---|---|---|
origin | integer | yes | Region base address (hex literal or int). |
length | integer | yes | Region length in bytes. |
Status
Most board fields aren’t consumed by the build pipeline yet — they’re ahead-of-time declarations for upcoming consumers (linker script generation, kernel features gated on peripherals, flash tooling). The fields will be tightened up as the consumers land.
CLI
The aves command-line interface drives every workspace operation.
aves build
Build everything declared in aves_manifest.toml for the project’s
board.
aves build
Outputs land at build/<package>/<board>/*.{uf2,hex,zip} (the exact
extensions depend on each package’s [build] outputs globs).
aves packages list
List packages discovered in the workspace.
aves packages list
Filter by category:
aves packages list --category bootloader
Categories follow the [package] category field —
bootloader, kernel, softdevice, tool, library.
Roadmap
Aves is alpha — the build pipeline works, the rest of the SDE is arriving piece by piece.
Near term
- Kernel. A Rust nRF52840 kernel as a first-class package.
- Vendor packages. Nordic SoftDevice,
nrfutil, flashing helpers, and the supporting glue. aves init. Scaffold a workspace from a template.
Medium term
- Per-dependency overrides in
aves_manifest.toml— override variables passed to a single package’s build without forking the package definition. aves flash— push artifacts onto a connected board over DFU/UF2/JTAG.- Linker script generation from
board.tomlmemory regions.
Longer term
- Additional MCU families beyond nRF52-class. The build system is not nRF-specific; the package and board libraries grow with contributions.
- Multi-target builds — emit artifacts for several boards from a
single
aves buildinvocation. - Signed releases and a public registry for third-party packages.
Filing requests in the issue tracker is the best way to influence what lands first.
Contributing
Aves is a small, opinionated project — contributions are welcome, but please open an issue before starting any non-trivial work so we can agree on shape.
Filing issues
The issue tracker lives at https://git.paximi.com/samuel/aves/issues. Bug reports should include:
- The output of
aves --version. - The host platform (Linux distribution + kernel version, or macOS version).
- The relevant
aves.toml,aves_manifest.toml, and (if applicable) the failingpackage.toml. - The full error output, with
RUST_BACKTRACE=1set if the CLI panicked.
Submitting a patch
- Open an issue describing the change.
- Fork and branch from
main. - Run
cargo fmtandcargo clippy --all-targets -- -D warningsbefore pushing. - Send a pull request that references the issue.
Adding a package
New packages live under packages/<name>/package.toml. See the
package reference for the schema, and
the existing nrf52-adafruit-uf2-bootloader package for a working
example.
Adding a board
New boards live under boards/<name>.toml. See
boards for the schema. Project-local boards
should be added to the project itself; shared boards belong in the
global registry and are reviewed before merging.