recorded decision · public · no signup

accepted2026-06-22rust-lang/rfcs

Crates io username identity

What was chosen

  • Existing active crates.io accounts will have their crates.io username set to their current GitHub username.adr
  • Crates.io will adopt a concept of a "crates.io username" distinct from users' GitHub usernames.adr
  • Users will be able to edit their crates.io username to any available name, making usernames first-come-first-served.adr
  • When a crates.io username differs from the associated GitHub username, a warning icon will be displayed.adr
  • The `cargo owner --add` command will error if a non-prefixed username has a mismatch, requiring disambiguation.adr
  • Typosquatting checks and limits on username changes and re-use will be implemented to mitigate impersonation.adr

What was ruled out· 1

  • Forcing users to specify `cratesio:` or `github:` for every lookup was considered but rejected due to potential confusion.adr

Constraints

  • This change is necessary to enable authentication with various services while minimizing user confusion.adr
  • Crates.io usernames must adhere to specific character rules, uniqueness, length, and reserved name lists.adr

Consequences

  • Crates.io will no longer automatically update a user's crates.io username if their GitHub account is renamed.adr

The recorded why

§ Summary

Someday, we would like to enable people to log in to crates.io with other services in addition to GitHub. This RFC is not yet about adding other services for login. It is proposing that crates.io change to have the concept of a "crates.io username" separate and possibly different from users' GitHub usernames. Crates.io needs this change to make authenticating with different services possible while minimizing confusion.

🚨 After this RFC is accepted and implemented, you will still only be able to log in to crates.io via GitHub. This is a prerequisite of the eventual goal to add other methods of logging in. 🚨

The biggest changes to crates.io as a result of this RFC will be:

  • There will be a crates.io username that may not always match the associated GitHub username
  • Crates.io will no longer automatically update your crates.io username if you rename your GitHub account

§ Motivation

Crates.io's code currently has a one-to-one mapping between crates.io accounts and GitHub accounts. The URL https://crates.io/users/some_username displays the crates owned by the user with the GitHub account some_username, and running cargo owner --add some_username adds some_username as an owner of the current crate. Owners of a crate appear in the sidebar. Crate ownership conveys trust.

Eventually (after future RFCs and additional work after this RFC), we'd like to add the ability to create crates.io accounts by logging in via OAuth with accounts from services other than GitHub, as well as associating OAuth accounts from multiple services to one crates.io account.

The same username on GitHub is not guaranteed to belong to the same person on other services, and one person's usernames across different services are not guaranteed to be the same. When we add more services, crates.io's codebase needs to be able to handle these situations and clearly convey crates.io user identities to minimize the possibility of confusion or deliberate impersonation.

§ Guide-level explanation

This section will address changes to what users will experience on the crates.io website and via the cargo owner CLI.

Today, crates.io usernames always match the GitHub username of the account used to log in to crates.io (with exceptions for renamed or deleted GitHub accounts that will be discussed below). The link between a crates.io account and a GitHub account is public information.

After this change, there will be the concept of a crates.io username that may or may not match the GitHub username of the associated account. When there are multiple ways of logging in in the future, the crates.io username may or may not match the usernames on the other services. All existing active (that is, not deleted or renamed) accounts will have their crates.io username set to their current username, their GitHub username.

When you visit your account settings page, you will be able to edit your username to anything that isn't already claimed as a crates.io username. Thus, crates.io usernames will become first-come-first-served as crate names are today. Crates.io admins will not change an account's username without the consent of the current username holder, except in cases such as Code of Conduct or usage policy violations like impersonation (see Unresolved Questions about username squatting).

When you visit a user's page at https://crates.io/users/example_username or see a user account listed as an owner of a crate in the crate's sidebar and the account's crates.io username differs from the GitHub username associated with the account, you will see some sort of icon to indicate that something is different (this RFC will use the warning icon ⚠️, but we may decide to use something more neutral such as an "information" ℹ️, to be determined during implementation). Accompanying text will says something like "username does not match GitHub username". Given that the common case, and what people are used to being able to know, will be that the GitHub and crates.io usernames will match, this will make it obvious in cases where that assumption does not hold. We may decide after some transition period (say, 1-2 years) that the username mismatch warning is no longer needed (especially once crates.io supports OAuth services other than GitHub).

If you run cargo owner --add example_username and the account's crates.io username differs from the GitHub username associated with the account, the command will error with a message similar to:

$ cargo owner --add example_username
error: username `example_username` is possibly ambiguous

Caused by:
  The crates.io account `example_username` is associated with:

  - https://github.com/something_else
  - [any other accounts once we have that ability]

  To confirm this is the account you want to add, please run one of the following:

  $ cargo owner --add cratesio:example_username
  $ cargo owner --add github:something_else

  If this is not the account you want to add, verify the crates.io username of the account you want.

Returning an error and requesting the user re-run a command with a disambiguation prefix to confirm is the easiest way to maintain compatibility with existing versions of Cargo. With some additional work on Cargo, newer versions could be made that only require a y or n confirmation; see the "Prior Art" section on Keybase for one possibility.

After this RFC is implemented, if you create an account on crates.io with an OAuth account (GitHub or otherwise), whether or not the associated OAuth account's username is currently claimed on crates.io, you will be asked to register your crates.io account by choosing a username that hasn't yet been taken on crates.io. The crates.io username field will be prefilled with the associated OAuth account's username and an indication of whether that username is available on crates.io or not.

Renamed and deleted GitHub accounts

GitHub allows users to change their username (but keep the same GitHub ID number so that crates.io can know it's the same account) or delete their account, which makes the username available for someone else to claim (with a different GitHub ID number than was previously associated with it).

Crates.io currently does not proactively check GitHub for account status, at the time of this RFC's drafting, but we hope to implement proactive checking before this RFC is implemented and independently of this RFC's acceptance. Before this RFC is implemented, we would check with GitHub for all renames/deletes to update crates.io's usernames one last time. After this RFC is implemented, the script would be changed to only update the oauth_github.username and not the crates.io username.

There's still a possibility of a window of time between when a user renames or deletes their GitHub account and when crates.io learns about it (depending on how often we check each account), during which the following scenarios could occur.

If someone takes these actions:

  1. Creates GitHub account with the username "example"
  2. Logs in to crates.io with that account so that they have the crates.io username "example"
  3. Renames their GitHub account to "something_else"
  4. Never logs in to crates.io with that GitHub account again

Their crates.io username will remain "example", until such a point that they decide to log in to crates.io again. Currently, crates.io will then update their username in our database to match.

This RFC proposes decoupling GitHub account renaming from crates.io username completely, so that GitHub account renames do NOT automatically become crates.io account renames.

A similar situation occurs when a user deletes their GitHub account. The username "example" will then be available for someone else to claim on GitHub, but will remain claimed on crates.io.

If a different user does one of the following:

  • Creates the GitHub account "example" and logs in to crates.io
  • Tries to edit their username to "example"
  • Logs in to crates.io with an account on some service other than GitHub with username "example"

At that point, crates.io will:

  • See that the crates.io username "example" is taken
  • Require the user with the GitHub username "example" to pick a different crates.io username

If the old "example" account had it via their associated GitHub account (and thus didn't have the mismatch ⚠️ warning discussed above), then a new associated GitHub account logs in with the GitHub username "example" (and a different GitHub ID), at that point we know the GitHub account "example" does NOT belong to the crates.io account "example" and the crates.io account "example" should get the mismatch ⚠️ warning.

If a user manually changes their crates.io username to best_rust_programmer_ever (and doesn't have the matching GitHub account and thus has the warning symbol), and then later someone creates a GitHub account with the username best_rust_programmer_ever and logs in to crates.io, the GitHub user best_rust_programmer_ever will need to choose a different crates.io username. Both crates.io accounts will have the warning symbol. The latter user may see this as unfair, but this is where the first-come-first-serve policy should be enforced.

Crates.io username requirements

Crates.io usernames will largely use the same rules that GitHub usernames use today. All existing crates.io accounts will be valid under whatever rules we decide on.

Crates.io usernames must:

  • Only contain alphanumeric characters [a-zA-Z0-9], hyphens -, and underscores _ 1.
  • Be unique case insensitively and hyphen/underscore insensitively, much like crate names. That is, uniqueness will be determined by normalizing case and normalizing hyphens and underscores together. For example, the crates.io usernames hello-there and Hello_There will be considered to be the same: once a user named hello-there exists, a user named Hello_There will not be allowed.
  • Not start with a hyphen or underscore. See Unresolved Questions for why not prohibiting ending with these characters or prohibiting two of these characters in a row.
  • Not exceed 39 characters.

We have a list of reserved crate names that no one may register that includes top-level Rust standard library modules and keywords, reserved Windows filenames, and some swear words or slurs (which will never be exhaustive but contains the most common ones in English). We'll have a similar list of reserved usernames that no one may use; GitHub's Terms of Service is providing us some protection currently that we'd need to manage ourselves.

These requirements will be clearly documented on a page on crates.io as well as in the signup form when we are requiring the person to pick a crates.io username.

Crates.io account rename restrictions

The biggest concerns with allowing crates.io username changes are impersonation and resurrection attacks.

Impersonation is already possible and is already against crates.io policies, but of course we don't want to make it easier to falsely gain the trust of crates.io users by pretending to be a well-known person. We plan to add typosquatting checks on usernames similar to those we're already doing for crate names. We also plan to limit how often you can change your crates.io username (say, not more often than once every 30 days).

Resurrection attacks are a subset of impersonation, where a user named carols10cents, for example, renames away from that username or deletes their account and another user claims the carols10cents username to appear to be that person to users who don't know about the rename or deletion. We plan to limit the re-use of usernames, using a similar mechanism that we have today that prevents re-use of a deleted crate name, so that no one could claim an abandonded username for, say, 30 days. We will allow the person who changed away from a username to reclaim/"revert to" that username within the re-use prevention period.

We also plan to mitigate the effectiveness of impersonation attacks by making the display of the linked accounts associated with a crates.io account very clear so that anyone is able to feel confident that the crates.io account has the same owner as the GitHub, GitLab, etc account they trust.

In the database, accessible by admins only, we will track history of username changes (starting from whenever the feature is implemented; we don't have historical data of GitHub username changes). This could be useful for forensic investigation of accounts that may be attempting to impersonate other users. We could display historical usernames and their dates on a user's page for transparency, but this would be problematic in cases such as someone transitioning and wanting to remove all association with their deadname (if their name was part of their crates.io username). We will update the privacy policy section on crates.io to make this retention clear, and we will delete even admin-only viewable information from the database on request.

Currently, the knowledge that a crates.io account corresponds to a GitHub account is public information. This link is displayed on user pages and is present in the database dumps. This RFC is deliberately going to continue this status quo and publicly display the GitHub account that a crates.io account is linked with (whether or not the usernames match). We will add documentation in the signup process making it clear that knowledge of your GitHub account will be publicly associated with your crates.io account, and that you should not make a crates.io account with any GitHub account you do not want to have linked with your work on crates.io. See the [Private Linked Accounts][private-linked-accounts] section under Future Possibilities for the possibility of adding a way to have a private linked account in the future.

§ Reference-level explanation

This section will address changes to crates.io's HTTP API. It will not address low-level implementation details of the crates.io database schema or backend code changes; those will be worked out during implementation of this RFC.

User API

The find_user API is currently defined to respond to URLs in the form /api/v1/users/{user}, where {user} is currently the GitHub username that crates.io has been told about for that account.

This route would be changed such that {user} would be assumed to be the crates.io username only. So for a user with crates.io username carols10cents and GitHub username carolgithub, the API request /api/v1/users/carols10cents would return this user's information, and requesting /api/v1/users/carolgithub would return a 404 Not Found, because there is no crates.io username carolgithub.

We could choose to have this route's implementation attempt a lookup in the table of GitHub usernames (and eventually in other services' tables when those are supported) if no crates.io username is found, but that seems like it could cause confusion.

See the Unresolved Questions section for possibly changing this API's parameter to optionally accept a prefix with the username to allow lookup by GitHub (or other service) username.

/api/v1/users/{user} currently returns this information (for the carols10cents user):

{
	"user": {
		"id": 396, // crates.io database ID
		"login": "carols10cents", // crates.io and GitHub username
		"name": "Carol (Nichols || Goulding)", // display name (as set in GitHub)
		"avatar": "https://avatars.githubusercontent.com/u/193874?v=4", // from GitHub
		"url": "https://github.com/carols10cents" // assumes GitHub
	}
}

This RFC would change the following:

  • login would be the crates.io username
  • name would be deprecated and the UI would use login (we could continue to provide the name attribute with its value set to login to ease migration. Also see the "display name" Unresolved Question)
  • There would still be an avatar URL returned; for how that is managed, see the Unresolved Questions
  • url would be deprecated in favor of explicitly requesting linked account information (we could continue to provide a GitHub URL if the user has a linked GitHub account)
  • A field named github_username_matches (with a value of a boolean that the user's GitHub username is either the same as, or different from, their crates.io username)

So that the response eventually looks like this (without the values supporting deprecated fields that we may choose to offer for more compatibility) for crates.io user carols10cents that has GitHub username carolgithub:

{
	"user": {
		"id": 396, // same as before; crates.io database ID
		"login": "carols10cents", // now the crates.io username
		"avatar": "https://avatars.githubusercontent.com/u/193874?v=4",
        "github_username_matches": false,
	}
}

This request would require querying the users table and the oauth_github table, but not any other services' linked tables to limit database load by default. The github_username_matches would be used to decide whether to show the ⚠️ warning about username mismatches discussed in the Guide section.

If desired, the requester could instead use /api/v1/users/{user}?include=linked_accounts (much like the current crate API allows for opt-in of returning related data) which would query all OAuth tables and return all account information for this user once crates.io supports more services:

{
	"user": {
		"id": 396, // same as before; crates.io database ID
		"login": "carols10cents", // now the crates.io username
		"avatar": "https://avatars.githubusercontent.com/u/193874?v=4",
        "github_username_matches": false,
	},
    "linked_accounts": [
        {
            "service": "github",
            "login": "carolgithub",
            "avatar": "https://avatars.githubusercontent.com/u/193874?v=4"
        },
        {
            "service": "gitlab",
            "login": "carols10cents"
            "avatar": "https://secure.gravatar.com/avatar/5eefdbf7a532f1a36d5cdce703a3b346cadbebc6098c4fce5354af871f662f55"
        }
    ]
}

The ?include=linked_accounts variant would be called by the frontend when visiting https://crates.io/users/carols10cents, to be able to display all of a user's linked accounts.

We would likely not request linked accounts for crate pages when displaying ownership information; we could add a way to view that information on a crate page on-demand, for example when hovering over an owner (or tapping on an icon next to the owner that the frontend shows when viewed on mobile devices), we could request the linked account information then and display a "detail card" for that owner showing the information on their linked accounts.

Owner APIs

The current API request for inviting user owners or adding

§ Footnotes

  1. Even though github.com does not allow you to create an account with a username that contains an underscore, Enterprise Managed Users get a username that ends in _[enterprise shortcode]. We have accounts of this sort in crates.io's database today.

Excerpt — the full document is at the cited source: text/3946-crates-io-username-identity.md