Alternative Registries

cargo gained a new feature this week! You can now download dependencies from alternative registries, alongside the dependencies you download from crates.io. This is an important step in enabling organizations to distribute their internal libraries through cargo without requiring them to upload those libraries to a public registry.

This feature will be available on nightly only, and it is gated behind the alternative-registries feature gate. We’ve used feature gates to iterate on new and unstable features in rustc since the 1.0 release, but this is the first time we’ve used feature gates in cargo.

Background on registries and sources

Since before 1.0, cargo has been paired with the crates.io registry. This registry contains thousands of crates which make up the majority of the Rust library ecosystem, and cargo makes it convenient to download and use libraries stored in the crates.io registry.

However, there are many reasons why a user might not wish to upload their library to crates.io. Proprietary code, in particular, is not suitable for the registry, which allows anyone to download any crate it contains. For this reason, cargo has always supported alternative sources to the registry. Specifically, it has supported pulling dependencies from a file path or a git repository.

On the other hand, cargo so far has only allowed you to depend on crates from a single registry at a time. Registries like crates.io are preferable to git sources in many situations because they are able to more effectively manage multiple versions of your crate, and perform version resolution for you. With this new feature, you will be able to pull in crates from a registry other than crates.io.

How to add a crate from an alternative registry

Turning on the nightly feature

Because this feature is still unstable, you need to declare your use of the feature flag in your Cargo.toml. This is done using a top level array member named cargo-features. Just add this to the top your Cargo.toml file in the project you’ll be adding the dependency to:

cargo-features = ["alternative-registries"]

[package]
name = "my-crate"
version = "1.0.0"
...

Declaring a new registry

Once you’ve turned the alternative-registries feature on for your crate, you need to make sure that you have declared your registry so that cargo can find it. You need to do this in .cargo/config file (not in a Cargo.toml file).

The .cargo/config has a new section called registries. Declare a subsection with the name of your registry. It must have at least one member called index, which is the URL of the index for your registry (for example, crates.io’s index is located at https://github.com/rust-lang/crates.io-index).

[registries.my-registry]
index = "https://gitlab.com/my-organization/my-registry"

We require you to declare the index of the registry in you .cargo/config rather than in a Cargo.toml to encourage robustness in the event the index has to be moved to a new location. If you depend on crates on one registry that depend on crates in another, the actual location of that registry should be provided at the user level, rather than stored with the crate, so that it is easier for the user to change it if it ever needs to be changed.

This also matches the best default for what we believe will be the primary use case for alternative registries: internal registries for private organizations. Rather than requiring each new project to redeclare its dependency on that registry, each machine can be preconfigured to be able to locate the registry.

Pulling in a crate from your registry

Once you’ve declared a registry, depending on crates from it looks very similar to depending on crates from crates.io. The only difference is that you must specify a registry key in that dependency’s section of your Cargo.toml, so that cargo knows to download the crate from your registry instead of the default one:

[dependencies.foobar]
version = "1.4.0"
registry = "my-registry"

If your registry has a matching, unyanked crate with a satisfactory version number, running the cargo fetch should resolve and download this dependency and you’re good to go.

Creating an alternative registry

We’ve looked at how you can download crates from an alternative registry, but we haven’t discussed how to actually create a registry you can use. Today, we don’t yet have any tools that make it easy to bootstrap your own registry, but we have attempted to document what each registry must provide to work with cargo. Specifically, the minimum viable registry requires two components:

  • A git repository which provides the index of the crates in your registry.
  • An HTTP source from which cargo can download tarballs of crates.

Indexes

Each cargo registry provides an index, which is a git repository following a particular format. The index is used by cargo to resolve dependencies, as well as the transitive dependencies of your direct dependencies. This is the information cargo uses to figure out which crates it needs to download for you to build your crate.

The index contains a file for each crate in the registry. These files contain information regarding each version of that crate, as a series of newline separated JSON objects. Each object contains information like all of the dependencies and features of that crate, a checksum of the tarball associated with that version, and so on.

To avoid having overly large directories, cargo separates these files into directories based on the initial characters of the package’s name. For example, the foobar package is located in fo/ob/foobar.

Additionally, the index also contains a file in its root named config.json. This file contains a JSON object which must at minimum contain the dl key, which identifies the root URL for the source of crate tarballs.

The full index specification is provided in RFC #2141.

The crate API

The API behind the dl route provides the actual source of the crate dependencies to be built. GET requests to the route $dl-route/$crate-name/$crate-version should return the tarballs generated by the cargo package corresponding to that version of the crate. The sha256sum of the tarball needs to match the the checksum in the index file for that version of the crate.

Warning: cloning crates.io may be ill-advised

Creating and updating a registry manually is not a fun process. Users will almost certainly want some sort of tooling to automate the process of creating and maintaining private registries, but such a tool does not yet exist. We don’t currently intend to sponsor any tool officially; we predict that multiple tools along these lines will develop in the community ecosystem over time.

One solution that seems viable today is to clone the crates.io project and set up and instance of it yourself. This solution will work, but you should be aware that it comes with some downsides:

  • crates.io is a full-fledged web app, and you will be responsible for operationally maintaining your instance of it. This may be more ops overhead than you want to take on.
  • Much of the overhead comes from features that may not be suitable for your use case, such as its web interface, GitHub account auth, and various crate discovery features.
  • As crates.io grows both in features and traffic, it will continue to develop more operational complexity. The development of crates.io will always prioritize its primary use case as the public, default registry over the use cases of anyone operating a clone of it.

Hopefully, other solutions will be developed soon which are specifically targeted at the alternative registries use case, but I wanted to put a little caveat emptor on the notion that cloning crates.io is the obvious way to create your own registry.

Next steps

Though we now support fetching crates from alternative registries, there’s still more to do to fully build out this feature:

  • We want to support performing other operations on alternative registries, such as cargo publish and cargo yank. We have a design for supporting these kinds of commands in alternative registries without having too negative of an experience if the registry isn’t capable of supporting them.
  • We want cargo to manage credentials for multiple registries. This also possibly means support for a wider variety of authentication schemes than cargo possibly knows how to support today.
  • We plan to include a subcommand which generates the metadata necessary to create an index entry for a crate, to assist in writing tooling to create alternative registries.

If you’re interested in getting involved in the rest of this work - or just giving feedback on what we’ve got so far - I encourage you to get in touch with the cargo team. You can reach us in the alternative registries gitter channel, the cargo repository, or the #cargo channel on Mozilla’s IRC server.

Thanks to @cswindle, @carols10cents, @alexcrichton and everyone else for helping to bring this feature into reality!