CI/CD Integration Guide
OpenForge integrates with all major CI/CD platforms to automate cryptographic hardware verification as part of your development workflow. This guide covers setup for GitHub Actions, GitLab CI, Jenkins, and Azure DevOps — including pre-merge gates, nightly crypto verification, SARIF security reporting, and webhook-driven notifications.
Overview #
A typical OpenForge CI/CD pipeline uses a tiered verification strategy that balances thoroughness against execution time:
| Trigger | Stage | Checks | Duration | Purpose |
|---|---|---|---|---|
| Every push | Quick | Lint + smoke simulation | ~2 min | Catch syntax errors, basic regressions |
| Pull request | Standard | Full simulation + formal + basic crypto | ~15 min | Pre-merge quality gate |
| Nightly | Full | All crypto engines + exhaustive NTT + SCA | ~2 hr | Comprehensive security verification |
| Release tag | Complete | Full + FIPS compliance report + timing | ~4 hr | Release qualification |
OpenForge provides a scaffolding command that generates CI configuration files for your platform:
# Generate CI configuration for your platform
$ openforge ci setup --github # → .github/workflows/openforge.yml
$ openforge ci setup --gitlab # → .gitlab-ci.yml
$ openforge ci setup --jenkins # → Jenkinsfile
$ openforge ci setup --azure # → azure-pipelines.yml
# Generate with all tiers (push + PR + nightly + release)
$ openforge ci setup --github --tiers all
GitHub Actions #
The recommended GitHub Actions workflow uses OpenForge's official Docker image and supports parallel job execution, artifact caching, and SARIF upload to GitHub Advanced Security.
Complete Workflow
# .github/workflows/openforge.yml
name: OpenForge Verification
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
schedule:
- cron: '0 2 * * *' # Nightly at 2 AM UTC
workflow_dispatch:
inputs:
verification_level:
type: choice
options: [quick, standard, full, release]
env:
OPENFORGE_IMAGE: ghcr.io/dyber/openforge:0.4.0
OPENFORGE_LICENSE: ${{ secrets.OPENFORGE_LICENSE_KEY }}
jobs:
# ── Quick checks (every push) ──
lint-and-smoke:
runs-on: ubuntu-latest
container:
image: ${{ env.OPENFORGE_IMAGE }}
steps:
- uses: actions/checkout@v4
- name: Restore tool cache
uses: actions/cache@v4
with:
path: ~/.openforge/cache
key: openforge-${{ hashFiles('openforge.yaml') }}
- name: Lint
run: openforge verify --lint --report junit --output results/lint.xml
- name: Smoke simulation
run: openforge verify --sim --quick --report junit --output results/sim.xml
- name: Upload results
if: always()
uses: actions/upload-artifact@v4
with:
name: lint-results
path: results/
# ── Standard verification (pull requests) ──
verification:
if: github.event_name == 'pull_request'
needs: lint-and-smoke
runs-on: ubuntu-latest
container:
image: ${{ env.OPENFORGE_IMAGE }}
strategy:
matrix:
stage: [simulation, formal, crypto-basic]
steps:
- uses: actions/checkout@v4
- name: Run ${{ matrix.stage }}
run: |
case "${{ matrix.stage }}" in
simulation)
openforge verify --sim --coverage \
--report junit --output results/sim.xml
;;
formal)
openforge verify --formal --timeout 3600 \
--report junit --output results/formal.xml
;;
crypto-basic)
openforge verify --crypto --constant-time --ntt-validation \
--report sarif --output results/crypto.sarif
;;
esac
- name: Upload SARIF
if: matrix.stage == 'crypto-basic' && always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results/crypto.sarif
category: openforge-crypto
- name: Upload artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.stage }}-results
path: results/
# ── Nightly full crypto verification ──
nightly-crypto:
if: github.event_name == 'schedule'
runs-on: ubuntu-latest
container:
image: ${{ env.OPENFORGE_IMAGE }}
options: --cpus 8 --memory 32g
timeout-minutes: 180
steps:
- uses: actions/checkout@v4
- name: Full crypto verification
run: |
openforge verify --all \
--crypto --all-engines \
--ntt-exhaustive \
--sca-traces 50000 \
--report sarif --output results/full_crypto.sarif \
--report html --output results/full_report.html \
--report json --output results/full_results.json
- name: Upload security results
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results/full_crypto.sarif
category: openforge-nightly
- name: Notify on failure
if: failure()
uses: slackapi/slack-github-action@v1
with:
payload: |
{"text": "🔴 Nightly crypto verification FAILED — ${{ github.repository }}"}
Required Secrets
| Secret | Required | Description |
|---|---|---|
OPENFORGE_LICENSE_KEY | Yes (commercial) | License key for crypto verification engines |
SLACK_WEBHOOK_URL | Optional | Slack notification endpoint for failures |
OPENFORGE_API_KEY | Optional | API key for OpenForge Cloud dashboard |
GitLab CI/CD #
# .gitlab-ci.yml
image: registry.gitlab.com/dyber/openforge:0.4.0
variables:
OPENFORGE_LICENSE: $OPENFORGE_LICENSE_KEY
stages:
- lint
- verify
- crypto
- report
cache:
key: openforge-${CI_COMMIT_REF_SLUG}
paths:
- .openforge/cache/
# ── Quick lint (every push) ──
lint:
stage: lint
script:
- openforge verify --lint --report junit --output results/lint.xml
artifacts:
reports:
junit: results/lint.xml
# ── Simulation + formal (merge requests) ──
simulation:
stage: verify
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
script:
- openforge verify --sim --coverage --report junit --output results/sim.xml
artifacts:
reports:
junit: results/sim.xml
formal:
stage: verify
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
script:
- openforge verify --formal --timeout 3600 --report junit --output results/formal.xml
artifacts:
reports:
junit: results/formal.xml
# ── Crypto verification (nightly + merge requests) ──
crypto-verification:
stage: crypto
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
when: manual
allow_failure: true
script:
- |
openforge verify --crypto --all-engines \
--report sarif --output results/crypto.sarif \
--report html --output results/crypto_report.html
artifacts:
reports:
sast: results/crypto.sarif
paths:
- results/crypto_report.html
tags:
- high-memory # Requires runner with ≥16 GB RAM
Jenkins #
// Jenkinsfile
pipeline {
agent {
docker {
image 'ghcr.io/dyber/openforge:0.4.0'
args '--cpus 8 --memory 16g'
}
}
environment {
OPENFORGE_LICENSE = credentials('openforge-license-key')
}
stages {
stage('Lint') {
steps {
sh 'openforge verify --lint --report junit --output results/lint.xml'
}
post {
always { junit 'results/lint.xml' }
}
}
stage('Verify') {
parallel {
stage('Simulation') {
steps {
sh 'openforge verify --sim --coverage --report junit --output results/sim.xml'
}
}
stage('Formal') {
steps {
sh 'openforge verify --formal --timeout 3600 --report junit --output results/formal.xml'
}
}
}
post {
always { junit 'results/*.xml' }
}
}
stage('Crypto Verification') {
when {
anyOf {
triggeredBy 'TimerTrigger' // Nightly
changeRequest() // Pull request
}
}
steps {
sh '''
openforge verify --crypto --all-engines \
--report sarif --output results/crypto.sarif \
--report html --output results/crypto_report.html
'''
}
post {
always {
archiveArtifacts artifacts: 'results/**'
recordIssues tool: sarif(pattern: 'results/crypto.sarif')
}
}
}
}
triggers {
cron('H 2 * * *') // Nightly at ~2 AM
}
}
Azure DevOps #
# azure-pipelines.yml
trigger:
branches:
include: [main, develop]
pr:
branches:
include: [main]
schedules:
- cron: '0 2 * * *'
displayName: Nightly verification
branches:
include: [main]
pool:
vmImage: ubuntu-latest
container: ghcr.io/dyber/openforge:0.4.0
variables:
- group: openforge-secrets
stages:
- stage: QuickCheck
jobs:
- job: LintAndSmoke
steps:
- script: openforge verify --lint --sim --quick --report junit --output $(Build.ArtifactStagingDirectory)/results.xml
- task: PublishTestResults@2
inputs:
testResultsFormat: JUnit
testResultsFiles: '$(Build.ArtifactStagingDirectory)/*.xml'
- stage: Verification
condition: eq(variables['Build.Reason'], 'PullRequest')
jobs:
- job: FullVerification
timeoutInMinutes: 60
steps:
- script: |
openforge verify --all \
--report sarif --output $(Build.ArtifactStagingDirectory)/crypto.sarif \
--report junit --output $(Build.ArtifactStagingDirectory)/verify.xml
- task: PublishTestResults@2
inputs:
testResultsFiles: '$(Build.ArtifactStagingDirectory)/*.xml'
Pre-Merge Gates #
OpenForge can enforce verification gates that block merges when critical checks fail. Configure these as required status checks in your repository settings.
Recommended Gate Policy
| Check | Gate Level | Block Merge? | Rationale |
|---|---|---|---|
| Lint (zero errors) | Required | Yes | Prevents broken syntax from entering main branch |
| Simulation pass | Required | Yes | Basic functional correctness is non-negotiable |
| Formal (bounded) | Required | Yes | Critical properties must hold within bounded depth |
| Constant-time | Required | Yes | Timing leaks are security-critical bugs |
| NTT validation | Required | Yes | Algorithm correctness is non-negotiable |
| TVLA (side-channel) | Advisory | No | Long-running; nightly results inform decisions |
| FIPS compliance | Advisory | No | Informational until certification process begins |
| Fault injection | Advisory | No | Very long-running; nightly only |
# openforge.yaml — CI gate configuration
ci_integration:
merge_gates:
required:
- check: lint
fail_on: error
- check: simulation
fail_on: error
coverage_threshold: 80 # Minimum line coverage %
- check: formal
timeout: 1800
- check: constant_time
- check: ntt_validation
advisory:
- side_channel
- fips_compliance
- fault_injection
Nightly Builds #
Nightly builds run the full crypto verification suite including computationally expensive checks that are impractical for every PR. Results are stored historically for trend analysis and regression detection.
# Run full nightly verification via CLI
$ openforge ci run --stage nightly
# Equivalent to:
$ openforge verify --all \
--crypto --all-engines \
--sca-traces 50000 \
--ntt-exhaustive \
--fault-injection --campaigns 10000 \
--fips-compliance \
--report sarif --output nightly/crypto.sarif \
--report html --output nightly/report.html \
--report json --output nightly/results.json
Configure notification channels for nightly failures:
ci_integration:
notifications:
on_failure:
- type: slack
webhook: ${SLACK_WEBHOOK_URL}
channel: "#hw-verification"
- type: email
to: ["hw-team@dyber.com"]
on_regression:
- type: webhook
url: ${PAGERDUTY_WEBHOOK}
SARIF Security Reporting #
OpenForge exports crypto verification findings in SARIF (Static Analysis Results Interchange Format) v2.1.0 for integration with GitHub Advanced Security, GitLab SAST, Azure DevOps, and other platforms that support the standard.
Each finding includes:
| SARIF Field | OpenForge Mapping |
|---|---|
ruleId | Engine-specific check ID (e.g., openforge-ct/tainted-control-flow) |
level | Mapped from severity: Critical → error, High → warning, Medium → note |
message | Human-readable description with remediation guidance |
locations | RTL source file, line number, and signal name |
relatedLocations | Taint source, dataflow path through design |
fixes | Suggested countermeasure (when applicable) |
# Generate SARIF report
$ openforge verify --crypto --all --report sarif --output crypto.sarif
# Validate SARIF output (useful for custom integrations)
$ openforge report validate --format sarif crypto.sarif
# Convert SARIF to other formats
$ openforge report convert --from sarif --to html crypto.sarif -o report.html
$ openforge report convert --from sarif --to csv crypto.sarif -o findings.csv
Webhook Integration #
OpenForge can send real-time webhook notifications during verification runs. Configure endpoints in openforge.yaml or via the REST API:
ci_integration:
webhooks:
- url: https://hooks.slack.com/services/T.../B.../xxx
events: [job.completed, job.failed]
secret: ${WEBHOOK_SECRET}
- url: https://api.pagerduty.com/v2/enqueue
events: [crypto.critical_finding]
headers:
Authorization: Token ${PAGERDUTY_TOKEN}
Available webhook event types:
| Event | Trigger |
|---|---|
job.started | Verification job begins execution |
job.completed | Verification job finishes (pass or fail) |
job.failed | Verification job encounters an error or failure |
stage.completed | Individual verification stage completes |
crypto.finding | Any crypto verification finding (any severity) |
crypto.critical_finding | Critical severity crypto finding detected |
coverage.regression | Code coverage drops below previous run |
PR Status Comments #
OpenForge can post formatted verification summary comments directly on pull requests. This requires a GitHub App or personal access token with pull_request:write permission.
# GitHub Actions step — post PR comment
- name: Post verification summary
if: github.event_name == 'pull_request' && always()
run: |
openforge report comment \
--format github \
--results results/ \
--pr ${{ github.event.pull_request.number }} \
--repo ${{ github.repository }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
The posted comment includes a collapsible summary with pass/fail status per engine, coverage delta, timing summary, and links to full report artifacts.
Caching & Performance #
OpenForge supports incremental verification and result caching to minimize CI execution time. When source files haven't changed, previously verified results are reused.
# openforge.yaml — caching configuration
ci_integration:
caching:
enabled: true
cache_dir: .openforge/cache
strategy: content-hash # content-hash | timestamp | none
max_age: 7d # Invalidate cache entries older than 7 days
incremental: true # Only re-verify changed modules
Performance Tips
| Technique | Impact | Trade-off |
|---|---|---|
| Incremental verification | Skip unchanged modules | Requires cache persistence between runs |
| Parallel matrix jobs | Run sim/formal/crypto in parallel | Higher runner cost |
| Reduced SCA traces | Use 10K traces for PR, 50K for nightly | Lower confidence in PR results |
| Quick formal mode | BMC depth 20 instead of 100 | May miss deep bugs |
| Selective crypto checks | Run only CT + NTT for PRs | Missing SCA/FIPS/FI coverage on PR |
Air-Gapped CI #
For defense and government customers operating in air-gapped environments, OpenForge supports fully offline CI/CD deployment with pre-loaded Docker images and local NIST test vector caches.
# Pre-load everything for air-gapped deployment
# 1. Export Docker image (on internet-connected machine)
$ docker save ghcr.io/dyber/openforge:0.4.0 | gzip > openforge-0.4.0.tar.gz
# 2. Pre-download NIST test vectors
$ openforge vectors download --all --output nist-vectors/
# 3. Transfer to air-gapped network (USB, one-way diode, etc.)
# 4. Load on air-gapped Jenkins/GitLab runner
$ docker load < openforge-0.4.0.tar.gz
# 5. Configure offline mode
$ openforge config set --global offline_mode true
$ openforge config set --global nist_vector_cache /opt/nist-vectors/