rustPlatform.fetchCargoVendor: set a custom User-Agent#512735
Conversation
|
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? |
|
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: |
|
I also experienced this today. Changing the user-agent fixed the issue for me. While testing, I added the The https://crates.io/data-access#api page states:
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. |
|
Why would this create a mass rebuild? These are all FODs? |
|
@NickCao The tarball downloads themselves are FOD-cached, but runCommand "${name}-vendor" {
inherit vendorStaging;
nativeBuildInputs = [ fetchCargoVendorUtil cargo replaceWorkspaceValues ];
} ''
fetch-cargo-vendor-util create-vendor "$vendorStaging" "$out"
''That |
That's unfortunate, I hope crates.io can grant us an exception for now. |
|
The staging part is a FOD, but there is a non-staging part, which is not a FOD. (e.g. separating files or ditching the non-staging part completely and moving it into the setup hook) |
|
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 |
| ) | ||
| session = requests.Session() | ||
| # crates.io blocks the default python-requests/<version> User-Agent | ||
| session.headers["User-Agent"] = "nixpkgs fetchCargoVendor (https://github.com/NixOS/nixpkgs)" |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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).
|
From the upstream issue:
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 ( |
|
@emilazy Will push in a bit, thanks for the suggestions 🙏 |
ff2edda to
5a4b429
Compare
| @@ -0,0 +1,416 @@ | |||
| import functools | |||
There was a problem hiding this comment.
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"
There was a problem hiding this comment.
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
dlkey 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.)
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
I separated the changes into separate commits as requested.
| status_forcelist=[500, 502, 503, 504] | ||
| ) | ||
| session = requests.Session() | ||
| session.headers["User-Agent"] = "nixpkgs fetchCargoVendor/2 (https://github.com/NixOS/nixpkgs)" |
There was a problem hiding this comment.
Maybe we can create an issue on nixpkgs for reports and just link to it here? WDYT @emilazy?
There was a problem hiding this comment.
We also don't want to make the UA a giant string, so maybe just an email might be the way to go?
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
Updated with your suggestion.
There was a problem hiding this comment.
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.
|
Feel free to push directly to this branch to fix any remaining issues (I may be offline for the next hours). |
…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).
…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).
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.
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.
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
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
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
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
* 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
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.
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.
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.
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.
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
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
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>
- 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.
…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>
Rust package builds using
fetchCargoVendorhave started failing intermittently with HTTP 403 responses when downloading crate tarballs fromhttps://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 specificpython-requests/<version>User-Agent strings. Testing directly against crates.io confirms this:python-requests/2.32.5andpython-requests/2.33.1both return 403, while older versions, empty UAs, and any custom string return 302 as expected.fetch-cargo-vendor-util.pybuilds itsrequests.Sessionwithout overriding the default User-Agent, so every tarball request goes out aspython-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 tohttps://static.crates.io/crates/<name>/<version>/downloadto 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 FODvendorStagingstage (where HTTP downloads happen and the UA matters) and the input-addressed-vendorrunCommandstage (where it doesn't). Even thoughvendorStagingis an FOD and its output path is stable, touching the util referenced by the-vendorstage rewrites every downstreambuildRustPackagederivation in the tree, a mass rebuild that has to go throughstaging. To avoid that,fetch-cargo-vendor-util.pyis left untouched and the fix is applied in a parallel copyfetch-cargo-vendor-util-v2.pythat'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 nextstagingcycle.Things done
passthru.tests.nixpkgs-reviewon this PR. See nixpkgs-review usage../result/bin/.