> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chainloop.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Contracts

<img src="https://mintcdn.com/chainloop/4m_Z_ZeRnSV7jb7V/get-started/img/adding-contract-1.png?fit=max&auto=format&n=4m_Z_ZeRnSV7jb7V&q=85&s=c9161652bc759a7b7eb6ce054a96af2b" alt="info" width="807" height="522" data-path="get-started/img/adding-contract-1.png" />

A Chainloop contract becomes the interface between the developers who must instrument their CI/CD pipelines and Security and Compliance teams.

A Workflow Contract **defines the expectations of what content a workflow must send as part of their attestation**. For example, a contract could explicitly state that the URI\@digest of the generated container image, the container rootfs used during build, and the Software Bill of Materials of that container image must be present in the attestation.

There are two additional properties of Workflow Contracts that aims to ease the management of workflows in your organization

* A Workflow Contract is immutable and versioned, new revisions of the contract with new/different requirements can be added over time. This is especially useful for iterative integrations.

* A Workflow Contract can belong to more than one Workflow. This means that a change in a single contract will be propagated to many workflows. This is especially useful for org-wide standardization and fleet management.

**Example**: three CI/CD pipelines associated with their respective Chainloop workflows. Two of those Workflows are sharing the same Contract (using the same or different revision).

<img src="https://mintcdn.com/chainloop/qP5Aepelxm7aUyXA/concepts/img/contracts-1.png?fit=max&auto=format&n=qP5Aepelxm7aUyXA&q=85&s=0df3e283d6706ad531b66cc93a578ee3" alt="info" width="1247" height="607" data-path="concepts/img/contracts-1.png" />

## Managing Contracts

<Tabs>
  <Tab title="Web UI">
    Contracts can be managed through the ["Contracts" section](https://app.chainloop.dev/contracts) in Chainloop platform:

    <img src="https://mintcdn.com/chainloop/qP5Aepelxm7aUyXA/concepts/img/contracts-2.png?fit=max&auto=format&n=qP5Aepelxm7aUyXA&q=85&s=6d502d319a5bd8281280d00291c696c3" alt="info" width="992" height="676" data-path="concepts/img/contracts-2.png" />
  </Tab>

  <Tab title="CLI">
    ```
     chainloop workflow contract ls
    ```

    <Accordion title="Command Output">
      ```bash theme={"dark"}
      ┌─────────────────────────────────────────────┬─────────────────┬─────────────────────┬─────────────┐
      │ NAME                                        │ LATEST REVISION │ CREATED AT          │ # WORKFLOWS │
      ├─────────────────────────────────────────────┼─────────────────┼─────────────────────┼─────────────┤
      │ demo-test-gitlab                            │               1 │ 05 Mar 25 12:56 UTC │           1 │
      │ with-policies                               │               2 │ 01 Feb 25 20:40 UTC │           1 │
      │ demo-test-string                            │               1 │ 08 Jan 25 14:44 UTC │           1 │
      │ chainloop-contribution                      │               1 │ 06 Jan 25 20:49 UTC │           0 │
      │ demo-spring-petclinic                       │               3 │ 17 Dec 24 15:19 UTC │           8 │
      │ demo-spring-petclinic-build-container-image │               3 │ 16 Dec 24 23:58 UTC │           0 │
      │ default-contract                            │               1 │ 12 Dec 24 10:50 UTC │           0 │
      └─────────────────────────────────────────────┴─────────────────┴─────────────────────┴─────────────┘
      ```
    </Accordion>
  </Tab>
</Tabs>

<Tip>
  Contracts can also be managed declaratively as YAML files. See the [Declarative Resource Management](/guides/declarative-resource-management) guide.
</Tip>

There is always a default, empty contract to speed up organization onboarding. A new contract can be added by clicking "Create Contract" form, where you can specify its specification:

<Tabs>
  <Tab title="Web UI">
    <img src="https://mintcdn.com/chainloop/qP5Aepelxm7aUyXA/concepts/img/contracts-3.png?fit=max&auto=format&n=qP5Aepelxm7aUyXA&q=85&s=dfc1c1026bb558a19cdd54773404ecff" alt="info" width="613" height="572" data-path="concepts/img/contracts-3.png" />
  </Tab>

  <Tab title="CLI">
    ```bash theme={"dark"}
    chainloop workflow contract create -h
    Create a new contract

    Usage:
    chainloop workflow contract create [flags]

    Flags:
    -f, --contract string      path or URL to the contract schema
      --description string   description of the contract
    -h, --help                 help for create
      --name string          contract name
    ```
  </Tab>
</Tabs>

<Info>
  A Workflow Contract can be provided in json or yaml (cue format is also supported through the Chainloop CLI).
</Info>

### Contract Management Scope

Contracts in Chainloop operate at two different scopes with corresponding management permissions:

* **Organization-scope contracts**: Managed by users with Organization Admin or Organization Owner roles. These contracts can be used across all projects within the organization.
* **Project-scope contracts**: Managed by Project Admins. These contracts are specific to individual projects and can only be used within their respective project scope.

### Restricting Contract Creation

Organizations can optionally enable the **"Restrict Contract Creation to Organization Admins"** setting to enforce stricter governance over contract management. When this setting is enabled:

* Only users with Organization Admin or Organization Owner roles can create new contracts (regardless of the contract scope)
* Project Admins that are not Organization Admins can no longer create their own contracts
* Workflows created by non administrators must use existing contracts

This restriction helps organizations maintain tighter control over contract definitions and ensures consistency across projects by centralizing contract governance.

#### Enabling the setting

Organization owner and admins can enable this setting in the [organization settings](https://app.chainloop.dev/settings).

<img src="https://mintcdn.com/chainloop/FjKl_PcPBq_IpPvC/concepts/img/contract-create-restriction.png?fit=max&auto=format&n=FjKl_PcPBq_IpPvC&q=85&s=86eb91c5d238546d906f6a29d94cf05a" alt="info" width="2504" height="288" data-path="concepts/img/contract-create-restriction.png" />

or using the CLI:

```bash theme={"dark"}
chainloop org update --name [my-org-name] --restrict-contract-creation
```

For detailed information about roles and permissions, see our [RBAC documentation](/reference/rbac).

## Associating workflows to contracts

Once contracts are created, they can be attached to as many workflows as needed. Remember that workflows represent a unique source of data ingestion (attestations), usually representing a CI/CD pipeline.

<Tabs>
  <Tab title="Web UI">
    In the workflow view, click on the edit icon and associate the contract. Once the contract is associated, new attestations will automatically download and apply it, including required materials, policies, and so on.

    <img src="https://mintcdn.com/chainloop/qP5Aepelxm7aUyXA/concepts/img/contracts-5.png?fit=max&auto=format&n=qP5Aepelxm7aUyXA&q=85&s=d4bd5ea70020f0aead354d4e4124d642" alt="info" width="711" height="519" data-path="concepts/img/contracts-5.png" />
  </Tab>

  <Tab title="CLI">
    ```bash theme={"dark"}
     chainloop workflow update --name <my-workflow> --project <my-project> --contract <my-contract>
    ```
  </Tab>
</Tabs>

## Writing contracts

### Example

See below a contract schema example:

<Note>
  Note that the contract below is just an example crafted for educational purposes. In a real scenario, the contract will not contain such a despaired amount of materials or annotations.
</Note>

```yaml theme={"dark"}
apiVersion: chainloop.dev/v1
kind: Contract
metadata:
  name: skynet-contract
spec:
  # Arbitrary set of annotations can be added to the contract and will be part of the attestation
  annotations:
    - name: version
      value: oss # if the value is left empty, it will be required and resolved at attestation time
  # https://docs.chainloop.dev/concepts/operator/material-types
  materials:
    # CONTAINER_IMAGE kinds will get resolved to retrieve their repository digest
    - type: CONTAINER_IMAGE
      name:
        skynet-control-plane
        # The output flag indicates that the material will be part of the attestation subject
      output: true
      # Arbitrary annotations can be added to the material
      annotations:
        - name: component
          value: control-plane
        # The value can be left empty so it can be provided at attestation time
        - name: asset
    # ARTIFACT kinds will first get uploaded to your artifact registry via the built-in Content Addressable Storage (CAS)
    # Optional dockerfile
    - type: ARTIFACT
      name: dockerfile
      optional: true
    # SBOMs will be uploaded to the artifact registry and referenced in the attestation
    # Both SBOM_CYCLONEDX_JSON and SBOM_SPDX_JSON are supported
    - type: SBOM_CYCLONEDX_JSON
      name: skynet-sbom
    # CSAF_VEX and OPENVEX are supported
    - type: OPENVEX
      name: disclosure
    # And static analysis reports in SARIF format
    - type: SARIF
      name: static-out
    # or additional tools
    - type: TWISTCLI_SCAN_JSON
      name: scan-result
    # Large artifact that's already stored externally - skip CAS upload but record metadata
    - type: ARTIFACT
      name: large-binary
      skip_upload: true
  # https://docs.chainloop.dev/concepts/policies
  policies:
    materials: # policies applied to materials
      - ref: file://cyclonedx-licenses.yaml
    attestation: # policies applied to the whole attestation
      - ref: https://github.com/chainloop/chainloop-dev/blob/main/docs/examples/policies/chainloop-commit.yaml # (2)
        gate: true # Enable/disable (true/false) control gate per policy, overrides org default https://docs.chainloop.dev/concepts/control-gates
  # Env vars we want the system to resolve and inject during attestation initialization
  # Additional ones can be inherited from the specified runner context below
  envAllowList:
    - CUSTOM_VAR
  # Enforce in what runner context the attestation must happen
  # If not specified, the attestation crafting process is allowed to run anywhere
  runner:
    type: "GITHUB_ACTION"
```

Once they are created, contracts can be edited. Every change will be stored as a new version of the contract. Chainloop CLI will automatically retrieve the latest version of the contract when a new attestation is performed.

The contract summary view will provide details about the contract:

<img src="https://mintcdn.com/chainloop/qP5Aepelxm7aUyXA/concepts/img/contracts-4.png?fit=max&auto=format&n=qP5Aepelxm7aUyXA&q=85&s=f2bcb814fe046aa080f3c2f7769fbff3" alt="info" width="705" height="604" data-path="concepts/img/contracts-4.png" />

### Schema

<Tabs>
  <Tab title="Schema">
    | Name                                   | Required | Description                                                                                                                                                                  |
    | -------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `apiVersion`                           | yes      | API version of the schema, must be `chainloop.dev/v1`                                                                                                                        |
    | `kind`                                 | yes      | Resource type, must be `Contract`                                                                                                                                            |
    | `metadata`                             | yes      | Metadata about the contract                                                                                                                                                  |
    | `metadata.name`                        | yes      | Name of the contract                                                                                                                                                         |
    | `metadata.description`                 | no       | Optional description of the contract                                                                                                                                         |
    | `metadata.annotations`                 | no       | Name/Value pairs of arbitrary annotations that will be added to the attestation. **If the value is not provided, it will be required during the attestation process.**       |
    | `spec`                                 | yes      | Contract specification containing all contract configuration                                                                                                                 |
    | [`spec.materials`](#material-schema)   | no       | List of [materials](#material-schema) to be added to the attestation                                                                                                         |
    | `spec.envAllowList`                    | no       | List of environment variables that will be resolved and injected in the attestation                                                                                          |
    | [`spec.runner`](#runner-context)       | no       | Specific runner type associated with this contract. If not set, this contract will be valid to be run `anywhere` but you'll miss out some of [its benefits](#runner-context) |
    | [`spec.policies`](#policy-attachments) | no       | Attachments to existing Chainloop policies. See [policies reference guide](/concepts/policies) for more information                                                          |
  </Tab>

  <Tab title="Deprecated Schema">
    <Warning>
      This schema format is deprecated. Please use new schema format.
    </Warning>

    | Name                              | Required | Description                                                                                                                                                                  |
    | --------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `schemaVersion`                   | yes      | Version of the schema, it needs to be `v1`                                                                                                                                   |
    | [`materials`](#material-schema)   | no       | List of [materials](#material-schema) to be added to the attestation                                                                                                         |
    | `envAllowList`                    | no       | List of environment variables that will be resolved and injected in the attestation                                                                                          |
    | [`runner`](#runner-context)       | no       | Specific runner type associated with this contract. If not set, this contract will be valid to be run `anywhere` but you'll miss out some of [its benefits](#runner-context) |
    | `annotations`                     | no       | Name/Value pairs of arbitrary annotations that will be added to the attestation. **If the value is not provided, it will be required during the attestation process.**       |
    | [`policies`](#policy-attachments) | no       | Attachments to existing Chainloop policies. See [policies reference guide](/concepts/policies) for more information                                                          |
  </Tab>
</Tabs>

### Materials

The contract can require one or more pieces of evidence (a.k.a material) to be attached during the attestation process.

| Name          | Required | Default | Description                                                                                                                                                                                                                                                                                                                                                                                            |
| ------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `name`        | yes      |         | unique identifier of the material                                                                                                                                                                                                                                                                                                                                                                      |
| `type`        | yes      |         | Refer to [material-types](./material-types) for the list of supported material types.                                                                                                                                                                                                                                                                                                                  |
| `output`      | no       | `false` | If set to `true` the material will get injected in the `subject` section of the [in-toto statement](https://github.com/slsa-framework/slsa/blob/main/docs/attestation-model.md#overview).                                                                                                                                                                                                              |
| `optional`    | no       | `false` | if set to `true`, providing this material during attestation will be optional. This is **useful for soft rollouts of new requirements**                                                                                                                                                                                                                                                                |
| `skip_upload` | no       | `false` | if set to `true`, the material will not be uploaded to CAS (Content Addressable Storage). Only metadata like digest and filename will be recorded in the attestation. This is **useful for large artifacts already stored externally** or when you want to reduce storage costs while maintaining attestation metadata. Note: `CONTAINER_IMAGE` and `STRING` types are not uploaded to CAS by default. |
| `annotations` | no       |         | Name/Value pairs of arbitrary annotations that will be added to the attestation. **If the value is not provided, it will be required during the attestation process.**                                                                                                                                                                                                                                 |

### Policy attachments

When defining a contract, a new `policies` section can be specified. Policies can be applied to any material, but also to the attestation statement as a whole.

```yaml theme={"dark"}
apiVersion: chainloop.dev/v1
kind: Contract
metadata:
  name: policy-example-contract
spec:
  materials:
    - name: sbom
      type: SBOM_CYCLONEDX_JSON
    - name: another-sbom
      type: SBOM_CYCLONEDX_JSON
    - name: my-image
      type: CONTAINER_IMAGE
  policies:
    materials: # policies applied to materials
      - ref: file://cyclonedx-licenses.yaml # (1)
    attestation: # policies applied to the whole attestation
      - ref: https://github.com/chainloop/chainloop-dev/blob/main/docs/examples/policies/chainloop-commit.yaml # (2)
```

Read the docs for full details on [policies](/concepts/policies).

### Runner Context

<Info>
  New runner contexts will be added over time. If yours is not implemented yet, please [contact us](https://chainloop.dev/contact)
</Info>

An **optional** runner type can be provided in a workflow contract.

```yaml skynet.contract.yaml showLineNumbers theme={"dark"}
apiVersion: chainloop.dev/v1
kind: Contract
metadata:
  name: skynet-contract
spec:
  materials:
    - type: CONTAINER_IMAGE
      name: skynet-control-plane
  envAllowList:
    - CUSTOM_VAR
  # highlight-start
  runner:
    type: "GITHUB_ACTION"
  # highlight-end
```

It has the following effect on the attestation process.

* **Require** the attestation process to be executed in the target runner type unless the `--dry-run` flag is set during initialization.
* **A link to the workload** (i.e Github Action Run link) **will be recorded** both in the attestation and in the control plane during initialization.
* An additional set of environment variables will be resolved in addition to the ones defined in the contract `envAllowList`.

Currently, we support the following runner types

#### `AZURE_PIPELINE`

The following environment variables will be automatically added to the attestation. For more information on what they mean, refer to [this link](https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops\&tabs=yaml).

* `BUILD_REQUESTEDFOREMAIL`
* `BUILD_REQUESTEDFOR`
* `BUILD_REPOSITORY_URI`
* `BUILD_REPOSITORY_NAME`
* `BUILD_BUILDID`
* `BUILD_BUILDNUMBER`
* `BUILD_BUILDURI`
* `BUILD_REASON`
* `AGENT_VERSION`
* `TF_BUILD`

A link to the Azure Pipeline build will be recorded in the control plane too during initialization.

#### `CIRCLECI_BUILD`

The following environment variables will be automatically added to the attestation. For more information on their meaning, refer to [the official CircleCI documentation](https://circleci.com/docs/variables/).

* `CIRCLE_BUILD_URL`
* `CIRCLE_JOB`
* `CIRCLE_BRANCH` (optional)
* `CIRCLE_NODE_TOTAL`
* `CIRCLE_NODE_INDEX`

A link to the CircleCI build will be recorded in the control plane too, during initialization.

#### `DAGGER_PIPELINE`

To use Chainloop With Dagger you can use [this Dagger module](https://github.com/chainloop-dev/chainloop/tree/main/extras/dagger)

**Commit Signature Verification**: Dagger pipelines can run in various CI environments. Chainloop automatically detects the underlying platform (GitHub Actions or GitLab CI) and verifies commit signatures through the appropriate API when `GITHUB_TOKEN` or `CI_JOB_TOKEN` is available. The verification status and signature algorithm are recorded in the attestation annotations.

#### `GITHUB_ACTION`

The following environment variables will be automatically added to the attestation. For more information on what they do refer to [this link](https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables).

* `GITHUB_ACTOR`
* `GITHUB_REF`
* `GITHUB_REPOSITORY`
* `GITHUB_REPOSITORY_OWNER`
* `GITHUB_RUN_ID`
* `GITHUB_SHA`
* `RUNNER_NAME`
* `RUNNER_OS`

A link to the Github Action will be recorded in the control plane too during initialization.

**Commit Signature Verification**: When `GITHUB_TOKEN` is available, Chainloop automatically verifies commit signatures through GitHub's API and records the verification status (`verified`, `unverified`, `unavailable`, `not_applicable`) and signature algorithm (e.g., `PGP`, `SSH`, `X509`) in the attestation annotations. This provides auditable evidence that commits are cryptographically signed and verified by GitHub.

The default `GITHUB_TOKEN` provided by GitHub Actions has sufficient permissions to query commit verification status. No additional configuration is required - the token is automatically available in the workflow environment.

```yaml theme={"dark"}
- name: Initialize attestation
  run: chainloop attestation init ...
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

To check the interpretation of the verification status, refer to [GitHub's official documentation](https://docs.gitlab.com/user/project/repository/signed_commits/).

#### `GITLAB_PIPELINE`

The following environment variables will be automatically added to the attestation. More information about what they mean in [GitLab's official documentation](https://docs.gitlab.com/ee/ci/variables/predefined_variables.html)

* `GITLAB_USER_EMAIL`
* `GITLAB_USER_LOGIN`
* `CI_PROJECT_URL`
* `CI_COMMIT_SHA`
* `CI_JOB_URL`
* `CI_PIPELINE_URL`
* `CI_RUNNER_VERSION`
* `CI_RUNNER_DESCRIPTION`
* `CI_COMMIT_REF_NAME`

A link to the GitLab CI job will be recorded in the control plane too, during initialization.

**Commit Signature Verification**: When `CI_JOB_TOKEN` is available, Chainloop automatically verifies commit signatures through GitLab's API and records the verification status (`verified`, `unverified`, `unavailable`, `not_applicable`) and signature algorithm (e.g., `PGP`, `SSH`, `X509`) in the attestation annotations. This provides auditable evidence that commits are cryptographically signed and verified by GitLab.

The `CI_JOB_TOKEN` is automatically available in GitLab CI pipelines and has the necessary permissions to query commit signature information.

```yaml theme={"dark"}
attest:
  script:
    - chainloop attestation init ...
  variables:
    CI_JOB_TOKEN: $CI_JOB_TOKEN
```

To check the interpretation of the verification status, refer to [GitLab's official documentation](https://docs.gitlab.com/ee/user/project/repository/commit_signatures/#viewing-the-signature-status-of-commits).

#### `JENKINS_JOB`

The following environment variables will be automatically added to the attestation. For more information on how to use Jenkins environment variables, refer to [the official Jenkins documentation](https://www.jenkins.io/doc/book/pipeline/jenkinsfile/#using-environment-variables).

* `JOB_NAME`
* `BUILD_URL`
* `GIT_BRANCH` (optional)
* `GIT_COMMIT` (optional)
* `AGENT_WORKDIR`
* `NODE_NAME`

A link to the build will be recorded in the control plane too, during initialization.

#### `TEAMCITY_PIPELINE`

The following environment variables will be automatically added to the attestation. For more information on TeamCity environment variables, refer to [the official TeamCity documentation](https://www.jetbrains.com/help/teamcity/predefined-build-parameters.html).

* `BUILD_URL`
* `TEAMCITY_PROJECT_NAME`
* `TEAMCITY_VERSION`
* `BUILD_NUMBER`
* `USER`
* `TEAMCITY_GIT_VERSION`
* `BUILD_VCS_NUMBER`
* `HOME`

A link to the TeamCity build will be recorded in the control plane too, during initialization.

<Tip>
  Remember, if all the **env variables** that you need are not defined in the context, you can extend such list via the `envAllowList` option.
</Tip>

#### `TEKTON_PIPELINE`

The following variables are automatically discovered and added to the attestation. No environment variable wiring is needed in your Pipeline YAML — the runner discovers everything from Kubernetes pod labels and the ServiceAccount namespace file.

| Variable                    | Source                              | Required                         |
| --------------------------- | ----------------------------------- | -------------------------------- |
| `TEKTON_TASKRUN_NAME`       | `tekton.dev/taskRun` pod label      | Yes (falls back to hostname)     |
| `TEKTON_NAMESPACE`          | ServiceAccount namespace file       | Yes                              |
| `TEKTON_TASK_NAME`          | `tekton.dev/task` pod label         | No (absent with inline taskSpec) |
| `TEKTON_PIPELINE_NAME`      | `tekton.dev/pipeline` pod label     | No (only in Pipeline TaskRuns)   |
| `TEKTON_PIPELINERUN_NAME`   | `tekton.dev/pipelineRun` pod label  | No (only in Pipeline TaskRuns)   |
| `TEKTON_PIPELINE_TASK_NAME` | `tekton.dev/pipelineTask` pod label | No (only in Pipeline TaskRuns)   |
| `HOSTNAME`                  | Environment variable                | No                               |

A run link will be recorded in the control plane during initialization, using the format `tekton://<namespace>/pipelineruns/<name>`. If the `TEKTON_DASHBOARD_URL` environment variable is set, a Tekton Dashboard URL will be used instead.

<Note>
  **RBAC requirement**: The TaskRun ServiceAccount needs `get` permission on pods in its namespace for pod label discovery. If the permission is missing, the runner degrades gracefully and captures only the variables available without it.
</Note>

#### Gather Additional Runner Configuration

The Chainloop CLI Enterprise Edition provides a [runner context](/reference/runner-context) that can be used to gather the runner context.

When Chainloop CLI Enterprise Edition is used it enables automatic retrieval of the following data:

* Branch protection rules
* Pull request requirements
* Commit policies

This information is retrieved directly from your CI/CD system settings without you having to manually enter anything.

The gathered security information becomes part of your attestation, which helps:

* Verifying that proper protections were in place during the build process
* Understanding what security measures are protecting the code
* Proving that security policies were followed during development

Essentially, Chainloop creates an automatic security audit trail that shows your code was properly protected
throughout the development process.
