User Guide

A walkthrough of every key activity in the OpenFactstore web UI.

1. Introduction

OpenFactstore is an open-source Supply Chain Compliance Fact Store for teams operating in regulated environments. The web UI gives engineers and compliance officers a single place to:

  • Define delivery processes (Flows) with mandatory quality gates.
  • Browse build records (Trails) and the artifacts they produced.
  • Assert whether a specific container image digest is compliant before deployment.
  • Inspect evidence files (test reports, scan results, approval decisions) attached to each build.
  • Monitor deployment environments for drift and policy violations.
  • Search across all artifacts, trails, and attestations in seconds.

Screenshots in the full source guide (docs/user-guide.md) are generated automatically by the Playwright E2E test suite — run npm run test:e2e from the frontend/ directory to refresh them.

2. Prerequisites

Requirement Version Notes
Java 21+ (Eclipse Temurin) Required to run the backend
Node.js 20+ Required for the frontend dev server
Docker Any recent version Optional — enables docker-compose up
Browser Chrome / Edge / Firefox Desktop viewport recommended

See the Get Started page for full stack setup instructions.

4. Dashboard

The Dashboard (/) provides an at-a-glance summary of your delivery pipeline's compliance health.

Dashboard overview showing summary cards and compliance bar
Dashboard — compliance summary cards and recent activity

Key elements

Element Description
Total Flows card Count of registered delivery pipelines
Total Trails card Count of all build records with Compliant / Non-Compliant / Pending breakdown
Compliance Rate card Percentage of completed builds that are compliant, shown as a colour-coded bar (green ≥ 80%, yellow ≥ 50%, red < 50%)
Recent Trails table The 5 most recent trails with commit SHA, branch, author, date, and compliance status badge. Click any row to open the trail detail view.

5. Managing Flows

A Flow is a named template that declares which attestation types must be present — and passing — before a release is approved. Navigate to Flows in the navigation bar.

Flows list page showing compliance flow templates
Flows list — browse and manage compliance flow templates

Creating a flow

  1. Click New Flow.
  2. Enter a name (e.g. payment-service-ci) and optional description.
  3. Add one or more required attestation types as a comma-separated list (e.g. unit-tests, security-scan).
  4. Set the visibility (public or private).
  5. Click Create.

Viewing flow details

Click any flow in the list to open its detail view. The detail panel shows all associated trails, aggregate compliance statistics, and the required attestation type manifest.

6. Asserting Compliance

Navigate to Assert to check whether a specific container image is compliant with a given flow.

  1. Enter the full SHA-256 digest of the container image (e.g. sha256:abc123…).
  2. Select the flow from the dropdown.
  3. Click Assert Compliance.
Assert compliance page with digest input and result
Assert — check a container image digest against a compliance flow

The result panel shows COMPLIANT or NON_COMPLIANT with a breakdown of which attestation types passed, failed, or are missing.

7. Evidence Vault

The Evidence Vault page lists all uploaded evidence files across all trails. Evidence files are typically JUnit XML, SARIF scan reports, or PDF approvals uploaded via the API or CLI.

Evidence Vault listing uploaded evidence files
Evidence Vault — browse and download uploaded evidence files
  • Filter by trail, type, or date range using the toolbar controls.
  • Click a file name to download the original evidence artifact.
  • The hash column shows the SHA-256 of each file, enabling tamper detection.

8. Environments

Environments represent deployment targets (e.g. staging, production-eu). Each environment holds a snapshot — the set of artifacts currently deployed there.

Environments list showing deployment targets
Environments — deployment targets with artifact snapshots
  • The environment list shows each environment's name, current artifact count, and last-updated timestamp.
  • Click an environment to open the detail view with its full artifact snapshot.
  • Snapshots are written by the CLI or API at deploy time using POST /api/v1/environments/{id}/snapshots.

9. Logical Environments

Logical Environments group multiple physical environments under a single name (e.g. production grouping production-eu and production-us). This lets compliance officers assert that all members of a logical group are up to date.

Logical environments grouping page
Logical Environments — group physical environments for aggregate compliance checks

Navigate to Logical Environments in the navigation bar to create groups and assign physical environments to them.

10. Audit Log

The Audit Log is an append-only record of every compliance event in the system — trail creation, attestation recording, gate decisions, and approval actions. Each entry includes:

  • Event type — e.g. ATTESTATION_RECORDED, GATE_ALLOWED
  • Actor — the user or system that triggered the event
  • Timestamp — immutable UTC timestamp
  • HMAC digest — cryptographic integrity proof
Audit log listing compliance events
Audit Log — immutable append-only compliance event history

Use the filter toolbar to narrow by event type, actor, or date range. Each entry can be expanded to show the full JSON payload.

11. Deployment Policies

Policies are OPA (Open Policy Agent) rules that gate deployments. A policy evaluates a trail's attestations and returns allow or deny with a reason.

Deployment policies management page
Policies — manage OPA deployment gate rules

Creating a policy

  1. Navigate to Policies and click New Policy.
  2. Enter a name and optional description.
  3. Choose the Type: OPA_REGO (recommended) or CEL.
  4. Choose the Evaluator: jq to use a jq expression for status evaluation, or NONE for purely Rego-based evaluation.
  5. Paste the Rego expression (e.g. input.attestations[_].status == "PASSED").
  6. Click Create.

The policy is immediately applied to all subsequent compliance assertions for the flows it is linked to.

12. Compliance Frameworks

The Compliance page (/compliance) lets you browse a library of pre-built compliance framework templates — including SOX, PCI-DSS v4.0, GDPR, and ISO 27001. Each template maps a regulatory body's controls to flow attestation types.

  • Click Import as Flow on any framework template to generate a pre-configured Flow with all required attestation types already defined.
  • Once imported, the flow appears in your Flows list and can be used immediately for trail creation.
  • An aggregate compliance score shows how many controls are currently passing across your trails.
Compliance frameworks with control scores
Compliance — regulatory framework templates with import-as-flow capability

13. Drift Detection

Drift occurs when the artifact deployed in an environment does not match the one approved by the compliance flow. The Drift page shows:

  • Environments where the deployed artifact's SHA-256 is not in the approved set.
  • A diff of the expected vs actual artifact digests.
  • The timestamp when drift was first detected.
Drift detection page showing environment divergence
Drift Detection — identify environments where deployed artifacts have diverged from approved state

15. Attestation Types

The Attestation Types page (/attestation-types) is your organisation's registry of all recognised quality gate categories. Each type defines the shape of evidence that must be collected before a trail is considered compliant.

Attestation Types management page
Attestation Types — registry of custom quality gate categories with schema validation

Viewing types

The types table shows one row per registered type with these columns:

Column Description
Name Unique identifier used in flow definitions and attestation submissions
Description Human-readable explanation of what evidence this type collects
Version Schema version counter — increments on every save
Status ACTIVE or ARCHIVED

Toggle Show archived types to include retired types in the list.

Creating a type

  1. Click + New Type in the top-right corner.
  2. Enter a Name (e.g., SAST, SBOM, DAST), an optional description, and a JSON Schema.
  3. Optionally add a jq expression to compute PASSED / FAILED status automatically.
  4. Click Save.

JSON Schema example

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": ["tool", "result"],
  "properties": {
    "tool":   { "type": "string" },
    "result": { "enum": ["PASSED", "FAILED", "SKIPPED"] },
    "issues": { "type": "integer", "minimum": 0 }
  }
}

Any attestation payload submitted against this type is validated against the schema. A non-conforming payload is rejected with HTTP 422.

jq compliance expression

.result == "PASSED" and .issues == 0

If the expression evaluates to true the attestation receives status PASSED; false maps to FAILED. Omit the field to let the CI pipeline set the status explicitly.

Archiving types

Click Archive on any row to retire a type without deleting historical data. Archived types can be shown by checking Show archived types. Each save bumps the Version counter, providing an audit trail of schema evolution.

16. Compliance Posture

The Compliance Posture dashboard (/compliance-posture) gives leadership a bird's-eye view of compliance health across every flow in the organisation.

Compliance Posture dashboard showing per-flow breakdown
Compliance Posture — organisation-wide compliance overview with per-flow breakdown

Summary cards

Card Description
Total Flows Number of active delivery pipelines
Compliant Trails % Percentage of all trails that passed every required attestation
Non-Compliant Trails Count of trails with at least one FAILED or missing attestation
Pending Trails Trails still accumulating evidence

Per-flow breakdown

The table shows one row per flow with Compliant / Non-Compliant / Pending counts. The Compliance % column is colour-coded: green ≥ 80%, yellow ≥ 50%, red < 50%. A high Pending count typically means CI pipelines have not completed or evidence upload is failing silently — investigate the trail detail view.

17. Snapshot Diff

The Snapshot Diff viewer (/environments/:id/snapshot-diff) lets you compare two point-in-time snapshots of an environment to understand exactly what changed between them. Open it from any Environment detail page via Compare Snapshots.

Snapshot Diff viewer with two drop-downs and diff table
Snapshot Diff — compare two environment snapshots to see exactly what changed

Selecting snapshots

Two drop-down menus at the top of the page let you select Snapshot A (baseline) and Snapshot B (later state). Each drop-down lists all available snapshots for the environment in reverse-chronological order. After selecting both, click Compare to load the diff table.

Reading the diff table

Column Meaning
Image Container image name and tag
Digest A / B SHA-256 digest in the older / newer snapshot
Change ADDED, REMOVED, or CHANGED

Row background colours make changes immediately scannable: green = ADDED, red = REMOVED, amber = CHANGED.

Typical use-cases: incident investigation, deployment verification, and compliance audits demonstrating that only approved artifacts entered production between two dates.

18. CI Integration — GitHub Actions

OpenFactstore ships a reusable composite GitHub Actions action at .github/actions/factstore-attest/ that records attestations directly from your CI workflows with no extra tooling required.

Quick-start example

# .github/workflows/ci.yml
name: CI

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build and push image
        id: build
        run: |
          docker build -t myapp:${{ github.sha }} .
          docker push myapp:${{ github.sha }}
          echo "sha=$(docker inspect --format='{{index .RepoDigests 0}}' \
            myapp:${{ github.sha }})" >> "$GITHUB_OUTPUT"

      - name: Create build trail
        id: trail
        run: |
          TRAIL_ID=$(curl -sf -X POST "${{ secrets.FACTSTORE_URL }}/api/v1/trails" \
            -H "Content-Type: application/json" \
            -d '{
              "flowId":    "${{ vars.FACTSTORE_FLOW_ID }}",
              "commitSha": "${{ github.sha }}",
              "branch":    "${{ github.ref_name }}",
              "author":    "${{ github.actor }}"
            }' | jq -r '.id')
          echo "trail-id=$TRAIL_ID" >> "$GITHUB_OUTPUT"

      - name: Record JUnit attestation
        uses: ./.github/actions/factstore-attest
        with:
          factstore-url: ${{ secrets.FACTSTORE_URL }}
          trail-id:      ${{ steps.trail.outputs.trail-id }}
          type:          junit
          status:        PASSED
          details:       "All tests green on ${{ github.sha }}"
          evidence-url:  ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
          api-key:       ${{ secrets.FACTSTORE_API_KEY }}

      - name: Record Snyk attestation
        uses: ./.github/actions/factstore-attest
        with:
          factstore-url: ${{ secrets.FACTSTORE_URL }}
          trail-id:      ${{ steps.trail.outputs.trail-id }}
          type:          snyk
          status:        PASSED
          details:       "No critical vulnerabilities found"
          evidence-url:  ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
          api-key:       ${{ secrets.FACTSTORE_API_KEY }}

      - name: Assert compliance before deploy
        run: |
          curl -sf "${{ secrets.FACTSTORE_URL }}/api/v1/assert" \
            -G --data-urlencode "sha256=${{ steps.build.outputs.sha }}" \
               --data-urlencode "flowId=${{ vars.FACTSTORE_FLOW_ID }}"

Action inputs

Input Required Description
factstore-url Factstore server base URL
trail-id Trail to attach the attestation to
type Attestation type key (e.g., junit, snyk, sbom)
status ✅ (default: PASSED) PASSED, FAILED, or PENDING
details Free-text description of the attestation
evidence-url Link to external evidence (e.g., CI run URL)
api-key API key for authenticated instances

The action outputs attestation-id — the UUID of the newly created attestation — which can be used in subsequent steps.

19. Infrastructure as Code — Terraform

OpenFactstore provides a Terraform provider in terraform/provider/ so you can manage flows and environments alongside your infrastructure using standard HCL and GitOps workflows.

Provider configuration

terraform {
  required_providers {
    factstore = {
      source  = "local/factstore"
      version = "~> 1.0"
    }
  }
}

provider "factstore" {
  url     = "http://localhost:8080"
  api_key = var.factstore_api_key   # optional
}

Managing a Flow

resource "factstore_flow" "payment_service" {
  name        = "payment-service"
  description = "Compliance pipeline for the payment microservice"

  required_attestation_types = [
    "junit",
    "snyk",
    "sbom",
    "change-approval",
  ]

  tags = {
    team      = "payments"
    sox-scope = "true"
  }
}

output "flow_id" {
  value = factstore_flow.payment_service.id
}

Managing an Environment

resource "factstore_environment" "production" {
  name        = "production"
  description = "Live customer-facing environment"
  type        = "KUBERNETES"

  tags = {
    region = "eu-west-1"
    tier   = "production"
  }
}

Applying changes

cd terraform/provider

# Initialise the provider
terraform init

# Preview changes
terraform plan -var="factstore_api_key=$FACTSTORE_TOKEN"

# Apply
terraform apply -var="factstore_api_key=$FACTSTORE_TOKEN"

Terraform state tracks resource IDs so subsequent plan / apply runs update only drifted attributes — enabling GitOps-style management of your compliance configuration.

20. Command-Line Interface (CLI)

The factstore CLI is a Go binary that lets pipelines and engineers interact with the Factstore API from any terminal or CI environment — without needing the web UI.

Installation

Build from source (requires Go 1.21+):

git clone https://github.com/MaximumTrainer/OpenFactstore.git
cd OpenFactstore/cli
make build
# binary at ./bin/factstore

sudo mv bin/factstore /usr/local/bin/

Configuration

Run the interactive wizard to save credentials to ~/.factstore.yaml:

factstore configure
# API host (e.g. https://api.factstore.example.com):
# Query host (leave blank to use API host for reads):
# Bearer token: ••••••••

Alternatively, set environment variables (useful in CI):

export FACTSTORE_HOST=https://api.factstore.example.com
export FACTSTORE_TOKEN=my-bearer-token
# Optional: separate read host for CQRS deployments
export FACTSTORE_QUERY_HOST=https://query.factstore.example.com

Global Flags

FlagDescription
--hostOverride configured API host
--query-hostOverride configured query host (GET requests)
--tokenOverride configured bearer token
--jsonEmit raw JSON instead of a table

Verify Connectivity

factstore login

Flows

factstore flows list
factstore flows get <id>
factstore flows create \
  --name "My Flow" \
  --description "CI pipeline" \
  --attestation-types "SBOM,VULNERABILITY_SCAN"
factstore flows update <id> --name "Updated Name"
factstore flows delete <id>

Trails

factstore trails list
factstore trails list --flow-id <flow-id>
factstore trails get <id>
factstore trails create \
  --flow-id <flow-id> \
  --commit abc123def456 \
  --branch main \
  --author "Alice Smith" \
  --author-email alice@example.com

Artifacts

factstore artifacts list --trail-id <trail-id>
factstore artifacts find --sha256 sha256:abc123...
factstore artifacts create \
  --trail-id <trail-id> \
  --image-name myapp \
  --image-tag v1.2.3 \
  --sha256 sha256:abc123... \
  --reported-by ci-bot \
  --registry ghcr.io

Attestations

factstore attestations list --trail-id <trail-id>
factstore attestations create \
  --trail-id <trail-id> \
  --type SBOM \
  --status PASSED \
  --details "Generated by syft"
factstore attestations upload-evidence \
  --trail-id <trail-id> \
  --attestation-id <attestation-id> \
  ./sbom.json

Compliance & Deployment Gates

# Assert an artifact meets a flow's requirements
factstore assert --sha256 sha256:abc123... --flow-id <flow-id>

# Evaluate a deployment gate
factstore gate evaluate \
  --artifact sha256:abc123... \
  --environment production \
  --flow-id <flow-id>

# List recent gate results
factstore gate list --environment production

# Full chain of custody report
factstore compliance artifact sha256:abc123...

Typed attest commands

Higher-level commands parse known report formats and set status automatically:

# JUnit XML — parses failures from the report
factstore attest junit \
  --trail-id "$TRAIL_ID" \
  --results junit-results.xml \
  --name "Unit Tests"

# Snyk JSON — parses critical/high vulnerabilities
factstore attest snyk \
  --trail-id "$TRAIL_ID" \
  --results snyk-report.json \
  --name "Snyk Scan"

# Custom attestation type
factstore attest custom \
  --trail-id "$TRAIL_ID" \
  --type "change-approval" \
  --status PASSED \
  --details "Approved by Alice Smith (Jira CHANGE-42)" \
  --evidence-url "https://jira.example.com/browse/CHANGE-42"

Deployment report

# Record a deployment event against an environment
factstore report deployment \
  --environment "production" \
  --artifact "sha256:abc123..." \
  --flow-id "$FLOW_ID"

Environment snapshots

# Capture the current state of a Kubernetes namespace
factstore environment snapshot \
  --environment "production" \
  --source kubernetes \
  --namespace default

# List recent snapshots
factstore environment snapshots --environment "production"

Webhooks

factstore webhooks list
factstore webhooks create --source github --secret <secret> --flow-id <flow-id>
factstore webhooks delete <id>
factstore webhooks deliveries <id>

JSON Output

All commands support --json to emit raw JSON for scripting:

factstore flows list --json | jq '.[].name'

GitHub Actions Integration

Use the CLI in CI to record facts after every build. The example below creates a trail, attaches an artifact and SBOM attestation, then asserts compliance:

- name: Record compliance facts
  env:
    FACTSTORE_HOST: ${{ secrets.FACTSTORE_HOST }}
    FACTSTORE_TOKEN: ${{ secrets.FACTSTORE_TOKEN }}
  run: |
    # Create a build trail
    TRAIL_ID=$(factstore trails create \
      --flow-id "$FLOW_ID" \
      --commit "$GITHUB_SHA" \
      --branch "$GITHUB_REF_NAME" \
      --author "$GITHUB_ACTOR" \
      --author-email "$GITHUB_ACTOR@github" \
      --json | jq -r '.id')

    # Record the container artifact
    factstore artifacts create \
      --trail-id "$TRAIL_ID" \
      --image-name myapp \
      --image-tag "$IMAGE_TAG" \
      --sha256 "$IMAGE_SHA" \
      --reported-by github-actions

    # Record the SBOM attestation
    factstore attestations create \
      --trail-id "$TRAIL_ID" \
      --type SBOM \
      --status PASSED \
      --details "Generated by syft"

    # Assert compliance before deployment
    factstore assert \
      --sha256 "$IMAGE_SHA" \
      --flow-id "$FLOW_ID"

21. Troubleshooting

Empty lists / "No data" messages

Most list pages show an empty state when no records have been created yet. Follow the Get Started guide or use the CLI (factstore flow create) to seed initial data.

Frontend cannot reach the backend

If the UI shows network errors, verify that the backend is running on port 8080 and that the Vite proxy (frontend/vite.config.ts) forwards /api to http://localhost:8080.

Compliance assertion returns NON_COMPLIANT unexpectedly

Check the trail's attestation list to confirm that all required types for the selected flow are present with status PASSED. Missing or FAILED attestations will block the assertion.

Full source guide

The full developer guide including API examples, CLI commands, and CI integration patterns is in docs/user-guide.md on GitHub ↗.

22. Flow Setup Patterns

A Flow is the centrepiece of compliance configuration — it declares exactly which evidence must be collected before an artifact may be deployed. This section shows how to configure flows for the most common real-world scenarios, starting from scratch or by importing a framework template.

Designing a flow: what to decide first

Question Guidance
What evidence do regulators or your team require? Start with a fixed list (e.g., test results, CVE scans, SBOM) then grow from there.
Does the release need human sign-off? Enable Requires Approval on the flow — the assertion gate blocks until an approval record is granted.
Is this pipeline internal-only? Set Visibility: private to hide it from public listings.
Which regulatory framework applies? Use Compliance FrameworksImport as Flow to bootstrap all required attestation types automatically.

Pattern 1 — Basic CI/CD quality gate

Suitable for any service without a formal regulatory obligation. Ensures every release has passing unit tests and no known critical vulnerabilities.

Name payment-service-ci
Required Attestation Types unit-tests, vulnerability-scan
Requires Approval No
Tags team=payments, tier=standard

In CI: record a unit-tests attestation (PASSED/FAILED) after the test step, and a vulnerability-scan attestation after running Snyk/Trivy. Use FAILED status when the tool reports issues — this immediately marks the trail NON_COMPLIANT and blocks the assertion gate.

Pattern 2 — Security-focused pipeline

For services that handle sensitive data or are internet-facing. Requires static analysis, a software bill of materials, dynamic testing, and a dependency vulnerability scan.

Name api-gateway-security
Required Attestation Types unit-tests, sast, sbom, vulnerability-scan, dast
Requires Approval No
Tags team=platform, tier=critical

Map each step to a tool: SAST → Semgrep/SonarQube, SBOM → Syft/Grype, vulnerability-scan → Snyk, DAST → OWASP ZAP. Record the attestation immediately after each tool finishes so the trail accumulates evidence as the pipeline progresses.

Pattern 3 — Regulatory / SOX-compliant pipeline

For services in scope for SOX, PCI-DSS, or similar frameworks. Adds a software bill of materials for supply chain evidence and requires a human change-approval record before the assertion gate can pass.

Name finance-service-sox
Required Attestation Types unit-tests, vulnerability-scan, sbom
Requires Approval Yes — the assertion gate blocks until an approval record is granted for the trail
Tags sox-scope=true, team=finance

With Requires Approval enabled, a compliance assertion always checks for an APPROVED approval record on the trail in addition to all attestations being PASSED. Record the approval via the Approvals API after a change-advisory board or PR review is complete.

Pattern 4 — Bootstrapping from a compliance framework

If your organisation is aligning to a specific standard (ISO 27001, SOC 2, PCI-DSS, GDPR), skip manual flow design and import a pre-built template:

  1. Navigate to Compliance (/compliance).
  2. Browse the framework cards and locate the relevant standard.
  3. Click Import as Flow.
  4. Review the generated flow name and the pre-populated Required Attestation Types.
  5. Adjust the name and tags if needed, then click Create.
  6. The new flow appears in your Flows list immediately.

The imported flow's required attestation types exactly match the framework's mandatory controls. You can edit the flow afterwards to add organisation-specific gates.

Attestation type naming conventions

Attestation type identifiers must match exactly between the flow definition and the values submitted by CI pipelines. Adopt a consistent naming scheme across all flows:

Recommended identifier Meaning Typical tool
unit-tests Unit / integration test suite result JUnit, pytest, Jest
vulnerability-scan Dependency / container CVE scan Snyk, Grype, Trivy
sast Static application security testing Semgrep, SonarQube, CodeQL
sbom Software bill of materials generation Syft, CycloneDX
dast Dynamic application security testing OWASP ZAP, Burp Suite
container-scan Container image layer scanning Trivy, Clair
change-approval Human change-advisory board approval Jira, ServiceNow, manual

Register each identifier in the Attestation Types registry (/attestation-types) with an optional JSON Schema and jq expression so that payload validation and automatic status computation are applied when evidence is submitted.

23. The Fact Lifecycle

A fact in OpenFactstore is an attestation — a structured record asserting that a specific quality gate was evaluated for a build. Facts accumulate on a Trail (one per build) and are later evaluated when you assert compliance against a Flow.

Understanding the lifecycle helps you configure CI pipelines correctly and diagnose why an artifact is reported as non-compliant.

Stage 1 — Create a Trail

A Trail is created at the very beginning of a CI build — before any tests run. It acts as an evidence folder for the build.

POST /api/v1/trails
{
  "flowId":       "<flow-uuid>",
  "gitCommitSha": "a1b2c3d4",
  "gitBranch":    "main",
  "gitAuthor":    "alice",
  "gitAuthorEmail": "alice@example.com"
}

The response includes a trailId UUID that all subsequent API calls reference. The trail starts in PENDING status and stays there as long as no attestation with status FAILED has been recorded.

Stage 2 — Record the Artifact

After the container image is built and pushed, record its immutable SHA-256 digest. This is the fingerprint that ties evidence to a specific image binary.

POST /api/v1/trails/<trailId>/artifacts
{
  "imageName":    "myapp",
  "imageTag":     "v2.4.1",
  "sha256Digest": "sha256:abc123...",
  "reportedBy":   "github-actions",
  "registry":     "ghcr.io"
}

Recording the same digest in multiple trails is allowed — the compliance assertion searches all matching artifacts and returns COMPLIANT if any one of their trails satisfies all flow requirements.

Stage 3 — Submit Attestations (Facts)

For each quality gate in the flow, record an attestation on the trail. Each attestation carries a type (matching the flow's required types), a status, and optional evidence metadata.

POST /api/v1/trails/<trailId>/attestations
{
  "type":        "unit-tests",
  "status":      "PASSED",
  "details":     "1 247 tests passed, 0 failures",
  "evidenceUrl": "https://ci.example.com/runs/42"
}

Attestation statuses explained

Status Meaning Effect on compliance
PASSED The quality gate succeeded Satisfies the requirement for this type
FAILED The quality gate detected a problem Immediately marks the trail NON_COMPLIANT; blocks the assertion gate
PENDING Evidence is expected but not yet evaluated Treated as missing — does not satisfy the requirement

⚠️ A FAILED attestation permanently marks the trail NON_COMPLIANT. To recover, open a new trail for the same commit (after the fix is applied), re-run the pipeline, and assert compliance against the new trail's artifact digest.

Uploading evidence files

Binary evidence files (JUnit XML, SARIF, PDF approvals) can be attached to an attestation via a multipart upload. The file is stored in the Evidence Vault and its SHA-256 hash is recorded on the attestation for tamper detection.

POST /api/v1/attestations/<attestationId>/evidence
Content-Type: multipart/form-data

file=@junit-results.xml

Stage 4 — Assert Compliance

At deploy time, assert that the specific image digest satisfies the flow's requirements. This is a point-in-time gate check — it does not mutate trail status.

GET /api/v1/assert?sha256=sha256:abc123...&flowId=<flow-uuid>

How the assertion is evaluated

  1. Find all artifacts in the store that match the provided sha256 digest.
  2. For each matching artifact, check its trail against the flow's required attestation types:
    • All required types must have a PASSED attestation on the trail.
    • Types with only PENDING or no attestation are treated as missing.
  3. If the flow has Requires Approval enabled, additionally verify that the trail has an APPROVED approval record.
  4. Return COMPLIANT if any artifact's trail satisfies all requirements; otherwise return NON_COMPLIANT with the list of missing and failed types.

Every assertion emits an audit event: GATE_ALLOWED (compliant) or GATE_BLOCKED (non-compliant), visible in the Audit Log.

Trail status state machine

Status Trigger What it means
PENDING Trail created Evidence is accumulating; no FAILED attestation yet
NON_COMPLIANT A FAILED attestation is recorded on this trail Build has a known failure; assertion gate will block this trail's artifacts

Note: trail status reflects accumulated evidence quality. A trail that stays PENDING with all PASSED attestations will produce a COMPLIANT assertion result — the assertion is the definitive compliance check, not the trail status alone.

Audit events emitted during the lifecycle

Lifecycle stage Audit event
Artifact recorded / deployed to environment ARTIFACT_DEPLOYED
Artifact removed from environment ARTIFACT_REMOVED
Attestation submitted ATTESTATION_RECORDED
Approval granted by a reviewer APPROVAL_GRANTED
Approval rejected by a reviewer APPROVAL_REJECTED
Compliance assertion passed GATE_ALLOWED
Compliance assertion failed GATE_BLOCKED
Environment created or deleted ENVIRONMENT_CREATED / ENVIRONMENT_DELETED

End-to-end example: a compliant release

The following sequence shows the complete happy path for a single release:

  1. 🏗️ CI starts → create trail (status: PENDING)
  2. 🐳 Image built and pushed → record artifact (digest: sha256:abc…)
  3. ✅ Tests pass → record unit-tests attestation (status: PASSED) → audit: ATTESTATION_RECORDED
  4. 🔍 Vulnerability scan clean → record vulnerability-scan attestation (status: PASSED) → audit: ATTESTATION_RECORDED
  5. 📄 SBOM generated → record sbom attestation (status: PASSED) → audit: ATTESTATION_RECORDED
  6. 🚀 Deploy step → assert compliance → all attestations PASSED → result: COMPLIANT → audit: GATE_ALLOWED
  7. 🌍 Deployment succeeds → record environment snapshot

End-to-end example: a blocked release

What happens when a quality gate fails:

  1. 🏗️ CI starts → create trail (status: PENDING)
  2. 🐳 Image built → record artifact
  3. ✅ Tests pass → record unit-tests attestation (PASSED)
  4. 🔴 Vulnerability scan finds critical CVEs → record vulnerability-scan attestation (status: FAILED) → trail immediately set to NON_COMPLIANT
  5. 🚫 Deploy step → assert compliancevulnerability-scan is FAILED → result: NON_COMPLIANT → audit: GATE_BLOCKED
  6. 🛑 Deployment is blocked. CI pipeline should exit with a non-zero code.

After fixing the vulnerabilities: open a new trail for a new commit, re-run the pipeline, and assert against the new artifact digest. Do not reuse the original trail — each build should have its own trail.

24. Compliance Reporting

OpenFactstore surfaces compliance data through several built-in reporting channels — from real-time dashboards for engineers to exportable audit packages for regulators.

Reporting surface overview

Surface Audience Key metric
Dashboard (/) Engineers Compliance Rate bar; recent trail statuses
Compliance Posture (/compliance-posture) Engineering leads / CISO Per-flow breakdown: compliant %, non-compliant count, pending count
Audit Log (/audit) Compliance officers / auditors Timestamped event history with actor attribution
Trail Audit endpoint Auditors / automated export All audit events scoped to a single build trail
Compliance Report API Automated tooling / GRC systems Machine-readable JSON compliance report

Dashboard compliance rate

The Compliance Rate card on the Dashboard shows the percentage of trails whose status is NON_COMPLIANT versus PENDING. Use it as a daily health signal:

  • ≥ 80% — pipeline is well-controlled; few or no failures
  • 50–79% — investigate recent failures; review non-compliant trails
  • < 50% — significant compliance issues; escalate immediately

Click any row in the Recent Trails table to drill into that trail's detail view and see exactly which attestations are missing or failed.

Compliance Posture dashboard (for leadership)

Navigate to Posture (/compliance-posture) for an organisation-wide view. The per-flow table lets you identify which pipelines are generating the most compliance debt:

  1. Sort the table by Non-Compliant count (highest first) to find the worst-performing pipelines.
  2. A high Pending count suggests CI pipelines are not completing attestation steps — check that all pipeline steps post results to the API.
  3. Click a flow name to navigate directly to its trail list for root-cause investigation.

Audit Log — regulatory evidence

The Audit Log (/audit) provides a tamper-evident record of every compliance event. For regulatory audits, use the following workflow:

  1. Navigate to /audit.
  2. Filter by Event Type:
    • ATTESTATION_RECORDED — shows all evidence submissions
    • GATE_ALLOWED / GATE_BLOCKED — shows all deployment gate decisions
    • APPROVAL_GRANTED — shows human approval records
  3. Optionally filter by Actor to scope to a specific CI system or user.
  4. Export the filtered events via the API: GET /api/v1/audit (returns JSON).

Per-trail audit package (for auditors)

To produce a complete compliance package for a single build (e.g., for an external audit), gather the following for a given trailId:

  1. Trail details — commit SHA, branch, author, timestamp:
    GET /api/v1/trails/<trailId>
  2. Attestations — all evidence records and their statuses:
    GET /api/v1/trails/<trailId>/attestations
  3. Audit events scoped to the trail — every gate decision and evidence submission:
    GET /api/v1/trails/<trailId>/audit
  4. Evidence files — download each attached binary from the Evidence Vault:
    GET /api/v1/evidence/<evidenceId>/download

Combine these four responses into a single JSON bundle or PDF submission for your GRC system. The HMAC digest on each audit event provides cryptographic proof that the log has not been tampered with.

CLI report commands

Use the CLI to generate machine-readable reports from any terminal or CI script:

# Full compliance report for a specific artifact digest
factstore compliance artifact sha256:abc123...

# List all non-compliant trails for a flow (JSON for piping)
factstore trails list --flow-id <flow-id> --json \
  | jq '[.[] | select(.status == "NON_COMPLIANT")]'

# Export all audit events for a trail as JSON
curl "$FACTSTORE_HOST/api/v1/trails/<trailId>/audit" \
  -H "Authorization: Bearer $FACTSTORE_TOKEN" \
  | jq '.' > trail-audit.json

# Compliance posture summary across all flows
curl "$FACTSTORE_HOST/api/v1/compliance/posture" \
  -H "Authorization: Bearer $FACTSTORE_TOKEN" | jq '.'

Interpreting a NON_COMPLIANT assertion response

When an assertion returns NON_COMPLIANT, the response body identifies exactly which types are missing and which have failed:

{
  "sha256Digest": "sha256:abc123...",
  "flowId":       "...",
  "status":       "NON_COMPLIANT",
  "missingAttestationTypes": ["sbom"],
  "failedAttestationTypes":  ["vulnerability-scan"],
  "details":      "Missing required attestations: sbom"
}

Use this response to:

  • Missing types — add the attestation step to your CI pipeline for the affected type.
  • Failed types — fix the underlying quality issue (vulnerabilities, test failures) and open a new trail.