Skip to content

flake: send identifying User-Agent on fetchurl (crates.io 403 workaround)#337

Merged
fenio merged 2 commits into
mainfrom
flake-fetchurl-crates-io-ua
May 29, 2026
Merged

flake: send identifying User-Agent on fetchurl (crates.io 403 workaround)#337
fenio merged 2 commits into
mainfrom
flake-fetchurl-crates-io-ua

Conversation

@fenio
Copy link
Copy Markdown
Collaborator

@fenio fenio commented May 28, 2026

Summary

crates.io has long had a crawler policy requiring an identifying User-Agent header. The policy was formalised in RFC 3463. What changed early 2026 is enforcement: the default curl/X.Y.Z UA that Nix's fetchurl sends started getting actively rejected with HTTP 403, alongside default python-requests/* (rust-lang/crates.io#13482).

The bite is silent on main because the Nix binary cache covers every crate that's been built before — only PRs that introduce a new crate version hit a live crates.io download and fail. PR #336 (http/jiff patch bump) tripped it; every future dep bump will too until this is fixed.

This overlay wraps pkgs.fetchurl so every curl invocation appends an identifying UA of the shape the policy recommends (appname (contact)). It's an umbrella workaround until NixOS/nixpkgs#512735 — which addresses fetchCargoVendor directly — propagates through our channel (estimated weeks).

After this lands, retrying #336's CI run should turn the Nix engine job green.

Test plan

  • nix flake check --no-build — all outputs evaluate clean.
  • CI Nix engine build on this PR — passes (the binary cache will serve everything anyway, but at least confirms the overlay doesn't break eval at build time).
  • Rerun deps: bump http 1.4.0 → 1.4.1 and jiff 0.2.24 → 0.2.27 #336's failed Nix engine job after this lands — http 1.4.1, jiff 0.2.27, jiff-static 0.2.27 tarballs fetch successfully.
  • Manual sanity: in the CI logs for any subsequent build that hits crates.io live, confirm no more curl: (22) ... error: 403.

Risks / scope

  • The overlay touches every fetchurl call, not just rustPlatform.importCargoLock. Effect on other fetchers: they get a -A "nasty-engine-build (...)" appended, which servers almost universally ignore. The one corner is fetchers that deliberately set their own UA — curl uses the last -A, so ours would override theirs. No known callers in our derivation tree do that, but worth knowing.
  • Remove this overlay once nixpkgs#512735 propagates so we're not maintaining a workaround past its shelf life.

fenio added 2 commits May 28, 2026 23:15
crates.io enforces its long-standing crawler policy
(https://crates.io/policies) by rejecting clients with unidentifying
User-Agent headers. As of early 2026, default `curl/X.Y.Z` is on the
blocklist, which makes `rustPlatform.importCargoLock` 403 on any
crate tarball not already in the binary cache.

The symptom: PRs that bump a crate to a version not previously
cached fail the Nix engine build job with
"cannot download crate-${name}-${version}.tar.gz from any mirror",
while the actual code paths (clippy, fmt, test, webui) pass.
Builds on `main` stay green because the binary cache shields every
crate that's already been fetched once.

Wrap `fetchurl` in `mkPkgs` so every curl invocation appends an
identifying UA that matches the policy's recommended shape
(`appname (contact)`). nixpkgs has a more surgical fix in flight
(NixOS/nixpkgs#512735) that targets `fetchCargoVendor` specifically;
this overlay is a temporary umbrella that unblocks dep work until
that change reaches our channel.

Verified with `nix flake check --no-build`: all outputs evaluate
cleanly. The real test is the CI run on this PR.
CI on the previous commit failed during eval: somewhere inside the
rustPlatform → makeOverridable chain, `fetchurl` gets invoked with a
function-shaped value rather than an attrset (probably the
callPackage auto-call / makeOverridable wrapper checking shape).
Doing `args // {...}` on a function fails with "expected a set but
found a function".

Branch on isAttrs so the User-Agent injection only fires for actual
fetch requests (which always carry url + hash in an attrset).
Function-shaped calls pass through untouched.
@fenio fenio merged commit e0f4fde into main May 29, 2026
7 checks passed
@fenio fenio deleted the flake-fetchurl-crates-io-ua branch May 29, 2026 02:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant