provisioning/schemas/lib/keeper_policy.ncl

52 lines
2.5 KiB
Text
Raw Permalink Normal View History

# schemas/lib/keeper_policy.ncl — Keeper auto-sign policy schema (ADR-038)
#
# Declarative-only closed shape parsed by the keeper-daemon Rust matcher.
# Policy files (policy-<workspace>/policy.ncl) MUST conform to PolicyDef and
# MUST NOT contain Nickel function definitions or imports beyond this schema.
# Constraint: policy-files-are-declarative-only (ADR-038).
#
# Usage:
# let kp = import "schemas/lib/keeper_policy.ncl" in
# { policy | kp.PolicyDef = { auto_sign = [...], require_manual = [...] } }
# Op type wildcard contract — superset of ops_contract.ncl OpsType that also accepts "*"
let OpTypeOrAny =
std.contract.custom (
fun label =>
fun value =>
let valid = ["deploy", "scale", "restart", "secret_update", "drain", "*"] in
if std.array.any (fun x => x == value) valid then
'Ok value
else
'Error {
message = "Invalid op_type '%{value}'.\nValid values: deploy | scale | restart | secret_update | drain | *"
}
)
in
# A single match rule — all fields are glob patterns applied by the Rust matcher.
# Absent / defaulted-to-"*" field means "match any value for this dimension".
# The matcher evaluates rules top-to-bottom; first matching rule wins.
let _MatchRule = {
op_type | OpTypeOrAny | doc "Op type this rule applies to; '*' matches any op type" | default = "*",
image_patterns | Array String | doc "Glob patterns matched against OCI image reference in the op payload (deploy ops only)" | default = ["*"],
target_patterns | Array String | doc "Glob patterns matched against the op target name (e.g., 'staging-*', 'vapora')" | default = ["*"],
scope_patterns | Array String | doc "Glob patterns matched against JWT scope entries (<op_type>:<target_pattern>)" | default = ["*"],
} in
# Top-level policy file schema. Evaluation order: auto_sign rules checked first (top-to-bottom),
# then require_manual. If no rule matches, the op is held pending for manual review.
let _PolicyDef = {
version | Number | doc "Schema version — keeper-daemon rejects files with unknown versions" | default = 1,
auto_sign | Array _MatchRule | doc "Rules for operations the keeper-daemon may sign automatically" | default = [],
require_manual | Array _MatchRule | doc "Rules for operations that must be signed interactively via keeper-cli" | default = [],
} in
{
OpTypeOrAny = OpTypeOrAny,
MatchRule = _MatchRule,
PolicyDef = _PolicyDef,
make_policy | not_exported = fun data => data | _PolicyDef,
}