Skip to content

rustPlatform.fetchCargoVendor: set a custom User-Agent#512735

Merged
emilazy merged 3 commits into
NixOS:masterfrom
andresilva:rust-fetch-cargo-vendor-user-agent
Apr 26, 2026
Merged

rustPlatform.fetchCargoVendor: set a custom User-Agent#512735
emilazy merged 3 commits into
NixOS:masterfrom
andresilva:rust-fetch-cargo-vendor-user-agent

Conversation

@andresilva
Copy link
Copy Markdown
Member

@andresilva andresilva commented Apr 23, 2026

Rust package builds using fetchCargoVendor have started failing intermittently with HTTP 403 responses when downloading crate tarballs from https://crates.io/api/v1/crates/<name>/<version>/download. The failures are not caused by any recent change in nixpkgs, they're triggered server-side by crates.io, which has begun blocklisting specific python-requests/<version> User-Agent strings. Testing directly against crates.io confirms this: python-requests/2.32.5 and python-requests/2.33.1 both return 403, while older versions, empty UAs, and any custom string return 302 as expected.

fetch-cargo-vendor-util.py builds its requests.Session without overriding the default User-Agent, so every tarball request goes out as python-requests/<version> and gets rejected. This also lines up with crates.io's long-standing published policy asking clients to identify themselves with a contact URL (https://crates.io/data-access), which the default UA doesn't satisfy.

The fix is a one-line change in create_http_session() to set User-Agent: nixpkgs fetchCargoVendor/2 (https://github.com/NixOS/nixpkgs) on the session. This identifies the client per crates.io's guidance and sidesteps the version-pattern blocklist. The PR also switches the download URL to https://static.crates.io/crates/<name>/<version>/download to bypass the 1 req/s rate limit on the API servers, as recommended by the crates.io maintainer in rust-lang/crates.io#13482.

Landing this naively would change fetch-cargo-vendor-util.py, which is an input to both the FOD vendorStaging stage (where HTTP downloads happen and the UA matters) and the input-addressed -vendor runCommand stage (where it doesn't). Even though vendorStaging is an FOD and its output path is stable, touching the util referenced by the -vendor stage rewrites every downstream buildRustPackage derivation in the tree, a mass rebuild that has to go through staging. To avoid that, fetch-cargo-vendor-util.py is left untouched and the fix is applied in a parallel copy fetch-cargo-vendor-util-v2.py that's wired only into the FOD. Since the FOD output is content-addressed by declared hash, the rest of the world sees no change. The duplication is deliberate and temporary, it can be collapsed back into a single file on the next staging cycle.

Things done

  • Built on platform:
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • Tested, as applicable:
  • Ran nixpkgs-review on this PR. See nixpkgs-review usage.
  • Tested basic functionality of all binary files, usually in ./result/bin/.
  • Nixpkgs Release Notes
    • Package update: when the change is major or breaking.
  • NixOS Release Notes
    • Module addition: when adding a new NixOS module.
    • Module update: when the change is significant.
  • Fits CONTRIBUTING.md, pkgs/README.md, maintainers/README.md and other READMEs.

github-actions[bot]

This comment was marked as outdated.

@andresilva
Copy link
Copy Markdown
Member Author

This is a mass rebuild but right now I can't build any rust package, so not sure whether this should still go through staging. Maybe this is also targeted by geo IP and other people aren't getting 403?

@nixpkgs-ci nixpkgs-ci Bot requested a review from TomaSajt April 23, 2026 10:26
@nixpkgs-ci nixpkgs-ci Bot added 10.rebuild-linux: 501+ This PR causes many rebuilds on Linux and should normally target the staging branches. 10.rebuild-darwin: 501+ This PR causes many rebuilds on Darwin and should normally target the staging branches. 10.rebuild-darwin: 5001+ This PR causes many rebuilds on Darwin and must target the staging branches. 10.rebuild-linux: 5001+ This PR causes many rebuilds on Linux and must target the staging branches. labels Apr 23, 2026
@lunagl
Copy link
Copy Markdown
Contributor

lunagl commented Apr 23, 2026

Can confirm this is an issue for me too (thank you so much for submitting this PR, I just spent a while desperately trying to figure out what's going on lol)

For some reason it doesn't block all requests, maybe it's still being rolled out or something somewhere is doing caching?

Oh just so people can find here when searching for the error message:

 Exception: Failed to fetch file from https://crates.io/api/v1/crates/<name>/<version>/download. Status code: 403

@B4dM4n
Copy link
Copy Markdown
Contributor

B4dM4n commented Apr 23, 2026

I also experienced this today. Changing the user-agent fixed the issue for me.

While testing, I added the response.text to the raised exception, which revealed the following message (manually formatted):

{
  "errors": [
    {
      "detail": "We are unable to process your request at this time. This usually means that you are
        in violation of our API data access policy (https://crates.io/data-access). Please email
        help@crates.io and provide the request id b874fda9-5523-3db7-3c1a-f84d3db0a664"
    }
  ]
}

The https://crates.io/data-access#api page states:

  1. A maximum of 1 request per second, and
  2. A user-agent header that identifies your application. We strongly suggest providing a way for us to contact you (whether through a repository, or an e-mail address, or whatever is appropriate) so that we can reach out to work with you should there be issues.

Setting the user-agent would conform to 2., which sounds like the correct solution. It seems 1. is not actually enforced while downloading crates or the existing retry logic handles rate limits.

@NickCao
Copy link
Copy Markdown
Member

NickCao commented Apr 23, 2026

Why would this create a mass rebuild? These are all FODs?

@andresilva
Copy link
Copy Markdown
Member Author

andresilva commented Apr 23, 2026

@NickCao The tarball downloads themselves are FOD-cached, but fetch-cargo-vendor.nix has a second stage right after:

runCommand "${name}-vendor" {
  inherit vendorStaging;
  nativeBuildInputs = [ fetchCargoVendorUtil cargo replaceWorkspaceValues ];
} ''
  fetch-cargo-vendor-util create-vendor "$vendorStaging" "$out"
''

That -vendor derivation is the one that's actually consumed as cargoDeps by buildRustPackage and has no outputHash, so it's input-addressed. Its store path depends on the util's store path, which this PR changes. At least that's my understanding.

@NickCao
Copy link
Copy Markdown
Member

NickCao commented Apr 23, 2026

but fetch-cargo-vendor.nix has a second stage right after:

That's unfortunate, I hope crates.io can grant us an exception for now.

@TomaSajt
Copy link
Copy Markdown
Contributor

The staging part is a FOD, but there is a non-staging part, which is not a FOD.
And since both use the same file for their logic, it leads to a rebuild...
I have proposed some solutions to this rebuild issue, but have not pushed for them too strong.

(e.g. separating files or ditching the non-staging part completely and moving it into the setup hook)

@emilazy
Copy link
Copy Markdown
Member

emilazy commented Apr 23, 2026

For now we can copy the Python file, fix the user agent in only one of them, and use that one in the FOD. That should avoid rebuilds and can be cleaned up on staging.

@niklaskorz niklaskorz mentioned this pull request Apr 23, 2026
3 tasks
)
session = requests.Session()
# crates.io blocks the default python-requests/<version> User-Agent
session.headers["User-Agent"] = "nixpkgs fetchCargoVendor (https://github.com/NixOS/nixpkgs)"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nixpkgs issues can easily go unnoticed if they don't ping anyone. I'm wondering if we should include a suggestion to ping here or perhaps an email address (Nixpkgs core would be fine for this although it would likely be pure indirection since I expect we'd just end up opening an issue ourselves).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also I think it would be a good idea to include some kind of version information here. That could be a manual integer specific to fetchCargoVendor for now as including the Nixpkgs version info would require the split Toma mentioned (so that release manager tasks don't go to staging).

@emilazy
Copy link
Copy Markdown
Member

emilazy commented Apr 23, 2026

From the upstream issue:

I've temporarily disabled the block for the python-requests UAs again.

I would also recommend that you download the files directly from the CDN instead of going through our API servers: e.g. https://static.crates.io/crates/libc/0.2.185/download or https://static.crates.io/crates/libc/libc-0.2.185.crate. That addresses the rate limit concern mentioned in NixOS/nixpkgs#512735 (comment) since the 1 req/sec limit only applies to our API servers.

for completeness, the technically correct path for downloading crates is: load https://index.crates.io/config.json, look at the dl key in the JSON file, build the download URL via {dl}/{name}/{version}/download.

Since it sounds like the rate limit isn't an immediate problem for us, I suggest we do the file-copying hack now on unstable and 25.11 to get a UA set so the crates.io admins can reinstate the block, and if it would be feasible when cleaning up on staging, adjust the fetcher to follow the config.json protocol and possibly split up FOD-relevant parts of the fetcher cleanly. That should avoid hitting their API server at all and I wouldn't be surprised if it means faster downloads.

(importCargoLock will presumably need similar adjustment if it doesn't do this already? But is rarely used within Nixpkgs therefore less urgent.)

@andresilva
Copy link
Copy Markdown
Member Author

@emilazy Will push in a bit, thanks for the suggestions 🙏

@andresilva andresilva force-pushed the rust-fetch-cargo-vendor-user-agent branch from ff2edda to 5a4b429 Compare April 23, 2026 14:33
@@ -0,0 +1,416 @@
import functools
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

diff pkgs/build-support/rust/fetch-cargo-vendor-util.py pkgs/build-support/rust/fetch-cargo-vendor-util-v2.py
42a43
>     session.headers["User-Agent"] = "nixpkgs fetchCargoVendor/2 (https://github.com/NixOS/nixpkgs)"
71c72,74
<     return f"https://crates.io/api/v1/crates/{pkg["name"]}/{pkg["version"]}/download"
---
>     # Use static.crates.io (CDN) instead of crates.io/api to avoid the 1 req/sec
>     # rate limit on the API servers.
>     return f"https://static.crates.io/crates/{pkg["name"]}/{pkg["version"]}/download"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we shouldn’t hardcode this:

for completeness, the technically correct path for downloading crates is: load https://index.crates.io/config.json, look at the dl key in the JSON file, build the download URL via {dl}/{name}/{version}/download.

Let’s perhaps stick to the minimal change for now to reduce risk on unstable and 25.11 and unblock crates.io amins ASAP, and follow up with further improvements on staging? (Also, please split the copy into a separate commit to the other changes so that the diff is readable directly.)

Copy link
Copy Markdown
Member Author

@andresilva andresilva Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thinking was that the current approach is already wrong and it already hardcodes an URL, so we might as well hardcode the URL that doesn't have rate limiting.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I separated the changes into separate commits as requested.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough.

status_forcelist=[500, 502, 503, 504]
)
session = requests.Session()
session.headers["User-Agent"] = "nixpkgs fetchCargoVendor/2 (https://github.com/NixOS/nixpkgs)"
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can create an issue on nixpkgs for reports and just link to it here? WDYT @emilazy?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also don't want to make the UA a giant string, so maybe just an email might be the way to go?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UA is technically valid but parses oddly against RFC 9110 section 10.1.5.

The current format is parsed as two separate products: nixpkgs with no version, and fetchCargoVendor/2, rather than one. Replacing the space with a dash would combine them into a single product token, something like this: nixpkgs-fetchCargoVendor/2 (...).

We should revisit this when we do the cleanup on staging with proper separation between the FOD stage and the input-addressed stage.

This is just a suggestion though, and not meant to block if this needs to land fast (since rust builds are currently broken).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated with your suggestion.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replacing the repository URL with an issue URL for reports that the relevant parties can subscribe to and reporters can link their new issues in seems like a good idea to me.

@andresilva
Copy link
Copy Markdown
Member Author

Feel free to push directly to this branch to fix any remaining issues (I may be offline for the next hours).

@nixpkgs-ci nixpkgs-ci Bot added 10.rebuild-linux: 1-10 This PR causes between 1 and 10 packages to rebuild on Linux. 10.rebuild-darwin: 1-10 This PR causes between 1 and 10 packages to rebuild on Darwin. 6.topic: rust General-purpose programming language emphasizing performance, type safety, and concurrency. and removed 10.rebuild-linux: 501+ This PR causes many rebuilds on Linux and should normally target the staging branches. 10.rebuild-darwin: 501+ This PR causes many rebuilds on Darwin and should normally target the staging branches. 10.rebuild-darwin: 5001+ This PR causes many rebuilds on Darwin and must target the staging branches. 10.rebuild-linux: 5001+ This PR causes many rebuilds on Linux and must target the staging branches. labels Apr 23, 2026
dannyfaris added a commit to dannyfaris/nix-config that referenced this pull request May 28, 2026
…5) (#57)

Slice 3b set niri-flake.cache.enable = false to keep the upstream Cachix
substituter from being added implicitly via niri-flake's default-true
behaviour. The remaining cost was that niri then rebuilds from source on
every niri-flake bump (~10-30 min on metis hardware) — and worse,
nixpkgs's Rust crate fetcher currently 403s on some crates.io paths
(rust-lang/crates.io#13482, NixOS/nixpkgs#512735 in flight), so source
builds aren't actually reliable today.

A research pass reframed Q9: the principled shape isn't "stay opted
out"; it's "own the substituter delegation explicitly." Keep
cache.enable = false so niri-flake doesn't add it implicitly, then add
it ourselves in nix.settings with the same upstream key. The trust
delegation is recorded in source, dated, scoped to hosts that import
this module (currently metis only via slice 3d).

This is stricter than either alternative:
  - cache.enable = true would add the substituter via an upstream code
    path we don't own (and the wording is "implicit").
  - cache.enable = false alone leaves us paying the cost and exposes
    us to the upstream crates.io 403 cascade.

Trust footprint (recorded in the module header):
  - Maintainer: sodiboo.
  - Cache: niri-stable, niri-unstable, xwayland-satellite (x86_64
    only — niri.cachix.org does not serve aarch64).
  - Revocation: remove the module from any host's imports, OR delete
    the substituter/key lines. cache.enable = false guarantees
    niri-flake does not re-add.

aarch64 implications are nil for this repo — niri is only ever loaded
on x86_64 hosts (metis today, mothership later). The aarch64 host
(nixos-vm) doesn't import the desktop bundle.

Dormant on metis until slice 3d wires the desktop bundle in. Slice 3d
is the integration test that exercises the cache for real.

Peer-reviewed against nine correctness/convention checkpoints (public
key vs upstream flake.nix, merge semantics, scope of trust, niri-flake's
single mkIf path, comment accuracy, URL format, whitelist principle,
pre-commit hooks, interaction with repo's nix-daemon.nix).

Per ADR-028 §Implementation slice 3 (amended).
dannyfaris added a commit to dannyfaris/nix-config that referenced this pull request May 28, 2026
…8) (#59)

Companion to slice 3b.5 (#57). Slice 3b.5 added niri.cachix.org to
nix.settings in modules/core/nixos/niri.nix — that configures the
substituter on deployed systems *after* `nh os switch`. CI's nix daemon
reads its own config from cachix/install-nix-action's extra_nix_config,
which (until this commit) did not include niri.cachix.org.

Without this trust in CI, flake-check source-builds niri on the
x86_64-linux matrix entry when any host imports the desktop-env
bundle, and the source build hits the in-flight nixpkgs Rust crate
fetcher 403 cascade (rust-lang/crates.io#13482,
NixOS/nixpkgs#512735). Slice 3d (next) is the first commit that
imports the bundle into a host, so this trust must land first.

Implementation:

- Adds `extra-substituters = https://niri.cachix.org` and the matching
  `extra-trusted-public-keys` line to the workflow's extra_nix_config.
- Uses the `extra-` prefix so it appends to install-nix-action's
  defaults rather than replacing them (cache.nixos.org is preserved).
- `accept-flake-config = false` is preserved unchanged — transitive
  flake inputs still cannot silently mutate config; only this
  operator-owned line adds new trust. The whitelist principle is
  intact.

Cross-arch note: niri.cachix.org serves x86_64-linux only.
flake-check builds every host's system.build.toplevel including
the aarch64 nixos-vm host, but nixos-vm does not import the niri
module, so the aarch64 matrix entry lists the substituter but
never queries it.

Header comment updated to remove the prior "binary-cache config in a
follow-up workflow tweak" placeholder and replace with the rationale
+ source-of-key + cross-arch reasoning.

Peer-reviewed (8 checkpoints: extra- semantics, block-scalar format,
key byte-match against modules/core/nixos/niri.nix, whitelist
preservation, YAML indent, actionlint compatibility, cross-arch
correctness, concurrency).

Per ADR-028 §Implementation slice 3 (amendment).
@thefossguy thefossguy mentioned this pull request May 28, 2026
14 tasks
ericbmerritt added a commit to ericbmerritt/reeve that referenced this pull request May 28, 2026
crates.io added a User-Agent blocklist in late April 2026 that rejects
requests without an identifying UA. nixpkgs has two crate-fetching
paths:

- fetchCargoVendor — patched in NixOS/nixpkgs#512735 (merged 2026-04-26)
  to send 'User-Agent: nixpkgs fetchCargoVendor/2' and fetch from
  static.crates.io
- importCargoLock — used by cargoLock.lockFile = ...; has its own
  inline fetchCrate calling fetchurl; NOT yet patched upstream;
  receives 403 on every crate download

Switching to useFetchCargoVendor + cargoHash routes through the
patched path. Cargo.lock stays as the source of truth; only the
flake.nix attribute changes.
ericbmerritt added a commit to ericbmerritt/reeve that referenced this pull request May 28, 2026
crates.io added a User-Agent blocklist in late April 2026 that rejects
requests without an identifying UA. nixpkgs has two crate-fetching
paths:

- fetchCargoVendor — patched in NixOS/nixpkgs#512735 (merged 2026-04-26)
  to send 'User-Agent: nixpkgs fetchCargoVendor/2' and fetch from
  static.crates.io
- importCargoLock — used by cargoLock.lockFile = ...; has its own
  inline fetchCrate calling fetchurl; NOT yet patched upstream;
  receives 403 on every crate download

Switching to useFetchCargoVendor + cargoHash routes through the
patched path. Cargo.lock stays as the source of truth; only the
flake.nix attribute changes.
osmaczko added a commit to logos-messaging/libchat that referenced this pull request May 28, 2026
Temporary. The two crates.io UA fixes (NixOS/nixpkgs#512735 for
fetchCargoVendor's python-requests UA, NixOS/nixpkgs#524985 for
importCargoLock's curl UA) haven't propagated to nixos-unstable yet.
Switch to nixos-unstable-small and force logos-delivery to follow so
the smoketest gets the same fix. Revert once nixos-unstable catches up.

Refs:
- rust-lang/crates.io#13482
- rust-lang/crates.io#13783
- https://crates.io/data-access
osmaczko added a commit to logos-messaging/libchat that referenced this pull request May 28, 2026
Temporary. The two crates.io UA fixes (NixOS/nixpkgs#512735 for
fetchCargoVendor's python-requests UA, NixOS/nixpkgs#524985 for
importCargoLock's curl UA) haven't propagated to nixos-unstable yet.
Switch to nixos-unstable-small and force logos-delivery to follow so
the smoketest gets the same fix. Revert once nixos-unstable catches up.

Refs:
- rust-lang/crates.io#13482
- rust-lang/crates.io#13783
- https://crates.io/data-access
osmaczko added a commit to logos-messaging/libchat that referenced this pull request May 28, 2026
Temporary. The two crates.io UA fixes (NixOS/nixpkgs#512735 for
fetchCargoVendor's python-requests UA, NixOS/nixpkgs#524985 for
importCargoLock's curl UA) haven't propagated to nixos-unstable yet.
Switch to nixos-unstable-small and force logos-delivery to follow so
the smoketest gets the same fix. Revert once nixos-unstable catches up.

Refs:
- rust-lang/crates.io#13482
- rust-lang/crates.io#13783
- https://crates.io/data-access
osmaczko added a commit to logos-messaging/libchat that referenced this pull request May 28, 2026
Temporary. The two crates.io UA fixes (NixOS/nixpkgs#512735 for
fetchCargoVendor's python-requests UA, NixOS/nixpkgs#524985 for
importCargoLock's curl UA) haven't propagated to nixos-unstable yet.
Switch to nixos-unstable-small and force logos-delivery to follow so
the smoketest gets the same fix. Revert once nixos-unstable catches up.

Refs:
- rust-lang/crates.io#13482
- rust-lang/crates.io#13783
- https://crates.io/data-access
osmaczko added a commit to logos-messaging/libchat that referenced this pull request May 28, 2026
* chore(flake): accept extra system attr; add perl for openssl-sys build

forAllSystems calls the lambda with {system, pkgs}; strict
destructuring requires `..` to ignore the system attribute.

`pkgs.perl` is needed because openssl-sys is pulled vendored via
libsqlite3-sys / rusqlite / chat-sqlite, and its `perl Configure`
step needs FindBin.pm, which Fedora's system perl doesn't ship.

* feat: introduce client event system

- Core processing yields a `PayloadOutcome` enum — `Empty`, `Convo`, or
  `Inbox`. `ConvoOutcome` carries a conversation id and an optional
  decrypted `Content`; `InboxOutcome` adds a `NewConversation`
  (id + `ConversationClass`) for a peer-initiated conversation.
- Client translates `PayloadOutcome` into app-facing `Vec<Event>`
  (`ConversationStarted`, `MessageReceived`) at the boundary, so the
  application loop sees discrete events rather than core types.
- MLS group welcomes produce a `ConversationStarted` event with no
  initial content, fixing the silent-group-join case where the inbox
  layer dropped the observation.
- C FFI exposes an `EventList` opaque type with indexed accessors and
  an `Invalid` sentinel for out-of-bounds / non-applicable reads.
- Symmetric `Inbox` / `InboxV2` handlers: both return
  `Result<InboxOutcome, _>` and own the persistence + ephemeral-key
  cleanup for the conversations they create.
- Updated and simplified `docs/adr/0001-client-event-system.md`.

* chore(flake): bump nixpkgs to nixos-unstable-small

Temporary. The two crates.io UA fixes (NixOS/nixpkgs#512735 for
fetchCargoVendor's python-requests UA, NixOS/nixpkgs#524985 for
importCargoLock's curl UA) haven't propagated to nixos-unstable yet.
Switch to nixos-unstable-small and force logos-delivery to follow so
the smoketest gets the same fix. Revert once nixos-unstable catches up.

Refs:
- rust-lang/crates.io#13482
- rust-lang/crates.io#13783
- https://crates.io/data-access
kp-mariappan-ramasamy added a commit to expressvpn/lightway that referenced this pull request May 29, 2026
crates.io now rejects requests with curl/* User-Agent, returning HTTP 403
on `https://crates.io/api/v1/crates/<name>/<ver>/download`. nixpkgs
`fetchurl` calls curl with its default UA, so `rustPlatform.buildRustPackage`
with `cargoLock.lockFile` (which routes through `importCargoLock` and
fetches each crate tarball via `fetchurl`) fails on the first uncached
crate, e.g.:

  trying https://crates.io/api/v1/crates/autocfg/1.5.1/download
  curl: (22) The requested URL returned error: 403

Reproduced UA blocklist:

  default curl UA -> 403
  curl/8.5.0      -> 403
  empty UA        -> 302
  cargo (...)     -> 200

Add an overlay that wraps `fetchurl` and appends a cargo-style User-Agent
to `curlOptsList` via `overrideAttrs`. Unconditional - injecting the UA
on non-crates fetches is harmless and avoids matching `args.url` /
`args.urls`, which is brittle.

Upstream references:

  - rust-lang/crates.io#13482: original ticket (closed). Initially
    flagged python-requests/2.32.5+ UA; crates.io has since extended
    the blocklist to include curl/*.
  - NixOS/nixpkgs#512735 (merged 2026-04-26): sets
    `nixpkgs-fetchCargoVendor/2 (...)` UA and switches to
    `static.crates.io`. Only patches `fetchCargoVendor` - leaves
    `importCargoLock` (the path lightway uses) unfixed.

Remove this overlay once nixpkgs ships an `importCargoLock` UA fix.
kp-mariappan-ramasamy added a commit to expressvpn/lightway that referenced this pull request May 29, 2026
crates.io now rejects requests with curl/* User-Agent, returning HTTP 403
on `https://crates.io/api/v1/crates/<name>/<ver>/download`. nixpkgs
`fetchurl` calls curl with its default UA, so `rustPlatform.buildRustPackage`
with `cargoLock.lockFile` (which routes through `importCargoLock` and
fetches each crate tarball via `fetchurl`) fails on the first uncached
crate, e.g.:

  trying https://crates.io/api/v1/crates/autocfg/1.5.1/download
  curl: (22) The requested URL returned error: 403

Reproduced UA blocklist:

  curl/8.5.0          -> 403
  empty UA            -> 302
  cargo (...)         -> 200
  static.crates.io/*  -> 200 (no UA gate)

Override `rustPlatform.importCargoLock` with a copy of the upstream file
patched to use `https://static.crates.io/crates` instead of the API host.
This mirrors NixOS/nixpkgs#524985.

Upstream references:

  - rust-lang/crates.io#13482: original ticket (closed). Initially
    flagged python-requests/2.32.5+ UA; crates.io has since extended
    the blocklist to include curl/*.
  - NixOS/nixpkgs#512735 (merged 2026-04-26): sets
    `nixpkgs-fetchCargoVendor/2 (...)` UA and switches to
    `static.crates.io`. Only patches `fetchCargoVendor`.
  - NixOS/nixpkgs#524979 (closed 2026-05-27): tracking issue for the
    same 403 hitting `importCargoLock` (the path lightway uses).
  - NixOS/nixpkgs#524985 (merged 2026-05-27 to master): fix for
    `importCargoLock` - switches crate downloads to
    `https://static.crates.io/crates/<name>/<ver>/download`, which has
    no UA gate. As of this commit the fix is on master only;
    `nixpkgs-unstable` (the channel this flake tracks at f9d8b65,
    2026-05-25) does not yet contain it.

Drop this overlay once `flake.lock` is bumped to a `nixpkgs-unstable`
revision that includes NixOS/nixpkgs#524985.
kp-mariappan-ramasamy added a commit to expressvpn/lightway that referenced this pull request May 29, 2026
crates.io now rejects requests with curl/* User-Agent, returning HTTP 403
on `https://crates.io/api/v1/crates/<name>/<ver>/download`. nixpkgs
`fetchurl` calls curl with its default UA, so `rustPlatform.buildRustPackage`
with `cargoLock.lockFile` (which routes through `importCargoLock` and
fetches each crate tarball via `fetchurl`) fails on the first uncached
crate, e.g.:

  trying https://crates.io/api/v1/crates/autocfg/1.5.1/download
  curl: (22) The requested URL returned error: 403

Reproduced UA blocklist:

  curl/8.5.0          -> 403
  empty UA            -> 302
  cargo (...)         -> 200
  static.crates.io/*  -> 200 (no UA gate)

Override `rustPlatform.importCargoLock` with a copy of the upstream file
patched to use `https://static.crates.io/crates` instead of the API host.
This mirrors NixOS/nixpkgs#524985.

Upstream references:

  - rust-lang/crates.io#13482: original ticket (closed). Initially
    flagged python-requests/2.32.5+ UA; crates.io has since extended
    the blocklist to include curl/*.
  - NixOS/nixpkgs#512735 (merged 2026-04-26): sets
    `nixpkgs-fetchCargoVendor/2 (...)` UA and switches to
    `static.crates.io`. Only patches `fetchCargoVendor`.
  - NixOS/nixpkgs#524979 (closed 2026-05-27): tracking issue for the
    same 403 hitting `importCargoLock` (the path lightway uses).
  - NixOS/nixpkgs#524985 (merged 2026-05-27 to master): fix for
    `importCargoLock` - switches crate downloads to
    `https://static.crates.io/crates/<name>/<ver>/download`, which has
    no UA gate. As of this commit the fix is on master only;
    `nixpkgs-unstable` (the channel this flake tracks at f9d8b65,
    2026-05-25) does not yet contain it.

Drop this overlay once `flake.lock` is bumped to a `nixpkgs-unstable`
revision that includes NixOS/nixpkgs#524985.
kp-mariappan-ramasamy added a commit to expressvpn/lightway that referenced this pull request May 29, 2026
crates.io now rejects requests with curl/* User-Agent, returning HTTP 403
on `https://crates.io/api/v1/crates/<name>/<ver>/download`. nixpkgs
`fetchurl` calls curl with its default UA, so `rustPlatform.buildRustPackage`
with `cargoLock.lockFile` (which routes through `importCargoLock` and
fetches each crate tarball via `fetchurl`) fails on the first uncached
crate, e.g.:

  trying https://crates.io/api/v1/crates/autocfg/1.5.1/download
  curl: (22) The requested URL returned error: 403

Reproduced UA blocklist:

  curl/8.5.0          -> 403
  empty UA            -> 302
  cargo (...)         -> 200
  static.crates.io/*  -> 200 (no UA gate)

Override `pkgs.makeRustPlatform` (not `pkgs.rustPlatform.importCargoLock`):
nixpkgs' `pkgs/development/compilers/rust/make-rust-platform.nix`
constructs a fresh `importCargoLock` via `buildPackages.callPackage` against
a hardcoded path to `pkgs/build-support/rust/import-cargo-lock.nix`, so
overriding the top-level `rustPlatform.importCargoLock` is ignored by every
call site that goes through `makeRustPlatform`. Both `nix/modules/native.nix`
and `nix/modules/cross.nix` build their `rustPlatform` via
`(pkgsCross.)makeRustPlatform { cargo = ...; rustc = ...; }`, so a top-level
override silently misses macOS, Linux musl, and every cross target. Intercept
`makeRustPlatform` instead and re-inject the patched `importCargoLock` via
`overrideScope` on the returned scope so that `buildRustPackage` (resolved
through `callPackage` against the scope's `self`) picks it up.

The patched `import-cargo-lock.nix` is materialised at evaluation time via
`builtins.toFile`/`builtins.readFile`/`builtins.replaceStrings` rather than
`runCommand`. `runCommand` adds a build-time dependency on `/bin/sh` which
fails on macOS sandbox builds with
"requested impure path '/bin/sh', but it was not in allowed-impure-host-deps".
`builtins.toFile` produces a store path purely at eval time with no
derivation to build, sidestepping the sandbox check entirely.

The one-line URL change inside `import-cargo-lock.nix` mirrors
NixOS/nixpkgs#524985 byte-for-byte.

Verified across all four flake systems by inspecting the rendered
`cargo-vendor-dir.drv` graph - every crate tarball URL resolves to
`https://static.crates.io/crates/<name>/<ver>/download`:

  - x86_64-darwin            (native + Darwin-to-Darwin cross)
  - aarch64-darwin           (native + cross to x86_64-darwin)
  - x86_64-linux             (native + gnu/musl cross)
  - aarch64-linux            (native + gnu/musl cross)

Upstream references:

  - rust-lang/crates.io#13482: original ticket (closed). Initially
    flagged python-requests/2.32.5+ UA; crates.io has since extended
    the blocklist to include curl/*.
  - NixOS/nixpkgs#512735 (merged 2026-04-26): sets
    `nixpkgs-fetchCargoVendor/2 (...)` UA and switches to
    `static.crates.io`. Only patches `fetchCargoVendor`.
  - NixOS/nixpkgs#524979 (closed 2026-05-27): tracking issue for the
    same 403 hitting `importCargoLock` (the path lightway uses).
  - NixOS/nixpkgs#524985 (merged 2026-05-27 to master): fix for
    `importCargoLock` - switches crate downloads to
    `https://static.crates.io/crates/<name>/<ver>/download`, which has
    no UA gate. As of this commit the fix is on master only;
    `nixpkgs-unstable` (the channel this flake tracks at f9d8b65,
    2026-05-25) does not yet contain it.

Drop this overlay once `flake.lock` is bumped to a `nixpkgs-unstable`
revision that includes NixOS/nixpkgs#524985.
GirardR1006 pushed a commit to GirardR1006/nixpkgs that referenced this pull request May 29, 2026
The crates.io API server's 1 req/sec rate limit currently surfaces as
intermittent HTTP 403 errors when vendoring lockfiles. Switch to the CDN
endpoint as recommended by upstream (rust-lang/crates.io#13482), mirroring
the fix already applied to fetchCargoVendor in NixOS#512735.

fetchurl is content-addressed by sha256, so the URL change does not affect
any downstream store paths.

Fixes NixOS#524979
GirardR1006 pushed a commit to GirardR1006/nixpkgs that referenced this pull request May 29, 2026
The crates.io API server's 1 req/sec rate limit currently surfaces as
intermittent HTTP 403 errors when vendoring lockfiles. Switch to the CDN
endpoint as recommended by upstream (rust-lang/crates.io#13482), mirroring
the fix already applied to fetchCargoVendor in NixOS#512735.

fetchurl is content-addressed by sha256, so the URL change does not affect
any downstream store paths.

Fixes NixOS#524979
achandrasekar pushed a commit to kubernetes-sigs/inference-perf that referenced this pull request May 29, 2026
Fixed #514

FYI: NixOS/nixpkgs#512735

Track a stable channel rather than an unstable one: fewer "HEAD is
broken" regressions.
25.11 has the fetchCargoVendor User-Agent fix (needed so crates.io
doesn't 403 libtokenizers' crate downloads)

---------

Signed-off-by: ChengHao Yang <17496418+tico88612@users.noreply.github.com>
ChristopherJMiller added a commit to ChristopherJMiller/hearth that referenced this pull request May 31, 2026
- Bump nixpkgs (2026-04-14 → 2026-05-23) for the fetchCargoVendor
  User-Agent fix (NixOS/nixpkgs#512735) so kanidm-cli and other Cargo
  builds stop hitting crates.io 403s.
- Bump home-manager (2026-03-20 → 2026-05-30) past the neovim
  `extraConfig` null-list regression that was failing admin/developer
  per-user closure builds.
- Pin Kanidm 1.9 → 1.10.1 (1.9 EOL 2026-05-31): flake.nix overlay,
  nix/kanidm-cli.nix (version + hashes), docker-compose image tag,
  CLAUDE.md key conventions.
- Switch kanidm-unixd `home_attr` from `spn` to `name` and align
  `lib.buildUserEnv` so `/home/testuser` is the on-disk path. Keeps
  `@` out of paths where Nextcloud Desktop and similar clients fail;
  SPN login still works via `home_alias = spn`.
- Wire `hearth-office-oxt` into the home-manager LibreOffice module
  when `enableExtensions` is true, and pass `libreofficeExtensions =
  true` in `dev/fleet-vm.nix` so the Rust UNO bundle ships with the
  dev VM closure.
- Add sqlx PgPool keepalive (`test_before_acquire`, `idle_timeout`,
  `max_lifetime`) on both hearth-api and hearth-build-worker — the
  worker was silently losing its DB connection on idle and stalling
  the job queue.
- Pre-fetch Stalwart's `webadmin.zip` in `just setup`, bind-mount it
  into the container, and point `[webadmin] resource` at the local
  file. The container's GitHub download silently fails.
- Qualify all attic CLI calls with the `dev:` server prefix so pushes
  don't leak to a developer's personal default-server.
- Pin pnpm's `verifyDepsBeforeRun: false` in pnpm-workspace.yaml so
  `pnpm build` under `nix develop -c` doesn't abort on a missing TTY.
- Add `renovate.json` configuring weekly dependency-update PRs across
  nix flake inputs, Cargo, pnpm, Helm, and docker-compose tags.
- Document the recurring follow-ups and recent fixes in a new
  Operational Backlog section of ROADMAP.md.
alexandru-savinov added a commit to alexandru-savinov/nixos-config that referenced this pull request Jun 3, 2026
…s.io 403s python-requests UA) (#461)

The "Build sancta-claw" CI job has failed on every push since the
agent-browser-cli 0.14.0 vendor FOD fell out of the binary cache:
crates.io returns HTTP 403 to rustPlatform.fetchCargoVendor because its
Python `requests`-based fetcher sends a `python-requests/*` User-Agent,
which crates.io's crawler policy now blocks (rust-lang/crates.io#13482).

Switch the single agent-browser-cli buildRustPackage call from cargoHash
to cargoLock (importCargoLock), which fetches each crate via Nix's
libcurl fetchurl — not blocked by crates.io. The CLI lockfile
(cli/Cargo.lock, v4) has only crates.io-registry deps and zero git
sources, so no cargoLock.outputHashes are needed and there is one fewer
hash to maintain than cargoHash.

Upstream fix is NixOS/nixpkgs#512735 (set a descriptive User-Agent),
backported to release-25.11 on 2026-04-26 but not yet in this flake's
nixpkgs pin (2026-03-18); revert to cargoHash once the pin carries it.

Verified locally on aarch64-darwin: sancta-claw evaluates to a valid
drvPath, and the importCargoLock vendor dir builds end-to-end (all 43
crates fetched via libcurl, zero 403). The x86_64-linux compile/link is
exercised by the previously-failing "Build sancta-claw" CI job.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

6.topic: rust General-purpose programming language emphasizing performance, type safety, and concurrency. 8.has: port to stable This PR already has a backport to the stable release. 10.rebuild-darwin: 1-10 This PR causes between 1 and 10 packages to rebuild on Darwin. 10.rebuild-linux: 1-10 This PR causes between 1 and 10 packages to rebuild on Linux. backport release-25.11 Backport PR automatically

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants