CI/CD Integration

Vekt integrates into any CI/CD pipeline via the CLI. The typical pattern is to run vekt scan . (or the planned vekt ci command) as a pipeline step and let the exit code determine whether the build passes.

Table of contents


Exit codes

Code Meaning
0 Scan completed with no findings
1 Scan completed and findings were detected
2 Error (parse failure, network error, invalid arguments)

Use --malicious-only if you want the build to fail only on confirmed malicious packages and not on CVEs.


GitHub Actions

Basic workflow

name: Supply chain scan

on:
  push:
    branches: [main]
  pull_request:

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

      - name: Install Vekt
        run: |
          curl -fsSL https://kief.dev/vekt/install.sh | sh
          echo "$HOME/.local/bin" >> "$GITHUB_PATH"

      - name: Scan dependencies
        run: vekt scan . --quiet
        env:
          VEKT_API_KEY: ${{ secrets.VEKT_API_KEY }}

Fail on high severity only

      - name: Scan dependencies
        run: vekt scan . --quiet
        env:
          VEKT_API_KEY: ${{ secrets.VEKT_API_KEY }}
        # vekt ci --fail-on high is coming soon
        # Until then, combine with --malicious-only for the strictest check:
        # run: vekt scan . --malicious-only --quiet

Separate malicious and vulnerability checks

      - name: Check for malicious packages (blocking)
        run: vekt scan . --malicious-only --quiet
        env:
          VEKT_API_KEY: ${{ secrets.VEKT_API_KEY }}

      - name: Check for vulnerabilities (non-blocking)
        run: vekt scan . --quiet || true
        env:
          VEKT_API_KEY: ${{ secrets.VEKT_API_KEY }}

Upload JSON results as artifact

      - name: Scan dependencies
        id: vekt
        run: |
          vekt scan . --json > vekt-results.json
          exit_code=$?
          echo "exit_code=$exit_code" >> "$GITHUB_OUTPUT"
          exit $exit_code
        env:
          VEKT_API_KEY: ${{ secrets.VEKT_API_KEY }}
        continue-on-error: true

      - name: Upload scan results
        uses: actions/upload-artifact@v4
        with:
          name: vekt-results
          path: vekt-results.json

      - name: Fail if findings detected
        if: steps.vekt.outputs.exit_code == '1'
        run: exit 1

GitLab CI

vekt-scan:
  stage: test
  image: ubuntu:24.04
  before_script:
    - apt-get update -qq && apt-get install -y -qq curl
    - curl -fsSL https://kief.dev/vekt/install.sh | sh
    - export PATH="$HOME/.local/bin:$PATH"
  script:
    - vekt scan . --quiet
  variables:
    VEKT_API_KEY: $VEKT_API_KEY
  artifacts:
    when: always
    reports:
      # SARIF support coming soon via vekt ci --sarif
      # For now, capture JSON output
    paths:
      - vekt-results.json
    expire_in: 7 days
  allow_failure: false

To capture findings as a JSON artifact:

  script:
    - vekt scan . --json > vekt-results.json; VEKT_EXIT=$?
    - exit $VEKT_EXIT

Generic CI usage

Any CI system that can run shell commands works. The pattern is the same:

  1. Install the vekt binary
  2. Set VEKT_API_KEY from a secret
  3. Run vekt scan .
  4. The exit code drives pass/fail
# Install
curl -fsSL https://kief.dev/vekt/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"

# Scan
VEKT_API_KEY="$VEKT_API_KEY" vekt scan . --quiet

Using vekt ci (coming soon)

The vekt ci command is planned and will provide a cleaner interface for CI use:

# Fail on any finding at or above HIGH severity
vekt ci --fail-on high

# Fail only on malicious packages
vekt ci --fail-on malicious

# Generate SARIF for GitHub Advanced Security
vekt ci --sarif > vekt.sarif

Until vekt ci ships, use vekt scan directly.


SARIF output for GitHub Advanced Security

Coming soon via vekt ci --sarif.

Once available, SARIF output can be uploaded to GitHub Advanced Security to surface findings in the Security tab and annotate pull requests:

      - name: Vekt supply chain scan
        run: vekt ci --sarif > vekt.sarif
        continue-on-error: true
        env:
          VEKT_API_KEY: ${{ secrets.VEKT_API_KEY }}

      - name: Upload SARIF results
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: vekt.sarif
          category: supply-chain

Blocking PRs on new findings

To fail only on findings that are new in a PR (not pre-existing), use vekt diff once it ships. Until then, the recommended approach is to run vekt scan . on every PR and treat exit code 1 as a blocking check.

Coming soon -- vekt diff <base-lockfile> <head-lockfile> will compare two lockfile states and report only net-new findings, making it suitable for incremental PR checks that do not block on pre-existing debt.


README badge

Coming soon on Team and Enterprise plans.

Once available, generate a badge SVG for your README:

# Generate badge URL for your repository
vekt ci --badge
# Outputs: https://kief.dev/vekt/badge/<token>.svg

Embed in your README:

![Vekt supply chain](https://kief.dev/vekt/badge/<token>.svg)

The badge reflects the most recent scan result. It updates whenever you run vekt ci in your pipeline.


Tips

Cache the binary between runs. The vekt binary is a single static file. Cache ~/.local/bin/vekt in your CI cache to avoid re-downloading on every run.

Pin the version. Use a versioned install URL (https://kief.dev/vekt/releases/v0.1.0/vekt-linux-x86_64) in production pipelines rather than latest to avoid unexpected behavior changes.

Store the API key as a secret. Never hardcode the key in workflow files. Use your CI platform's secret store (${{ secrets.VEKT_API_KEY }} in GitHub Actions, CI/CD variables in GitLab, etc.).

Use --quiet in cron scans. If you run a scheduled nightly scan in addition to PR checks, --quiet suppresses output when the project is clean, keeping your logs readable.