cargo-cooldown 0.3.0

Cargo wrapper that enforces a cooldown window for freshly published registry crates for improved supply chain security.
# Configuration

`cargo-cooldown` uses `cooldown.toml`. Allow rules live in the same file under
the `allow` section.

Create one interactively:

```bash
cargo cooldown init
```

## Minimal File

```toml
cooldown_minutes = 1440
enforcement = "cargo_compatible"
cargo_compatible_accept = "prompt"
lockfile_baseline = "floor"
```

Meaning:

- `cooldown_minutes`: releases newer than this window are considered fresh
- `enforcement`: what cooldown does when Cargo still requires fresh versions
- `cargo_compatible_accept`: whether to prompt before accepting fresh versions
  Cargo still requires
- `lockfile_baseline`: whether the initial `Cargo.lock` is used as a version
  floor

## Resolution Order

Configuration is loaded in this order:

1. environment variables
2. active member `cooldown.toml`, when exactly one workspace member is targeted
3. workspace root or crate root `cooldown.toml`
4. `$CARGO_HOME/cooldown.toml`

Environment variables always win.

Recommended layout:

- single crate: `<crate-root>/cooldown.toml`
- workspace: `<workspace-root>/cooldown.toml`
- optional workspace override: `<member-dir>/cooldown.toml`

Member overrides apply only to unambiguous member runs, such as:

```bash
cargo cooldown check --package member
cargo cooldown check --manifest-path member/Cargo.toml
```

They do not apply to workspace-wide runs such as `--workspace`,
`--package a --package b`, or `--exclude`.

## Supported Keys

`cooldown.toml` supports:

- `cooldown_minutes`
- `enforcement`
- `cargo_compatible_accept`
- `lockfile_baseline`
- `now`
- `ttl_seconds`
- `cache_dir`
- `http_retries`
- `verbose`
- `skip_registries`
- `allow`

Environment variables:

- `COOLDOWN_MINUTES`
- `COOLDOWN_ENFORCEMENT`
- `COOLDOWN_CARGO_COMPATIBLE_ACCEPT`
- `COOLDOWN_LOCKFILE_BASELINE`
- `COOLDOWN_NOW`
- `COOLDOWN_TTL_SECONDS`
- `COOLDOWN_CACHE_DIR`
- `COOLDOWN_HTTP_RETRIES`
- `COOLDOWN_VERBOSE`
- `COOLDOWN_SKIP_REGISTRIES`

Unknown keys and invalid values fail configuration loading.

Set `verbose = true` or `COOLDOWN_VERBOSE=1` for debug logs. Normal output stays
compact and ends with one Cargo-style summary of lockfile changes.

## `enforcement`

`enforcement` controls what happens after cooldown tries to make the dependency
graph old enough. This matters because cooldown is a supply-chain safety guard:
it reduces the chance that a normal Cargo command downloads a crate version that
was published very recently and may still be in the highest-risk window for a
supply-chain attack.

Values:

- `strict`: fail and restore the original `Cargo.lock` if any fresh version
  still cannot be replaced with an older version accepted by Cargo
- `cargo_compatible`: try to cool every eligible registry package, keep the best
  Cargo-valid lockfile that cooldown can produce, and warn about fresh versions
  that Cargo still requires
- `off`: skip cooldown entirely

If `enforcement` is omitted, config loading defaults to `strict`. New files
generated by `cargo cooldown init` default to `cargo_compatible`.

`cargo_compatible_accept` controls the unresolved fresh-version prompt:

- `prompt` (default): ask before keeping fresh versions that Cargo still
  requires. The prompt lists each crate, version, registry, and publish date.
- `auto`: do not ask; keep the best Cargo-valid lockfile and warn.

Use `auto` for non-interactive workflows that intentionally accept the
Cargo-compatible result:

```toml
cargo_compatible_accept = "auto"
```

or:

```bash
COOLDOWN_CARGO_COMPATIBLE_ACCEPT=auto cargo cooldown update
```

Example:

```toml
enforcement = "cargo_compatible"
```

or:

```bash
COOLDOWN_ENFORCEMENT=cargo_compatible cargo cooldown update
```

`cargo_compatible` is still protective: it cools every package Cargo can accept
without producing an invalid `Cargo.lock`. It is flexible only at the point where
Cargo's resolver requires a fresh version. In that case, cooldown keeps the best
valid lockfile it found and prints the remaining fresh versions for review.
It also downgrades release-time metadata failures to warnings; use `strict` if a
registry that cannot prove release ages should fail closed.

Common reasons Cargo may still require a fresh version:

- the current `Cargo.toml` requires a version range whose valid releases are
  still inside the cooldown window
- a transitive crate uses an exact dependency on a fresh version
- enabled features or target-specific dependencies activate a newer dependency
  path
- several crates must move together, but no older compatible set exists
- `lockfile_baseline = "floor"` protects the pre-run lockfile floor
- an allow rule or skipped registry intentionally exempts the package from
  normal cooldown handling

Use `strict` when unresolved fresh downloads should be blocked entirely. This
version does not ask interactively before later Cargo commands download a
resolver-constrained fresh version that was not already present in the initial
`Cargo.lock`; `strict` is the fail-closed choice for that workflow.

## `lockfile_baseline`

`lockfile_baseline` controls whether cooldown may go below versions already
present in the initial `Cargo.lock`.

Values:

- `floor` (default): use the initial `Cargo.lock` as the minimum version floor
- `ignore`: ignore that floor and allow cooldown below initial `Cargo.lock`
  versions

With `cargo cooldown update`, the initial lockfile means the real file that
existed before the isolated temp-workspace `cargo update` ran.

Example:

```toml
lockfile_baseline = "ignore"
```

or:

```bash
COOLDOWN_LOCKFILE_BASELINE=ignore cargo cooldown update
```

Important: `lockfile_baseline = "ignore"` is not a force downgrade setting.
Cooldown still asks Cargo to validate the final graph. If Cargo rejects every
older assignment, the fresh version remains unresolved.

## Policy Combinations

Use this table as the main mental model:

| Config | Human meaning |
| --- | --- |
| `lockfile_baseline = "floor"` + `enforcement = "cargo_compatible"` | `cargo cooldown init` default. Keep the pre-run lockfile as the floor. Cool only versions added or changed by this run. Ask before accepting any remaining fresh version unless `cargo_compatible_accept = "auto"` is set. |
| `lockfile_baseline = "floor"` + `enforcement = "strict"` | Fail-closed policy. Keep the pre-run lockfile as the floor. Cool only versions added or changed by this run. Fail if any new fresh version remains. |
| `lockfile_baseline = "ignore"` + `enforcement = "strict"` | Try to cool every eligible locked registry package, including packages already in `Cargo.lock`. Fail if any fresh version still cannot be cooled. |
| `lockfile_baseline = "ignore"` + `enforcement = "cargo_compatible"` | Try to cool every eligible locked registry package, keep Cargo's best valid result, and warn about the remaining fresh versions. |

Why can a fresh version remain?

- the current `Cargo.toml` requires a fresh version range
- a transitive crate uses an exact dependency
- features or target-specific dependencies activate a newer package
- a group of crates has no older combination that Cargo accepts
- an allow rule or skipped registry exempts the package
- `lockfile_baseline = "floor"` protects the pre-run lockfile floor

## Allow Rules

Allow rules reduce the cooldown window for selected crates.

```toml
[[allow.exact]]
crate = "serde"
version = "1.0.218"

[[allow.package]]
crate = "tokio"
minutes = 60

[[allow.package]]
crate = "openssl"
minutes = 0
```

Rules:

- `[[allow.exact]]`: allow one exact `(crate, version)` pair
- `[[allow.package]]`: use a shorter cooldown for one crate name
- `minutes = 0`: exclude that crate from cooldown
- `[allow.global]`: define a shorter default cooldown for all registry crates

`allow.global` and `allow.package` only reduce the effective cooldown. They do
not increase it above `cooldown_minutes`.

Workspace merge behavior:

- `allow.global`: member value replaces workspace value
- `allow.package`: member entries override workspace entries with the same crate
- `allow.exact`: member and workspace entries are unioned and deduplicated

## `skip_registries`

Skip a registry by logical name or effective URL:

```toml
skip_registries = ["crates-io", "sparse+https://example.com/index/"]
```

Environment variable form:

```bash
COOLDOWN_SKIP_REGISTRIES=crates-io,sparse+https://example.com/index/
```

Skipped registries are not inspected, fetched through fallback HTTP, or
downgraded. Their packages still participate in Cargo's resolver constraints.

## `cargo cooldown init`

Use `cargo cooldown init` from the project root to create `cooldown.toml`
interactively.

- in a crate root, it creates one `cooldown.toml`
- in a workspace root, it can create one shared file plus optional member
  override files
- it refuses to overwrite existing `cooldown.toml` files

This is cargo-cooldown's setup wizard. It does not forward to Cargo's
`cargo init`.