Skip to content

Commit 206318a

Browse files
authored
Merge pull request #2141 from integer32llc/alternative-registries
Add support to Cargo for alternative registries
2 parents 09e34be + cbae4a3 commit 206318a

File tree

1 file changed

+365
-0
lines changed

1 file changed

+365
-0
lines changed

text/2141-alternative-registries.md

+365
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
1+
- Feature Name: cargo_alternative_registries
2+
- Start Date: 2017-09-06
3+
- RFC PR: https://github.com/rust-lang/rfcs/pull/2141
4+
- Rust Issue: https://github.com/rust-lang/rust/issues/44931
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
This RFC proposes the addition of the support for alternative crates.io servers to be used
10+
alongside the public crates.io server. This would allow users to publish crates to their own
11+
private instance of crates.io, while still able to use the public instance of crates.io.
12+
13+
# Motivation
14+
[motivation]: #motivation
15+
16+
Cargo currently has support for getting crates from a public server, which works well for open
17+
source projects using Rust, however is problematic for closed source code. A workaround for this is
18+
to use Git repositories to specify the packages, but that means that the helpful versioning and
19+
discoverability that Cargo and crates.io provides is lost. We would like to change this such that
20+
it is possible to have a local crates.io server which crates can be pushed to, while still making
21+
use of the public crates.io server.
22+
23+
# Guide-level explanation
24+
[guide-level-explanation]: #guide-level-explanation
25+
26+
## Registry definition specification
27+
[registry-definition-specification]: #registry-definition-specification
28+
29+
We need a way to define what registries are valid for Cargo to pull from and publish to. For this
30+
purpose, we propose that users would be able to define multiple registries in a [`.cargo/config`
31+
file](http://doc.crates.io/config.html). This allows the user to specify the locations of
32+
registries in one place, in a parent directory of all projects, rather than needing to configure
33+
the registry location within each project's `Cargo.toml`. Once a registry has been configured with
34+
a name, each `Cargo.toml` can use the registry name to refer to that registry.
35+
36+
Another benefit of using `.cargo/config` is that these files are not typically checked in to the
37+
projects' source control. The registries might have credentials associated with them, which should
38+
not be checked in. Separating the URLs and the use of the URLs in this way encourages good security
39+
practices of not checking in credentials.
40+
41+
In order to tell Cargo about a registry other than crates.io, you can specify and name it in a
42+
`.cargo/config` as follows, under the `registries` key:
43+
44+
```toml
45+
[registries]
46+
choose-a-name = "https://my-intranet:8080/index"
47+
```
48+
49+
Instead of `choose-a-name`, place the name you'd like to use to refer to this registry in your
50+
`Cargo.toml` files. The URL specified should contain the location of the registry index for this
51+
registry; the registry format is specified in the [Registry Index Format Specification
52+
section][registry-index-format-specification].
53+
54+
Alternatively, you can specify each registry as follows:
55+
56+
```toml
57+
[registries.choose-a-name]
58+
index = "https://my-intranet:8080/index"
59+
```
60+
61+
If you need to specify authentication information such as a username or password to access a
62+
registry's index, those should be specified in a `.cargo/credentials` file since it has more
63+
restrictive file permissions than `.cargo/config`. Adding a username and password to
64+
`.cargo/credentials` for a registry named `my-registry` would look like this:
65+
66+
```toml
67+
[registries.my-registry]
68+
username = "myusername"
69+
password = "mypassword"
70+
```
71+
72+
### CI
73+
74+
Because this system discourages checking in the registry configuration, the registry configuration
75+
won't be immediately available to continuous integration systems like TravisCI. However, Cargo
76+
currently supports configuring any key in `.cargo/config` using environment variables instead:
77+
78+
> Cargo can also be configured through environment variables in addition to the TOML syntax above.
79+
> For each configuration key above of the form `foo.bar` the environment variable `CARGO_FOO_BAR`
80+
> can also be used to define the value. For example the build.jobs key can also be defined by
81+
> `CARGO_BUILD_JOBS`.
82+
83+
To configure TravisCI to use an alternate registry named `my-registry` for example, you can use
84+
[Travis' encrypted environment variables feature](https://docs.travis-ci.com/user/environment-variables/#Defining-encrypted-variables-in-.travis.yml) to set:
85+
86+
`CARGO_REGISTRY_MY_REGISTRY_INDEX=https://my-intranet:8080/index`
87+
88+
## Using a dependency from another registry
89+
90+
*Note: this syntax will initially be implemented as an [unstable cargo
91+
feature](https://github.com/rust-lang/cargo/pull/4433) available in nightly cargo only and
92+
stabilized as it becomes ready.*
93+
94+
Once you've configured a registry (with a name, for example, `my-registry`) in `.cargo/config`, you
95+
can specify that a dependency comes from an alternate registry by using the `registry` key:
96+
97+
```toml
98+
[dependencies]
99+
secret-crate = { version = "1.0", registry = "my-registry" }
100+
```
101+
102+
## Publishing to another registry; preventing unwanted publishes
103+
104+
Today, Cargo allows you to add a key `publish = false` to your Cargo.toml to indicate that you do
105+
not want to publish a crate anywhere. In order to specify that a crate should only be published to
106+
a particular set of registries, this key will be extended to accept a list of registries that are
107+
allowed with `cargo publish`:
108+
109+
```
110+
publish = ["my-registry"]
111+
```
112+
113+
If you run `cargo publish` without specifying an `--index` argument pointing to an allowed
114+
registry, the command will fail. This prevents accidental publishes of private crates to crates.io,
115+
for example.
116+
117+
Not having a `publish` key is equivalent to specifying `publish = true`, which means publishing to
118+
crates.io is allowed. `publish = []` is equivalent to `publish = false`, meaning that publishing to
119+
anywhere is disallowed.
120+
121+
## Running a minimal registry
122+
123+
The most minimal form of a registry that Cargo can use will consist of:
124+
125+
- A registry in the format specified in the [Registry index format specification
126+
section][registry-index-format-specification], which contains a pointer to:
127+
- A location containing the `.crate` files for the crates in the registry.
128+
129+
## Running a fully-featured registry
130+
131+
This RFC does not attempt to standardize or specify any of crates.io's APIs, but it should be
132+
possible to take crates.io's codebase and run it along with a registry index in order to provide
133+
crates.io's functionality as an alternate registry.
134+
135+
## Crates.io
136+
137+
Because crates.io's purpose is to be a reliable host for open source crates, crates that have
138+
dependencies from registries other than crates.io will be rejected at publish time. Crates.io
139+
cannot make availability guarantees about alternate registries, so much like git dependencies
140+
today, publishing with dependencies from other registries won't be allowed.
141+
142+
In crates.io's codebase, we will add a configuration option that specifies a list of approved
143+
alternate registry locations that dependencies may use. For private registries run using
144+
crates.io's code, this will likely include the private registry itself plus crates.io, so that
145+
private crates are allowed to depend on open source crates. Any crates with dependencies from
146+
registries not specified in this configuration option will be rejected at publish time.
147+
148+
## Interaction with existing features
149+
150+
This RFC is not proposing any changes to the way [source
151+
replacement](http://doc.crates.io/source-replacement.html) and
152+
[cargo-vendor](https://crates.io/crates/cargo-vendor) work; everything proposed here should be
153+
compatible with those.
154+
155+
Mirrors will still be required to serve exactly the same files (matched checksums) as the source
156+
they're mirroring.
157+
158+
# Reference-level explanation
159+
[reference-level-explanation]: #reference-level-explanation
160+
161+
## Registry index format specification
162+
[registry-index-format-specification]: #registry-index-format-specification
163+
164+
Cargo needs to be able to get a registry index containing metadata for all crates and their
165+
dependencies available from an alternate registry in order to perform offline version resolution.
166+
The registry index for crates.io is available at
167+
[https://github.com/rust-lang/crates.io-index](https://github.com/rust-lang/crates.io-index), and
168+
this section aims to specify the format of this registry index so that other registries can provide
169+
their own registry index that Cargo will understand.
170+
171+
This is version 1 of the registry index format specification. There may be other versions of the
172+
specification someday. Along with a new specification version will be a plan for supporting
173+
registries using the older specification and a migration plan for registries to upgrade the
174+
specification version their index is using.
175+
176+
A valid registry index meets the following criteria:
177+
178+
- The registry index is stored in a git repository so that Cargo can efficiently fetch incremental
179+
updates to the index.
180+
- There will be a file at the top level named `config.json`. This file will be a valid JSON object
181+
with the following keys:
182+
183+
```json
184+
{
185+
"dl": "https://my-crates-server.com/api/v1/crates",
186+
"api": "https://my-crates-server.com/",
187+
"allowed-registries": ["https://github.com/rust-lang/crates.io-index", "https://my-intranet:8080/index"]
188+
}
189+
```
190+
191+
The `dl` key is required specifies where Cargo can download the tarballs containing the source
192+
files of the crates listed in the registry.
193+
194+
The `api` key is optional and specifies where Cargo can find the API server that provides the
195+
same API functionality that crates.io does today, such as publishing and searching. Without the
196+
`api` key, these features will not be available. This RFC is not attempting to standardize
197+
crates.io's API in any way, although that could be a future enhancement.
198+
199+
The `allowed-registries` key is optional and specifies the other registries that crates in this
200+
index are allowed to have dependencies on. The default will be nothing, which will mean only
201+
crates that depend on other crates in the current registry are allowed. This is currently the
202+
case for crates.io and will remain the case for crates.io going forward. Alternate registries
203+
will probably want to add crates.io to this list.
204+
205+
- There will be a number of directories in the git repository.
206+
- `1/` - holds files for all crates whose names have one letter.
207+
- `2/` - holds files for all crates whose names have two letters.
208+
- `3/` - holds files for all crates whose names have three letters.
209+
- `aa/aa/` etc - for all crates whose names have four or more letters, their
210+
files will be in a directory named with the first and second letters of
211+
their name, then in a subdirectory named with the third and fourth letters
212+
of their name. For example, a file for a crate named `sample` would be
213+
found in `sa/mp/`.
214+
215+
- For each crate in the registry, there will be a file with the name of that crate in the directory
216+
structure as specified above. The file will contain metadata about each version of the crate,
217+
with one version per line. Each line will be valid JSON with, minimally, the keys as shown. More
218+
keys may be added, but Cargo may ignore them. The contents of one line are pretty-printed here
219+
for readability.
220+
221+
```json
222+
{
223+
"name": "my_serde",
224+
"vers": "1.0.11",
225+
"deps": [
226+
{
227+
"name": "serde",
228+
"req": "^1.0",
229+
"registry": "https://github.com/rust-lang/crates.io-index",
230+
"features": [],
231+
"optional": true,
232+
"default_features": true,
233+
"target": null,
234+
"kind": "normal"
235+
}
236+
],
237+
"cksum": "f7726f29ddf9731b17ff113c461e362c381d9d69433f79de4f3dd572488823e9",
238+
"features": {
239+
"default": [
240+
"std"
241+
],
242+
"derive": [
243+
"serde_derive"
244+
],
245+
"std": [
246+
247+
],
248+
},
249+
"yanked": false
250+
}
251+
```
252+
253+
The top-level keys for a crate are:
254+
255+
- `name`: the name of the crate
256+
- `vers`: the version of the crate this row is describing
257+
- `deps`: a list of all dependencies of this crate
258+
- `cksum`: a SHA256 checksum of the tarball downloaded
259+
- `features`: a list of the features available from this crate
260+
- `yanked`: whether or not this version has been yanked
261+
262+
Within the `deps` list, each dependency should be listed as an item in the `deps` array with the
263+
following keys:
264+
265+
- `name`: the name of the dependency
266+
- `req`: the semver version requirement string on this dependency
267+
- `registry`: **New to this RFC: the registry from which this crate is available**
268+
- `features`: a list of the features available from this crate
269+
- `optional`: whether this dependency is optional or not
270+
- `default_features`: whether the parent uses the default features of this dependency or not
271+
- `target`: on which target this dependency is needed
272+
- `kind`: can be `normal`, `build`, or `dev` to be a regular dependency, a build-time
273+
dependency, or a development dependency
274+
275+
If a dependency's registry is not specified, Cargo will assume the dependency can be located in the
276+
current registry. By specifying the registry of a dependency in the index, cargo will have the
277+
information it needs to fetch crate files from the registry indices involved without needing to
278+
involve an API server.
279+
280+
## New command: `cargo generate-index-metadata`
281+
282+
Currently, the knowledge of how to create a file in the registry index format is spread between
283+
Cargo and crates.io. This RFC proposes the addition of a Cargo command that would generate this
284+
file locally for the current crate so that it can be added to the git repository using a mechanism
285+
other than a server running crates.io's codebase.
286+
287+
## Related issues
288+
289+
In order to make working with multiple registries more convenient, we would also like to support:
290+
291+
- Adding a `cargo add-registry` command that could prompt for index URL and authentication
292+
information and place the right information in the right format in the right files to make setup
293+
for each user easier.
294+
- [Being able to specify the API location rather than the index
295+
location](https://github.com/rust-lang/cargo/issues/4208), so that, for example, you could
296+
specify `https://host.company.com/api/cargo/private-repo` rather than
297+
`https://github.com/host-company/cargo-index`. We do not want to *require* specifying the API
298+
location, since some registries will choose not to have an API at all and only supply an index
299+
and a location for crate files. This would require the API to have a way to tell Cargo where the
300+
associated registry index is located.
301+
- [Being able to save multiple tokens in
302+
`.cargo/credentials`](https://github.com/rust-lang/cargo/issues/3365), one per registry, so that
303+
people publishing to multiple registries don't need to log in over and over or specify tokens on
304+
every publish.
305+
- Being able to specify `--registry registry-name` for all Cargo commands that currently take
306+
`--index`
307+
- Being able to use a dependency under a different name. Alternate registries that are not mirrors
308+
should be allowed to have crates with the same name as crates in any other registry, including
309+
crates.io. In order to allow a crate to depend on both, say, the `http` crate from crates.io and
310+
the `http` crate from a private registry, at least one will need to be renamed when listed as a
311+
dependency in `Cargo.toml`. [RFC
312+
2126](https://github.com/aturon/rfcs/blob/path-clarity/text/0000-path-clarity.md#basic-changes)
313+
proposes this change as follows:
314+
315+
> Cargo will provide a new crate key for aliasing dependencies, so that e.g. users who want to
316+
> use the `rand` crate but call it `random` instead can now write `random = { version = "0.3",
317+
> crate = "rand" }`.
318+
319+
- Being able to use environment variables to specify values in `.cargo/credentials` in the same way
320+
that you can use environment variables to specify values in `.cargo/config`
321+
- For registries that don't require any authentication to access, such as public registries or
322+
registries only accessible within a firewall, we could support a shorthand where the index
323+
location (or API location when that is supported) is specified entirely within a crate dependency:
324+
325+
```toml
326+
[dependencies]
327+
my-crate = { version = "1.0", registry = "http://crate-mirror.org/index" }
328+
```
329+
330+
In order to discourage/disallow credentials checked in to `Cargo.toml`, if the URL contains a
331+
username or password, Cargo will deliberately remove it. If the registry is then inaccessible,
332+
the error message will mention that usernames and passwords in URLs in `Cargo.toml` are not
333+
allowed.
334+
335+
# Drawbacks
336+
[drawbacks]: #drawbacks
337+
338+
Supporting alternative registries, and having multiple public registries, could fracture the
339+
ecosystem. However, we feel that supporting private registries, and the Rust adoption that could
340+
enable, outweighs the potential downsides of having multiple public registries.
341+
342+
# Rationale and Alternatives
343+
[alternatives]: #alternatives
344+
345+
A [previous RFC](https://github.com/rust-lang/rfcs/pull/2006) proposed having the registry
346+
information completely defined within `Cargo.toml` rather than using `.cargo/config`. This requires
347+
repeating the same information multiple times for multiple projects, and encourages checking in
348+
credentials that might be needed to access the registries. That RFC also didn't specify the format
349+
for the registry index, which needs to be shared among all registries.
350+
351+
An alternative design could be to support specifying the registry URL in either `.cargo/config` or
352+
`Cargo.toml`. This has the downsides of creating more choices for the user and potentially
353+
encouraging poor practices such as checking credentials into a project's source control. The
354+
implementation of this feature would also be more complex. The upside would be supporting
355+
configuration in ways that would be more convenient in various situations.
356+
357+
# Unresolved questions
358+
[unresolved]: #unresolved-questions
359+
360+
- Are the names of everything what we want?
361+
- `cargo generate-index-metadata`?
362+
- `registry = my-registry`?
363+
- `publish-registries = []`?
364+
365+
- What kinds of authentication parameters do we need to support in `.cargo/credentials`?

0 commit comments

Comments
 (0)