opschain approval-policies convert-settings-approvers
packs:change-governance
opschain approval-policies convert-settings-approvers [--dry-run]
Converts the retired settings requires_approval_from entries into approval
policies (ChangeGovernanceService.ConvertSettingsApprovers). The old
node-settings entries were never enforced by this server — an operator who
configured them believed changes were gated when they were not — so the
converge, the node compare and the settings editor no longer serve the key,
and approval policies are the enforced successor. This verb is the operator
path from one to the other: it scans every stored settings document (the
global document and every node's current settings version) and creates one
policy per entry, through the same store the policy write verbs use.
Each converted entry becomes a policy at the owning node's path:
- the policy's path pattern is the owning node's path covering its whole
subtree (
<node path>/**; a global entry becomes/**), matching how the old entries inherited down the settings converge; - the entry's
change_filter.actionsbecome the policy's action patterns ("all"becomes*; literal names and*-globs carry over); - the entry's
user_namesandldap_groupsbecome the policy's named approvers, with quorum 1 and self-approval allowed — the old gate was satisfied by any one named approver, a change creator among them included; - the policy's scope is the owning node's path, so the usual delegable governance determination governs who may edit it afterwards.
The report names every stored entry: converted (with the new policy's name
and id), already converted (a re-run creates nothing twice — an entry whose
converted shape already exists as a policy is skipped, whatever the policy
has since been renamed to) or unconvertible. Unconvertible entries are left
in place and must be recreated by hand; the reasons are property-placeholder
approver names ({{ … }}) that nothing resolves any more, created_by
change filters (approval policies do not match on the change's creator),
extended action globs (?, […], {a,b} — policies support * only),
entries naming no approvers, and entries stored on templates or template
versions (no node path to anchor the policy to). Email addresses in
user_names are copied verbatim — the old resolver's email-to-username
lookup is not applied, so check such approvers against real usernames.
The verb requires a PostgreSQL-backed server holding the
packs:change-governance entitlement, the change-governance determination
(updatable on /governance/change, the root scope — the conversion writes
policies across the whole estate) and a stepped-up session. A writing run is
audited as api:settings_approvers:converted; --dry-run produces the same
report without writing anything and is not audited.
The stored entries themselves are not deleted: the server ignores them
everywhere, and this verb keeps reading them so a re-run can report them.
Remove the global document's entries afterwards with
opschain admin settings prune (the key is in the pruneable set); node-level
entries can be removed by editing each node's settings document.
Flags
| Flag | Meaning |
|---|---|
--dry-run | Report what would convert without writing any policy. |
-o json / -o yaml emit the typed report in its protojson wire shape.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Report produced (a writing run may have created policies; no stored requires_approval_from entries found when the estate holds none). |
| 1 | The conversion failed — the error names the server's Connect code and message, including the packs:change-governance funnel refusal, the missing governance determination and the step-up refusal. |
| 2 | Usage error, including a missing server target. |
Examples
$ opschain approval-policies convert-settings-approvers --dry-run
[would convert] /projects/demo filter={"actions":["deploy"]} approvers=alice, dba -> policy "Converted settings approvers /projects/demo 1a2b3c4d" (path /projects/demo/**, actions deploy)
[unconvertible] System Configuration filter={"actions":"all"} approvers={{ approvers }} — the approver "{{ approvers }}" is a property placeholder the retired settings resolver would have resolved — resolve it by hand
1 convertible, 0 already converted, 1 unconvertible (left in place)
$ opschain approval-policies convert-settings-approvers
[converted] /projects/demo filter={"actions":["deploy"]} approvers=alice, dba -> policy "Converted settings approvers /projects/demo 1a2b3c4d" (01993…, path /projects/demo/**, actions deploy)
...