background grid image
Image for post introducing-flakehub
Aug 22, 2023 by Graham Christensen

Introducing FlakeHub

Today, we at Determinate Systems are extremely excited to announce the release of FlakeHub, a platform for discovering and publishing Nix flakes. FlakeHub provides the Nix ecosystem with a variety of new capabilities:

We think that FlakeHub could be a transformative force in the Nix ecosystem and provide a crucial inflection point for flake adoption within and outside of the Nix community. We can’t wait to see what people do with it. Check out our flake publishing wizard at flakehub.com/new if you want to get started now or read on to learn more.

What FlakeHub offers

A world of flakes

FlakeHub enables you to explore the current universe of Nix flakes in a variety of new ways.

Search. With FlakeHub, you can search all published flakes by publisher, project, description, and tags. To activate the FlakeHub search widget you can either click on the magnifying glass icon in the navbar or press Cmd + K on macOS or Ctrl + K on Linux, or

List all flakes. Go to flakehub.com/flakes to see a listing of all published flakes.

Organizations. Go to flakeshub.com/orgs to see all the organizations that have published flakes.

Tags. You can use the /tag/:tag endpoint to search for flakes by tag. flakehub.com/tag/nixos, for example, shows you all flakes with the nixos tag.

Semantic versioning

Flakes are currently rooted in revisions. Revision hashes are valuable because they’re highly granular; any change in the contents of the objects in the commit produces a new revision ID. But Nix, even with flakes, doesn’t have a built-in concept of versions.

As a refresher, here’s what semantic versions look like:

SemVer is popular because it’s expressive. Major, minor, and patch are straightforward markers (hence the “semantic”). The difference between version 1.4.1 and 1.4.2 varies based on the project, and it isn’t always clear what should constitute a patch version versus a minor version, but it at least provides a framework for making those judgments. Revision hashes do not.

In the Nix ecosystem, upgrading a flake input using nix flake update has thus far meant switching to the most recent Git revision for that reference. FlakeHub changes that by embedding semantic versioning directly into flake references. Here’s the basic structure:

https://flakehub.com/f/org/flake-name/version-requirement.tar.gz

Here are some example nix flake metadata commands that illustrate what FlakeHub makes possible:

Terminal window
# See the most recent Nixpkgs stable
nix flake metadata "https://flakehub.com/f/NixOS/nixpkgs/*.tar.gz"
# See the most recent Nixpkgs under major version 0.2305
nix flake metadata "https://api.flakehub.com/f/NixOS/nixpkgs/0.2305.tar.gz"
# See the most recent Nixpkgs from NixOS 0.2305, the current stable release
nix flake metadata "https://flakehub.com/f/NixOS/nixpkgs/0.2305.x.tar.gz"
# See the most recent Nixpkgs under minor version 0.2305
nix flake metadata "https://api.flakehub.com/f/NixOS/nixpkgs/0.2305.*.tar.gz"

In addition to *, FlakeHub also supports the ~ and = operators. This URL denotes the most recent minor version using ~:

https://flakehub.com/f/NixOS/nixpkgs/~0.2305.tar.gz

To see the metadata:

Terminal window
nix flake metadata "https://flakehub.com/f/NixOS/nixpkgs/~0.2305.tar.gz"

This URL denotes an exact version using =:

https://flakehub.com/f/NixOS/nixpkgs/=0.2305.tar.gz

To see the metadata:

Terminal window
nix flake metadata "https://flakehub.com/f/NixOS/nixpkgs/=0.2305.tar.gz"

Now let’s see how this looks in a flake.nix file:

{
inputs.nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.2305.*.tar.gz";
outputs = { self, nixpkgs }: {
# Use the nixpkgs input here
};
}

Now when you run nix flake show in this directory, the flake.lock will pin Nixpkgs to the most recent revision under 0.2305. When you run nix flake update, it will stay in that series.

Publish your flakes

You can publish flakes to FlakeHub using GitHub Actions. To automatically publish your flake every time you push a new tag, you can add this workflow config to your project:

name: Push flake to FlakeHub
on:
push:
tags:
- "v*.*.*"
jobs:
flakehub:
runs-on: ubuntu-22.04
permissions:
id-token: write
contents: read
steps:
- uses: DeterminateSystems/nix-installer-action@v4
- uses: actions/checkout@v3
- name: Push to FlakeHub
uses: determinatesystems/flakehub-push@main
with:
visibility: "public"

To adopt a “rolling” strategy:

on:
push:
branches:
- main
jobs:
flakehub:
# Other configs from above
- name: Push to FlakeHub
uses: determinatesystems/flakehub-push@main
with:
rolling: true
visibility: "public"

This sets a rolling prefix to be suffixed with the commit count. v0.1, for example, would be suffixed with .123, producing a release named v0.1.123-{revision}.

We’ve also provided a handy publishing wizard at flakehub.com/new that walks you through the process step by step.

You should adopt flakes

Here at Determinate Systems, we are 100% all in on Nix flakes because we firmly believe that they are the future of Nix. Here are some reasons why:

  • Flakes overcome all of the deficiencies of Nix channels, most importantly surrounding pinning to specific revisions of your code via the flake.lock file.
  • Flakes make Nix expressions dramatically more introspectable through commands like nix flake show and nix flake metadata.
  • Flake references provide a convenient universal mechanism for specifying the location of a flake.
  • Flakes provide standard way to structure Nix outputs.
  • In place of a mélange of files like default.nix, configuration.nix, and shell.nix, flakes are standardized a single flake.nix file as the entry point of a project.

And our confidence in flakes isn’t just rhetorical. We’ve acted on it:

  • The Determinate Nix Installer, our unofficial, still-experimental installer for Nix, enables flakes by default.
  • Zero to Nix, an opinionated learning resource for Nix that we created, is centered around flakes and actively promotes beginning your Nix journey with flakes rather than channels.
  • We created both Flake Checker, a tool that performs “health checks” on your flake.lock files, and the update-flake-lock Github Action, which automates flake.lock updates for GitHub projects.

FlakeHub is our most ambitious salvo yet and part of a broader story that guides everything we do as a company.

Current Nix practices

SemVer has become standard practice in most programming language communities: JavaScript, Python, Rust, Java, Ruby, and on down the line. Noticeably absent from this list: Nix.

Prior to the introduction of flakes, most Nix development used Nixpkgs as the only external Nix input. Typically, developers would subscribe to a channel of Nixpkgs, like 22.11 or 23.05, continuously update to the most recent revision in that channel, and then switch channels every six months.

The introduction of flakes began to change things. Because flakes provide primitives to compose and pin your Nix inputs, we began to see a wider variety of inputs in Nix projects—a decentralization of the Nix universe.

But even with flakes, Nix development still typically involves one of two approaches to handling Nix flake inputs:

  • Give me the most recent revision in this specific branch. And there are only a few prominent flakes, like Nixpkgs, where branches, such as nixpkgs-unstable, are used. More often the approach is…
  • Just give me the most recent revision.

The consequence is that operations like nix flake update are too often fraught with peril. You run it, you hope for the best, you try to fix what breaks, and you steel yourself to do the same over and over again in the future.

We hope that FlakeHub and the introduction of SemVer to flakes enables the Nix community to move beyond this state of affairs and into harmony with other languages. We want nix flake update to feel a lot more like housekeeping and a lot less like a venturesome leap into the unknown.

How it works

In order to make semantic versions work in FlakeHub, we submitted a change to Nix that enables it to handle arbitrary URL endpoints. If the endpoint returns an HTTP 301 status code and a Link header, Nix fetches the flake tarball from that URL and records that URL in the flake.lock file. See the FlakeHub docs for information on which versions of Nix support this.

Take this flake.nix file:

{
inputs.nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.2305.490090.tar.gz";
# Flake outputs here
}

If you run a nix flake command like nix flake show in this directory, the generated flake.lock looks like this:

{
"nodes": {
"nixpkgs": {
"locked": {
"narHash": "sha256-7el+r373PubFExJSr/FA4wwr66ekBn4afDJuEiuefO8=",
"rev": "475d5ae2c4cb87b904545bdb547af05681198fcc",
"revCount": 490311,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.2305.490311%2Brev-475d5ae2c4cb87b904545bdb547af05681198fcc/018a1928-9148-72c2-b387-ee7e2286c1b9/source.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://api.flakehub.com/f/NixOS/nixpkgs/0.2305.%2A.tar.gz"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

As you can see, Nix has resolved the URL into a specific tarball reference using the FlakeHub server’s HTTP response. FlakeHub’s implementation includes not just Git revision information but also a UUID in the tarball URL.

And FlakeHub is just one implementation. With this feature now in Nix, you can build your own HTTP servers to serve flakes according to whatever scheme you wish, SemVer or not.

Room for improvement

There are a few areas where FlakeHub isn’t quite where we want it to be yet:

  • CLI tool. In the coming weeks, we intend to introduce a CLI tool that enables you to perform a variety of actions against the FlakeHub API, such as search.
  • Private flakes. Upon launch, all the flakes on FlakeHub are either public or unlisted; unlisted flakes can be used by Nix but don’t show up on the website. Down the road, we want to enable you to make flakes available only to people within your org.

Implications

We’re confident that FlakeHub will be a major boon to flakes adoption and to the Nix ecosystem more broadly. A dedicated home for flakes will enable people to see which flakes exist and add them to their daily workflows more easily than ever before—and within those workflows, the update path will be predictable in a way that we haven’t yet seen in the world of Nix.

Whether SemVer takes hold in Nix is up to you. But we’re confident that adoption will happen swiftly now that Nix users have the means to adopt it.

Take a look at flakehub.com, browse the docs at docs.determinate.systems, and publish your flake today. Or join us on Discord if you have questions or problems or just want to connect with other Nix users.


Share
Avatar for Graham Christensen
Written by Graham Christensen

Graham is a Nix and Rust developer, with a passion and focus on reliability in the lower levels of the stack. He founded Determinate Systems, Inc to support Nix adoption at your workplace.