Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

Chainloop Module for Dagger

dagger-tested-version

Daggerized version of Chainloop that can be used to attest and collect pieces of evidence from your Dagger pipelines.

Prerequisites

  • This module requires existing familiarity with Chainloop and its attestation process. Please refer to this guide to learn more. You need an API Token previously generated by your Chainloop administrator.
  • By default, this dagger module points to Chainloop's upstream instance, to point to your own instance, see the section below.

Attestation Crafting

The attestation process starts with its initialization (init) or resume, then adding as many materials/pieces of evidence as needed (add-raw-evidence or add-file-evidence), and finally, signing and pushing the attestation to the Chainloop control plane (push).

You can invoke this module in two ways: either from the Dagger CLI dagger call ... or from your own Dagger pipeline by importing this module as a dependency.

Using the Chainloop module in your Dagger pipeline

To use Chainloop in your module, first, you need to add it as a dependency.

dagger install github.com/chainloop-dev/chainloop

Once done, you'll have access to the Chainloop client via dag.Chainloop() and start the attestation process with Init().

You can find a full example of how to integrate attestation crafting in your Go pipeline here

Using the Dagger CLI

The attestation process starts with its initialization (init) or resume, then adding as many materials/pieces of evidence as needed (add-raw-evidence or add-file-evidence), and finally, signing and pushing the attestation to the Chainloop control plane (push).

This module is designed to support function chaining, so after initializing the attestation, you can chain the subcommands to add pieces of evidence and push the attestation. For example

dagger call -m github.com/chainloop-dev/chainloop \
  # Initialize the command
  init --token env:CHAINLOOP_TOKEN \
  # we chain subcommands after the initialization
  # add a raw evidence
  add-raw-evidence --name my-evidence --value "my-value" \
  # and push the result
  push --key file:/path/to/cosign.key --passphrase env:COSIGN_PASSPHRASE

If the attestation process end-to-end is not completed in one go, you can store the attestation-id after init and resume the attestation process using the resume method at any time down the line.

# Initialize but this time we store the attestation-id
ATTESTATION_ID=$(dagger call -m github.com/chainloop-dev/chainloop  init --token env:CHAINLOOP_TOKEN attestation-id)


# and we use it to resume the attestation process
dagger call -m github.com/chainloop-dev/chainloop \
  resume --token env:CHAINLOOP_TOKEN --attestation-id $ATTESTATION_ID \
  # we chain subcommands after the initialization
  ....

1 - Init attestation (docs)

Initialize an attestation using the Chainloop token stored in the CHAINLOOP_TOKEN environment variable.

NOTE: --token can be provided only by referencing an environment variable (env:MY_VAR), not by value

# Initialize the attestation and get its ID
dagger call -m github.com/chainloop-dev/chainloop \
  init \
  --token env:CHAINLOOP_TOKEN \
  --workflow-name the-name-of-the-workflow \
  --project-name the-name-of-the-project \
  --repository /path/to/repo \ # optional flag to automatically attest a Git repository \
  --contract-name my-existing-contract \ # optional flag to specify an existing contract that will be used during the creation of a workflow
Controlling "latest" promotion

The optional --mark-latest flag controls whether the project version is promoted to latest. It is an enum with three values:

  • ON_CREATE (default): newly created versions become latest; existing versions are left unchanged.
  • TRUE: force-promote a pre-release version to latest.
  • FALSE: do not promote the version to latest.

TRUE and FALSE only affect promotion: they never demote a version that is already latest. In particular, FALSE prevents a newly created version from becoming latest, but it has no effect on an existing version (its latest status is left untouched).

dagger call -m github.com/chainloop-dev/chainloop \
  init \
  --token env:CHAINLOOP_TOKEN \
  --workflow-name the-name-of-the-workflow \
  --project-name the-name-of-the-project \
  --version 1.0.0 \
  --mark-latest FALSE # create the new version 1.0.0 without making it latest

2 - Get the status (docs)

Resuming a previous attestation

dagger call -m github.com/chainloop-dev/chainloop \
  resume --token env:CHAINLOOP_TOKEN --attestation-id $ATTESTATION_ID \
  status

or chaining the command right after initialization

dagger call -m github.com/chainloop-dev/chainloop \
  init --token env:CHAINLOOP_TOKEN \
  status

3 - Add pieces of evidence (docs)

You can attest pieces of evidence by providing its material name and its value, either in the form of a path to a file (--path) or a raw value (--value).

A path to a file is required for materials derived from artifacts, such as Software Bill Of materials, or any other file-based evidence.

# Provide a material of kind artifact through its path
# Remember, we first resume the attestation or chain the commands
dagger call -m github.com/chainloop-dev/chainloop \
  resume --token env:CHAINLOOP_TOKEN --attestation-id $ATTESTATION_ID \
  add-file-evidence --name my-sbom --path ./path/to/sbom.json
# Or one with a raw value such as a container image reference
dagger call -m github.com/chainloop-dev/chainloop \
  resume --token env:CHAINLOOP_TOKEN --attestation-id $ATTESTATION_ID \
  add-raw-evidence --name my-container-image --value ghcr.io/chainloop-dev/chainloop/control-plane

If you're attesting materials that don't belong to the target contract, you can allow Chainloop to figure out its type and if discovered, it will be added to the attestation. In order to do that, don't pass the --name, just provide the path to the file.

# Provide a material only through its path
dagger call -m github.com/chainloop-dev/chainloop \
  resume --token env:CHAINLOOP_TOKEN --attestation-id $ATTESTATION_ID \
  add-file-evidence --path ./path/to/sbom.json

In some cases, you might be providing a private container image as a piece of evidence. In this case, you'll also need to preload the container registry credentials.

dagger call -m github.com/chainloop-dev/chainloop \
  resume --token env:CHAINLOOP_TOKEN --attestation-id $ATTESTATION_ID \
  # Now load the registry credentials
  with-registry --address ghcr.io --username my-username --password MY_PAT_TOKEN \
  # And perform the attestation of the private container image
  add-raw-evidence --name my-container-image --value ghcr.io/chainloop-dev/chainloop/control-plane

4 - Sign and push attestation (docs)

Sign and push the attestation using a cosign key stored in a file and a passphrase stored in an environment variable.

NOTE: neither --signing-key nor --passphrase can be provided by value. You need to provide them either as a file (file:/) or an environment variable (env:/).

dagger call -m github.com/chainloop-dev/chainloop \
  resume --token env:CHAINLOOP_TOKEN --attestation-id $ATTESTATION_ID \
  push --key file:/path/to/cosign.key --passphrase env:COSIGN_PASSPHRASE

Alternatively, you can also provide the signing key in an environment variable --key env:MY_COSIGN_KEY

5 - Cancel/mark attestation as failed

dagger call -m github.com/chainloop-dev/chainloop \
  resume --token env:CHAINLOOP_TOKEN --attestation-id $ATTESTATION_ID \
  mark-failed --reason "Something went wrong"

or cancel the attestation process

dagger call -m github.com/chainloop-dev/chainloop \
  resume --token env:CHAINLOOP_TOKEN --attestation-id $ATTESTATION_ID \
  mark-canceled --reason "nothing to see here"

Attestation in a GitHub/GitLab PR/MR

When running Chainloop via Dagger inside GitHub Actions or GitLab CI, you can enable automatic PR/MR metadata collection by passing the parent CI environment context to the init command. This allows Chainloop to automatically detect and include pull request or merge request information in your attestations. More information about the feature can be found in the official documentation where we give a broader context on how Chainloop can help to build Control Gates in your GitHub and GitLab repositories.

GitHub Actions Example (SDK)

When using the Dagger SDK in GitHub Actions, you can pass PR context as individual parameters. The event file would need to be provided as a File parameter from your module's calling context:

attestation, err := dag.Chainloop().Init(
  ctx,
  dag.SetSecret("chainloop-token", os.Getenv("CHAINLOOP_TOKEN")),
  dagger.ChainloopInitOpts{
    WorkflowName: "build",
    ProjectName: "my-project",
    Repository: dag.CurrentModule().Source(),
    GithubEventName: os.Getenv("GITHUB_EVENT_NAME"),
    GithubHeadRef: os.Getenv("GITHUB_HEAD_REF"),
    GithubBaseRef: os.Getenv("GITHUB_BASE_REF"),
    // GithubEventFile would be passed from caller if available
    GithubEventFile: eventFile,
  },
)
if err != nil {
  return err
}

Note: For full GitHub PR detection with event file parsing, using the Dagger CLI (see below) is recommended as it can directly mount the event file from $GITHUB_EVENT_PATH.

Required Environment Variables:

  • GITHUB_EVENT_NAME - Must be "pull_request" or "pull_request_target"
  • GITHUB_HEAD_REF - Source branch name
  • GITHUB_BASE_REF - Target branch name
  • GITHUB_EVENT_PATH - Path to event JSON file (for CLI usage)

GitLab CI Example (SDK)

GitLab MR detection from the Dagger SDK:

attestation, err := dag.Chainloop().Init(
  ctx,
  dag.SetSecret("chainloop-token", os.Getenv("CHAINLOOP_TOKEN")),
  dagger.ChainloopInitOpts{
    WorkflowName: "build",
    ProjectName: "my-project",
    Repository: dag.CurrentModule().Source(),
    GitlabPipelineSource: os.Getenv("CI_PIPELINE_SOURCE"),
    GitlabMRIID: os.Getenv("CI_MERGE_REQUEST_IID"),
    GitlabMRTitle: os.Getenv("CI_MERGE_REQUEST_TITLE"),
    GitlabMRDescription: os.Getenv("CI_MERGE_REQUEST_DESCRIPTION"),
    GitlabMRSourceBranch: os.Getenv("CI_MERGE_REQUEST_SOURCE_BRANCH_NAME"),
    GitlabMRTargetBranch: os.Getenv("CI_MERGE_REQUEST_TARGET_BRANCH_NAME"),
    GitlabMRProjectURL: os.Getenv("CI_MERGE_REQUEST_PROJECT_URL"),
    GitlabUserLogin: os.Getenv("GITLAB_USER_LOGIN"),
  },
)
if err != nil {
  return err
}

Required Environment Variables:

  • CI_PIPELINE_SOURCE - Must be "merge_request_event"
  • CI_MERGE_REQUEST_IID - MR number
  • CI_MERGE_REQUEST_TITLE - MR title
  • CI_MERGE_REQUEST_DESCRIPTION - MR description
  • CI_MERGE_REQUEST_SOURCE_BRANCH_NAME - Source branch
  • CI_MERGE_REQUEST_TARGET_BRANCH_NAME - Target branch
  • CI_MERGE_REQUEST_PROJECT_URL - Project URL
  • GITLAB_USER_LOGIN - User login

Dagger CLI Usage (from GitHub Actions)

When calling the Dagger module directly from the Dagger CLI in a GitHub Actions workflow:

dagger call -m github.com/chainloop-dev/chainloop \
  init \
  --token env:CHAINLOOP_TOKEN \
  --workflow-name build \
  --project-name my-project \
  --github-event-file=$GITHUB_EVENT_PATH \
  --github-event-name=$GITHUB_EVENT_NAME \
  --github-head-ref=$GITHUB_HEAD_REF \
  --github-base-ref=$GITHUB_BASE_REF \
  add-raw-evidence \
  --name evidence \
  --value data \
  push \
  --key env:SIGNING_KEY

Note: All GitHub PR parameters are passed as individual flags. The event file is automatically mounted by Dagger CLI.

Dagger CLI Usage (from GitLab CI)

dagger call -m github.com/chainloop-dev/chainloop \
  init \
  --token env:CHAINLOOP_TOKEN \
  --workflow-name build \
  --project-name my-project \
  --gitlab-pipeline-source=$CI_PIPELINE_SOURCE \
  --gitlab-mriid=$CI_MERGE_REQUEST_IID \
  --gitlab-mrtitle=$CI_MERGE_REQUEST_TITLE \
  --gitlab-mrdescription=$CI_MERGE_REQUEST_DESCRIPTION \
  --gitlab-mrsource-branch=$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME \
  --gitlab-mrtarget-branch=$CI_MERGE_REQUEST_TARGET_BRANCH_NAME \
  --gitlab-mrproject-url=$CI_MERGE_REQUEST_PROJECT_URL \
  --gitlab-user-login=$GITLAB_USER_LOGIN \
  add-raw-evidence \
  --name evidence \
  --value data \
  push \
  --key env:SIGNING_KEY

Commit signature verification (from GitLab CI)

To verify the signature of the attested commit with GitLab, also pass the GitLab CI context. The GitLab commit signature API does not accept the job token, so for private and internal projects pass a personal, project or group access token with the read_api scope in --gitlab-token.

If your self-managed GitLab server uses a certificate from a private CA, also pass that CA certificate, for example --gitlab-ca=$CI_SERVER_TLS_CA_FILE when the GitLab Runner is configured with it.

dagger call -m github.com/chainloop-dev/chainloop \
  init \
  --token env:CHAINLOOP_TOKEN \
  --workflow-name build \
  --project-name my-project \
  --gitlab-ci=$GITLAB_CI \
  --gitlab-server-url=$CI_SERVER_URL \
  --gitlab-project-path=$CI_PROJECT_PATH \
  --gitlab-job-url=$CI_JOB_URL \
  --gitlab-job-token=env:CI_JOB_TOKEN \
  --gitlab-token=env:GITLAB_TOKEN \
  push

Notes

  • All PR/MR parameters are optional - if not provided, attestations work normally without PR metadata
  • PR detection will fail gracefully if required env vars are missing or invalid - the attestation will continue without PR metadata

CLI operations

This module also provides access to the underlying Chainloop CLI by exposing some of its commands

Workflow Creation

 dagger call -m github.com/chainloop-dev/chainloop \
   workflow-create --token env:CHAINLOOP_TOKEN --name [your-workflow-name] --project [its-project]

Pointing to a different Chainloop Instance

By default, this dagger module points to Chainloop's upstream instance, but if you want to point to your own instance, you can do it by running the with-instance command just right after dagger call

dagger call with-instance --controlplane-api api.cp.chainloop.dev:443 --cas-api api.cas.chainloop.dev:443 [SUBCOMMAND]

example initializing an attestation

dagger call with-instance --controlplane-api api.cp.chainloop.dev:443 --cas-api api.cas.chainloop.dev:443 \
init --token env:CHAINLOOP_TOKEN --workflow-name test-from-dagger-2 attestation-id

Please run dagger call with-instance -h for more information on the available options.

Documentation

To learn more, please visit the Chainloop project's documentation website, https://docs.chainloop.dev where you will find a getting started guide, FAQ, examples, and more.

Community / Discussion / Support

Chainloop is developed in the open and is constantly improved by our users, contributors and maintainers. Got a question, comment, or idea? Please don't hesitate to reach out via: