External Publication
Visit Post

[Pre-RFC] DNS domains as package namespaces

Rust Internals [Unofficial] April 25, 2026
Source

Package namespacing is one of those perennial topics that seems to attract designs but not much implementation. I took my own preferred approach (DNS domains) and got some robots to implement a proof-of-concept.

The code isn't great (and the robots hallucinate various minor details) but overall it's enough to get a feel for how DNS-based namespacing would work.

Cargo branch (raw LLM output): GitHub - jmillikin/upstream__cargo at domain-namespaces-raw-llm-output · GitHub

crates.io branch (raw LLM output): GitHub - jmillikin/upstream__crates.io at domain-namespaces-raw-llm-output · GitHub

I want to get a feeling for how the Cargo / crates.io team feel about the overall concept before I put any effort into cleaning up the proof-of-concept code.

  • My impression from past discussions is that those teams have historically objected to namespaces for aesthetic reasons ("why name your crate boring.com/base64 when you could just name it base64inator9000?").
  • But also opinions may currently be different than they were a year or two ago.

Rough outline of the design:

  • Package names are allowed (not required) to start with a {namespace}/ prefix, where the namespace is a DNS-style dotted name like example.com or jdoe.github.io.
  • This is designed to address name squatting ("I want to publish my wasm crate but there's already an empty crate named wasm").
    • Control over subsets of the global namespace ("My project is named cool so I want to prevent someone else from publishing a crate named cool-wasm) is out of scope.
    • You can use your own domain as a namespace if you care about this, but it doesn't prevent someone else from registering packages with a given prefix either in the global namespace or their own domain.
  • Cargo strips off the namespace prefix before passing the crate name to rustc, so example.com/base64 is base64 to the compiler.
    • If you want to depend on base64 and example.com/base64 in the same crate then you'll need to use existing Cargo mechanisms to rename one in the local compilation context.
  • When a crate is first created on crates.io, if it has a namespace then that namespace will be interpreted as a DNS name and verified by fetching a list of authorized user keys.
    • The URL of the file is https://{namespace}/.well-known/rust-lang.org/package-namespace.json.
    • This file contains a list of keys that identify who is allowed to create packages in that namespace.
    • The key format is determined by the registry. In the case of crates.io it's computed as approximately sha256((namespace, user_id)) -- see gen-auth-key.py for details. Note that user_id part of existing API responses, so no part of this key is secret.
  • The use of https://{namespace}/.well-known/ as opposed to DNS TXT records (etc) is to enable users to publish namespaced crates without having to first pay for a domain.
    • Specifically they can put something on {username}.github.io.
  • Domain verification only happens when a crate is first created. Subsequent uploads use the existing permissions (either a crate owner, or a TrustedPub key).
    • This means that if control of a domain is lost then the new owner can't use that access to publish new versions of existing crates.
  • Whenever the crate name needs to be encoded somewhere that a / is semantically meaningful (URLs, file paths) it gets encoded to %2F.
    • This includes the paths in source archives.
    • Yes this means the crates.io URLs for namespaced crates are kinda ugly. I think this doesn't matter.

Screenshots:

Example package-namespace.json:

{
    "authorized_keys": [
        "sha256-kdRCstHTc6I8OxwyuW+QC8gFkn4tT8/g4a0M//cvCXk="
    ]
}

Example Cargo.toml:

cargo-features = ["domain-namespaces"]

[package]
name = "example.com/hello"
version = "0.1.0"
edition = "2024"

description = "Hello, world!"
license = "0BSD"
repository = "https://github.com/example/hello"
homepage = "https://example.com/hello"
documentation = "https://example.com/hello-docs"

Discussion in the ATmosphere

Loading comments...