Skip to content
Install the CLI

Reference Policy and runtime contracts

Scorecard and rules

The Kix scorecard checks your cluster for security, reliability, and operational best practices every time you compile. By default, built-in rules report warnings during kix check and kix build without blocking your work, and you can promote rules to build-failing errors or add custom organization policies whenever you are ready.

Rules live under scorecard.rules.<category>.<ruleName>. Each rule’s canonical name is <category>.<ruleName> (for example, security.nonRoot).

scorecard.rules.organization.requiresOwner = {
description = "Every package must declare an owner";
level = "package";
severity = "warning";
tags = [ "governance" ];
appliesTo = { meta, ... }: (meta.deprecated or null) == null;
check = { meta, ... }:
if (meta.owner or null) == null then
[ { message = "package has no meta.owner"; } ]
else
[ ];
};
FieldTypeRequiredDefaultMeaning
descriptionstrno""Human-readable summary of what the rule checks.
level"manifest" | "package" | "namespace" | "cluster"yesnoneScope at which the rule runs. An unknown or missing level fails evaluation.
severity"info" | "warning" | "error"noscorecard.ruleDefaults.severity, then "warning"Declared severity of findings produced by the rule before overrides and scorecard.maxSeverity are applied.
tagslistOf strno[ ]Free-form labels copied onto every finding from this rule.
appliesToctx -> boolno_: trueOptional filter predicate receiving the level context. When it returns false, check is skipped.
checkctx -> listOf { message, namespace?, resource? }yesnoneEvaluates the target and returns [ ] when it passes, or a list of finding attribute sets when it fails.
LevelRuns once perContext arguments passed to appliesTo and checkLocation fields filled automatically
manifestEmitted Kubernetes resourceresource, meta, instanceName, namespaceName, acceptedSecretSourcesnamespace, instance, resource
packageActive package instanceresources, parts, config, meta, instanceName, namespaceNamenamespace, instance
namespaceCluster namespaceinstances, resources, namespaceName, networkPolicyKindsnamespace
clusterCluster buildallInstances, allResources, clusterConfig, externalNamesnone (the rule’s finding may supply namespace and resource)

At the manifest and package levels, resources includes all resources shipped for the instance, including derived PodDisruptionBudget and network policy objects. Kix resolves a rule’s effective severity before calling appliesTo or check; if a rule is disabled via ruleOverrides.byRule.<name>.enabled = false, neither function is called.

When scorecard.enable = true and no error-severity finding aborts evaluation, Kix writes the scorecard report to kix-scorecard.json in the cluster build output.

{
"findings": [
{
"rule": "reliability.hasProbes",
"severity": "warning",
"level": "manifest",
"namespace": "apps",
"instance": "web",
"resource": "web",
"message": "container 'web' has no livenessProbe",
"tags": ["reliability", "observability"]
}
],
"summary": {
"total": 1,
"errors": 0,
"warnings": 1,
"info": 0,
"byNamespace": { "apps": 1 },
"byRule": { "reliability.hasProbes": 1 }
}
}
FieldTypeMeaning
rulestringCanonical <category>.<ruleName> identifier.
severity"info" | "warning" | "error"Effective severity after defaults, overrides, and scorecard.maxSeverity.
level"manifest" | "package" | "namespace" | "cluster"Rule evaluation level.
namespacestring | nullTarget namespace (null for cluster-level findings that do not specify one; counted under "unknown" in summary.byNamespace).
instancestring | nullPackage instance name for manifest and package findings.
resourcestring | nullKubernetes object name for manifest findings or cluster rules that report a specific resource.
messagestringDiagnostic message from check (defaults to "check failed" if omitted).
tagsarray of stringTags copied from the rule definition.

When a finding resolves to "error" (which requires scorecard.maxSeverity = "error"), Kix converts it into a failed cluster assertion and stops the build. kix check --sarif exports the scorecard report (or the evaluation failure) in SARIF 2.1.0 format.

Configure scorecard.ruleOverrides to disable individual rules or adjust severities by rule name, package owner, or namespace.

scorecard = {
maxSeverity = "error";
ruleOverrides = {
byRule."reliability.hasProbes" = {
enabled = true;
severity = "error";
};
byOwner."platform"."security.*".severity = "warning";
byNamespace."preview-*"."reliability.*".severity = "info";
};
};
SectionKey patternSupported fieldsApplies to levels
byRule.<fullRuleName>Exact <category>.<ruleName> (no wildcards)enabled (bool, default true), severitymanifest, package, namespace, cluster
byOwner.<ownerPattern>.<rulePattern>Exact string, "*", or prefix "prefix*"severitymanifest, package (matches meta.owner)
byNamespace.<nsPattern>.<rulePattern>Exact string, "*", or prefix "prefix*"severitymanifest, package, namespace

Avoid overlapping wildcard patterns within the same section (byOwner or byNamespace); Kix picks the first matching pattern in Nix attribute order rather than the most specific prefix.

Kix computes a finding’s effective severity in this order:

  1. ruleOverrides.byRule.<name>.enabled = false disables the rule completely.
  2. ruleOverrides.byRule.<name>.severity
  3. ruleOverrides.byOwner.<ownerPattern>.<rulePattern>.severity
  4. ruleOverrides.byNamespace.<nsPattern>.<rulePattern>.severity
  5. The rule’s own severity field.
  6. scorecard.ruleDefaults.severity
  7. "warning"

Finally, Kix caps the resolved severity at scorecard.maxSeverity (default "warning"). Raising a rule to "error" fails the build only when scorecard.maxSeverity = "error".

Packages can inspect the resolved severity of a rule at build time with:

kix.scorecard.effectiveSeverity {
inherit clusterConfig namespaceName;
rule = "security.nonRoot";
owner = meta.owner or null;
}

It returns "info", "warning", "error", or null (when the scorecard or rule is disabled).

Defined in kix.rules.security (kix/lib/rules/security.nix). Unless noted otherwise, manifest rules in this category apply to Deployment, StatefulSet, DaemonSet, Job, and CronJob resources.

RuleLevelDeclared severitymeta.unsafe exemptionWhat it checks
security.secretSourcemanifesterrornoneWhen cluster.secrets.accept is non-null, checks every Secret and scope.mkSecretRef marker to ensure all non-plain keys come from an accepted source (inline, sops, external, controller, or configuration).
security.nonRootmanifesterror"run-as-root"Every regular and init container resolves to runAsNonRoot = true, or to runAsNonRoot = null with a non-zero runAsUser (container securityContext overrides pod securityContext).
security.noPrivilegedmanifesterror"privileged"No regular or init container sets securityContext.privileged = true.
security.readOnlyRootfsmanifestwarningnoneEvery regular and init container sets securityContext.readOnlyRootFilesystem = true.
security.noHostNetworkmanifesterror"host-network"The pod spec does not enable hostNetwork, hostPID, or hostIPC.
security.noClusterAdminmanifesterror"cluster-admin"Checks ClusterRoleBinding and ClusterRole resources: no ClusterRoleBinding may bind cluster-admin, and no ClusterRole rule may set "*" on all three of apiGroups, resources, and verbs.
security.noLatestTagmanifestwarningnoneEvery regular and init container image has an explicit tag other than :latest.
security.noUnresolvedPlaceholdermanifestwarningnoneNo container image string contains REPLACE_ME, REPLACEME, CHANGE_ME, CHANGEME, PLACEHOLDER, TODO, FIXME, XXX, or an unexpanded ${...}.
security.hasNetworkPolicynamespacewarningnoneEvery namespace containing a Deployment, StatefulSet, or DaemonSet also contains at least one NetworkPolicy or enforcer policy kind (out.netpol.policyKinds).

When a package declares one of the four meta.unsafe grants above, the corresponding security rule is skipped for that package and architecture.unsafeGrants records an info finding so the exception stays visible in audits.

Defined in kix.rules.reliability (kix/lib/rules/reliability.nix). All rules in this category run at the manifest level on Deployment, StatefulSet, and DaemonSet workloads.

RuleLevelDeclared severityWhat it checks
reliability.boundedResourcesmanifestwarningEvery regular and init container sets both cpu and memory under resources.requests and resources.limits.
reliability.hasResourceLimitsmanifestwarningEvery regular and init container has a non-empty resources.limits block.
reliability.hasResourceRequestsmanifestwarningEvery regular and init container has a non-empty resources.requests block.
reliability.hasProbesmanifestwarningEvery regular container (excluding init containers) defines both livenessProbe and readinessProbe.
reliability.gracefulShutdownmanifestinfoThe pod spec sets terminationGracePeriodSeconds to a positive integer.
reliability.volumeHasSourcemanifesterrorEvery entry in spec.template.spec.volumes retains a volume source after manifest normalization (catches constructors where all optional fields were null and collapsed without kix.keep).

Defined in kix.rules.governance (kix/lib/rules/governance.nix).

RuleLevelDeclared severityWhat it checks
governance.hasOwnerpackagewarningThe package’s meta.owner is set (for example, "platform" or "app-team").
governance.hasDescriptionpackageinfoThe package’s meta.description is a non-empty string.
governance.labelStandardsmanifestinfoEvery Deployment, StatefulSet, and DaemonSet carries app.kubernetes.io/name and app.kubernetes.io/managed-by in metadata.labels.

Defined in kix.rules.architecture (kix/lib/rules/architecture.nix). These rules check dependency-graph hygiene, label and selector uniqueness, and service addressing.

RuleLevelDeclared severityWhat it checks
architecture.annotationDomainOwnershippackagewarningDomain-prefixed keys in metadata.annotations must belong to a reserved domain (kubernetes.io, k8s.io, kix.run, helm.sh, kubebuilder.io), a domain in scorecard.knownAnnotationDomains, or a domain declared in an in-cluster package’s meta.annotationDomains that this package depends on (directly or transitively).
architecture.controllerRequiresCrdspackagewarningWhen a package renders at least one CustomResourceDefinition and at least one Deployment, StatefulSet, or DaemonSet, at least one of those workloads must depend on one of the definitions (through requires, extraDeps, or string context). Listing the definitions in root.requires orders consumers after them, not the controller.
architecture.untrackedReferencesclusterinfoAudits every kix.untrackedRef name that does not identify a creator via { kind; name; by; } or a single-owner { name; by = "self"; }.
architecture.unsafeGrantspackageinfoLists every grant in a package’s meta.unsafe ("raw-pvc", "host-network", "privileged", "run-as-root", "cluster-admin").
architecture.distinctWorkloadSelectorspackageerrorWithin an instance, no workload’s spec.selector.matchLabels (on Deployment, StatefulSet, DaemonSet, or Job) may be a subset of another workload’s pod template labels.
architecture.consistentPortNamespackagewarningA named container port must map to the same port number across all workloads in the same package instance.
architecture.distinctSameKindLabelspackageerrorWhen an instance renders multiple Service objects, no Service’s metadata.labels may be equal to or a subset of another Service’s metadata.labels (add a distinguishing label such as app.kubernetes.io/component).
architecture.selectorCoherencemanifestwarningEvery non-empty Service selector must carry Nix dependency context from a workload (for example, via kix.service.fromWorkload or workload.out.selector), unless the Service carries kix.run/operand-of.
architecture.monitorSelectorMatchesServiceclustererrorEvery ServiceMonitor and VMServiceScrape selector must match the metadata.labels of exactly one Service (or scope.mkServiceRef) with a matching port, unless exempted with kix.run/monitors-external-service: "true".
architecture.externalMonitorTargetsmanifestinfoAudits every ServiceMonitor or VMServiceScrape annotated with kix.run/monitors-external-service: "true".
architecture.fqdnWithoutContextmanifestwarningStrings containing <service>.<namespace>.svc in a workload spec must carry Nix dependency context from a Service or scope.mkServiceRef (out.fqdn or out.url).
architecture.clusterDomainLiteralclustererrorWhen clusterDomain is not "cluster.local", no rendered resource (other than a CustomResourceDefinition) may contain a string with literal cluster.local DNS labels.