> ## 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.

# Content Addressable Storage (CAS) backend

As part of an attestation process, you might want to collect different pieces of evidence such as Software Bill Of Materials (SBOMs), test results, runner logs, etc and then attach them to the final in-toto attestation.

Chainloop helps with this process by providing a Content Addressable Storage API proxy that:

* **Abstracts away the underlying storage backend**. Currently, we support OCI registries as storage backends but you can expect blob storage, Artifactory and other storage backends to be supported in the future.
* Makes sure that the pieces of evidence are stored **in a tamper-proof manner**. This is achieved by storing the evidences named after their SHA256 content digest, which is calculated by the client, verified by the CAS server.
* **Enables support of large pieces of evidence** since the content digest reference is what will be stored in the attestation.

<img src="https://mintcdn.com/chainloop/qP5Aepelxm7aUyXA/concepts/img/cas-backend.png?fit=max&auto=format&n=qP5Aepelxm7aUyXA&q=85&s=c55b0d03a4ff699022b6d6358890c3fd" alt="" width="942" height="381" data-path="concepts/img/cas-backend.png" />

## Manage Storage Backends

You can setup as many CAS backends as you want, but you can only have **one enabled as default and one as fallback at the time**. This **default backend will be used** during the attestation process **to store the pieces of evidence**. The **fallback backend will be used** when the default backend fails or is unreachable,

<Info>
  Chainloop periodically validates the credentials of configured CAS backends. Only backends that are marked as **default** or **fallback** and are not the built-in `inline` backend are checked. If any of these backends start to fail authentication or access checks, Chainloop will automatically send an email notification to the organization's owners so they can rotate or fix the credentials.
</Info>

### Fallback Backend

The fallback backend provides high availability for your artifact storage. When configured:

1. Chainloop first attempts to store artifacts in the default backend
2. If the default backend is unreachable or fails validation, Chainloop automatically switches to the fallback backend
3. Your attestation process continues without interruption

<Tabs>
  <Tab title="Web UI">
    You can manage your CAS backends in the [Storage Backends Section](https://app.chainloop.dev/cas-backends).

    <img src="https://mintcdn.com/chainloop/qP5Aepelxm7aUyXA/concepts/img/cas-backends-list.png?fit=max&auto=format&n=qP5Aepelxm7aUyXA&q=85&s=c5938537dcb542cf98701da40a3db543" alt="info" width="2908" height="1452" data-path="concepts/img/cas-backends-list.png" />
  </Tab>

  <Tab title="CLI">
    In Chainloop, CAS backends can be managed with the `chainloop cas-backend` command.

    ```bash theme={"dark"}
    $ chainloop cas-backend ls
    ```

    <Accordion title="Command Output">
      ```bash theme={"dark"}
      ┌─────────────────────────────────┬──────────┬─────────────────────────────────────┬───────────────┬─────────┬──────────┐
      │ LOCATION                        │ PROVIDER │ DESCRIPTION                         │ LIMITS        │ DEFAULT │ FALLBACK │
      ├─────────────────────────────────┼──────────┼─────────────────────────────────────┼───────────────┼─────────┼──────────┤
      │                                 │ INLINE   │ Embed artifacts content in the atte │ MaxSize: 500K │ false   │ false    │
      │                                 │          │ station                             │               │         │          │
      ├─────────────────────────────────┼──────────┼─────────────────────────────────────┼───────────────┼─────────┼──────────┤
      │ ghcr.io/cyberdyne/chainloop-lab │ OCI      │                                     │ MaxSize: 100M │ true    │ false    │
      └─────────────────────────────────┴──────────┴─────────────────────────────────────┴───────────────┴─────────┴──────────┘
      ```
    </Accordion>
  </Tab>
</Tabs>

## Storage Backend providers

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

### Inline

Chainloop comes pre-configured with what we call an `inline` backend that **embeds** the pieces of evidence in the resulting attestations.

<Warning>
  Inline backend is useful to get started quickly but since the metadata is embedded in the attestation, its max size is limited.

  **We recommend to switch to a more robust backend such when moving to production.**
</Warning>

### OCI registry

#### Add a new OCI registry backend

<Tabs>
  <Tab title="Google Artifact Registry" default>
    ```bash theme={"dark"}
      # Using json-based service account
      # https://console.cloud.google.com/iam-admin/serviceaccounts

      $ chainloop cas-backend add oci \
        --name [cas-backend-name] \
        # i.e us-east1-docker.pkg.dev/my-project/chainloop-cas-devel
        --repo [region]-docker.pkg.dev/[my-project]/[my-repository] \
        --username _json_key \
        --password "$(cat service-account.json)" \
        --default
    ```
  </Tab>

  <Tab title="Azure Container Registry">
    ```bash theme={"dark"}
      # Using token-auth with push (content/read + content/write) permissions
      # https://learn.microsoft.com/en-us/azure/container-registry/container-registry-repository-scoped-permissions

      $ chainloop cas-backend add oci \
        --name [cas-backend-name] \
        # i.e chainloopcas.azurecr.io/cas
        --repo [registry-name].azurecr.io/[my-repository] \
        --username [token-name] \
        --password [token-password] \
        --default
    ```
  </Tab>

  <Tab title="GitHub packages">
    ```bash theme={"dark"}
      # Using personal access token with write:packages permissions
      # https://github.com/settings/tokens

      $ chainloop cas-backend add oci \
        --name [cas-backend-name] \
        # i.e ghcr.io/chainloop-dev/chainloop-cas
        --repo ghcr.io/[username or org]/[my-repository] \
        --username [username] \
        --password [personal access token] \
        --default
    ```
  </Tab>

  <Tab title="DockerHub" default>
    ```bash theme={"dark"}
    # Create a personal access token at
    # https://hub.docker.com/settings/security

    $ chainloop cas-backend add oci \
        --name [cas-backend-name] \
        --repo index.docker.io/[username] \
        --username [username] \
        --password [personal access token] \
        --default
    ```
  </Tab>

  <Tab title="AWS Container Registry" default>
    <Info>
      AWS Container Registry is not supported yet.
    </Info>
  </Tab>
</Tabs>

#### Rotate credentials

```bash theme={"dark"}
chainloop cas-backend update oci --name [BACKEND_NAME] --username [NEW_USERNAME] --password [NEW_PASSWORD]
```

#### Set as default

```bash theme={"dark"}
chainloop cas-backend update oci --name [BACKEND_NAME] --default=true
```

#### Set as fallback

```bash theme={"dark"}
chainloop cas-backend update oci --name [BACKEND_NAME] --fallback=true
```

### AWS S3

Chainloop also supports storing artifacts in [AWS S3 Blob Storage](https://aws.amazon.com/s3/).

#### Pre-requisites

To connect your AWS account to Chainloop you'll need:

* **S3 Bucket Name**
* **Bucket Region**
* **AccessKeyID**
* **SecretAccessKey**

**Create an S3 bucket**

Create an S3 bucket and take note of the bucket name and region

<img src="https://mintcdn.com/chainloop/qP5Aepelxm7aUyXA/concepts/img/aws-1.png?fit=max&auto=format&n=qP5Aepelxm7aUyXA&q=85&s=9081397a69403eb543eabeb78243db53" alt="" width="964" height="606" data-path="concepts/img/aws-1.png" />

**Create an IAM user with access to that bucket**

Next we are going to create a policy that has write/read permissions to the bucket.

You can use the snippet below by just replacing `[bucketName]` with the actual name of the bucket you created in the step before.

```json theme={"dark"}
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Action": [
				"s3:ListBucket"
			],
			"Resource": "arn:aws:s3:::[bucketName]"
		},
		{
			"Effect": "Allow",
			"Action": [
				"s3:GetObject",
				"s3:PutObject"
			],
			"Resource": "arn:aws:s3:::[bucketName]/*"
		}
	]
}
```

Then create an user, attach the policy to it and click on "create access Key"

<img src="https://mintcdn.com/chainloop/qP5Aepelxm7aUyXA/concepts/img/aws-2.png?fit=max&auto=format&n=qP5Aepelxm7aUyXA&q=85&s=bca69901004adb215610089fdc115168" alt="" width="1098" height="688" data-path="concepts/img/aws-2.png" />

Then select third-party service and copy the access key ID and secret access key

We are now ready to connect our AWS account to Chainloop

```bash theme={"dark"}
 $ chainloop cas-backend add aws-s3 \
    --name [BACKEND_NAME] \
    --access-key-id [accessKeyID] \
    --secret-access-key [secretAccessKey] \
    --region [region] \
    --bucket [bucketName]
```

#### Rotate credentials

```bash theme={"dark"}
chainloop cas-backend update aws-s3 --name [BACKEND_NAME] --access-key-id [new-accessKeyID] --secret-access-key [new-secretAccessKey] --region [new-region]
```

### Azure Blob Storage

Chainloop also supports storing artifacts in [Azure Blob Storage](https://azure.microsoft.com/en-us/products/storage/blobs).

#### Pre-requisites

To connect your Azure storage account you'll need the following information

* **Active Directory Tenant ID**
* **Service Principal ID**
* **Service Principal Secret**
* **Storage account name**

We'll walk you through the process of how to find this information

**Register an application to create the service principal**

First, you'll need to register an application in your Azure Active Directory tenant. You can do this using the Azure CLI or from the Azure portal

<img src="https://mintcdn.com/chainloop/qP5Aepelxm7aUyXA/concepts/img/azure-1.png?fit=max&auto=format&n=qP5Aepelxm7aUyXA&q=85&s=de8c54a45062c2ef2e08394bb8a8a428" alt="" width="894" height="286" data-path="concepts/img/azure-1.png" />

Once done, in the application overview you should be able to find the tenantID, and Service principal ID

<img src="https://mintcdn.com/chainloop/qP5Aepelxm7aUyXA/concepts/img/azure-3.png?fit=max&auto=format&n=qP5Aepelxm7aUyXA&q=85&s=7d4207d0a67b1f5617e0e41fb10766d7" alt="" width="1044" height="373" data-path="concepts/img/azure-3.png" />

Next, let's create a secret for the service principal

<img src="https://mintcdn.com/chainloop/qP5Aepelxm7aUyXA/concepts/img/azure-2.png?fit=max&auto=format&n=qP5Aepelxm7aUyXA&q=85&s=0aef08a124f42a0db9b9e35d063bb186" alt="" width="1307" height="514" data-path="concepts/img/azure-2.png" />

**Create a storage account and give permissions to the service principal**

Next, we'll create a storage account (or you can use an existing one), take a note on the storage account name.

<img src="https://mintcdn.com/chainloop/qP5Aepelxm7aUyXA/concepts/img/azure-4.png?fit=max&auto=format&n=qP5Aepelxm7aUyXA&q=85&s=2190d9803a87fc0a924425fe491a2139" alt="" width="883" height="176" data-path="concepts/img/azure-4.png" />

And once created, we'll give permissions to the service principal, go to IAM assign-roles.

<img src="https://mintcdn.com/chainloop/qP5Aepelxm7aUyXA/concepts/img/azure-5.png?fit=max&auto=format&n=qP5Aepelxm7aUyXA&q=85&s=9dac6c99c00cc60417ae753b7c357a89" alt="" width="973" height="290" data-path="concepts/img/azure-5.png" />

Search for the application we just registered and assign the Storage Blob Data Contributor role

<img src="https://mintcdn.com/chainloop/qP5Aepelxm7aUyXA/concepts/img/azure-6.png?fit=max&auto=format&n=qP5Aepelxm7aUyXA&q=85&s=0faf6feeeac929a359d166484af99c62" alt="" width="864" height="96" data-path="concepts/img/azure-6.png" />

At thi point we have all the information we need to connect our Azure storage account to Chainloop

```bash theme={"dark"}
 $ chainloop cas-backend add azure-blob \
    --name [BACKEND_NAME] \
    --client-id [servicePrincipalID] \
    --client-secret [servicePrincipalSecret] \
    --tenant [Active directory tenant] \
    --storage-account [Storage Account name] \
    --container [optional Storage account container] \
    --endpoint [optional custom endpoint suffix, e.g., blob.core.usgovcloudapi.net]
```

#### Rotate credentials

```bash theme={"dark"}
chainloop cas-backend update azure-blob --name [BACKEND_NAME] --client-id [new-clientID] --client-secret [new secret] --tenant [updated tenant]
```

### Cloudflare R2

[Cloudflare R2](https://www.cloudflare.com/developer-platform/r2/) is compatible with AWS S3 and can be configured in Chainloop by providing a custom endpoint.

Pre-requisites

* **AccessKeyID**
* **SecretAccessKey**
* **Bucket Name**
* **Endpoint**

Follow [this instructions](https://developers.cloudflare.com/r2/api/s3/tokens/) to create a compatible AccessKeyID and SecretAccessKey. Then copy the bucket name and endpoint from the bucket settings.

<img src="https://mintcdn.com/chainloop/qP5Aepelxm7aUyXA/concepts/img/cloudflare.png?fit=max&auto=format&n=qP5Aepelxm7aUyXA&q=85&s=d86237004e3b8f2b24d5334f584cd811" alt="" width="893" height="566" data-path="concepts/img/cloudflare.png" />

Finally register the Cloudflare R2 bucket using the `aws-s3` provider and providing the custom endpoint.

```bash theme={"dark"}
 $ chainloop cas-backend add aws-s3 \
    --name [BACKEND_NAME] \
    --access-key-id [accessKeyID] \
    --secret-access-key [secretAccessKey] \
    --bucket [bucketName] \
    --endpoint [endpoint] # provide the custom endpoint
```

### Minio

[Minio](https://min.io/) is an S3-compatible blob storage that can be configured in Chainloop by providing a custom endpoint.

Pre-requisites

* **AccessKeyID**
* **SecretAccessKey**
* **Bucket Name**
* **Minio Endpoint**

You can create a new AccessKey from the Minio console.

<img src="https://mintcdn.com/chainloop/qP5Aepelxm7aUyXA/concepts/img/minio-1.png?fit=max&auto=format&n=qP5Aepelxm7aUyXA&q=85&s=ce0001ccd90715e16610472a2076f865" alt="" width="1037" height="492" data-path="concepts/img/minio-1.png" />

Then copy the bucket name and Minio endpoint.

<img src="https://mintcdn.com/chainloop/qP5Aepelxm7aUyXA/concepts/img/minio-2.png?fit=max&auto=format&n=qP5Aepelxm7aUyXA&q=85&s=d4ced6fbb61e70935b57bcb0abb52971" alt="" width="1061" height="425" data-path="concepts/img/minio-2.png" />

Finally register the Minio bucket using the `aws-s3` provider and providing the custom endpoint

```bash theme={"dark"}
 $ chainloop cas-backend add aws-s3 \
    --name [BACKEND_NAME] \
    --access-key-id [accessKeyID] \
    --secret-access-key [secretAccessKey] \
    --bucket [bucketName] \
    --endpoint [endpoint] # provide the custom endpoint
```

## Give it a try

If everything went well, you should be able to upload and download artifact materials, let's give it a try

```bash Upload a file to your OCI repository theme={"dark"}
$ chainloop artifact upload -f myfile
myfile@sha256:c5cc0a2c712497c29f29c3ba11e7fcc0c3cc725ab591720db595e5d6469f3f37 ... done! [1.03KB in 0s; 5.48KB/s]
```

```bash Download by content digest (sha256) theme={"dark"}
$ chainloop artifact download -d sha256:c5cc0a2c712497c29f29c3ba11e7fcc0c3cc725ab591720db595e5d6469f3f37
INF downloading file name=myfile to=/tmp/myfile
INF file downloaded! path=/tmp/myfile
```
