Skip to main content
View Markdown ↗

Copy this page

Select and copy the Markdown below, then paste it into your LLM.

On this page

Store attestations in Archivista

Archivista is the searchable evidence store CI/lock integrates with by default. This guide covers how to configure CI/lock to push signed attestations to it, and how to retrieve them later.

When to use Archivista

Workflow artifacts (GitHub Actions / GitLab job artifacts) are fine for the first few weeks of adoption. Move to Archivista when you need:

  • Cross-team verification: a release-gate workflow that fetches evidence about an artifact built last quarter, without rerunning CI.
  • Searchable history: query by subject digest, predicate type, identity, or time window.
  • Long retention: outlive your CI platform's artifact lifetime (typically 30–90 days).

The TestifySec Platform evidence store (default)

The TestifySec Platform runs Archivista at <platform-url>/archivista, so https://platform.testifysec.com/archivista for the hosted platform. cilock run derives that address and uploads to it by default whenever it has a platform identity that may upload. Each identity uploads with its own credential, never with its signing token:

IdentityUploads by default?Credential
Enrolled agentyes; the run fails if the platform issues no upload tokenthe agent's own upload token
cilock login sessionyesthe session
GitHub Actions job (id-token: write, repository connected to your tenant)yesa job token for audience <platform-url>/archivista, which CI/lock mints and refreshes itself
GitLab.com jobonly if the job declares an ID token with audience <platform-url>/archivista (for example CILOCK_ARCHIVISTA_ID_TOKEN)that token
Buildkite or CircleCI jobno; it signs only, unless the worker has cilock loginthe session

The three audiences stay separate: Fulcio signing uses sigstore, cilock login uses <platform-url>/login, and upload uses <platform-url>/archivista.

In an authorized GitHub Actions job, the whole recipe is:

cilock run --step build -- go build ./cmd/myapp

It signs keyless as the workflow, timestamps against the platform TSA, and uploads. If the platform refuses the upload, the run fails with upload to <platform> rejected ... the attestation was signed but not stored, and keeps the signed envelope on disk. To keep the evidence only as a file, pass --enable-archivista=false; an explicit value always wins. For a self-hosted platform, add --platform-url with your platform's URL.

Self-hosted Archivista

CI/lock also uploads to a standalone Archivista you run yourself:

ModelWhen to pick it
Static API keySelf-hosted Archivista with a long-lived service account; quick setup.
OIDC (audience-bound)CI workflows with a short-lived OIDC token; no static credentials in the pipeline.

API key (raw CLI)

--offline keeps the TestifySec Platform's TSA and upload out of this run, so the evidence goes only where you point it.

cilock run --step build \
  --offline \
  --enable-archivista \
  --archivista-server "$ARCHIVISTA_URL" \
  --archivista-headers "Authorization: $ARCHIVISTA_API_KEY" \
  --signer-file-key-path ./signing.key \
  --outfile attestation.json \
  -- go build ./cmd/myapp

The --archivista-headers flag accepts arbitrary HTTP headers in Header: value form, which is how the API key gets forwarded.

The GitHub Action

When using aflock-ai/cilock-action, Archivista upload uses OIDC by default, no static API key in the workflow:

- name: build
  uses: aflock-ai/cilock-action@d39bb9b17e392520f9e7eef17cf355c027a5c283 # v1.0.4
  with:
    step: build
    command: go build ./cmd/myapp
    attestations: environment git github sbom
    platform-url: ${{ env.PLATFORM_URL }}   # archivista URL derived from this
    # enable-archivista: true is the default

For the raw CLI in a GitHub Actions step, no token step is needed: use the recipe above.

Useful flags

FlagPurpose
--enable-archivistaEnable the Archivista sink. Turned on by default when CI/lock has a platform identity that may upload (table above); --enable-archivista=false always wins.
--archivista-server <url>Archivista server URL. Derived from --platform-url if omitted.
--archivista-headers <h>Add an HTTP header (e.g. Authorization: <token>). Repeatable.
--archivista-oidcUse OIDC auth instead of headers. On by default in GitHub Actions.
--archivista-audience <aud>Audience claim for the OIDC token. For the platform it is derived as <platform-url>/archivista; for another server it is that server's URL.

All verified from rookery/cilock/internal/options/run.go.

Verifying the upload

Each successful upload returns a GitOID that uniquely identifies the stored attestation. The cilock-action exposes it as a step output:

- name: build
  id: build
  uses: aflock-ai/cilock-action@d39bb9b17e392520f9e7eef17cf355c027a5c283 # v1.0.4
  with:
    step: build
    command: go build ./cmd/myapp
    platform-url: ${{ env.PLATFORM_URL }}

- name: Print evidence GitOID
  run: echo "Stored at ${{ steps.build.outputs.git_oid }}"

For raw CLI: the Stored in archivista as sha256:... line appears in CI/lock's stderr at upload time.

Querying stored evidence

Archivista exposes a GraphQL API, an HTTP API, and a CLI (archivistactl).

Option A: archivistactl (upstream CLI)

The fastest way for ad-hoc queries. Source: in-toto/archivista/cmd/Readme.md.

# Store
$ archivistactl store build.attestation.json
build.attestation.json stored with gitoid 4462a729...

# Search by subject digest (algorithm:value)
$ archivistactl search sha256:423da4cff198bbffbe3220ed9510d32ba96698e4b1f654552521d1f541abb6dc
Gitoid: 4462a729...
Collection name: build
Attestations: https://witness.dev/attestations/git/v0.1, .../environment/v0.1, ...

# Retrieve subjects for a stored attestation
$ archivistactl retrieve subjects 4462a729...

# Retrieve the full DSSE envelope
$ archivistactl retrieve envelope 4462a729...

archivistactl ships separately from CI/lock and is the canonical reader-side tool.

Option B: GraphQL (programmatic)

The schema's top-level fields for evidence queries are dsses(where: ...) and subjects(where: ...). Both are GraphQL connections: each call returns one page of edges and a pageInfo. A digest lookup that reads every match asks for pageInfo and repeats the query with after set to endCursor while hasNextPage is true, which is how the Go client's searches page (attestation/archivista/client.go):

query($algo: String!, $digest: String!, $after: Cursor) {
  dsses(
    first: 100,
    after: $after,
    where: {
      hasStatementWith: {
        hasSubjectsWith: {
          hasSubjectDigestsWith: { value: $digest, algorithm: $algo }
        }
      }
    }
  ) {
    edges {
      node {
        gitoidSha256
        statement {
          attestationCollections {
            name
            attestations { type }
          }
        }
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

Variables: {"algo": "sha256", "digest": "1a2b3c...", "after": null} for the first page, then "after" set to the previous page's endCursor. The same hasSubjectDigestsWith predicate works under subjects(where: ...) if you want subjects without the surrounding DSSE.

Option C: HTTP endpoints (direct curl)

For tooling that doesn't want to depend on GraphQL:

# Upload (POST)
curl -X POST "$ARCHIVISTA_URL/v1/upload" \
  -H "Content-Type: application/json" \
  --data-binary "@attestation.json"
# → {"gitoid":"72d83847..."}

# Download (GET)
curl "$ARCHIVISTA_URL/v1/download/<gitoid>"
# → the raw DSSE envelope JSON

# Ad-hoc GraphQL POST
curl -X POST "$ARCHIVISTA_URL/v1/query" \
  -H "Content-Type: application/json" \
  -d '{"query":"{ subjects(first: 100) { edges { node { name subjectDigests { algorithm value } } } pageInfo { hasNextPage endCursor } } }"}'
# → one page; while hasNextPage is true, repeat with subjects(first: 100, after: "<endCursor>")

For the full schema, self-hosting setup, and the legacy ER diagram, see in-toto/archivista and the upstream cmd/Readme.md.

Troubleshooting evidence upload

SymptomLikely cause
upload to <platform> rejected ... the attestation was signed but not storedThe platform refused the credential: an expired or wrong-audience token, a revoked or unenrolled identity, or a repository the platform has not been told to trust. The signed envelope is kept on disk.
archivista upload failed: 403API key doesn't have write permission for that self-hosted Archivista instance.
No upload and no errorThe run had no identity that uploads: a Buildkite or CircleCI job without cilock login, a GitLab job with no ID token for <platform-url>/archivista, --offline (or an empty --platform-url), or an explicit --enable-archivista=false.
Successful run but no GitOID printedCheck stderr, successful uploads log Stored in archivista as <gitoid>.

Reference generated from the product documentation. Match commands and support details to your installed release.