What Blueprint is showed what a
Blueprint.roc says. This chapter makes one work on your machine. It
installs blueprint, writes a first Blueprint.roc that needs nothing but
the binary, locks its download, runs a task in the environment it
describes, and then adds a tool from Nix.
Install blueprint§
Each release attaches a blueprint binary for x86_64 Linux,
arm64 Linux and Apple Silicon macOS, named blueprint-x86_64-linux,
blueprint-aarch64-linux and blueprint-aarch64-darwin, with their SHA-256
sums in blueprint.sha256. Download the one for your system from the
latest release, check it and put it on your PATH:
sha256sum --check --ignore-missing blueprint.sha256
install -m 755 blueprint-x86_64-linux ~/.local/bin/blueprint
That is all the first project below needs. Nix
with flakes enabled is needed for the tools a project takes from a Nix
package source and for its sandboxed builds, and for nothing else:
blueprint requirements says which of a project’s tasks need it. Where Nix
is installed, the same version also runs from the flake:
nix run github:lukewilliamboswell/roc-blueprint/<version> -- --help
nix profile install github:lukewilliamboswell/roc-blueprint/<version>
In GitHub Actions, the
setup-blueprint
action installs Nix and a release’s binary and checks its sum.
Loading a configuration runs one exact Roc nightly. The binary uses a roc on
PATH when it is that nightly, and otherwise downloads it from its release,
checks it against a digest the binary records, and keeps it in its store of
downloads. Set ROC to choose the executable yourself; it must be that same
nightly.
Write a first Blueprint.roc§
Put this in Blueprint.roc at the root of your repository. The platform URL
is the latest release’s:
app [config] { pf: platform "https://github.com/lukewilliamboswell/roc-blueprint/releases/download/0.11.0/EuSPs8vc71qJNFg29YzegvVRL1bR9pKqUYDHFiSCPTQ1.tar.zst" }
import pf.Recipes
config = [
Name("my-project"),
Environment("dev", [
Programs([Fetch("roc-stable", Recipes.roc_nightly("nightly-2026-10-28-bad769b"))]),
Expect(["git"]),
]),
Shell("default", [Use("dev")]),
Task("version", [Use("dev"), Run(["roc-stable", "version"])]),
Task("status", [Use("dev"), Run(["git", "status", "--short"])]),
]
Use each release’s blueprint binary with that release’s platform URL. The
two are built and tested together.
The file declares one environment, dev. It has two commands:
roc-stableis fetched:Recipes.roc_nightlysays where that Roc nightly is downloaded from on each system, andBlueprint.lockwill hold the digest of each download.gitis expected: the machine must already have it.blueprintchecks for it and installs nothing.
Each task names its environment and an exact argument list.
Lock the downloads and run something§
blueprint check # validate Blueprint.roc; needs no lock
blueprint update # record every download's digest in Blueprint.lock
blueprint run version # run the task's command in dev
blueprint shell # enter dev: your own shell, with roc-stable on PATH
blueprint requirements # what each task needs of the machine
blueprint --help # list this project's shells and tasks
blueprint update is the only command that decides anything: it downloads
the compiler for your system, hashes it, and writes the digest for every
system to Blueprint.lock. Every other command uses that file and fails if
it no longer matches Blueprint.roc. blueprint run then downloads nothing:
the compiler is kept in a per-user store and checked against the lock on
every use.
None of this starts Nix, and blueprint requirements says so:
system x86_64-linux
providers: none
task version [dev]: programs roc-stable; expects git
task status [dev]: programs roc-stable; expects git
Add a tool from Nix§
A tool that should be installed, rather than fetched as one program or
expected of the machine, comes from a Nix package source. Declare the source,
and write each tool with the source’s name. Inside config:
Packages("nix", From(NixPackages("github:NixOS/nixpkgs/nixos-unstable"))),
Environment("native", [Tools(["nix#zig", "nix#pkg-config"])]),
Task("zig-version", [Use("native"), Run(["zig", "version"])]),
To give native the fetched compiler and git as well, start its settings
with Extend("dev").
The project now declares something Nix resolves, so blueprint update needs
Nix: it pins the source’s revision in the same Blueprint.lock. After that,
only what uses the source starts Nix. blueprint run zig-version does;
blueprint run version still does not, on a machine with Nix or without.
blueprint update nix moves that one source and keeps every other pin and
digest.
Commit the lock, not the generated files§
Commit Blueprint.roc and Blueprint.lock. The lock is the authority: a
versioned S-expression listing each download with its digest and each pinned
source with its revision.
blueprint keeps what it generates under .blueprint/ and writes a
.gitignore there, so that directory never reaches a commit. If your
repository already tracks .blueprint/, untrack it once:
git rm -r --cached .blueprint
Where to go next§
Write Blueprint.roc describes every setting.
Work without a provider says where downloads are kept and what a project without Nix can do.
Build artifacts and run workflows covers sandboxed builds.
The examples are complete projects, attached to each release with that release’s platform URL.