This feature is in beta. Controls, decisions, the controls CLI commands (
kosli create control and related), server-side evaluation (kosli publish rego-policy and kosli evaluate policy), and the for_control policy requirement are all available in Kosli but in beta — the UI, CLI flags, and policy YAML schema may still change. It is rolled out per organization, so reach out to the Kosli team to enable it for yours, or with any questions and comments.- Define a control library in Kosli that mirrors your existing controls catalog
- Write, test, and publish a policy that decides a control
- Record decisions against controls by evaluating published policies from your pipelines
- Require controls in environment policies and gate deployments on them
- Review the decisions and version history of each control
- Manage your catalog: list and archive controls
Prerequisites
- Install Kosli CLI.
- Get a Kosli API token.
- Have at least one Flow and Trail already created.
Setup
Understanding controls
Before creating controls, it helps to understand how they fit into the Kosli data model. Controls are the named governance requirements your organization enforces. They have a stable identity (the control identifier), a human-readable name, and an optional description and links pointing back to your GRC system or policy document.Kosli holds a mirror to your existing control definitions — it does not replace your GRC system or ServiceNow instance. The control catalog in Kosli is a lightweight copy that enables querying and reporting.
RCTL-043 is satisfied for this artifact.” In Kosli, the PDP is a server-side evaluation: Kosli evaluates a published policy and records the outcome as a decision against the control. A decision is recorded at the point where the judgment is made — typically during a release or promotion step.
A decision must be complete: one decision closes a control, and there is no partial decision. When a control depends on several checks — for example unit, integration, and system tests — the policy that records the control’s decision has to cover all of them.
Multi-level evaluations are not supported yet: a policy cannot read the outcome of other evaluations or decisions, so several evaluations cannot be combined into one control’s decision. Until then, write one policy per control that checks everything the control requires.
kosli assert artifact --environment checks an artifact against the environment’s policy and exits non-zero if it doesn’t comply. Place it at the point in your pipeline where you want to gate progress on control compliance.
Creating a control
A control has the following fields:
Only org admins can create or edit controls. Create one in the Kosli app, with the CLI, or through the API.
- Kosli app
- CLI
- API
Navigate to Controls in the Kosli app sidebar, select New control, and fill in the fields.
kosli update control, or from the control’s detail page in the app:
kosli tag control CONTROL-IDENTIFIER --set key=value. Tagging doesn’t create a new version.
Writing a policy for the control
A decision comes from a policy: Kosli evaluates a policy, written with Ergo or in plain Rego, against your trails and records the outcome against the control. Write the policy, test it against a real trail, then publish it. The example below decidesRCTL-043 with the simplest possible check: the trail has a compliant pull request attestation. The attestation name is read from params — values passed to each evaluation with --params — so the same policy works whatever your pipelines call the attestation.
- Ergo
- Rego
Ergo is an open source Rego library, maintained by Kosli, that lets you write a policy’s requirements as YAML sentences instead of Rego rules. Its report records every check it ran, passed or failed, which makes a denial easy to read.Download Write the requirements in Then hand the requirements to Ergo in Pass both the policy directory and the library wherever a command takes See the Ergo documentation for subjects, sentences, and the report format.
ergo.rego from a pinned release, so a new Ergo version can’t change your policy unexpectedly, and create a directory for the policy:rctl-043/policy.ergo.yaml. Kosli loads *.ergo.yaml files into data, so the policy can read them:rctl-043/policy.ergo.yaml
rctl-043/policy.rego. allow is Ergo’s overall answer, and violations lists the checks that failed:rctl-043/policy.rego
--path:Test the policy
Before publishing, evaluate the local files against a trail that already has the attestation.--path sends the files with the evaluation, Kosli runs them exactly as it will run the published policy, and nothing is recorded:
RESULT: ALLOWED or RESULT: DENIED, with the violations. Try it against a trail without the attestation to see the denial. See Seeing the input to inspect the data the policy receives.
Publish the policy
Publish the policy under an identifier. Each publish stores a new version, and evaluations run the newest valid one.--input-schema trail gives the policy a single trail moment as input.trail:
Recording a decision against a control
Evaluate the published policy and name the control it decides. Use--fingerprint to identify the artifact the decision applies to — this is what allows kosli assert artifact --environment to later check that all required controls have passing decisions for that artifact specifically.
Kosli evaluates the policy where the trails are stored and records the decision in the same request, so the outcome never passes through your pipeline. The decision carries its evidence: the policy version that ran, the params, the result, and the policy files as an attachment. You can record more than one decision for the same control; the most recent one is used when evaluating compliance.
Pinning a policy version
By default,--identifier runs the policy’s current version, so publishing a new version changes what every later decision runs. To keep a control’s decisions on a version you have reviewed, pin it by appending @v<n> to the identifier:
--params must match that version’s params schema. Move the pin when you are ready to adopt a newer version.
Evaluating several trails
A control often depends on evidence spread across trails, such as a build trail with the pull request and a release trail with the approval. Name each trail with its own--context. Kosli reads them all at the same instant and passes them to the policy as the input.trails array:
--input-schema trails, and reads input.trails instead of input.trail. The order of the array carries no meaning, so select a trail by its flow_name and trail_name. The decision is still recorded in the one trail named by --flow and --trail. See Input data and the multi-trail example.
Requiring controls in environment policies
Environment policies define the requirements an artifact must satisfy before it can run in a given environment. You can require that specific named controls have a passing decision recorded, making individual controls explicit policy gates.prod-policy.yaml
attestations rule with type: decision and a for_control identifier requires a compliant decision recorded for that control. The control must already exist in your organization — unlike recording a decision, a policy that references an unknown control identifier is rejected. With this policy in place, an artifact deployed to this environment will be marked non-compliant if a passing decision has not been recorded for that artifact for each listed control. The artifact can still be deployed — Kosli records compliance state but does not block deployments by default. To gate deployments on compliance, use kosli assert in your pipeline. This abstracts the policy from the specific tooling your pipelines use: instead of “has an attestation of type snyk with zero criticals”, the policy expresses “control RCTL-1866 has been satisfied” — and the decision attestation carries the evidence of how that judgment was reached.
For the complete policy syntax, including attestation requirements and exceptions, see the Environment Policies page.
Asserting artifact compliance
kosli assert artifact --environment is the Policy Enforcement Point (PEP). It checks that an artifact satisfies all requirements in the environment’s attached policy — including any controls required via for_control rules — and exits non-zero if it doesn’t. Use it as a pipeline gate before promoting an artifact to an environment.
If the artifact satisfies all policy requirements — including passing decisions for every listed control — the command exits 0. If not, the command exits non-zero and prints which requirements were not met, failing the pipeline step.
End-to-end pipeline flow
The sequence below shows a single artifact moving through a build trail and a release trail, with the compliance decision made at release time and enforced by the release controller checking the environment policy.Viewing a control
Navigate to Controls in the Kosli app and select a control to see its detail view. The header shows the identifier, current version, name, any links, description, and tags. Below it are two tabs: Decisions and Versions.Decisions
The Decisions tab is the default view. It lists every decision recorded against this control across all flows and trails, with the timestamp, the trail it was recorded on, the artifact fingerprint, and the outcome (compliant or non-compliant). Filter by outcome and flow, and sort by timestamp, to narrow the list.
policy.tar.gz) attached as evidence.

Versions
The Versions tab lists every version of the control definition — the name, description, and links as they were at each version, with who made the change and when. Because every decision references the control version that was current when it was recorded, this history lets auditors see exactly what the control required at the time of each decision. Changes to the policy that decides the control don’t appear here: policy versions are tracked separately, and each decision names the policy version that judged it.Managing your controls catalog
As your catalog grows, list and search it, and archive controls you retire.Listing controls
Navigate to Controls in the Kosli app sidebar to browse your full controls catalog. Search by name or identifier, and filter by tag to narrow the list. Each control shows its identifier, description, tags, name, and current version.
kosli list controls, fetch a single control with kosli get control CONTROL-IDENTIFIER, or use the controls API (GET /api/v2/controls/{org}).
Archiving a control
When a control is retired, archive it rather than deleting it. Archiving moves the control out of the main catalog into its Archived view but preserves its full history — past decisions and every version of the definition remain intact for audit — and an archived control can be unarchived later if it comes back into use. Archive or unarchive a control from its detail page (org admins only), from the CLI withkosli archive control CONTROL-IDENTIFIER and kosli unarchive control CONTROL-IDENTIFIER, or through the controls API (POST /api/v2/controls/{org}/{identifier}/archive and .../unarchive). Both actions appear as events in the control’s Versions tab, alongside Created and Edited events, so the full lifecycle is auditable.
What you’ve accomplished
You have learned how to define controls in Kosli, write, test, and publish a policy that decides a control, record decisions by evaluating that policy withkosli evaluate policy --control as the PDP, require controls in environment policies with for_control, assert artifact compliance using kosli assert artifact --environment as the PEP, and review the decisions and version history of each control.
Your controls catalog is now the bridge between the evidence Kosli collects and the requirements your auditors, control owners, and regulators care about. For each production change, you can now answer: “which of our controls have a compliant decision recorded, and which don’t?”
From here you can:
- Learn more about controls
- Learn more about environment policies
- Learn more about attestations
- Read the Rego policy reference
- Learn to write policies with Ergo