SkillAgentSearch skills...

Riff

Riff automatically provides external dependencies for Rust projects, with support for other languages coming soon.

Install / Use

/learn @DeterminateSystems/Riff
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Riff

logo-light logo-dark

Riff is a tool that automatically provides external dependencies[^1] for software projects. To enter a shell environment with all your project's external dependencies installed, run this at the project root:

riff shell

You can also directly run commands with the shell environment applied but without entering the shell:

riff run cargo build

Riff currently supports [Rust] with support for other languages coming soon. It uses the [Nix] package manager to handle dependencies but doesn't require you to know or use Nix.

For a video demo of Riff in action, see below.

Requirements

To use Riff, you need to install these binaries on your system:

  • [nix][nix-install]
  • [cargo][rust-install]

Installation

Using Nix

To install Riff using Nix (make sure to have [flakes] enabled):

nix profile install github:DeterminateSystems/riff

Using Homebrew

To install Riff on macOS using [Homebrew]:

brew install DeterminateSystems/riff/riff

Note: The riff Homebrew formula does not install [Nix] or [Cargo].

Using cURL

You can find instructions for installing Riff using cURL on the [releases page][releases].

GitHub Actions

You can install Riff in your [GitHub Actions][actions] pipelines using [install-riff-action][install-riff-action]. Here's an example configuration:

steps:
  - uses: actions/checkout@v3
  - name: Install Nix
    uses: DeterminateSystems/nix-installer-action@main
  - name: Install Rust
    uses: actions-rs/toolchain@v1
    with:
      toolchain: stable
  - name: Install Riff
    uses: DeterminateSystems/install-riff-action@v1
  - name: Build Rust app
    run: riff run cargo build -- --release

Prompt Customization

You can customize your shell's prompt to display when you're in a Riff shell environment by relying on the $IN_NIX_SHELL environment variable.

If you use [Starship], you get this information for free because the [Nix shell module] is enabled by default. However, if you want to add this to your shell prompt yourself, you can do that by adding $name to your prompt when $IN_NIX_SHELL is set.

In Bash, this might look something like:

export PS1="$PWD \${IN_NIX_SHELL:+\$name }\$ "

Note the escaping of \${IN_NIX_SHELL} and \$name. This prevents Bash from taking the current values of those environment variables and using them even after one of them has changed.

In Zsh, it might look similar to:

export PROMPT="$PWD \${IN_NIX_SHELL:+\$name }\$ "

And in Fish, you might use something like:

function fish_prompt
    echo -n "$PWD "
    if set -q IN_NIX_SHELL
        echo -n "$name "
    end
    echo -n "\$ "
end

Escaping $name is unnecessary here because Fish doesn't capture the value of $name until the function is run when your prompt is displayed.

The idea extends to any shell: if you can change the prompt and prevent it from evaluating the values of $IN_NIX_SHELL and $name until the prompt is displayed, you can add this information to your prompt.

What Riff provides

Most programming languages use language-specific package managers to handle dependencies, such as [Cargo] for the [Rust] language. But these language-specific tools typically don't handle dependencies written in other languages very well. They expect you to install those dependencies using some other tool and fail in mysterious ways when they're missing. Here's an example error from trying to build the [octocrab][octocrab] crate without [OpenSSL] installed:

--- stderr
thread 'main' panicked at '

Could not find directory of OpenSSL installation, and this `-sys` crate cannot
proceed without this knowledge. If OpenSSL is installed and this crate had
trouble finding it,  you can set the `OPENSSL_DIR` environment variable for the
compilation process.

Make sure you also have the development packages of openssl installed.
For example, `libssl-dev` on Ubuntu or `openssl-devel` on Fedora.

In cases like this, it's up to you to install missing external dependencies, which can be laborious, error prone, and hard to reproduce.

Riff enables you to bypass this problem entirely. It uses your your project's language-specific configuration to infer which external dependencies are required and creates a shell environment with those dependencies both installed and properly linked. In cases where those dependencies can't be inferred, for example in your [build.rs][build.rs] script, you can explicitly declare them in your Cargo.toml.

These environments are transient in the sense that they don't affect anything outside the shell; they install dependencies neither globally nor in your current project, so you don't have to worry about Riff breaking anything on your system. When you exit the Riff shell, the dependencies are gone.

Offline mode

In cases where you want to limit Riff's access to the Internet, you can run it in offline mode, which disables all network usage except what's required by the nix develop command (which Riff runs in the background). You can enable offline mode using either the --offline flag or the RIFF_OFFLINE environment variable. Here are some examples:

# Via flag
riff run --offline

# Via environment variable
RIFF_OFFLINE=true riff shell

Example usage

In this example, we'll build the [Prost] project from source. Prost has an external dependency on [OpenSSL], without which commands like cargo build and cargo run are doomed to fail. Riff provides those dependencies automatically, without you needing to install them in your regular environment. Follow these steps to see dependency inference in action:

git clone https://github.com/tokio-rs/prost.git
cd prost

# Enter the Riff shell environment
riff shell
# ✓ 🦀 rust: cargo, cmake, curl, openssl, pkg-config, rustc, rustfmt, zlib

# Check for the presence of openssl
which openssl
# The path should look like this:
# /nix/store/f3xbf94zykbh6drw6wfg9hdrfgwrkck7-openssl-1.1.1q-bin/bin/openssl
# This means that Riff is using the Nix-provided openssl

# Build the project
cargo build

# Leave the shell environment
exit

# Check for openssl again
which openssl
# This should either point to an openssl executable on your PATH or fail

How to declare package inputs

While Riff does its best to infer external dependencies from your project's crate dependencies, you can explicitly declare external dependencies if necessary by adding a riff block to the package.metadata block in your Cargo.toml. Riff currently supports three types of inputs:

  • build-inputs are external dependencies that some crates may need to link against.
  • environment-variables are environment variables you want to set in your dev shell.
  • runtime-inputs are libraries you want to add to your LD_LIBRARY_PATH to ensure that your dev shell works as expected.

Both build-inputs and runtime-inputs can be any packages available in [Nixpkgs]. You may find this particularly useful for [build.rs scripts][build.rs].

Here's an example Cargo.toml with an explicitly supplied Riff configuration:

[package]
name = "riff-example"
version = "0.1.0"
edition = "2021"

[package.metadata.riff]
build-inputs = [ "openssl" ]
runtime-inputs = [ "libGL" ]

[package.metadata.riff.environment-variables]
HI = "BYE"

# Other configuration

When you run riff shell in this project, Riff

  • adds [OpenSSL] to your build environment
  • sets the LD_LIBRARY_PATH environment variable to include [libGL]'s library path
  • sets the HI environment variable to have a value of BYE

Target-specific dependencies

If a project has OS-, architecture-, or vendor-specific dependencies, you can define them in a targets block under package.metadata.riff. Here's an example for Apple M1 (aarch64-apple-darwin) systems:

[package.metadata.riff.targets.aarch64-apple-darwin]
build-inputs = [
  "darwin.apple_sdk.frameworks.CoreServices",
  "darwin.apple_sdk.frameworks.Security"
]

The Rust project maintains [a list of well-known targets][targets] that you can view by running nix run nixpkgs#rustup target list. This field can also contain custom targets, such as riscv32imac-unknown-xous-elf, although riff makes no effort to support cross compiling at this time.

When target-specific dependencies are present, the build-inputs and runtime-inputs sections are unioned (joined), while the target-specific environment variables override default environment variables.

macOS framework dependencies

macOS users may encounter issues with so-called "framework" dependencies, such as [Foundation][foundation], [CoreServices][coreservices], and [Security][security]. When these dependencies are missing, you may see error messages like this:

= note: ld: framework not found CoreFoundation

You can solve this by adding framework dependencies to your build-inputs as darwin.apple_sdk.frameworks.<framework>, for example darwin.apple_sdk.frameworks.Security. Here's an example Cargo.toml configuration that adds multiple framework dependencies:

[package.metadata.riff.targets.x86_64-apple-darwin]
build-inputs = [
  "darwin.apple_sdk.frameworks.CoreServices",
  "darwin.apple_sdk.frameworks.Security"
]

[package.metadata.riff.targets.aarch64-apple-darwin]
build-inputs = [
  "darwin.apple_sdk.frameworks.CoreServices",
  "darwin.apple_sdk.frameworks.Security"
]

Riff understands dependencies transitively

If you add Riff metadata to Cargo.toml, this doesn't just make it easier to build and run your project: it actually benefits consumers of your crate as well. That's because Riff can use this metadata transitively to infer which

Related Skills

View on GitHub
GitHub Stars487
CategoryCustomer
Updated2mo ago
Forks14

Languages

Rust

Security Score

100/100

Audited on Jan 27, 2026

No findings