Overview

An attestation is a signed and verifiable unit of data sent to Chainloop. Users and CI systems use the Chainloop CLI to “craft” attestations, add pieces of evidence (materials) to them, and “push” them to Chainloop service (the evidence store).

Attestations are performed with the Chainloop command line interface (CLI) and its lifecycle has the following stages: init, add, push or reset.

As you can see, it mimics the workflow of a commonly used version control tool, and this is not by coincidence. Chainloop wants to make sure that the tooling feels familiar to developers and that no security jargon leaks into this stage of the process. For a developer, creating an attestation must be as simple as initializing it, adding pieces of evidence (materials) to it, and pushing it.

Attestation lifecycle

# initialize the attestation. Downloading the contract
> chainloop att init --workflow workflow-name --project project-name

# add materials to the attestation
> chainloop att add --value ghcr.io/chainloop-dev/chainloop/control-plane:latest
> chainloop att add --value reports/cyclonedx.json --kind SBOM_CYCLONEDX_JSON

# generate, sign and push the attestation to chainloop
> chainloop att push

attestation init

A project version and optionally a version can be provided during initialization. You can read more here.

During this stage, the crafting tool will contact chainloop control plane to

  • Signal the intent of starting an attestation.
  • Retrieve or create the associated workflow contract
  • If the contract has a specified runner context type, check that we are compliant with it.
  • Initialize environment variables, explicitly stated in the contract and other contextual information.

attestation add

Add the materials required by the contract and any other additional (see contractless materials pieces of evidence, i.e artifact, OCI image ref, SBOM.

The add command knows how to handle each kind of material transparently to the user.

For example

  • ARTIFACT kinds will be uploaded to your artifact registry and referenced by their content digest.
  • CONTAINER_IMAGE kinds will be resolved to obtain their repository digests using the local authentication keychain.
  • SBOM_CYCLONEDX_JSON will validate the right SBOM format and upload it to the artifact registry.

For a complete list of available material types, see the reference section.

attestation push

This stage will take the current crafting state, validate that it has all the required materials and

  • Create a signed, attestation envelope.
  • Push it to the control plane for storage

You can leverage multiple signing mechanisms, including keyless signing and verification. For a complete list please refer to the signing guide.

attestation reset

By using the reset command we can indicate to the control plane that something went wrong or we want to abort the attestation process.

attestation status

See the state of the current crafting process.

Attestation format

The generated attestations are in the form of a Sigstore bundle signed with the signing mechanism of your choice.

This bundle contains the attestation in the form of a DSSE envelope, and the verification materials required to verify the attestation. These include intermediate certificates, in the case of ephemeral certificates or timestamp services.

Example

{
  "mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.3",
  "verificationMaterial": {
    "certificate": {
      "rawBytes": "MIIB5DCCAWqgAwIBAgIUXMnEd59cEi+F8IwW5xPEZowFKGgwCgYIKoZIzj0EAwMwLDEWMBQGA1UECgwNY2hhaW5sb29wLmRldjESMBAGA1UEAwwJY2hhaW5sb29wMB4XDTI1MDMyNDExNTMzOFoXDTI1MDMyNDEyMDMzOFowLzEtMCsGA1UEChMkOTNkMDIyNzUtODUzYy00YWQ2LTlkNjAtOGY1NjJiMTIzZmQyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEelmK4JQi5e/4Uv4DFi/IszhxhZUMtMhS5pMlt1M/y5V9JBNXllQ7lFCO0UZshUP0E8pLU2lb62dHUoaCen6qiKNnMGUwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQWBBTDS8wdOFGSi79ymJWQy3p9irm6szAfBgNVHSMEGDAWgBTmzi6ht2KmAQFfrGvKtZDRGyR0hzAKBggqhkjOPQQDAwNoADBlAjBEEgeDUR69LF/bMPEQ8pT0h/kd6r8FfhXZS7l04PVUxGHLLQfVSdmFoG6CIKUV18cCMQDpJBZw9ARQk29XKXzg/D8K96RG4MF0vS+F2K/+RmtsRDuSSwVugQFwiQ5cK0Jkvds="
    },
    "timestampVerificationData": {
      "rfc3161Timestamps": [
        {
          "signedTimestamp": "MIIEIzADAgEAMIIEGgYJKoZIhvcNAQcCoIIECzCCBAcCAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG9w0BCRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIHIr+U3/ec9GMyj/uc5SV8SYq76EGyficbNlHb5Mfr7gAhACdzgFINJaQks5u3Cw+hDFGA8yMDI1MDMyNDExNTMzOFoxggN2MIIDcgIBATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0ECEAuuZrxaun+Vh8b56QTjMwQwDQYJYIZIAWUDBAIBBQCggdEwGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTAzMjQxMTUzMzhaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFNvThe5i29I+e+T2cUhQhyTVhltFMC8GCSqGSIb3DQEJBDEiBCCA8prjLb9KaHEJLWT0JjlVNEjGC5rKAWM++pSkO5Co3DA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCB2dp+o8mMvH0MLOiMwrtZWdf7Xc9sF1mW5BZOYQ4+a2zANBgkqhkiG9w0BAQEFAASCAgCWm6hXI4Bz3i1lLEs4LQaZdG+Znq51CnrmvWK/Pmh9YNAGg9zmL7tOIJDe/0x1uGXIvSugHmeb3lxCn5xVayh0gfcl3TvaT0MT2b/FgJZ7LAcu4onnpmASRT/36imQNO0zkrsfwanhtmTvSlAnmiDBqpypf2MvHkuYIxZEaGj8czsc7iJaFQbnc2yHNjwXYej7Q4R/TsRFZ76BY5QjuRnFXEs4ay/9wHONpfe3mqkW5xFRSg80owcQil84xPoy/vrHq+eQUXKSOwg1oUrxrmQQbj9h99I13df7FjK8TStdjIJYFF9iRCo27jceczL2lmcwHjxbTu209DlRyrkrFqoAGT2ayfRnVhLgUiuuCtQjhtrt2bZYzqqOC6vU1yuqXvNz7O1bv/KxOy3jWOPAUDKF+ScxJX9hlx60PhtetEl9/WGhSdynzRkvA5x7HnzeQ5zrv7cfXcOx5IxxKJxznVCPbpRPbNyLbHU2N0OwIg3Act4ZkCtsOmQwTTbz+zDsoQN/ZiKiRtnWBwSS3nCwAQeadHpy2qa/Px2arYK3KbMskyk8WvrX7vtfA20Qhp/P6gaG29nHCUEm8qhUR/MXP5hrPZPXliv5S099UZxhH1isSM977dnYOKFt5SlBh1FV72pEP0wmenQrPl47vLYNIjr/Jt64v5Y9rzZGoe/hKltugQ=="
        }
      ]
    }
  },
  "dsseEnvelope": {
    "payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoiY2hhaW5sb29wLndvcmtmbG93LmNvZGVxbCIsImRpZ2VzdCI6eyJzaGEyNTYiOiIxY2NkNjMyM2I3ZGZiODU5YTU1NDMzNGY4MmU2ZWUwYzY0NTVkOGVjZjQ2NzYwODNiNmYwODUxOTI3Y2UzNTg0In19LHsibmFtZSI6ImdpdC5oZWFkIiwiZGlnZXN0Ijp7InNoYTEiOiIwY2M0YjAyYWI4OWM3MDA2Y2I1NTVlYmU5YmFlZjE4OGJhNTc0NzE1In0sImFubm90YXRpb25zIjp7ImF1dGhvci5lbWFpbCI6IjQ5Njk5MzMzK2RlcGVuZGFib3RbYm90XUB1c2Vycy5ub3JlcGx5LmdpdGh1Yi5jb20iLCJhdXRob3IubmFtZSI6ImRlcGVuZGFib3RbYm90XSIsImRhdGUiOiIyMDI1LTAzLTI0VDExOjQ5OjE2WiIsIm1lc3NhZ2UiOiJjaG9yZShkZXBzKTogQnVtcCBnaXRodWIuY29tL2dvbGFuZy1qd3Qvand0L3Y0IGZyb20gNC41LjEgdG8gNC41LjIgKCMxOTEyKVxuXG5TaWduZWQtb2ZmLWJ5OiBkZXBlbmRhYm90W2JvdF0gPHN1cHBvcnRAZ2l0aHViLmNvbT5cbkNvLWF1dGhvcmVkLWJ5OiBkZXBlbmRhYm90W2JvdF0gPDQ5Njk5MzMzK2RlcGVuZGFib3RbYm90XUB1c2Vycy5ub3JlcGx5LmdpdGh1Yi5jb20+IiwicmVtb3RlcyI6W3sibmFtZSI6Im9yaWdpbiIsInVybCI6Imh0dHBzOi8vZ2l0aHViLmNvbS9jaGFpbmxvb3AtZGV2L2NoYWlubG9vcCJ9XSwic2lnbmF0dXJlIjoiLS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS1cblxud3NGY0JBQUJDQUFRQlFKbjRVYThDUkMxYVE3dXU1VWhsQUFBdGlrUUFEN2poZnUxTllERmFkYzRmd3JHR2dvNVxuK1FjUy9GZ1puOFZ5a1lhYTNJWjFQaFFIZTJUMTAxZHBHd08vNC83OFVwdkdkUkRtdFczZHpyL1IyUWZjY2lHWVxuaUdYUFluZ3FBUWtUQkZWbnpPdVdNa3lWSjgrTFdtbTgxM2g4Yk1XRG13eFovSkVWWkNUbFpjRnBoZVd5NVluSlxuVnlDVEcyT3BHTlRZR3VZRUNYanZmSDFFaWNSaXYrSDdUVkJSYzZRM0tFUWZ0d2hOelI5Z2t6Nzl0dWg0Wld5VVxuTmRvalBhd0VhcXJqVUR0L1hiQlhPRzVGSFZNYW0wTXVnYXJrdHBySm1rZnMvZldwL2pkMnp5SDcvRzNkWnFVZVxuTnlMMWV6dTRvVkRKOWorWVJsdFpscnlpdnVxVDdFU3hNY2hDQWV6TGh6dHpWcUFvdVE1OW5JNHBISjJBWERVQlxuQUU4aTRLcUxjc2JVeW5yUjFWNDhBSGpuOWZYd0l3WjlsUzh4elcybHBBcmpyeGlNb0JPSGtqUDVoUnZHVjhHRlxuM3JpTWlPZUc3d3NPakUwcHFCRUhoRm12T3JEK0VIT3RnRkFKZ05ZbkZYdTFrUDRCMms0V2FjUDhtWG5qWGVmZ1xudFNsTHZxVEh2UUpWbm9EMUwxVUZZYUZzQjR0MnpPQjU5bUd3MVJSK3lHcUNLMEdMYmt2Y0o1RU14UzdzWVJaZ1xuTHI2dzd2VGVCbGV6SCtSTjUwUWJWaFBxakRNUG1WejhCeXVUY2VlbldFbk1aN0FHa2p5WFZFS21TcFpUN2l2OFxuZ09nanVQL0dvT3ZjQS9STVpzN0dPaGVBRGhONlY2V29TcmZnODl6UFkzTXZSdFhwNlg2b3BUL1N0RDdTZ21sRVxuZVg4QVkrVE1EVHJrNFp6SW55Z1Fcbj0zYXBUXG4tLS0tLUVORCBQR1AgU0lHTkFUVVJFLS0tLS1cblxuIn19LHsibmFtZSI6ImdvLnNhcmlmIiwiZGlnZXN0Ijp7InNoYTI1NiI6ImVjYTM3YjIyMDMxNDY4NGYxZmQ0OWQxMTJmNWFmY2ZhNzM2N2Y2ZDQwZGUyMGE3ZTFiNmRmODBhMDA1MGQ5NTAifSwiYW5ub3RhdGlvbnMiOnsiY2hhaW5sb29wLm1hdGVyaWFsLmNhcyI6dHJ1ZSwiY2hhaW5sb29wLm1hdGVyaWFsLm5hbWUiOiJzYXJpZi1yZXN1bHRzIiwiY2hhaW5sb29wLm1hdGVyaWFsLnR5cGUiOiJTQVJJRiJ9fV0sInByZWRpY2F0ZVR5cGUiOiJjaGFpbmxvb3AuZGV2L2F0dGVzdGF0aW9uL3YwLjIiLCJwcmVkaWNhdGUiOnsiYnVpbGRUeXBlIjoiY2hhaW5sb29wLmRldi93b3JrZmxvd3J1bi92MC4xIiwiYnVpbGRlciI6eyJpZCI6ImNoYWlubG9vcC5kZXYvY2xpLzAuMTgzLjBAc2hhMjU2OmVmNjIzZjY3ZTI3NDRjODFlMDc3NTNhMzdiOGM2NThhZDI2NGZlMTIwNWJmMzVjMTVlZTcxYzY4MTdkZmJkZjkifSwiZW52Ijp7IkdJVEhVQl9BQ1RPUiI6Im1pZ21hcnRyaSIsIkdJVEhVQl9SRUYiOiJyZWZzL2hlYWRzL21haW4iLCJHSVRIVUJfUkVQT1NJVE9SWSI6ImNoYWlubG9vcC1kZXYvY2hhaW5sb29wIiwiR0lUSFVCX1JFUE9TSVRPUllfT1dORVIiOiJjaGFpbmxvb3AtZGV2IiwiR0lUSFVCX1JVTl9JRCI6IjE0MDM0NTAxMTI1IiwiR0lUSFVCX1NIQSI6IjBjYzRiMDJhYjg5YzcwMDZjYjU1NWViZTliYWVmMTg4YmE1NzQ3MTUiLCJSVU5ORVJfTkFNRSI6IkdpdEh1YiBBY3Rpb25zIDU3IiwiUlVOTkVSX09TIjoiTGludXgifSwibWF0ZXJpYWxzIjpbeyJhbm5vdGF0aW9ucyI6eyJjaGFpbmxvb3AubWF0ZXJpYWwuY2FzIjp0cnVlLCJjaGFpbmxvb3AubWF0ZXJpYWwubmFtZSI6InNhcmlmLXJlc3VsdHMiLCJjaGFpbmxvb3AubWF0ZXJpYWwudHlwZSI6IlNBUklGIn0sImRpZ2VzdCI6eyJzaGEyNTYiOiJlY2EzN2IyMjAzMTQ2ODRmMWZkNDlkMTEyZjVhZmNmYTczNjdmNmQ0MGRlMjBhN2UxYjZkZjgwYTAwNTBkOTUwIn0sIm5hbWUiOiJnby5zYXJpZiJ9XSwibWV0YWRhdGEiOnsiY29udHJhY3ROYW1lIjoiY2hhaW5sb29wLXZhdWx0LWNvZGVxbCIsImNvbnRyYWN0VmVyc2lvbiI6IjEwIiwiZmluaXNoZWRBdCI6IjIwMjUtMDMtMjRUMTE6NTM6MzguNDM0NDIwOTY0WiIsImluaXRpYWxpemVkQXQiOiIyMDI1LTAzLTI0VDExOjQ5OjQyLjc1Mzc0MjY4N1oiLCJuYW1lIjoiY29kZXFsIiwib3JnYW5pemF0aW9uIjoicmVhZC1vbmx5LWRlbW8iLCJwcm9qZWN0IjoiY2hhaW5sb29wIiwicHJvamVjdFZlcnNpb24iOiJ2MC4xODQuMCIsInByb2plY3RWZXJzaW9uUHJlcmVsZWFzZSI6dHJ1ZSwidGVhbSI6ImNvcmUiLCJ3b3JrZmxvd0lEIjoiMjIzODk3ZTUtMTRiYi00ZDJjLWFmMDktZjU1Mzg4MzBjN2RiIiwid29ya2Zsb3dSdW5JRCI6IjE3MDI0ZWZkLTY1YWQtNDdkZC05NjE2LTVjYzA5ZTY2YjU2YSJ9LCJwb2xpY3lBdHRCbG9ja2VkIjpmYWxzZSwicG9saWN5QmxvY2tCeXBhc3NFbmFibGVkIjpmYWxzZSwicG9saWN5Q2hlY2tCbG9ja2luZ1N0cmF0ZWd5IjoiRU5GT1JDRUQiLCJwb2xpY3lFdmFsdWF0aW9ucyI6eyJDSEFJTkxPT1AuQVRURVNUQVRJT04iOlt7ImRlc2NyaXB0aW9uIjoiVmVyaWZpZXMgdGhhdCB0aGUgYXR0ZXN0YXRpb24gZXhwbGljaXRseSByZWZlcmVuY2VzIGEgc3BlY2lmaWMgR2l0IGNvbW1pdCIsIm5hbWUiOiJzb3VyY2UtY29tbWl0IiwicG9saWN5UmVmZXJlbmNlIjp7ImFubm90YXRpb25zIjp7Im5hbWUiOiJzb3VyY2UtY29tbWl0Iiwib3JnYW5pemF0aW9uIjoiIn0sImRpZ2VzdCI6eyJzaGEyNTYiOiIxNjRiYzMyMmJmMzUwZDZiYjQ1NzY4Yzg2ZGIxODBmNTMxNTA4NWM3OTFhZGViYWYwOTQ5MGY5Y2NlOTk1MjhjIn0sIm5hbWUiOiJzb3VyY2UtY29tbWl0IiwidXJpIjoiY2hhaW5sb29wOi8vY2hhaW5sb29wLXBsYXRmb3JtLWJhY2tlbmQucGxhdGZvcm0tcHJvZC5zdmMuY2x1c3Rlci5sb2NhbC9zb3VyY2UtY29tbWl0In0sInJlcXVpcmVtZW50cyI6WyJjaGFpbmxvb3AtYmVzdC1wcmFjdGljZXMvY29tbWl0LXNpZ25lZCJdLCJza2lwcGVkIjpmYWxzZSwidHlwZSI6IkFUVEVTVEFUSU9OIiwid2l0aCI6eyJjaGVja19zaWduYXR1cmUiOiJ5ZXMifX1dfSwicG9saWN5SGFzVmlvbGF0aW9ucyI6ZmFsc2UsInJ1bm5lclR5cGUiOiJHSVRIVUJfQUNUSU9OIiwicnVubmVyVVJMIjoiaHR0cHM6Ly9naXRodWIuY29tL2NoYWlubG9vcC1kZXYvY2hhaW5sb29wL2FjdGlvbnMvcnVucy8xNDAzNDUwMTEyNSJ9fQ==",
    "payloadType": "application/vnd.in-toto+json",
    "signatures": [
      {
        "sig": "MEQCIDsOnb1jq4cLgNwt5mIhskW6d0M1w0IZDqRsGiBA6FKbAiB4+KGLIDHFmajclbqNbUfNH465mSAljMsdJ9qTGN6I6Q=="
      }
    ]
  }
}

You can retrieve the attestation in multiple formats by inspecting its associated workflow run

Your first attestation

To perform your first attestation locally, please refer to the getting started guide.

CI integration

Integrating the attestation process usually requires:

Native CI/CD runner integrations (e.g., Jenkins plugin, GitHub action) are under development. In the meantime, please use the chainloop CLI as shown in these examples

# Example of GitHub action that 
# - Builds a go binary and associated container image using go-releaser
# - Extract a CycloneDX SBOM using Syft
# - Stores the required materials stated in this Chainloop contract
#   https://github.com/chainloop-dev/chainloop/blob/main/docs/examples/contracts/skynet/contract.yaml
# - Pushes the resulting attestation to the control plane
name: Release
on:
  push:
    tags:
      - "v*.*.*"
jobs:
  release:
    env:
      # Version of Chainloop to install
      CHAINLOOP_TOKEN: ${{ secrets.CHAINLOOP_WF_RELEASE }}
      # The name of the workflow registered in Chainloop control plane
      CHAINLOOP_WORKFLOW_NAME: build-and-test
      CHAINLOOP_PROJECT_NAME: skynet
    name: "Release binary and container images"
    runs-on: ubuntu-latest
    permissions:
      contents: write # required for goreleaser
    steps:
      - name: Install Chainloop
        run: |
          curl -sfL https://dl.chainloop.dev/cli/install.sh | bash -s 

      - name: Checkout
        uses: actions/checkout@v4

      - name: Initialize Attestation
        run: |
          chainloop attestation init --workflow $CHAINLOOP_WORKFLOW_NAME --project $CHAINLOOP_PROJECT_NAME

      - name: Set up Go
        uses: actions/setup-go@v3
        with:
          go-version: "1.23.6"

      # Generate SBOM using syft in cycloneDX format
      - uses: anchore/sbom-action@v0
        with:
          image: ****.dkr.ecr.us-east-1.amazonaws.com/container-image:${{ github.ref_name }}
          format: cyclonedx-json
          output-file: /tmp/skynet.cyclonedx.json

      - name: Add Attestation Artifacts
        run: |
          # Add binaries created by goreleaser
          chainloop attestation add --name [binary-name] --value [binary-path]

          # Created container image
          chainloop attestation add --name control-plane-image --value ****.dkr.ecr.us-east-1.amazonaws.com/container-image:${{ github.ref_name }}

          # This is just an example of adding a key/val material type
          # Alternatively, GITHUB_SHA could have been added to the contract env variables allowList
          chainloop attestation add --name build-ref --value ${GITHUB_SHA}

          # Attach SBOM
          chainloop attestation add --name skynet-sbom --value /tmp/skynet.cyclonedx.json

      - name: Finish and Record Attestation
        if: ${{ success() }}
        run: |
          # Note that these commands are using CHAINLOOP_TOKEN env variable to authenticate
          chainloop attestation push

      - name: Mark attestation as failed
        if: ${{ failure() }}
        run: |
          chainloop attestation reset

      - name: Mark attestation as cancelled
        if: ${{ cancelled() }}
        run: |
          chainloop attestation reset --trigger cancellation

Advanced features

The basics of the attestation process is described in our getting started guide. This guide, instead will focus in some advance features guide

Contract-less, auto-discoverable materials

Security and Compliance teams can define requirements on what must be included in the attestation through contracts.

Alternatively, and in addition to the materials defined on the contract, you can add as many contract-less materials as you like by providing its value.

The CLI will automatically discover the kind of material you are adding, but you can also specify it by providing the --kind flag.

For example

# Add a contract-less material by providing a name, value and kind
chainloop att add --name not-in-contract --value ./cyberdyne.cyclonedx.sbom --kind SBOM_CYCLONEDX_JSON
# If you do not provide the --kind, the CLI will automatically discover it
chainloop att add --name not-in-contract --value  --value ./cyberdyne.cyclonedx.sbom
# and if you do not provide the name, the CLI will generate one for you
chainloop att add --value ./cyberdyne.cyclonedx.sbom

Contract-less and auto-discovery and two features that walk hand by hand. They compose a new way of adding pieces of evidences to an existing contract in a frictionless way. You can see it in action in our quickstart guide.

Remote State

By default, the attestation process state is stored locally. But this setup is not suitable when running a multi-step attestation process in a stateless environment, like our Dagger module, or when you want to leverage CI multi-job parallelism or similar.

For that, we implemented attestation remote state. Simply put, instead of the attestation CLI being in charge of maintaining the state during the attestation, this can be delegated to the server and retrieved at any time by providing an “attestation-id.”

# You can enable the feature by providing the --remote-state flag
# and it will return an attestation-id
chainloop attestation init --name my-workflow --project my-project --remote-state

# Then you can add any piece of evidence by providing the attestation-id
chainloop attestation add --value cyberdyne.cyclonedx.sbom --attestation-id deadbeef

# And finally craft the attestation, sign-it and push it
chainloop attestation push --attestation-id deadbeef

With this optional feature, as long as you have the attestation-id, you can add any piece of evidence to the attestation from anywhere.