Skip to content

`POST /v1/orgs/{org}/policies/preview`.

POST
/v1/orgs/{org}/policies/preview
curl --request POST \
--url https://api.updawg.net/v1/orgs/example/policies/preview \
--header 'Content-Type: application/json' \
--data '{ "yaml": "example", "replaces": "example" }'

What saving this document would do to the fleet as it is right now. Reads and evaluates; writes nothing.

replaces is not optional in the way it looks

A proposal’s identity is its dedupe_key, which begins with the policy id. So previewing an edit without saying which policy it replaces compiles the candidate under a fresh id, every key differs, and the answer is “all of these would open and all of those would close” — which is true of a new policy and nonsense for an edit.

With replaces, the candidate is compiled under that policy’s id and substituted for it, and the diff is about the change rather than about the identity. Without it, the candidate is added to what is already there, which is the honest reading of previewing something that does not exist yet.

What this deliberately does not compare against

The proposals that are actually open. Those were written by the policies in force at some past moment, so comparing with them would fold “the policy changed” together with “the fleet changed since” — two answers in one number, and the editor only asked about the first. Both sides here are evaluated now, over the same snapshot.

org
required
string

Organization slug.

Media typeapplication/json
object
yaml

Option so that a missing field produces this API’s own 400 rather than axum’s plain-text 422 — see DAWG-246 and the note in routes::proposals::reject.

string | null
replaces

The policy this document would replace, as pol_<hex>. Absent means a policy that does not exist yet — see [preview].

string | null
Examplegenerated
{
"yaml": "example",
"replaces": "example"
}

What saving this would do: which proposals it would open, and — the half nobody asks for — which updates it would stop covering.

Media typeapplication/json
object
changing
required

Same key before and after, and something about it differs: more hosts, more packages, a worse severity — or, the one that matters, [Action::AutoMerge] where it used to be [Action::Propose]. Carries the after shape.

⚠️ This list exists because of a bug I wrote and then tested for. unchanged originally held every shared key, so turning propose into auto_merge — which opens no proposal, closes none and loses no coverage — made [Preview::is_empty] report a no-op. A portal using that to decide whether to warn would have stayed silent for the single most dangerous edit anybody can make to a policy.

Array<object>

One proposal, as the fleet would produce it.

object
action
required

What would happen. auto_merge here is the answer to “would this go out without anybody looking”, which is the question a preview is really for.

string
Allowed values: propose auto_merge ignore
dedupe_key
required
string
hosts
required

How many hosts would be in it.

integer
kind
required

Ord is derived so this can key an ordered collection — the policy engine groups proposals by kind in a BTreeMap and needs a total order to produce the same output twice. The order itself is the declaration order and carries no meaning; nothing should read severity into it.

string
Allowed values: security patch kernel dist_upgrade
max_severity
One of:

How bad an advisory is, on one scale.

Severity::None is not the same as no severity

This enum is what a vendor said. Somewhere it is stored as a nullable column and read back as Option<Severity>, and the two levels mean different things:

  • None (the variant) — rated, and rated as not worth acting on. Ubuntu’s negligible and Debian’s unimportant land here.
  • Option::None — nobody rated it. Debian DSAs frequently carry no rating at all.

Collapsing them would let a policy written severity >= low quietly skip everything nobody had got round to rating yet, which is the opposite of what somebody writing that rule wants.

string
Allowed values: none low medium high critical
packages
required

How many distinct packages, across every host.

integer
policy_id
required

A policy.

string format: uuid
rollout

The named rollout strategy, if the rule named one the policy defines.

string | null
rule_index
required

Which rule decided it, by position. Renders as “rule 3 of web-production” — and is the reason reordering churns keys.

integer
same_work_as

Set on an opening entry when a closing one covers exactly the same work under a different key, and the other way round. Almost always a rule that moved. See the module documentation.

string | null
settings
required

What else the rule decided. See [Settings] for why this is a struct rather than the one field that used to be here.

object
apply
required

When an approved proposal may start.

string
Allowed values: asap asap_in_window
approval
required

Whether a human has to say yes, and how many of them.

object
min_approvers
required

Ignored when required is false. Zero is meaningless and the schema rejects it; the engine treats anything below one as one rather than as “no approvers needed”, because rounding towards asking is the safe direction.

integer format: int32
required
required
boolean
preflight
required

A step that may be required before a proposal applies.

string
Allowed values: optional required
reboot
required

What to do about a host that needs a reboot afterwards.

string
Allowed values: never immediate in_window
snapshot
required

A step that may be required before a proposal applies.

string
Allowed values: optional required
closing
required

Proposals that exist now and would not.

Array<object>

One proposal, as the fleet would produce it.

object
action
required

What would happen. auto_merge here is the answer to “would this go out without anybody looking”, which is the question a preview is really for.

string
Allowed values: propose auto_merge ignore
dedupe_key
required
string
hosts
required

How many hosts would be in it.

integer
kind
required

Ord is derived so this can key an ordered collection — the policy engine groups proposals by kind in a BTreeMap and needs a total order to produce the same output twice. The order itself is the declaration order and carries no meaning; nothing should read severity into it.

string
Allowed values: security patch kernel dist_upgrade
max_severity
One of:

How bad an advisory is, on one scale.

Severity::None is not the same as no severity

This enum is what a vendor said. Somewhere it is stored as a nullable column and read back as Option<Severity>, and the two levels mean different things:

  • None (the variant) — rated, and rated as not worth acting on. Ubuntu’s negligible and Debian’s unimportant land here.
  • Option::None — nobody rated it. Debian DSAs frequently carry no rating at all.

Collapsing them would let a policy written severity >= low quietly skip everything nobody had got round to rating yet, which is the opposite of what somebody writing that rule wants.

string
Allowed values: none low medium high critical
packages
required

How many distinct packages, across every host.

integer
policy_id
required

A policy.

string format: uuid
rollout

The named rollout strategy, if the rule named one the policy defines.

string | null
rule_index
required

Which rule decided it, by position. Renders as “rule 3 of web-production” — and is the reason reordering churns keys.

integer
same_work_as

Set on an opening entry when a closing one covers exactly the same work under a different key, and the other way round. Almost always a rule that moved. See the module documentation.

string | null
settings
required

What else the rule decided. See [Settings] for why this is a struct rather than the one field that used to be here.

object
apply
required

When an approved proposal may start.

string
Allowed values: asap asap_in_window
approval
required

Whether a human has to say yes, and how many of them.

object
min_approvers
required

Ignored when required is false. Zero is meaningless and the schema rejects it; the engine treats anything below one as one rather than as “no approvers needed”, because rounding towards asking is the safe direction.

integer format: int32
required
required
boolean
preflight
required

A step that may be required before a proposal applies.

string
Allowed values: optional required
reboot
required

What to do about a host that needs a reboot afterwards.

string
Allowed values: never immediate in_window
snapshot
required

A step that may be required before a proposal applies.

string
Allowed values: optional required
dropped
required

⚠️ Updates a rule decides today and nothing would decide after.

The half that does not show up in proposal counts, and the one that leaves a fleet unpatched. An ignore rule counts as deciding: choosing to do nothing is not the same as nothing matching.

Array<object>

An update whose coverage changes.

object
hosts
required

How many hosts it affects.

integer
package
required
string
newly_covered
required

Updates nothing decides today and a rule would decide after.

Array<object>

An update whose coverage changes.

object
hosts
required

How many hosts it affects.

integer
package
required
string
opening
required

Proposals that do not exist now and would.

Array<object>

One proposal, as the fleet would produce it.

object
action
required

What would happen. auto_merge here is the answer to “would this go out without anybody looking”, which is the question a preview is really for.

string
Allowed values: propose auto_merge ignore
dedupe_key
required
string
hosts
required

How many hosts would be in it.

integer
kind
required

Ord is derived so this can key an ordered collection — the policy engine groups proposals by kind in a BTreeMap and needs a total order to produce the same output twice. The order itself is the declaration order and carries no meaning; nothing should read severity into it.

string
Allowed values: security patch kernel dist_upgrade
max_severity
One of:

How bad an advisory is, on one scale.

Severity::None is not the same as no severity

This enum is what a vendor said. Somewhere it is stored as a nullable column and read back as Option<Severity>, and the two levels mean different things:

  • None (the variant) — rated, and rated as not worth acting on. Ubuntu’s negligible and Debian’s unimportant land here.
  • Option::None — nobody rated it. Debian DSAs frequently carry no rating at all.

Collapsing them would let a policy written severity >= low quietly skip everything nobody had got round to rating yet, which is the opposite of what somebody writing that rule wants.

string
Allowed values: none low medium high critical
packages
required

How many distinct packages, across every host.

integer
policy_id
required

A policy.

string format: uuid
rollout

The named rollout strategy, if the rule named one the policy defines.

string | null
rule_index
required

Which rule decided it, by position. Renders as “rule 3 of web-production” — and is the reason reordering churns keys.

integer
same_work_as

Set on an opening entry when a closing one covers exactly the same work under a different key, and the other way round. Almost always a rule that moved. See the module documentation.

string | null
settings
required

What else the rule decided. See [Settings] for why this is a struct rather than the one field that used to be here.

object
apply
required

When an approved proposal may start.

string
Allowed values: asap asap_in_window
approval
required

Whether a human has to say yes, and how many of them.

object
min_approvers
required

Ignored when required is false. Zero is meaningless and the schema rejects it; the engine treats anything below one as one rather than as “no approvers needed”, because rounding towards asking is the safe direction.

integer format: int32
required
required
boolean
preflight
required

A step that may be required before a proposal applies.

string
Allowed values: optional required
reboot
required

What to do about a host that needs a reboot afterwards.

string
Allowed values: never immediate in_window
snapshot
required

A step that may be required before a proposal applies.

string
Allowed values: optional required
unchanged
required

Same key, same everything.

Array<object>

One proposal, as the fleet would produce it.

object
action
required

What would happen. auto_merge here is the answer to “would this go out without anybody looking”, which is the question a preview is really for.

string
Allowed values: propose auto_merge ignore
dedupe_key
required
string
hosts
required

How many hosts would be in it.

integer
kind
required

Ord is derived so this can key an ordered collection — the policy engine groups proposals by kind in a BTreeMap and needs a total order to produce the same output twice. The order itself is the declaration order and carries no meaning; nothing should read severity into it.

string
Allowed values: security patch kernel dist_upgrade
max_severity
One of:

How bad an advisory is, on one scale.

Severity::None is not the same as no severity

This enum is what a vendor said. Somewhere it is stored as a nullable column and read back as Option<Severity>, and the two levels mean different things:

  • None (the variant) — rated, and rated as not worth acting on. Ubuntu’s negligible and Debian’s unimportant land here.
  • Option::None — nobody rated it. Debian DSAs frequently carry no rating at all.

Collapsing them would let a policy written severity >= low quietly skip everything nobody had got round to rating yet, which is the opposite of what somebody writing that rule wants.

string
Allowed values: none low medium high critical
packages
required

How many distinct packages, across every host.

integer
policy_id
required

A policy.

string format: uuid
rollout

The named rollout strategy, if the rule named one the policy defines.

string | null
rule_index
required

Which rule decided it, by position. Renders as “rule 3 of web-production” — and is the reason reordering churns keys.

integer
same_work_as

Set on an opening entry when a closing one covers exactly the same work under a different key, and the other way round. Almost always a rule that moved. See the module documentation.

string | null
settings
required

What else the rule decided. See [Settings] for why this is a struct rather than the one field that used to be here.

object
apply
required

When an approved proposal may start.

string
Allowed values: asap asap_in_window
approval
required

Whether a human has to say yes, and how many of them.

object
min_approvers
required

Ignored when required is false. Zero is meaningless and the schema rejects it; the engine treats anything below one as one rather than as “no approvers needed”, because rounding towards asking is the safe direction.

integer format: int32
required
required
boolean
preflight
required

A step that may be required before a proposal applies.

string
Allowed values: optional required
reboot
required

What to do about a host that needs a reboot afterwards.

string
Allowed values: never immediate in_window
snapshot
required

A step that may be required before a proposal applies.

string
Allowed values: optional required
changes_nothing
required

Whether saving this would change anything at all.

boolean
complete
required

⚠️ false means the fleet was larger than one preview evaluates, so host counts are lower bounds. Which kinds of proposal appear is still reliable — those come from the rules rather than from the hosts.

boolean
hosts_evaluated
required

How many hosts this answer is about.

integer
hosts_total
required

How many the organization has.

integer format: int64
would_auto_merge
required

How many proposals would go out with nobody looking at them, after this.

integer
Example
{
"changing": [
{
"action": "propose",
"kind": "security",
"max_severity": "none",
"settings": {
"apply": "asap",
"preflight": "optional",
"reboot": "never",
"snapshot": "optional"
}
}
],
"closing": [
{
"action": "propose",
"kind": "security",
"max_severity": "none",
"settings": {
"apply": "asap",
"preflight": "optional",
"reboot": "never",
"snapshot": "optional"
}
}
],
"opening": [
{
"action": "propose",
"kind": "security",
"max_severity": "none",
"settings": {
"apply": "asap",
"preflight": "optional",
"reboot": "never",
"snapshot": "optional"
}
}
],
"unchanged": [
{
"action": "propose",
"kind": "security",
"max_severity": "none",
"settings": {
"apply": "asap",
"preflight": "optional",
"reboot": "never",
"snapshot": "optional"
}
}
]
}

The document does not compile.

Media typeapplication/json
object
detail
string | null
status
required
integer format: int32
title
required
string
type
required
string
Examplegenerated
{
"detail": "example",
"status": 1,
"title": "example",
"type": "example"
}

No session.

Media typeapplication/json
object
detail
string | null
status
required
integer format: int32
title
required
string
type
required
string
Examplegenerated
{
"detail": "example",
"status": 1,
"title": "example",
"type": "example"
}

Refused: no CSRF token or not this session’s, or not permitted for this role.

Media typeapplication/json
object
detail
string | null
status
required
integer format: int32
title
required
string
type
required
string
Examplegenerated
{
"detail": "example",
"status": 1,
"title": "example",
"type": "example"
}

No such organization, or not yours.

Media typeapplication/json
object
detail
string | null
status
required
integer format: int32
title
required
string
type
required
string
Examplegenerated
{
"detail": "example",
"status": 1,
"title": "example",
"type": "example"
}

Another live policy already holds that name — which is what saving it would answer, and therefore what previewing it has to.

Media typeapplication/json
object
detail
string | null
status
required
integer format: int32
title
required
string
type
required
string
Examplegenerated
{
"detail": "example",
"status": 1,
"title": "example",
"type": "example"
}

Over the organization’s request limit. Retry-After says when to try again; RateLimit-Limit is the burst.

Media typeapplication/json
object
detail
string | null
status
required
integer format: int32
title
required
string
type
required
string
Examplegenerated
{
"detail": "example",
"status": 1,
"title": "example",
"type": "example"
}