|
| 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