Policy as code

Cooldown policy

A cooldown policy hides newly published versions of packages from your repository index until they reach a minimum age based on the package version’s publish date.

Packages that do not meet the age requirement are hidden from the index, preventing package managers from accessing them. This protects your supply chain from recently published packages that may carry malware or have not yet undergone sufficient community scrutiny, while automatically resolving to the latest compliant version when one is available and automatic resolution is supported by the package manager.

A cooldown policy applies at the Cloudsmith index level to packages that originate from a public upstream source. It can apply to packages proxied directly from an upstream source as well as packages that are cached in your Cloudsmith workspace.

Supported formats:

  • Cargo
  • Conda
  • Docker
  • Go
  • Maven
  • npm
  • NuGet
  • Python
  • Ruby

Automatic reversal

If a cooldown policy is updated, disabled, or deleted, packages will be re-evaluated. This is different from other policies that only apply to cached packages and do not have automatic reversal.

How it works

A cooldown policy is a templated Rego policy with specific customizable settings and variables. For information about which parts of your cooldown policy are customizable, see Policy configuration.

Only one cooldown policy can be created per workspace.

A cooldown policy is evaluated directly on the package index, which is made up of two types of packages:

  • Upstream cached packages already in your Cloudsmith workspace.
  • Packages that originate from an upstream source but are not yet cached in your Cloudsmith workspace.

When a cooldown policy is enabled, packages that do not meet the configured age requirement are hidden from the index.

  • Packages that are not yet cached in Cloudsmith and need to be requested directly from a public upstream source are hidden from the index. They will not be downloaded or cached in Cloudsmith.
  • Packages that are already cached in Cloudsmith are quarantined and hidden from the index until the cooldown period elapses. Once the cooldown period expires, the package is unquarantined and becomes available for download and visible in the index, provided it does not violate any other active policies.

Tip

You can exclude packages that are already cached in your Cloudsmith workspace from the policy scope:

  • Via the web app: By leaving the Apply to packages already in use option unchecked in the Edit policy view.
  • Via the API: By setting the include_local_packages variable to false in your Rego policy logic.

Decision logs for a cooldown policy are only generated for packages that are already cached in your Cloudsmith workspace. They are accessible via the Cloudsmith web app and the Cloudsmith API.

Cloudsmith determines a package's age by using metadata retrieved from the upstream source. Where package age metadata is not populated, the cooldown policy will not apply, meaning the package will not be hidden from the index.

Supported package formats

FormatDescription
CargoApplies to packages where the created_at field exposed via the Cargo Crates JSON API (/api/v1/crates/{crate_name}) has been populated by the upstream source.
CondaThe cooldown policy uses the timestamp field from repodata.json in the upstream.

The timestamp field in Conda's repodata.json is a Unix timestamp in milliseconds that records the specific build or indexing time of a package artifact.
DockerFor artifacts pulled from Docker Hub via tag: The cooldown policy uses the last pushed timestamp in the Docker Hub tags API output for the image.

For artifacts pulled via digest regardless of the upstream source: The cooldown policy uses the Last-Modified header to populate the published_at date for the image.

For more information, see Docker images.
GoApplies to packages where the Time field has been populated by the Go module proxy.

This field is set when the proxy first fetches and caches a tagged version, and is exposed via the proxy's @v/<version>.info metadata. This timestamp is typically very close to, but not always identical to, the original tag creation time.

Cooldown policies are not supported for private GOPROXY upstreams.
MavenApplies to packages where the Last-Modified header has been populated by the upstream Maven repository.
Cloudsmith uses this header to populate the published_at timestamp. This value is typically a close approximation of the actual package publish date.
npmApplies to packages where the time field has been populated by the upstream source.
NuGetNuGet V2: Applies to packages where the data:Published field in the OData feed response has been populated by the upstream source.

NuGet V3: Applies to packages where the published field of the catalog leaf has been populated by the upstream source.
PythonApplies to packages where the upload-time field has been populated by the upstream source.
RubyApplies to packages where the created_at field exposed via the RubyGems Versions JSON API (/api/v1/versions/{gem_name}.json) has been populated by the upstream source.

If the created_at field isn't populated, we fall back to the created_at date on the /info/{gem_name} compact index, if available.

For more information, see Ruby packages.

Docker images

If you pull a Docker image that matches an active cooldown policy, the Docker client will not automatically resolve to the next compliant version available.

  • If the image has never been cached in your Cloudsmith workspace, the user will see the following error: Package is hidden. Reason: <policy description>.
  • If the image was previously cached in your repository and then matches a cooldown policy, it is hidden with status:hidden.

Note

If a previously-cached Docker image that was pulled from an upstream exists in your Cloudsmith repository, and the image tag is updated in the upstream, Cloudsmith will continue to serve the previously-cached image until the cooldown period passes.

Ruby packages

  • When using the compact index, policy changes may take up to 15 minutes to take effect. For example, Ruby packages can take up to 15 minutes to be hidden when you first enable a cooldown policy, and to reappear when they exit cooldown.
  • Bundler and RubyGems treat package versions in cooldown as yanked versions.
  • Without a lockfile, Bundler resolves the newest allowed version automatically.
  • If a Gemfile.lock pins a hidden package version, bundle install fails with Bundler's yanked gem message: …the author of <package-name> (<version>) has removed it.

The developer experience

What developers experience depends on how their dependencies are specified:

  • Compliant version available: The package manager resolves to the latest compliant version. Non-compliant versions are skipped automatically.
  • No compliant version available: Cloudsmith returns a 404 Not Found or 403 Forbidden error - this is common when pinned versions or a lockfile is used to resolve dependencies. Behavior may vary in different package managers. To resolve this, choose a version that satisfies the cooldown requirement.
ErrorCause
404 Not FoundThe specific pinned version you requested does not exist, or the version (or version within your declared range) is not old enough to be allowed.
403 ForbiddenThe package has been quarantined, or you do not have the requisite permissions to access the package.
200 OKThe requested version is accessible and has been downloaded.

Impact on existing packages

If caching is enabled on upstreams, packages previously pulled through Cloudsmith are already stored in your workspace. Excluding those from the policy means your existing builds are unlikely to break, because only new package versions entering the workspace are subject to the cooldown period. If a cooldown policy is applied to packages that are already in your workspace, or if upstreams are proxy-only, builds are more likely to break because non-compliant versions may be hidden.

Enhanced 403 error messaging

For supported package managers, Cloudsmith returns a customizable error message to the native client when a package is hidden from the index. The message is returned as part of the standard 403 response body and surfaced directly by the package manager, with no client-side configuration required.

The message includes:

  • Policy name: The name of the cooldown policy that matched the package.
  • Policy description: The policy description field, which you can use to include internal guidance. For example, who to contact, or a link to internal documentation.
  • Policy ID: The unique identifier for the policy action, for reference and troubleshooting.

Supported package managers

  • uv (Python)
  • npm
  • Maven
  • Docker

Policy configuration

Note

A cooldown policy is always set to Terminal, which prevents the evaluation of any further policies if this policy matches. This setting cannot be edited.

Note

Index-applicable policies have a higher precedence than any other policy. This setting cannot be edited.
Policies within a workspace are ordered based on the precedence integer defined in each policy. The policy with the lowest number is evaluated first, then each remaining policy is evaluated in order of ascending precedence.

You can configure and edit the following fields for your cooldown policy:

API parameters

FieldParameter nameDescription
Policy namenameA display name for the cooldown policy. The default policy name can be customized as needed.

For package managers where enhanced 403 error messaging is supported, the policy name is returned with any 403 error.
DescriptiondescriptionA description of the cooldown policy. The default policy description can be customized as needed.

For package managers where enhanced 403 error messaging is supported, the policy description is returned with any 403 error.
EnabledenabledA toggle that indicates whether the cooldown policy is enabled or disabled.

Rego variables

FieldVariable nameDescription
Included repositoriesincluded_repositoriesThe repositories that the cooldown policy applies to.

By default, the cooldown policy applies to all repositories in a workspace unless configured otherwise.
Excluded repositoriesexcluded_repositoriesThe repositories that the cooldown policy does not apply to.

By default, the cooldown policy applies to all repositories in a workspace unless configured otherwise.
Formatsupported_formatsThe package formats the policy applies to.

Currently supported:
cargo, conda, docker, go, maven, npm, nuget, python, and ruby.

Note: You must select at least one supported format before enabling your cooldown policy. Without a selected format, the policy will not apply to any package.
Cooldown periodwithin_past_daysThe minimum age, in days, a package must reach before it appears in the index. Packages published more recently than this value are hidden from the index.
Apply to packages already in useinclude_local_packagesUnchecked by default for policies created in the web app. For a cooldown policy created via the API or Terraform provider, you must manually set this value to true or false.

When unchecked or set to false, packages already in Cloudsmith will not be quarantined by the cooldown policy or hidden from the repository index.
Exemptionsexempt_package_prefixes
exempt_package_names
exempt_package_versions
Optional. Packages that skip the cooldown period, grouped by format. Each entry is a package name prefix (@acme/), a package name (lodash), or a package name and version (lodash 4.17.21).
Leave empty to apply the cooldown period to all packages in scope. For matching rules, see Exemptions.

To create and manage a cooldown policy, you must have access to policy as code in your workspace.

Important

Policy changes may take a few minutes to take effect on cached packages.

Note

Only one cooldown policy can be created per workspace.
  1. From the overview page in the Cloudsmith web app, go to the Policies tab and click Policy as code.
  2. Click + Create new policy.
  3. Select Start from a template, and then select the Cooldown period template.
    The Cooldown period policy template opens.
  4. Click Use template to create the cooldown policy. When created from a template, your cooldown policy is disabled by default.
    The cooldown policy opens in the Edit policy view.
  5. (Optional) Click the edit icon next to the policy name to customize the policy name and description.
  6. Customize the policy options. For guidance on which policy options are configurable, see Policy configuration.
  7. Click Save policy to save and apply your changes.

To edit an existing cooldown policy, go to the Policies tab of the overview page in the Cloudsmith web app, click Policy as code, and select the cooldown policy from the list. The policy opens in read-only mode. Click Edit policy to make changes.

For more information about editable policy fields, see Policy configuration.

Important

You must select at least one supported format before enabling your cooldown policy. Without a selected format, the policy will not apply to any package.

You can enable a cooldown policy from the Policies tab of the overview page in the Cloudsmith web app:

  1. From the overview page in the Cloudsmith web app, go to the Policies tab.
  2. Click Policy as code.
  3. In the policy list, find the cooldown policy that you want to enable. Hovering over the toggle next to the policy displays Click to enable policy.
  4. Click the toggle to enable the policy.

You can disable a cooldown policy from the Policies tab of the overview page in the Cloudsmith web app:

  1. From the overview page in the Cloudsmith web app, go to the Policies tab.
  2. Click Policy as code.
  3. In the policy list, find the cooldown policy that you want to disable. Hovering over the toggle next to the policy displays Click to disable policy.
  4. Click the toggle to disable the policy.

You can delete a cooldown policy from the Policies tab of the overview page in the Cloudsmith web app:

  1. From the overview page in the Cloudsmith web app, go to the Policies tab.
  2. Click Policy as code.
  3. In the policy list, find the cooldown policy that you want to delete.
  4. Click the options menu next to the policy.
  5. Click Delete policy.
  6. In the Delete policy: DANGER view that opens, click Delete to confirm that you want to delete the policy.

Creating and managing a cooldown policy via the Cloudsmith API

To create and manage a cooldown policy, you must have access to policy as code in your workspace.

Important

Policy changes may take a few minutes to take effect on cached packages.

Adding exemptions to existing policies

To add exemptions to a cooldown policy created via the API or Terraform provider before November 3, 2026, see Adding exemptions to an existing cooldown policy.

Policy actions are not automatically generated for cooldown policies created via the API

When you create a cooldown policy from the Cooldown period template in the Cloudsmith web app, Cloudsmith creates the required policy action automatically.

When you create a cooldown policy via the API, you must manually create a policy action to set the package state to hidden:

text
{"action_type": "SetPackageState", "package_state": "hidden"}

A cooldown policy with no "package_state": "hidden" action is evaluated and matches packages, but does not hide anything from the index.

A cooldown policy and its actions can be created and managed via the following endpoints.

Policies:

Policy actions:

Policy logic must meet specific criteria to be recognized and applied as a cooldown policy by Cloudsmith.

Ensure that your policy logic:

  • Is syntactically correct
  • Has a match rule
  • Contains the following required variables:
    • within_past_days
    • supported_formats
    • included_repositories
    • excluded_repositories
    • include_local_packages
  • Uses only supported operators:
    • ==
    • !=
    • >
    • >=
    • <
    • <=
    • in
    • not
  • Uses only supported fields:
    • input.v0.package.name
    • input.v0.package.version
    • input.v0.package.format
    • input.v0.package.filename
    • input.v0.package.group
    • input.v0.package.is_local
    • input.v0.package.upstream_metadata.published_at
    • input.v0.package.uploaded_at (cached packages only)
    • input.v0.repository.slug
  • Uses only supported functions:
    • time.now_ns
    • time.add_date
    • time.parse_rfc3339_ns
    • concat
    • startswith

Example policy:

rego
package cloudsmith

import rego.v1

# Cooldown policy
# Hides package versions from the index until they reach a minimum age,
# giving time for malicious releases to be found before they're used.
# Requires a SetPackageState action with package_state "HIDDEN". The web app
# and the API template helper add this action automatically.

default match := false

# --- Settings ---

# Minimum age, in days, from the upstream publish date.
# Newer packages are hidden from the index.
within_past_days := 3

# Formats this policy applies to. An empty set applies to no packages.
# Available: cargo, conda, docker, go, maven, npm, nuget, python, ruby
supported_formats := {"cargo", "conda", "docker", "go", "maven", "npm", "nuget", "python", "ruby"}

# Repositories this policy applies to, by slug.
# Leave empty to apply to all repositories.
included_repositories := {}

# Repositories this policy doesn't apply to, by slug.
# Takes priority over 'included_repositories'.
excluded_repositories := {}

# Package exemptions, grouped by format.
# Each entry applies only to the format it's listed under.

# Name prefixes. Example: {"npm": {"@acme/"}}
exempt_package_prefixes := {}

# Exact names, all versions. Example: {"maven": {"com.acme:commons-lang3"}}
exempt_package_names := {}

# Exact versions. Example: {"npm": {"lodash": {"4.17.21"}}}
exempt_package_versions := {}

# Only applies to packages not yet cached in this workspace, such as those
# from proxy-only upstreams. Set to 'true' to also apply to cached packages.
# Hiding cached packages is more likely to break builds.
include_local_packages := false

# --- Policy logic: do not edit below this line ---

match if count(reason) != 0

# Skips cached packages unless 'include_local_packages' is 'true'.
_should_evaluate if {
    not input.v0.package.is_local
}

_should_evaluate if {
    include_local_packages == true
}

_repo_allowed if {
    count(included_repositories) == 0
    not input.v0.repository.slug in excluded_repositories
}

_repo_allowed if {
    input.v0.repository.slug in included_repositories
    not input.v0.repository.slug in excluded_repositories
}

# If a format or name isn't listed, these rules don't match and the
# package is not exempt.
_package_exempt if {
    not input.v0.package.format in {"maven"}
    some prefix in exempt_package_prefixes[input.v0.package.format]
    startswith(input.v0.package.name, prefix)
}

_package_exempt if {
    input.v0.package.format in {"maven"}
    some prefix in exempt_package_prefixes[input.v0.package.format]
    startswith(concat(":", [input.v0.package.group, input.v0.package.name]), prefix)
}

_package_exempt if {
    not input.v0.package.format in {"maven"}
    input.v0.package.name in exempt_package_names[input.v0.package.format]
}

_package_exempt if {
    input.v0.package.format in {"maven"}
    concat(":", [input.v0.package.group, input.v0.package.name]) in exempt_package_names[input.v0.package.format]
}

_package_exempt if {
    not input.v0.package.format in {"maven"}
    input.v0.package.version in exempt_package_versions[input.v0.package.format][input.v0.package.name]
}

_package_exempt if {
    input.v0.package.format in {"maven"}
    input.v0.package.version in exempt_package_versions[input.v0.package.format][concat(":", [input.v0.package.group, input.v0.package.name])]
}

# Adds a reason when an in-scope package was published within the cooldown period.
reason contains msg if {
    _should_evaluate
    _repo_allowed
    not _package_exempt
    pkg := input.v0.package
    within_past_days_date := time.add_date(time.now_ns(), 0, 0, 0 - within_past_days)
    publish_date := time.parse_rfc3339_ns(pkg.upstream_metadata.published_at)
    publish_date >= within_past_days_date
    pkg.format in supported_formats
    msg := sprintf(
        "Package %v/%v (%v) is within the %v-day cooldown period: published %v",
        [pkg.name, pkg.version, pkg.format, within_past_days, pkg.upstream_metadata.published_at],
    )
}

Exemptions

You can exclude specific repositories, formats, and cached packages from a cooldown policy using the options in Policy configuration.

To let specific packages skip the cooldown period, add them to the inline exemption list in your cooldown policy's Rego. Exempt packages are available immediately, provided they don't violate any other active policies. For example, use this when a new release fixes a vulnerability and your teams need it straight away.

Inline package exemptions can be scoped in three ways:

ScopeMatchesExample
Package name prefixEvery package whose name starts with the text@acme/
Package nameAll versions of one packagelodash
Package name and versionOne exact versionlodash 4.17.21

Matching rules

  • Exemptions are created per format. An npm entry has no effect on Python packages, even if the names are identical.
  • Wildcards are not supported. * is matched literally and won't match other packages.
  • Matching is case sensitive, except where noted for specific formats below.
  • Package name entries are exact. lodash matches lodash only. It doesn't match lodash-es.

Warning

A prefix matches every package that starts with that text, whoever published it. For example, acme- matches any package named acme-* on the public registry. Where a format has publisher namespaces, include the full namespace and separator, for example @acme/ for npm. Cloudsmith doesn't verify that a prefix remains within a namespace.

Format-specific behavior for package name prefixes

FormatBehavior
npmOnly the scope owner can publish to a scope, so a scoped prefix such as @acme/ matches only that publisher's packages. Unscoped prefixes such as acme- match packages from any publisher.
MavenPackage name entries must use the full groupId:artifactId coordinate, for example org.apache.commons:commons-lang3. A bare artifact name won't match anything. Prefixes match the coordinate. See examples in the table below.
NuGetPackage IDs use dots by convention, for example Newtonsoft.Json, but the dots don't indicate ownership. Newtonsoft. matches packages from any publisher.
PythonPython treats requests_oauthlib, requests.oauthlib, and Requests-OAuthLib as the same project. An exemption entry with a different separator or case may not match. Copy the name exactly from the package detail page in the Cloudsmith web app.
DockerNames are lowercase. / separates the namespace from the repository.
GoModule paths are domain-based, for example github.com/org/repo. Use / as the boundary in prefixes.
Cargo, Conda, RubyNames have no publisher namespace, so any prefix matches packages from all publishers. Use package name or package name and version entries where possible.

Additional Maven examples for package name prefixes

PrefixMatchesDoesn't match
com.acme:Packages in the com.acme group onlycom.acme.tools:widget
com.acme.Packages in sub-groups of com.acmecom.acme:widget
com.acmeBoth of the above, and any group starting with com.acme, such as com.acmecorp:widget

When you update the exemption list, Cloudsmith re-evaluates packages. Cached packages hidden by the cooldown policy become available once they match an exemption. Changes typically take effect within a few minutes.

Changes to the exemption list are recorded in audit logs as a policy update.

Adding exemptions to an existing cooldown policy

Cooldown policies created in the web app are updated automatically to support exemptions.

Cooldown policies created via the API or Terraform provider before November 3, 2026 don't include the exemption list. To use exemptions, delete the policy and recreate it from the updated example policy. When you recreate it, also recreate the policy action that sets the package state to hidden.

Packages are re-evaluated when you delete a cooldown policy, so packages held in cooldown become available until you create the new policy. Create the new policy immediately after deleting the old one.

For policies managed with the Terraform provider, make all changes in Terraform. Changes made in the web app or API are overwritten on the next terraform apply.

Exemption policies don't apply to cooldown policies

Exemption policies only apply to package-applicable policies, which act on packages that already exist in Cloudsmith. A cooldown policy is index-applicable. It's evaluated against the repository index, and most packages it hides aren't cached in Cloudsmith. To let packages skip the cooldown period, use the inline exemptions described above.

Tip

Review your exemption list regularly and remove entries you no longer need.

Viewing packages that violate your cooldown policy

Upstream packages that are already cached in your Cloudsmith workspace and violate your cooldown policy are displayed as HIDDEN in the Cloudsmith web app and via the Cloudsmith API. These packages cannot be downloaded, unhidden, or unquarantined.

To find packages currently hidden due to a cooldown policy, search your workspace using the search term status:hidden:

Hovering over the HIDDEN status in the web app will display why the package is hidden:

When a cooldown policy is deleted or disabled, packages previously displayed as HIDDEN revert to their prior status.

After a cooldown policy is created, it may take several minutes for the non-compliant packages in your Cloudsmith workspace to appear as HIDDEN.