> For the complete documentation index, see [llms.txt](https://docs.sealsecurity.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.sealsecurity.io/discovering/private-versions.md).

# Private versions

Sealed versions whose fix introduces a breaking change, enabled per Seal Project from the Vulnerable packages tab after you acknowledge the impact.

A **private version** is a sealed version whose fix is not a perfect drop-in replacement for the origin version. Most sealed packages preserve the origin version's behavior exactly: a private version is the exception, used when a vulnerability cannot be remediated without changing something the origin version's users may have been depending on.

The [Partial fixes & open vulnerabilities](/discovering/partial-fixes.md) chapter is the canonical home for the case that produces private versions: a vulnerability that can only be removed by removing a feature, changing a default, or otherwise altering observable behavior. A private version is what Seal can offer when a customer is willing to accept the impact in exchange for the fix.

## What a private version is

A private version is built from the same origin version as a regular sealed version, with a closely related but distinct version-string suffix. Where a regular sealed version uses `-sp[N]` or `+sp[N]` (depending on the ecosystem), a private version uses `-sp[N]p[M]` or `+sp[N]p[M]`. `[N]` matches the public sealed version the private version is built on top of; `[M]` is a running count of private fixes atop that public sealed version. So `ejs@2.7.4-sp1p1` is the first private version sitting on top of the public `ejs@2.7.4-sp1`.

When Seal releases a new public sealed version for a package that already has private versions, the private versions are rebuilt to keep up. A customer using `ejs@2.7.4-sp1p1` will be offered `ejs@2.7.4-sp2p1` once `ejs@2.7.4-sp2` ships, with the private fix re-applied on top of the newer public sealed version. Private versions do not fall behind public sealed versions.

## Why a private version makes sense

A private version's compatibility caveat is real, but the breaking change usually affects features most customers do not use. Removing support for a deprecated, insecure cryptographic algorithm only breaks code paths that still call that algorithm; flipping a permissive default to a strict one usually only affects deployments that depended on the old default. The customers most likely to adopt a private version are the ones who can confirm their code does not use the changing behavior at all.

Even when the changing feature is not in use, the vulnerability is often still worth fixing:

* The vulnerable code may be reachable through another call path, independent of the feature the private version changes.
* The vulnerable code may not be reachable at all, but compliance frameworks or a downstream customer's policy may require remediation regardless.

If the changing feature is not used, the private version delivers the fix at no behavioral cost. Compared to the alternative when there is no public sealed version for the CVE, which is usually upgrading to a major new release of the package or of the framework that bundles it, a private version is far cheaper: enabling it for a Seal Project takes one confirmation in the Seal UI and one CLI run, rather than the weeks of testing and migration a major upgrade would need.

## When Seal builds a private version

Seal does not build a private version unless a customer asks for one. The trigger is always a customer request: a vulnerability that matters to the customer, that has no clean backport because the only fix involves a behavior change, and that the customer is willing to accept the impact of.

Examples:

* A package's only fix removes support for a deprecated, insecure cryptographic algorithm. Customers whose code does not use the algorithm can adopt the private version safely. Customers whose code does use it would silently break and have to migrate first.
* A package's only fix changes a default that was permissive to a stricter setting. Customers whose deployments rely on the permissive default would behave differently after the change.

In both cases, Seal could not ship the fix as a public sealed version: its compatibility contract would no longer hold for every customer of that package. Building it as a private version, only enabled for customers who have evaluated the impact, preserves the contract.

## Why usage is per Seal Project

Private versions are enabled per Seal Project, not tenant-wide and not platform-wide. Each Seal Project has its own list of allowed private versions; only versions on the list are served to that project's builds.

The reason is that the breaking-change impact depends on the customer's code in that specific project. A customer with two Seal Projects, one that uses the affected behavior and one that does not, may want the private version for the second project but not the first. Per-project enablement makes that a normal configuration choice rather than a global all-or-nothing.

## Requesting a private version Seal has not built yet

Seal only builds a private version when a customer asks for one, so the first step for a package that has no private version yet is a conversation:

1. **Identify the package and version**. The vulnerability is open in the [drawer](/discovering/package-drawer.md), and the [Unfixable](/discovering/package-states.md#unfixable) section of Package states is where Seal's analysis lands when no clean fix exists.
2. **Contact your Seal account team** to request a private version for that package and version.
3. **Review the breaking-change impact** with the account team. The conversation covers what behavior the private version changes, who would be affected, and how to test that the change is safe for your project.

Seal then builds the private version. Once it exists, you enable it yourself, per Seal Project, in the Seal UI. A private version another tenant already drove Seal to build is available to you the same way, with no request needed first.

## Enabling a private version

When Seal's recommendation for a package is a private version that the Seal Project has not enabled yet, the row's **Seal** button on the Vulnerable packages tab opens the **Enable private version** step instead of the usual sealing flow:

1. **Review what the step shows**. It names the package version and the Seal Project you are about to enable it for, states that the version carries a breaking change necessary for the security fix, and lists the vulnerabilities that only this version seals.
2. **Test for compatibility**. Enabling a private version is the point at which you take on its behavior change, so run your own tests against it.
3. **Select Enable >**. The private version is enabled for that Seal Project only. Enabling it in one Seal Project never applies it to another.
4. **Seal the package**. The flow continues straight into the usual sealing step, where you create the Sealing Rule. If a rule for the package already exists, it uses the private version automatically on the next CI run.
5. **Run your build**. The next CLI run, or the next pull through the [Seal Artifact Server](/discovering/artifact-server-discovery.md), pulls the private version.

## After a private version is enabled

A private version is a normal sealed version once enabled. You create a Sealing Rule for it the same way you do for a public sealed version, on the [Sealing rules tab](/discovering/protection-page/sealing-rules-tab.md) or with the **Seal** button on a Vulnerable packages row. The Vulnerable packages tab moves the row to Pending deploy and then to Sealed in the usual way; the row carries the same state semantics as any other sealed package.

## Disabling a private version

Disabling is the mirror of enabling, in the same UI. Two entry points open the same **Disable private version** confirmation:

* The `...` menu on the package's row on the Vulnerable packages tab.
* The **Packages with compatibility issues** section of the project drawer on the Projects page, which lists the private versions a Seal Project currently has enabled.

The confirmation names the package version and the Seal Project, and repeats the list of vulnerabilities that only the private version seals, so you can see what goes back to being open. Select **Disable** to remove it from that project's enabled private versions.

What happens to a Sealing Rule pinned to the disabled version depends on where the rule lives:

* A [Remote Sealing Rule](/using-platform/sealing-rules/remote-rules.md) pinned to it is deleted for you.
* A [Local Sealing Rule](/using-platform/sealing-rules/local-rules.md) pinned to it has to be removed from the `.seal-actions.yml` file yourself, as with any local rule.

The next CLI run no longer pulls the private version.

## Related

* [Partial fixes & open vulnerabilities](/discovering/partial-fixes.md): the broader framing of why some vulnerabilities can only ship as private versions.
* [The package drawer](/discovering/package-drawer.md): where you see the open vulnerability that motivated the request.
* [Package states](/discovering/package-states.md): the lifecycle a private-version-using package follows once enabled.
