Policies and overrides
If you’re a security lead deciding whether a BLOCKED install was the policy doing its job or a misconfiguration, this is where you check what the org is actually enforcing — and, on Org+ tiers, where you adjust Upwarden’s defaults without leaving an unauditable trail.
Two surfaces share this doc because they sit on the same setting:
- Policies — the inline section on the org detail page (
/admin/ui/orgs/<slug>, anchor#policies). Edits the org’s own enforcement settings: global blocklist inheritance and the allowlist. Available on every tier. - Policy overrides — the dedicated page at
/admin/ui/orgs/<slug>/settings/policy-overrides. Deviates from the tier-driven defaults Upwarden ships (rate limit, unknown disposition, session duration, audit retention). Org+ only.
Project-level overrides live one level down at /admin/ui/orgs/<slug>/projects/<id>/policy and follow the same idiom — they’re called out under “Tour the project policy form” below.
The Policies section (on the org detail page)
Section titled “The Policies section (on the org detail page)”Scroll to the POLICY eyebrow on the org page; the heading is Policies. The card contains a single form:
- “Inherit the global OSV/GHSA blocklist?” — the checkbox writes the
globalBlocklistEnabledfield. On = the org consumes Upwarden’s blocklist of known-bad versions; off = only the org’s custom blocklist applies. Almost every operator wants this on; turning it off is the kind of decision an auditor will ask about. - “Per-org allowlist” — a textarea writing the
allowlistfield. One entry per line aspackageName[@versionRange][ # reason], e.g.lodash@^4 # known-good. A blank line is ignored; the# reasonsuffix is free-form and surfaces in the audit row when an allowlist hit clears a verdict. - Save policies — posts to
/admin/ui/orgs/<slug>/policies. On success the page reloads with thePolicies updatedflash and anchors back to#policies.
The same surface also displays the JWKS URL read-only at the bottom of the card — used by SBOM consumers verifying signed verdicts; see Verdicts and the scanner.
What you don’t edit here
Section titled “What you don’t edit here”The enforcement enums (unknownDisposition, scanner) and rate-limit cap aren’t on this card. The org-level value for those is tier-driven by default; if you want to deviate, that’s the Policy overrides surface below.
Tour the project policy form
Section titled “Tour the project policy form”Each project has its own override form at /admin/ui/orgs/<slug>/projects/<id>/policy. The shape is the same idiom as the org form, with one extra concept: every field has an inherit org policy option as the empty value. Fields:
globalBlocklistEnabled—inherit org policy/true/falseenforcement.unknownDisposition—inherit org policy/allow/quarantine. The “what do these do?” disclosure explains the semantics:allowresolves as SAFE (default),quarantineholds until cleared by a scan.enforcement.scanner—inherit org policy/none/inProcess/byos/byosThenInProcess. Same disclosure pattern:byoswaits for a bring-your-own-scanner callback,byosThenInProcessfalls back to the built-in if no callback arrives.allowlist— same format as the org-level textarea; a blank textarea inherits the org allowlist (it does not pin an empty allowlist — to do that, hit the JSON admin API directly, per the footnote on the form).
Project overrides are a partial patch — unset fields fall back to the org policy at request time. There’s no “tighten vs loosen” gate on project overrides; the tier-policy gate only applies to the unified overrides surface below.
The Policy overrides surface (Org+ tier)
Section titled “The Policy overrides surface (Org+ tier)”Open it from the org page sidebar or directly at /admin/ui/orgs/<slug>/settings/policy-overrides. The heading is Policy overrides and the lead reads: “Owners on Org and Enterprise tier customise the policies Upwarden ships as defaults. Tighten freely; loosening requires typed acknowledgement and surfaces a persistent dashboard banner.”
Tier gating — what Free and Team tenants see
Section titled “Tier gating — what Free and Team tenants see”If the org isn’t on Org or Enterprise, the page renders the Upgrade to enable policy overrides card with the body:
This org is on the
<tier>tier. Policy overrides are an Org and Enterprise tier feature.
and an upgrade hint pointing at Upwarden billing. The override table is not rendered at all; there’s nothing to click. The capability gate (billing:w, owner-only) is enforced on top of that — a non-owner on Org tier sees the table read-only.
The empty catalog state
Section titled “The empty catalog state”Policies opt in to being overrideable by registering with the engine’s policy registry. Until at least one registration lands for the org, the page shows the No overrideable policies yet card. The body reads:
Policy overrides are configured per-policy by Upwarden. None are overrideable yet; this page will populate as policies opt in.
If you’re seeing this on Org+ today, the catalog is genuinely empty — the registry hasn’t been populated for this org’s deployment yet.
The overrides table
Section titled “The overrides table”When the catalog has rows, you see a three-column table: policy, current value, actions. Each row carries the policy’s wire name in <code> underneath the human label, the registered description, the current effective value (or (default) if no override is set), and an inline edit form supplied by the policy itself.
Policies that ship today include:
| Wire name | What it controls |
|---|---|
enforcement_rate_limit_rpm | Per-org rate-limit cap. Platform default is the RATE_LIMIT_RPM env knob. Lower or 0→positive = tightening. |
enforcement_unknown_disposition | Per-org unknown-version handling. Defaults to DEFAULT_ENFORCEMENT.unknownDisposition. allow → quarantine = tightening. |
session_duration_minutes | Authenticated dashboard session lifetime. Defaults to STYTCH_SESSION_DURATION_MINUTES. Shorter = tightening. |
audit_retention_days | How long audit rows are kept. Tier-dependent default (Free 7 / Team 30 / Org 365 / Enterprise 3650). Inverse: shorter window = looser (less defensible trail). |
The audit_retention_days inversion is the gotcha — shortening retention is the loosening direction and triggers the acknowledgement flow.
Saving an override — tighten vs loosen
Section titled “Saving an override — tighten vs loosen”Each row’s edit form posts to /admin/ui/orgs/<slug>/settings/policy-overrides/<policy-name>. The form carries the new value plus a hidden default_value field (the platform default the page knows about, JSON-encoded). The admin API server-side compares the two through the per-policy comparator:
- Tightening write. Save proceeds. The row updates, the page redirects with
Override saved for <policy-name>, and the audit log capturespolicy_override_tightened. - Loosening write. The save fails unless
acknowledgement_textis non-empty. The per-policy edit fragment is responsible for surfacing the typed-acknowledgement input. When the operator types the confirmation and submits, the audit log capturespolicy_override_loosenedwith the typed text — that text is what an auditor uses to verify the decision was deliberate.
If something goes sideways the page comes back with an inline error rather than a 500:
The submitted value couldn't be parsed— the form submitted malformed JSON. Usually a per-policy fragment bug.Save failed: <upstream error>— the admin API rejected the write. Common cause: a loosen attempt without the typed acknowledgement.
The loosen warning banner
Section titled “The loosen warning banner”When at least one policy is currently overridden in the loosening direction, every authed page in the org renders a persistent yellow banner near the top:
Policy override active:
<policy display name>is below Upwarden’s default.
with two inline actions:
- Restore default — posts to
…/policy-overrides/<name>/restore. Drops the override; the audit log capturespolicy_override_restored; the row reverts to the tier default on the next read. The flash isRestored Upwarden default for <policy-name>. - Dismiss banner — posts to
…/policy-overrides/<name>/dismiss-banner. Hides the banner for this policy on this tenant only; the override is still in effect. The audit capturespolicy_override_banner_dismissedso an auditor can tell the banner was acknowledged, not lost. The flash isBanner dismissed for <policy-name>.
Dismissing the banner does not loosen anything further — it’s an explicit “I see this and I’m running it deliberately” record.
Restoring without the banner
Section titled “Restoring without the banner”On any row with an active override, the actions column carries a Restore default button regardless of direction. Tightenings can be restored without ceremony; loosenings emit the same policy_override_restored audit row when restored from the table as from the banner.
Related
Section titled “Related”- Policy overrides — the cross-link how-to with the JSON admin API equivalents (
PUT/DELETEof/api/v1/admin/orgs/<slug>/policy-overrides/<name>) for scripted workflows. - Verdicts and the scanner — the model behind
unknownDispositionand the scanner modes. - Audit log — where the
policy_override_*envelopes surface in the dashboard. - Querying the audit log — filter shape for pulling override history out of
audit_log.