The problem
A Rust project gets nothing from Update-time. Its Cargo.toml is not scanned, its dependencies are not updated, and none of the four warnings reaches them — while the Dockerfile and the GitHub Actions workflow in the same repository are covered.
That is worth changing because Rust is the type where every check would work. Probed 2026-08-15:
| Question |
Answer |
| What crates.io reports per version |
num, created_at, yanked, checksum |
| Does OSV match a crates.io version? |
yes — time@0.1.44 returns GHSA-wcg3-cvx6-7396 and RUSTSEC-2020-0071 |
Publication dates make the cooldown and the staleness check possible, yanked is crates.io's own concept rather than something to approximate, and OSV matches versions properly — unlike the GitHub Actions ecosystem, where a version-bearing query comes back empty. Cargo would be the second dependency type after Python to get all five checks, so its per-type documentation would have a real answer in every subsection rather than several "none: no such concept".
What would be updated
Cargo.toml, searched for recursively. Cargo.lock stays untouched, since cargo maintains it.
- The dependencies in
[dependencies], [dev-dependencies], [build-dependencies], [workspace.dependencies], and the target-specific tables, in both spellings: the string form serde = "1.0" and the table form serde = { version = "1.0", features = ["derive"] }.
- Not a path or a git dependency, which crates.io does not serve. That is the same case as a dependency uv resolves through a
[tool.uv.sources] entry, and it wants the same treatment: left out of the update and out of the checks, rather than looked up under a name that may belong to an unrelated crate.
The design constraint: what counts as a pin
Cargo's default requirement is a caret range. serde = "1.0" means ^1.0, so it already admits 1.9, and the exact spelling is serde = "=1.0.203". Update-time's rule elsewhere is that only an exact pin is rewritten, which would leave almost every real Rust manifest untouched, since the caret form is the convention rather than a deliberate opt-out.
So the question this type has to answer first is whether a caret requirement is a pin to move forward or a range to leave alone. npm's answer is to delegate and let the range decide; Python's answer is to rewrite the exact pins and let a looser specifier be the opt-out. Rust sits between the two, because the idiomatic spelling is a range and there is no separate convention for "keep me here".
Who rewrites the manifest
Unverified, and it needs verifying before any of this is built. Cargo is installed on the machine this was written on only through rustup, with no default toolchain, so nothing could be run against a real project.
From cargo's documented behaviour, cargo update resolves within the requirement the manifest declares, as npm update does, and bumping the requirement itself is cargo upgrade, which ships in the separate cargo-edit crate rather than in cargo. If that holds, Update-time rewrites Cargo.toml itself and then runs cargo update to bring Cargo.lock along — the shape the pyproject.toml path already has, where Update-time writes the pins and uv relocks.
The five checks
| Check |
How |
| Update |
the versions crates.io lists, filtered by whatever the previous section settles about caret requirements |
| Cooldown |
created_at per version, applied by Update-time itself |
| Staleness |
the newest version's created_at |
| Yank |
yanked per version, so a yanked release is skipped when picking a new version and a reference left on one is warned about |
| Vulnerability |
OSV's crates.io ecosystem |
Pinning
The manifest holds no hash, so Update-time adds none — the answer Python dependencies already get. crates.io reports a checksum per version and Cargo.lock records it, and cargo maintains that file.
Markers
Cargo.toml is TOML and takes # comments, and a dependency is normally one per line, so both marker placements work and the comment leads need no change. A dependency declared in the table form spans several lines when it is formatted that way, which is the one placement wrinkle worth testing.
Out of scope
Cargo.lock, which cargo maintains.
rust-toolchain.toml, which names a toolchain rather than a dependency. It is the Rust counterpart of .python-version and deserves its own issue.
- Registries other than crates.io, declared through
[registries].
Documentation
- A new per-type section under "Details per dependency type", with all nine subsections the structure check requires.
- A row in the first dependency-type table, which then obliges a row in each of the other tables.
- The list of scanned files in the command-line help.
- A changelog entry under
[Unreleased].
Open questions
- Is a caret requirement a pin to move forward or a range to leave alone? Everything else about the type follows from this.
- Does Update-time rewrite the requirement itself, or shell out to
cargo upgrade when cargo-edit is installed and do nothing when it is not?
- What happens to a member that inherits from the workspace,
serde = { workspace = true }, whose version lives in the root manifest? The reference to update is in one file and the declaration naming it in another.
- Does a workspace get one
cargo update run at its root, or one per member manifest?
The problem
A Rust project gets nothing from Update-time. Its
Cargo.tomlis not scanned, its dependencies are not updated, and none of the four warnings reaches them — while the Dockerfile and the GitHub Actions workflow in the same repository are covered.That is worth changing because Rust is the type where every check would work. Probed 2026-08-15:
num,created_at,yanked,checksumtime@0.1.44returnsGHSA-wcg3-cvx6-7396andRUSTSEC-2020-0071Publication dates make the cooldown and the staleness check possible,
yankedis crates.io's own concept rather than something to approximate, and OSV matches versions properly — unlike the GitHub Actions ecosystem, where a version-bearing query comes back empty. Cargo would be the second dependency type after Python to get all five checks, so its per-type documentation would have a real answer in every subsection rather than several "none: no such concept".What would be updated
Cargo.toml, searched for recursively.Cargo.lockstays untouched, since cargo maintains it.[dependencies],[dev-dependencies],[build-dependencies],[workspace.dependencies], and the target-specific tables, in both spellings: the string formserde = "1.0"and the table formserde = { version = "1.0", features = ["derive"] }.[tool.uv.sources]entry, and it wants the same treatment: left out of the update and out of the checks, rather than looked up under a name that may belong to an unrelated crate.The design constraint: what counts as a pin
Cargo's default requirement is a caret range.
serde = "1.0"means^1.0, so it already admits1.9, and the exact spelling isserde = "=1.0.203". Update-time's rule elsewhere is that only an exact pin is rewritten, which would leave almost every real Rust manifest untouched, since the caret form is the convention rather than a deliberate opt-out.So the question this type has to answer first is whether a caret requirement is a pin to move forward or a range to leave alone. npm's answer is to delegate and let the range decide; Python's answer is to rewrite the exact pins and let a looser specifier be the opt-out. Rust sits between the two, because the idiomatic spelling is a range and there is no separate convention for "keep me here".
Who rewrites the manifest
Unverified, and it needs verifying before any of this is built. Cargo is installed on the machine this was written on only through rustup, with no default toolchain, so nothing could be run against a real project.
From cargo's documented behaviour,
cargo updateresolves within the requirement the manifest declares, asnpm updatedoes, and bumping the requirement itself iscargo upgrade, which ships in the separate cargo-edit crate rather than in cargo. If that holds, Update-time rewritesCargo.tomlitself and then runscargo updateto bringCargo.lockalong — the shape thepyproject.tomlpath already has, where Update-time writes the pins and uv relocks.The five checks
created_atper version, applied by Update-time itselfcreated_atyankedper version, so a yanked release is skipped when picking a new version and a reference left on one is warned aboutcrates.ioecosystemPinning
The manifest holds no hash, so Update-time adds none — the answer Python dependencies already get. crates.io reports a
checksumper version andCargo.lockrecords it, and cargo maintains that file.Markers
Cargo.tomlis TOML and takes#comments, and a dependency is normally one per line, so both marker placements work and the comment leads need no change. A dependency declared in the table form spans several lines when it is formatted that way, which is the one placement wrinkle worth testing.Out of scope
Cargo.lock, which cargo maintains.rust-toolchain.toml, which names a toolchain rather than a dependency. It is the Rust counterpart of.python-versionand deserves its own issue.[registries].Documentation
[Unreleased].Open questions
cargo upgradewhen cargo-edit is installed and do nothing when it is not?serde = { workspace = true }, whose version lives in the root manifest? The reference to update is in one file and the declaration naming it in another.cargo updaterun at its root, or one per member manifest?