build: add a 14-day publish-age cooldown to dependency resolution - #607
Conversation
…--locked everywhere Ports Commit-Boost/commit-boost-client#492. Cargo's unstable min-publish-age (rust-lang/cargo#17009) excludes crate versions published less than N days ago from resolution, a cooldown against freshly compromised releases. The policy lives in .cargo/config.toml; stable cargo 1.97.1 ignores the tables silently, so only the pinned nightly-2026-06-21 resolver behind `make update` enforces it. That is why `make lint`, `make test`, CI check/clippy and the Docker cook step now pass --locked: an unresolved manifest change fails loudly instead of being re-resolved on stable around the cooldown. `make update-allow PACKAGE= VERSION=` is the escape hatch for a version younger than the window; it bypasses the whole resolution, so the lockfile diff must be reviewed. `make cooldown-check` and a new advisory CI job surface lockfile entries younger than the window via `cargo update --dry-run`, which is the only form that flags them (`--workspace` does not), and never fail the build. As of today a full re-resolution under any window of 8+ days fails: rand 0.10 needs chacha20 0.10, whose 0.10.0/0.10.1 are yanked and whose only live release was published 2026-08-27. `make update` errors and the CI job warns until 2026-09-10. The lockfile is unchanged and still pins the yanked chacha20 0.10.0.
🤖 Kimi Code ReviewI'll review this PR which adds a supply-chain security mechanism (publish-age cooldown for dependencies) to the ethlambda project. Overall AssessmentThis is a well-designed supply-chain security feature with good documentation and tooling. However, I found several issues ranging from minor improvements to potential reliability concerns. Detailed Findings1. Hardcoded Future Date for Nightly Toolchain (Critical Reliability Issue)File: The nightly toolchain
This appears to be a placeholder or typo. As of early 2025, this date is in the future. Rust nightlies are only available for ~167 days before being pruned from the release servers. When this date passes or if it's invalid, the entire Recommendation: Use a recent, stable nightly date or track a specific version via # In a separate toolchain file or documented procedure
[toolchain]
channel = "nightly-2025-01-15" # Use actual available nightly
components = ["cargo"]2. Silent Failures in CI Cooldown Job (Medium Severity)File: if ! rustup toolchain install nightly-2026-06-21 --profile minimal; then
echo "::warning title=Publish-age cooldown check skipped::toolchain install failed"
exit 0
fiThe
Recommendation: Add a scheduled job or alternative verification path that can fail. At minimum, track skip frequency via metrics. 3. Inconsistent Error Handling Between Makefile and CIFile: The Makefile suppresses rustup toolchain install $(RESOLVER_TOOLCHAIN) --profile minimal > /dev/null 2>&1 && \If this fails, the Recommendation: Match CI behavior—surface warnings on toolchain install failure: @rustup toolchain install $(RESOLVER_TOOLCHAIN) --profile minimal || { echo "WARNING: failed to install $(RESOLVER_TOOLCHAIN)" >&2; exit 0; }4. Shell Quoting Vulnerability in
|
| Priority | Item | File | Line |
|---|---|---|---|
| 🔴 Critical | Future nightly date | .github/workflows/ci.yml, Makefile |
101, 23 |
| 🔴 Critical | Non-existent actions/checkout@v6 |
.github/workflows/ci.yml |
96 |
| 🟡 Medium | Silent CI failures on toolchain install | .github/workflows/ci.yml |
100-104 |
| 🟡 Medium | Suppressed errors in Makefile | Makefile |
42, 52, 60 |
| 🟡 Medium | Shell quoting in update-allow |
Makefile |
49 |
| 🟢 Low | Missing make add target |
CONTRIBUTING.md |
— |
| 🟢 Low | Fragile grep patterns |
.github/workflows/ci.yml |
111 |
The 🔴 Critical items will cause immediate failures and must be fixed before merge.
Automated review by Kimi (Moonshot AI) · kimi-k2.6 · custom prompt
🤖 Codex Code Review
No consensus-layer logic changed here, so fork choice, attestation processing, finalization, STF, XMSS, and SSZ correctness were not materially reviewable from this diff. The Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
🤖 Claude Code ReviewConfirmed the exact lines. Here's my review. Review: build/min-publish-age-cooldown (PR 607)Pure tooling/CI change (no Rust code touched), well documented and consistent with the existing Findings1. cooldown-check: ## 🔎 Warn about lockfile entries younger than the publish-age cooldown
@rustup toolchain install $(RESOLVER_TOOLCHAIN) --profile minimal > /dev/null 2>&1 && \
if ! out=$$(cargo +$(RESOLVER_TOOLCHAIN) update --dry-run -Z min-publish-age 2>&1); then \
Compare with the CI job ( if ! rustup toolchain install nightly-2026-06-21 --profile minimal; then
echo "::warning title=Publish-age cooldown check skipped::toolchain install failed"
exit 0
fiThe Makefile's cooldown-check:
@if ! rustup toolchain install $(RESOLVER_TOOLCHAIN) --profile minimal >/dev/null 2>&1; then \
echo "WARNING: publish-age cooldown check skipped: toolchain install failed"; exit 0; \
fi; \
if ! out=$$(cargo +$(RESOLVER_TOOLCHAIN) update --dry-run -Z min-publish-age 2>&1); then \
...Minor / non-blocking
Looks good
Automated review by Claude (Anthropic) · sonnet · custom prompt |
| # Stable cargo ignores the publish-age cooldown in .cargo/config.toml, so the | ||
| # lockfile can pin too-young crates (or deliberately via `make update-allow`). | ||
| # Surface them as annotations; this job never fails the build. |
There was a problem hiding this comment.
If this doesn't fail the build it's useless
| # Stable cargo ignores the publish-age cooldown in .cargo/config.toml, so the | |
| # lockfile can pin too-young crates (or deliberately via `make update-allow`). | |
| # Surface them as annotations; this job never fails the build. | |
| # Stable cargo ignores the publish-age cooldown in .cargo/config.toml, so the | |
| # lockfile can pin too-young crates (or deliberately via `make update-allow`). | |
| # Surface them as annotations and fail the build. |
There was a problem hiding this comment.
Done: the job now exits 1 on a too-young pin in either lockfile, and I narrowed the grep to Downgrading .*published because bare downgrades for MSRV or tightened requirements show up too (event-monitor has three today). Infrastructure failures still only warn, so the root probe, which currently fails on yanked chacha20 releases upstream, doesn't block every PR; it starts gating on 2026-09-10 when chacha20 0.10.2 ages out. One consequence: an allow-bypassed bump keeps CI red until it ages, which I think is the right trade but wanted to flag.
…-allow target Review follow-up. An advisory annotation nobody has to act on does not protect anything, so the CI cooldown job now exits non-zero when a lockfile pins a crate younger than the window, and covers tooling/event-monitor's lockfile as well as the root one. Toolchain download failures and resolutions that fail for reasons unrelated to age (today: yanked chacha20 releases upstream) still only warn, since they are outside the PR's control and would block every PR. The grep is narrowed to `Downgrading .*published`: a cooldown-driven downgrade carries the too-young version's publish date, while downgrades for other reasons (MSRV, a tightened requirement; the event-monitor lockfile shows several) do not, and a bare `Downgrading` match would have failed the build on those. `make update-allow` is removed; the escape hatch is the plain cargo env var on the existing target, `CARGO_RESOLVER_INCOMPATIBLE_PUBLISH_AGE=allow make update UPDATE_ARGS="-p <crate> --precise <ver>"`, and the docs say the cooldown job stays red until that version ages past the window. Two explanatory comments above `make lint` and the CI `cargo check` step are dropped, the benchmark smoke step builds `--locked` like everything else, and rustup's stderr is no longer swallowed so a failed toolchain install is visible.
Review follow-up. With the CI cooldown job failing the build on a too-young pin, a bypass that resolves past the window only produces a lockfile CI will reject, so there is nothing to document: the env var is removed from CLAUDE.md, CONTRIBUTING.md, the Makefile comment and the CI job comment.
🗒️ Description / Motivation
Ports Commit-Boost/commit-boost-client#492. Cargo's unstable
min-publish-age(rust-lang/cargo#17009) lets the resolver skip crate versions published less than N days ago, a cooldown against freshly compromised releases. This enables it at 14 days, adds the Makefile path for resolving under it, and makes every build--lockedso stable cargo cannot silently re-resolve around the policy.The feature is nightly-only; stable 1.97.1 ignores the tables silently. Only the pinned
nightly-2026-06-21resolver enforces the cooldown, so--lockedeverywhere is what makes an unresolved manifest change fail loudly instead of resolving on stable.What Changed
.cargo/config.toml[unstable] min-publish-age = true,[registry] global-min-publish-age = "14 days"Makefilemake update(resolve under the cooldown),make update-allow PACKAGE= VERSION=(escape hatch, bypasses the whole resolution),make cooldown-check(advisory).make lint/make testnow--locked.github/workflows/ci.ymlcargo check/clippyrun--locked; newcooldownjob annotates the run with a warning, never failsDockerfilecargo chef cookhonors$LOCKEDlike the final build; the shadow variant still sets it emptyCONTRIBUTING.md,CLAUDE.mdCorrectness / Behavior Guarantees
cargo update --workspace --dry-rundoes not flag too-new locked crates, so the check has to be the fullcargo update --dry-run -Z min-publish-age, greppingDowngrading|is too new. That run also refreshes git dependencies (leanSig, leanVM, ethrex, rust-libp2p) to their branch heads with no cooldown; git revs have no publish age and must be reviewed by hand.rand ^0.10conflict. Real cause: rand 0.10 needs chacha20 0.10, whose 0.10.0/0.10.1 are yanked and whose only live release (0.10.2) was published 2026-08-27.make updateerrors and the CI job emits a "probe failed" warning until 2026-09-10;make update-allowcovers an urgent bump before then. The committed lockfile already pins the yanked chacha20 0.10.0, which any future re-resolution will move.Tests Added / Run
make lintpasses with--locked.cargo check --lockedandcargo update --workspace --dry-run --lockedwith the new tables: no warnings, no changes.smallvec 1.16.0(3 days old) and ran the 7-day dry-run:Downgrading smallvec v1.16.0 -> v1.15.2 (...), confirming the grep pattern. Lockfile restored identical.make cooldown-check: both report the chacha20-driven probe failure as a warning with exit 0.Related Issues / PRs
✅ Verification Checklist
make fmt— clean (no Rust changes)make lint(clippy with-D warnings) — cleanmake test— not run; no Rust or fixture changes, only the--lockedflag was added