Policy overrides
A policy override is a per-tenant setting that overrides one of Upwarden’s tier-driven defaults. Tightening a policy (making it stricter than the default) is always allowed. Loosening a policy past the tier default requires a typed acknowledgement — the operator is putting their name on the deviation, and the row carries that confirmation forward into the audit log.
This page covers the customer-facing surface. The operator runbook for policy-override governance lives in docs/admin-guide.md.
Who can set overrides
Section titled “Who can set overrides”Owner role on a tenant. Most overrides require Org or Enterprise tier; the Rate-limit RPM cap is also available from Team tier. Free-tier tenants run on Upwarden’s defaults, and the upgrade path is “go to Team for the rate-limit cap, or Org to unlock the full override set”.
Owner-only is the same gating posture as IP allowlist (see Sign-in & access control) — a misset policy can lock the org out of its own dashboard, so the write capability stays on the smallest possible set of accounts.
The tighten-freely / loosen-with-confirm model
Section titled “The tighten-freely / loosen-with-confirm model”Every overrideable policy has two states relative to the tier default:
- Tighter than default. No acknowledgement needed. The owner clicks save, the row writes, the audit row records the change with
event_string = policy_override_tightened. Example: dropping the MFA grace period from 14 days to 0. - Looser than default. The dashboard demands a typed acknowledgement before the save button enables. The text the owner types lands in the audit row as
acknowledgement_textso an auditor can verify the step-up actually happened. Example: extending the MFA grace period from 14 days to 30.
The shape of “tighter vs looser” is policy-specific — Upwarden knows MFA tighter means more enforcement and audit-retention tighter means longer retention. The per-policy comparator lives in the engine and isn’t a knob.
Which policies are overrideable
Section titled “Which policies are overrideable”Today:
| Policy | Tier eligibility | Default | What it controls | Tighten direction |
|---|---|---|---|---|
| MFA enforcement | Org+ | Tier-driven (Team = owners only; Org+ = everyone) | The set of members required to register a TOTP factor at sign-in (see Sign-in & access control). | More members required, shorter grace period |
| Session duration (minutes) | Org+ | STYTCH_SESSION_DURATION_MINUTES (60 by default) | How long an authenticated dashboard session stays valid before re-auth. | Shorter window |
Rate-limit RPM (enforcement_rate_limit_rpm) | Team+ | Platform RATE_LIMIT_RPM env knob | Per-org proxy request rate cap. | Lower cap (or non-zero from 0) |
Unknown-version disposition (enforcement_unknown_disposition) | Org+ | DEFAULT_ENFORCEMENT.unknownDisposition (typically allow) | What the proxy does with a version it has no verdict for yet — allow (let through), quarantine (hold for review), or block. | allow → quarantine → block is the tightening direction |
Audit retention (days) (audit_retention_days) | Org+ | Tier-dependent (Free 7 / Team 30 / Org 365 / Enterprise 3650) | How long audit rows are kept before sweep. Inverse: shorter retention = looser (less defensible trail), so shortening prompts the acknowledgement. | Longer window |
The dashboard’s /admin/ui/orgs/<slug>/settings/policy-overrides page is the canonical list — anything not surfaced there is not overrideable yet. For a per-policy reference of the underlying wire shape (default source, comparator direction, edit-form layout) see Policies and overrides.
The audit_retention_days inversion is the gotcha — shortening retention is the loosening direction and triggers the typed-acknowledgement flow, the opposite of every other policy on the list. Upwarden knows this per-policy and routes accordingly; you don’t need to think about it.
Setting an override
Section titled “Setting an override”From the dashboard
Section titled “From the dashboard”/admin/ui/orgs/<slug>/settings/policies — every overrideable policy is listed with its tier default and the current effective value. Each row has an “Edit” button.
The edit form is policy-specific (MFA shows the three knobs — required owners, required members, grace period; future policies surface their own). When the new value would loosen the policy past the default, a typed-acknowledgement field appears below the save button. The save button enables only when the acknowledgement text is non-empty.
The dashboard also surfaces a banner at the top of the settings page when a policy is currently overridden in the looser direction — visual reminder that the org is running outside Upwarden’s tier defaults. Banner dismissal is per-policy per-tenant; dismissing audits a policy_override_banner_dismissed row.
From the JSON admin API
Section titled “From the JSON admin API”curl -fsS -X PUT \ -H "x-upwarden-admin-token: $ADMIN_API_TOKEN" \ -H "content-type: application/json" \ -d '{ "value": { "required_owner": true, "required_members": true, "grace_period_days": 7 }, "acknowledgement_text": "Extending grace from 14d to 7d (still tighter than default) — approved by CISO" }' \ "https://vg-admin.<your-domain>/api/v1/admin/orgs/<slug>/policy-overrides/mfa"acknowledgement_text is required when the new value loosens the policy past the default, and only then. A tighten-only call without acknowledgement text succeeds; a loosen call without it 400s with the per-policy reason string.
The response carries the stored override record (including the acknowledged_at and acknowledged_by timestamps the server stamped). Subsequent reads of the policy surface return the override until it’s restored.
Removing an override
Section titled “Removing an override”Restoring a policy to the tier default is a DELETE:
curl -fsS -X DELETE \ -H "x-upwarden-admin-token: $ADMIN_API_TOKEN" \ "https://vg-admin.<your-domain>/api/v1/admin/orgs/<slug>/policy-overrides/mfa"(Or “Reset to default” on the dashboard surface.) The audit row carries event_string = policy_override_restored. From that point the tier default applies until the next override.
Audit envelopes
Section titled “Audit envelopes”Every override mutation writes one row into audit_log. The decision_reason envelope carries the policy name and (for loosen calls) the typed acknowledgement:
| Event | Carries |
|---|---|
policy_override_tightened | { policy_name, default_value, override_value, by_user_id } |
policy_override_loosened | { policy_name, default_value, override_value, acknowledgement_text, by_user_id } |
policy_override_restored | { policy_name, by_user_id } |
policy_override_banner_dismissed | { policy_name, by_user_id } |
Find these rows the same way as any other admin mutation — see Querying the audit log for the filter shape. Pivot by policy_name to get a per-policy history; pivot by by_user_id to attribute the change to a specific operator.
What’s next
Section titled “What’s next”- Sign-in & access control — the MFA enforcement and IP allowlist surfaces overrides plug into.
- Querying the audit log — find the rows above.
- Tenants, projects, keys — the parallel surface for the API-key data plane (overrides are dashboard-only).