f020178c15
A1 install path → go install (D-115) A2 engine enum → reuse kyverno, distinguish by ruleId KJ_ prefix (D-116) A3 checkov/wiz signatures unchanged; meta-policies consume merged PCR list (D-117) A4 NOVA_TAG_NAMING kept + kyverno-json mirror + tagging-rules-agree meta-policy (D-118) A5 critical-override kept as defense-in-depth behind declarative meta-policy (D-119) A6 kyverno-json is deterministic not AI; is_configured guard ensures platform functions without it (D-120) ---ci--- project: acdl phase: 0 milestone: v1.25 status: clarify ---/ci---
164 lines
8.9 KiB
Markdown
164 lines
8.9 KiB
Markdown
# CLARIFY — v1.25 kyverno-json Unified Policy Engine
|
|
|
|
> **Autonomy:** full. Ambiguities are auto-resolved with assumption logging
|
|
> per `config.json autonomy.level: "full"` and
|
|
> `autonomy.decision_confidence_threshold: 0.6`. No human escalation.
|
|
|
|
## Ambiguities Identified
|
|
|
|
### A1 — kyverno-json install path (pip / go install / pinned binary release)
|
|
|
|
**Ambiguity:** kyverno-json is a Go project, not a Python package. Three
|
|
install paths exist: (a) `pip install` — not possible (no PyPI package);
|
|
(b) `go install github.com/kyverno/kyverno-json/cmd/kj@latest` — requires
|
|
Go toolchain in the CI image; (c) download a pinned binary release from
|
|
GitHub releases — no Go toolchain needed, but release artifacts are
|
|
platform-specific and must be checksummed.
|
|
|
|
**Resolution (auto, confidence 0.85):** `go install` (option b). A
|
|
`scripts/install-kyverno-json.sh` helper runs
|
|
`go install github.com/kyverno/kyverno-json/cmd/kj@latest` and prints
|
|
`kj version`. The CI image (`.github/workflows/ci.yml` +
|
|
`.gitea/workflows/ci.yml`) installs Go + kj when
|
|
`config.json.policy.engine == "kyverno-json"`; the install is cached via
|
|
the existing Go module cache. Rationale: `go install` is the upstream-
|
|
blessed path, tracks the latest stable release, avoids per-platform
|
|
binary management, and the project already accepts Go-based tooling
|
|
(checkov pulls Go-built transitive deps via pip). When `which kj` is
|
|
absent, `KyvernoJsonEngine.is_configured()` returns false → `SKIPPED`
|
|
PCR (mirrors the Wiz adapter pattern) — the platform functions without
|
|
the binary. Captured in REQ-293, REQ-294. Decision ID: D-115.
|
|
|
|
### A2 — `engine` enum value: new `"kyverno-json"` vs reuse `"kyverno"`
|
|
|
|
**Ambiguity:** `schemas/policy_check_result.schema.json` already lists
|
|
`engine: ["checkov", "kyverno", "opa", "wiz"]`. kyverno-json is a
|
|
distinct runtime from the K8s Kyverno admission controller, but both
|
|
are "Kyverno." Two options: (a) add a new `"kyverno-json"` enum value
|
|
— requires schema change + checkov/wiz adapter test regression check;
|
|
(b) reuse `"kyverno"` and distinguish by `ruleId` prefix.
|
|
|
|
**Resolution (auto, confidence 0.80):** Reuse `"kyverno"` (option b).
|
|
Adding `"kyverno-json"` would force a schema change + a test sweep for
|
|
no semantic gain — the `engine` field records the policy engine family,
|
|
not the specific binary. kyverno-json PCR records carry `engine:
|
|
"kyverno"` and `ruleId` prefixed `KJ_<policy_name>` (e.g.
|
|
`KJ_REQUIRE_TAGGING_STANDARD`), while the K8s adapter uses `KYVERNO_`
|
|
prefixes (e.g. `KYVERNO_INACTIVE_TF_STACK`). The two are distinguishable
|
|
in audit/telemetry by `ruleId` prefix and `evidence` payload shape (the
|
|
K8s adapter's evidence has `namespace`/`kind`; kyverno-json's has
|
|
`assertion`/`jmespath`). No schema change. Captured in REQ-293.
|
|
Decision ID: D-116.
|
|
|
|
### A3 — Do checkov/wiz adapters change their signatures to feed kyverno-json?
|
|
|
|
**Ambiguity:** The unified-orchestrator model places kyverno-json "on
|
|
top of" checkov/wiz. Two interpretations: (a) checkov/wiz now emit a
|
|
"raw findings" intermediate (not PCR) that kyverno-json meta-policies
|
|
consume — requires changing `adapt() -> list[PolicyCheckResult]` to
|
|
`adapt() -> list[RawFinding]`; (b) checkov/wiz keep emitting PCRs as
|
|
today, and the meta-policies in `adapters/kyverno-json/policies/meta/`
|
|
consume the **merged** PCR list as their payload.
|
|
|
|
**Resolution (auto, confidence 0.90):** Option (b). The existing
|
|
`adapt() -> list[PolicyCheckResult]` signatures are unchanged. The
|
|
meta-policies consume the merged PCR list (checkov + wiz + kyverno-json
|
|
plan-JSON policies) as their input payload. This preserves the
|
|
`PolicyCheckResult` schema as the single inter-adapter contract
|
|
(ARCHITECTURE.md §12.6), avoids a new "RawFinding" type, and means
|
|
the existing checkov/wiz adapter tests pass unchanged. The meta-policy
|
|
`block-on-any-critical.json` iterates the merged list; the
|
|
`tagging-rules-agree.json` meta-policy cross-checks the Checkov
|
|
`NOVA_TAG_NAMING` result against the kyverno-json
|
|
`KJ_REQUIRE_TAGGING_STANDARD` result by `resourceRef`. Captured in
|
|
REQ-303, D-117. Decision ID: D-117.
|
|
|
|
### A4 — `NOVA_TAG_NAMING` Checkov rule: rewrite as kyverno-json policy, keep, or both?
|
|
|
|
**Ambiguity:** The Checkov custom rule
|
|
`adapters/terraform/policy/custom_rules/nova_tagging.py` enforces the
|
|
Nova tagging standard over Terraform HCL (static scan + plan scan). The
|
|
kyverno-json milestone adds `require-tagging-standard.json` over the
|
|
resolved Stack IR. Three options: (a) rewrite — replace the Checkov
|
|
rule with the kyverno-json policy (loses Checkov's HCL-level coverage
|
|
and the `--external-checks-dir` integration); (b) keep Checkov only —
|
|
don't add a kyverno-json policy (the Stack IR is already the input to
|
|
terraform, so the Checkov rule catches it); (c) both — keep the
|
|
Checkov rule as the source of truth for HCL-level scanning AND add the
|
|
kyverno-json policy for IR-level coverage, with a meta-policy that
|
|
asserts the two agree.
|
|
|
|
**Resolution (auto, confidence 0.82):** Option (c) — both, with a
|
|
cross-check meta-policy. The Checkov rule stays the source of truth
|
|
for `terraform_plan` scanning (it reads HCL resource blocks directly);
|
|
the kyverno-json policy covers the Stack IR dict (which is the input
|
|
*before* terraform, so it catches IR-level violations that the
|
|
terraform adapter might mask via defaults). The P3 meta-policy
|
|
`tagging-rules-agree.json` asserts the two engines agree on every
|
|
resource; divergence emits an `error` PCR (defense-in-depth against
|
|
rule drift — if the two engines disagree, the operator must
|
|
investigate before proceeding). This is the only case in v1.25 where
|
|
two engines evaluate the same concern; it is intentional — the
|
|
tagging standard is the highest-impact rule (v1.8 D-tagging-standard,
|
|
v1.10 re-verification) and merits redundancy. Captured in REQ-297,
|
|
REQ-303, REQ-299. Decision ID: D-118.
|
|
|
|
### A5 — Critical-override: delegate to declarative meta-policy or keep hard-override?
|
|
|
|
**Ambiguity:** `core/confidence_signal.py` lines 144-157 hardcode
|
|
`PENALTY["critical"]: None` — a critical-severity `fail` PCR forces
|
|
`score = 0, band = block` regardless of the weighted-sum inputs. The
|
|
v1.25 meta-policy `block-on-any-critical.json` makes this declarative
|
|
(asserts no PCR in the merged list has `severity: critical` +
|
|
`result: fail`). Two options: (a) fully delegate — remove the
|
|
hard-override, rely on the meta-policy to emit a critical `fail` PCR
|
|
that the existing penalty logic then blocks; (b) keep both — the
|
|
meta-policy is the declarative source of truth, the hard-override is
|
|
defense-in-depth.
|
|
|
|
**Resolution (auto, confidence 0.88):** Option (b) — keep both. The
|
|
meta-policy is the *declarative* statement ("Nova blocks on any
|
|
critical finding from any engine"); the hard-override is the
|
|
*imperative* safety net that ensures a critical PCR can never slip
|
|
through even if the meta-policy is misconfigured or the
|
|
`PolicyEngineRegistry` returns a `NullEngine`. This is
|
|
defense-in-depth, not redundancy-for-its-own-sake: the meta-policy
|
|
runs *before* the confidence signal (it produces PCRs that flow in),
|
|
the hard-override runs *inside* the confidence signal (it is the last
|
|
gate). Removing the hard-override would make the platform's
|
|
"critical = block" guarantee depend on a single declarative policy
|
|
file — a regression in the provable-trust posture (Strategic
|
|
Objective #2). Captured in REQ-303, PROJECT.md hard-constraints.
|
|
Decision ID: D-119.
|
|
|
|
### A6 — Does kyverno-json break the "platform functions without AI" tenet?
|
|
|
|
**Ambiguity:** NORTH_STAR.md Strategic Objective #2: "the platform
|
|
functions without AI — 'AI decisions' are really automated decisions."
|
|
kyverno-json is a deterministic policy engine (no ML), but it is a new
|
|
runtime dependency. Does adding it violate the tenet?
|
|
|
|
**Resolution (auto, confidence 0.95):** No — kyverno-json is
|
|
deterministic, not AI. The tenet distinguishes "AI decisions" (LLM-
|
|
driven, non-reproducible) from "automated decisions" (rule-driven,
|
|
reproducible). kyverno-json is the latter — the same policy + payload
|
|
produces the same result on every run. It is *more* aligned with the
|
|
tenet than the current imperative Python in `core/env_transition.py`
|
|
and `core/regression_verify.py`, because the policy is declarative
|
|
(visible, auditable, version-controlled) rather than imperative (logic
|
|
hidden in function bodies). The `is_configured()` guard ensures the
|
|
platform functions without the binary (graceful skip), so the tenet
|
|
holds even in environments where kyverno-json is not installed.
|
|
Captured in PROJECT.md hard-constraints + RESEARCH.md G-Q1.
|
|
Decision ID: D-120.
|
|
|
|
## Summary
|
|
|
|
6 ambiguities identified; 6 auto-resolved at full autonomy (no human
|
|
escalation). All resolutions are binding and recorded as D-115..D-120.
|
|
The resolutions are captured in PROJECT.md hard-constraints,
|
|
REQUIREMENTS.md v1.25 sections, and will be referenced in RESEARCH.md +
|
|
PLAN.md. No PROJECT.md or REQUIREMENTS.md structural changes beyond the
|
|
v1.25 sections added in SPECIFY — the resolutions are already embedded
|
|
in the requirement text (REQ-293, REQ-297, REQ-303, etc.) via the
|
|
"Decision" annotations. |