# chainloop: guardrails and signed records for AI coding agents

> Chainloop is an Apache licensed platform for the part of software delivery that has no tooling yet: knowing what your coding agents and your pipelines actually did. Every session, build, software bill of materials, scan report and test result becomes a signed attestation in your own storage, and a policy written in Rego decides whether it is allowed to ship.

**chainloop-dev/chainloop** — Agentic SDLC infrastructure. Define guardrails. Collect signals. Enforce continuously. Signed records of what your AI coding agents and CI/CD pipelines did, checked against policies as code, with the loop closed in CI/CD. AI sessions, SBOMs, VEX, SARIF and SLSA provenance, built on Sigstore and in-toto. Apache 2.0.

- Repository: https://github.com/chainloop-dev/chainloop
- Website: https://docs.chainloop.dev
- Stars: 605 · Forks: 64
- Language: Go
- License: Apache-2.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/chainloop-dev-chainloop

## The pitch, and the problem it names

The readme's opening claim is that Chainloop collects what happens across your software delivery, from the first prompt to the release, into one trusted graph. You define once what good means, as code, and Chainloop enforces it everywhere: every agent, every pipeline, every release.

The next paragraph is the mechanism, and it is unusually concrete. Every AI session, pull request, build, software bill of materials, scan, and test report becomes a signed, timestamped record in your own registry or bucket. Each record links to the commit and the release it belongs to. Rules live in git. Break one and the pipeline fails. The job prints every failing policy and its reason where the agent reads it. The agent fixes the work and pushes again, until Chainloop is happy.

That last sentence is the one that matters, and it is the difference between a compliance tool and a development tool. A tool that only blocks a pull request teaches an organisation to disable it, or to click an override. A tool that prints the reason into the job log, where the agent is already reading, lets the agent fix the problem and try again. The loop closes on its own.

The problem the readme names before the solution is worth stating in its own terms. Every team already runs its own tools: software bill of materials generators, scanners, test suites, registries, and now coding agents. Each keeps its results in its own format, and none of them can enforce a rule that spans the others. Findings stay advisory, teams generate bills of materials nobody uses, and one new requirement means touching every pipeline.

That is an accurate description of a specific kind of waste. The bill of materials exists because somebody asked for it, it is uploaded to a place nobody queries, and the next requirement, that a particular component must not be present, is implemented by adding a step to four pipelines. Chainloop's answer is to sit on top of what you already run, so that a rule spanning several tools is written once.

The project is Apache 2.0 licensed, self-hostable, and the readme says it has been in production on critical infrastructure at highly regulated enterprises since 2024. The release cadence is fast, with a version released two days before the last push, and the badges show a security scorecard, a foundation health score, and a test status on the main branch.

## The three step loop

The how it works section is three numbered steps, and the readme's diagram caption names them: define guardrails, collect signals, enforce continuously, with findings going back to the agent, and then it loops.

Step one is defining the guardrails. A workflow contract says what every session and pipeline run must produce, and the policies say what each record must satisfy. The examples given for policy content are: approved models only, no secrets in prompts, tests pass, and every release has a software bill of materials. Those four are a good description of what a regulated organisation worries about now, and the first two are specifically about the agent era.

Step two is collecting signals, and it splits into two commands with two different shapes. The trace command records the agent session on the developer's machine and attests it on a push. The attestation command does the same inside any continuous integration job, for bills of materials, scans, test reports, and provenance. Every record is a signed, timestamped attestation in the in-toto format, stored in your own registry or bucket, with the named options being an OCI registry, S3, or Azure Blob.

Step three is enforcing. Chainloop checks every record against the contract and its policies. If it passes, it ships. If it fails, the pipeline fails, Chainloop signs the violations into the record, and the agent that opened the pull request reads them and tries again. The hosted platform version can also block the pull request itself.

The signing of the violations into the record is a detail with a lot of consequence. A failure is not just a message on a screen that disappears when the job ends; it becomes a signed statement that this run violated these rules. That is what makes the record useful as evidence later, and it is also what makes the feedback safe to hand to an agent, because the agent is reading a structured statement rather than parsing a log.

The table of good and bad outcomes is a compact statement of the whole policy model. In the good column: an approved agent and model wrote the change, tests pass and coverage holds, every release has a bill of materials and a clean scan, and the pipeline ran every step the contract asked for. In the bad column: an unapproved model or an agent nobody allowed, a secret in a prompt or a transcript or a commit, a critical vulnerability or a release with no bill of materials, and a skipped step, a force push, or a dangerous command.

Read as a list, the good column is a release checklist and the bad column is the set of things a modern delivery pipeline actually fails at. Four of the eight are about the agent, which tells you what the project is really for.

## Contracts and policies, and the pull request as the change mechanism

A contract is a small declarative document, and the example in the readme is short enough to read in full.

```yaml
apiVersion: chainloop.dev/v1
kind: Contract
metadata:
  name: build
spec:
  materials:
    - name: image
      type: CONTAINER_IMAGE
    - name: sbom
      type: SBOM_CYCLONEDX_JSON
  runner:
    type: GITHUB_ACTION
  policies:
    materials:
      - ref: https://raw.githubusercontent.com/chainloop-dev/chainloop/main/docs/examples/policies/sbom/cyclonedx-licenses.yaml
        gate: true
```

There are four parts. The version and kind identify the document. The list of artifacts says what the run has to hand in and how each one is typed, and the types are specific: a container image, and a bill of materials in a particular format. The runner says where it runs, here a GitHub action. And the policies attach rules to a material, with a reference to where the rule lives and a flag saying whether the rule gates.

That gate flag is worth a moment. A policy that gates blocks the run; a policy that does not is advisory. Being able to attach a rule to one material and leave another advisory is the difference between a policy system you can adopt incrementally and one you cannot.

And the policies are fetched by reference rather than embedded, which is the detail that makes the whole thing work as a code review process. The apply command pushes a directory of contracts, so a pull request is how a requirement changes.

The rule itself is a few lines of Rego, and the example fails a build whose scan report contains an error.

```rego
violations contains msg if {
	has_errors
	msg := "There are errors in the SARIF report"
}

has_errors if {
	some run in input.runs
	some result in run.results
	result.level == "error"
}
```

That is the whole policy language surface a user needs to know: a set of violation messages, and rules that produce them. The record is the input, and the rules are ordinary logic over it.

The readme then points at the policies in the repository, which include one that checks a bill of materials is present and one that bans certain licences, and at the full policy for the scan example, and it notes that the policies may also be written in WebAssembly.

The WebAssembly option is the interesting one for a security product, because a WebAssembly module is a sandboxed thing you can run inside a policy engine without giving it arbitrary access to the host. A Rego policy is limited to what the language can express; a WebAssembly policy can be anything that compiles to that target, and the boundary is the sandbox rather than the language.

## The stack, and what the dependencies reveal

The module file is the usual place to find out what a project really depends on, and this one is unusually informative.

The policy engine is Rego, and there is a WebAssembly plugin host, which is the pair that implements the two policy languages. The terminal form library is there, so the command line tool asks questions with a nice interface rather than with a prompt. The secret manager and object storage clients cover three clouds, which is where the attestations and the credentials live.

There is an entity framework for the database layer, which is how a project models relations without writing the query layer by hand. There is an SQL driver with OpenTelemetry instrumentation attached, which is a nice touch for a tool whose job is to observe a build.

The authorisation library is there, and the open identity client, and a machine identifier library, and those three together are the authentication story: single sign-on, a token, and a stable machine identity for the exporter process.

Then the supply chain of the project itself, which is what a security product has to live up to. There is a secret scanner, and a certificate and timestamp library, and a container registry client, and a Go implementation of git, and the file watcher library from the standard set, and a fake object storage server for tests, and an error reporting client.

The certificate and timestamp pair is the one that deserves explanation, because it is what makes a signature meaningful. A signature on its own says who signed something, and a signature with a timestamp says they signed it while a certificate was valid, which is what a verifier needs in order to check the signature after the certificate has expired. Those two libraries are the difference between an attestation that verifies for a week and one that verifies for a decade.

The version of the go language directive is a recent one, and the module is large, which is what you would expect for a project with a control plane, an artifact store, a command line tool, and several language bindings.

And there are three files at the top of the repository that say more about the project than any of the code: a policy file for the use of artificial intelligence, a file of instructions for coding agents, and a directory of editor and agent configurations. A project that lets agents write code, and that ships a policy for how they should, is a project that has thought about the recursion.

## The repository shape, and the build

The top level of the repository is the architecture, and the directory names are worth reading in order.

There is an app directory, which is where the three deployable pieces live, and their names appear in the build file: a control plane, an artifact store, and the command line client. There is a package directory with the shared libraries, among them the attestation crafter, which is the part that builds the signed records, and the credentials package with its own interface definition. There is an internal directory for the private implementation, a test directory, a development directory, a deployment directory, and a documentation directory.

Then there are four directories whose names are not obvious from the readme and are worth guessing at from the context. There is a directory of extras, with a dagger module file at the root beside it, which is a build system for the pipeline environment. There is a directory of labs, which is where experiments live. There is a directory for a sandbox environment, with a configuration file at the root. And there is a directory for the vulnerability exploit exchange data.

The presence of a pipeline build tool in the same repository as the thing that observes pipelines is a deliberate choice, and it means the project can demonstrate its own policies on its own build, which is a form of self-application that any team evaluating a policy engine should ask about.

The build file is small and structured around three kinds of generation. There is a target that generates the interface definitions, which runs the protobuf compiler in the attestation crafter, the control plane, and the artifact store. There is a target that generates the configuration definitions, which does the same for the credentials package and the configuration of each application. And a general target that runs the code generator over everything.

The interface generation is done with a protobuf toolchain that has its own lock file and its own configuration at the root, and the make targets are per-component, so each of the three applications can be regenerated on its own. The lint target runs the Go linter, the interface linter, and a lint step in each of the three applications. The test target is a single command over the whole module, and the comment says it covers both the unit and the integration tests.

The development build targets are the ones to note for anybody who wants to run the thing locally. One produces a single target build from the release tool with a snapshot flag, and the other produces container images, skipping the signing and the bill of materials steps so that a local build does not need a signing identity.

## Conclusion

chainloop fits a regulated organisation that has to show what produced a release, and that has started letting agents write code and needs the same evidence for the agent as for the pipeline. It does not fit a team that only wants a software bill of materials, since generating one is the smallest part of what this does, and it does not fit somebody who wants the rules somewhere other than the pipeline, because the enforcement point is deliberately the job. Verify first where your attestations will be stored, since the project insists they live in your own registry or bucket rather than in its service, and that the policies you write are evaluated against the record shape the tool produces rather than against a shape you assume.

## FAQ

### What is the chainloop project?

It is Apache 2.0 licensed infrastructure for what an organisation's coding agents and pipelines actually did. Every AI session, pull request, build, software bill of materials, scan and test report becomes a signed, timestamped attestation in your own registry or bucket, linked to the commit and release it belongs to, and policies written as code decide whether it may ship.

### How does chainloop close the loop with an AI coding agent?

When a record fails a policy the pipeline fails, Chainloop signs the violations into the record, and the job prints the failing policies and their reasons where the agent is already reading. The agent fixes the work and pushes again, and the check runs until it passes, so the enforcement is feedback rather than a block with no explanation.

### What is a chainloop contract?

A small declarative document with a version, a name, the list of materials the run must produce with the type of each, the runner that executes it, and the policies attached to each material with a flag saying whether that rule gates. Contracts are applied from a directory, so a pull request is how a requirement changes, and the policies are referenced by URL rather than embedded.

### What languages can chainloop policies be written in?

Rego or WebAssembly. The example policy in the readme is a few lines of Rego that produce violation messages from the record, and the WebAssembly option allows a policy to be anything that compiles to that target, with the sandbox rather than the language being the boundary.

### Where does chainloop store the attestations it creates?

In your own registry or bucket, with the named options being an OCI registry, S3, or Azure Blob. The records are in-toto attestations that are signed and timestamped, and the certificate and timestamp libraries in the dependencies are what make a signature verifiable long after the signing certificate has expired.

## Sources

- [chainloop-dev/chainloop on GitHub](https://github.com/chainloop-dev/chainloop)
- [License: Apache-2.0](https://github.com/chainloop-dev/chainloop/blob/main/LICENSE)
- [Project website](https://docs.chainloop.dev)
- [README](https://github.com/chainloop-dev/chainloop/blob/main/README.md)
- [Releases](https://github.com/chainloop-dev/chainloop/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/chainloop-dev-chainloop
