Skip to content

Configuring Org Policy

Org policy lives at /policy/npm (redirected from /policy) and controls how every install for your org is evaluated.

pkgPlease checks, in this order:

  1. Security blocks — malware, an unavailable malware provider (we fail closed), and any block pack whose class is security.
  2. Policy blocks — your own thresholds, such as the threat ceiling and the minimum package age.
  3. Steer — a package you have not endorsed when you have endorsed an interchangeable one.
  4. Needs approval — an endorsed package at a version outside the endorsed range.
  5. Endorsement — a soft allow, deliberately below every check above.
  6. Thresholds — the warn tiers.

An endorsement is not an escape hatch from a security block. Overriding one is possible, and it is a deliberate, recorded act — see Overrides below. A live endorsement authored specifically to override a given block pack satisfies that block for a human; an agent can never satisfy a block itself — it can only file a request through the approvals queue.

  • Block threshold (block_threat) — threat score at or above this blocks the install outright.
  • Warn threshold (warn_threat) — threat score at or above this (but below the block threshold) warns instead of blocking.
  • Warn trust (warn_trust) — trust score below this warns.
  • Completeness floor (completeness_floor) — minimum scoring confidence required before trust/threat are trusted at all. A score computed without the vulnerability/malware provider (for example, an early fast-path score written while the full scan is still queued) reports zero threat-completeness — it is never treated as “fully complete” just because no provider was asked in the first place — and fails closed rather than allowing the install.
  • Minimum package age (min_package_age_days) — set to 0 to disable. Note: an unpinned install (no exact version requested) always resolves to the newest version that clears this floor, regardless of the action setting below. Defaults to 7 days for an org with no configured value.
  • Package age action (min_package_age_action) — block, warn, or allow for a version younger than the floor. An org created after the minimum-package-age feature shipped (2026-09-03) defaults to block (secure by default). An org that existed before then keeps warn — the behavior this check had before the action setting existed — until it is changed here.

Click Save thresholds to apply changes.

Separately from the configurable thresholds above, pkgPlease automatically caps trust for packages under 30 days old — this cap is not adjustable via org policy. It only applies when the package’s age is known. If npm’s metadata for a package couldn’t be fetched (a transient registry error or timeout), its age is unknown rather than assumed to be zero, and the cap does not apply — an unscoreable age is reflected instead through the abstained package_age trust signal and the completeness floor above. This distinction matters most for agent-token installs, where a warn from the org policy escalates to a block: a package should never be blocked as “brand new” just because pkgPlease couldn’t reach npm.

Every org gets two packs of its own: org-blocks, for packages this org has decided to block outright, and org-endorsements, for packages this org has endorsed.

An endorsement names a package, an optional semver range (omitted or unset means all versions), and a reason. The range is what absorbs an ordinary bump — endorse left-pad at ^1.0.0 once, and every 1.x release satisfies it without a new entry. The reason is where the specifics belong: “9.x is ESM-only” is a reason, not a range — the range says which versions are endorsed, the reason says why, and that distinction is what keeps a coding agent from reading a version constraint as a compatibility note (or the reverse). The next person or agent that hits the same package reads the decision instead of re-litigating it.

Add an entry with POST /api/v1/policy/{registry_type}/packs/{slug}/entries — a package name, an optional semver range, and a reason.

From the app, the fastest way to write one is the “Endorse”/“Block” popup on a package’s own detail page (or the same popup from the ⋯ menu on the packages list) — it pre-fills the package and version, and offers quick-pick range helpers (“this version or newer, same major/minor”, “any version”) so you don’t have to hand-write a semver range. The /policy/{registry} Endorsements tab (?tab=endorsements) covers the same write with bare text inputs, useful when you already know the exact range you want; Blocks (?tab=blocks) is the equivalent tab for org-blocks.

A range that overlaps an existing entry for the same package is rejected at write time with a 409, rather than silently creating an ambiguous double match — two live entries for the same package would leave no deterministic answer for which reason applies to a given version. Widen the existing entry instead of adding a second one: delete it and re-add it with the broader range, or approve the out-of-range version from the Approvals queue, which performs that widening for you.

Every block is overridable by a person, and never by an agent — an agent that hits a block can only file a request through the approvals queue. The ceremony a person goes through depends on what’s being overridden:

  • A policy block (your own thresholds) is satisfied by an ordinary endorsement, or by an ordinary approval in the queue.
  • A security block (malware, or a block pack whose class is security) requires more: a permitting role, a written reason, and a mandatory expiry. The expiry is not incidental — an override with no expiry becomes a permanent hole in exactly the place a security block exists to close, silently outliving the incident it was written for. Set override_of_pack_id (naming the block pack being overridden) and expires_at on the entry to perform one.

Two ways exist to stop enforcing a managed pack, and both are recorded in the audit log:

  • Disable it wholesale — PUT /api/v1/policy/{registry_type}/packs/{slug} with {"enabled": false} turns a managed pack off for your org entirely, recorded as policy.pack.toggled.
  • Fork one rule — leave the managed pack enabled and write your own override entry (override_of_pack_id) against the specific rule you disagree with. The managed pack keeps evaluating everything else; only the named rule is superseded, recorded as policy.pack.entry.added.

Approving a request in the Approvals queue writes into org-endorsements through this same path automatically — endorsements compound (an approval widens an existing entry’s range rather than piling up a second one), where the old one-off exceptions only accumulated.

pkgPlease maintains curated Package Rules and Signal Rules packs, shown under the /policy/{registry} Rule Sets tab (?tab=ruleSets). Each pack has a name, a severity (block or warn), a description, and an entry count — toggle a pack on or off wholesale instead of adding its entries one at a time.

Use the Approvals queue to review anything your policy blocked, or the Audit Log to see every decision your policy has made.