Skip to main content
RelixQ
Menu

Release gates, PR comments and the GitHub App

Three escalating enforcement layers, a copy-paste GitHub Actions workflow, baselines, the eight-condition QAST release gate, and what the GitHub App does today.

Enforcement layers
3
Gate conditions
8
Modes
observe / warn / block
Browse the Developer Guide

The three enforcement layers

Enterprise enforces crypto posture in CI at three escalating layers. Each is independent — adopt them in order, or skip straight to the one you need. Layer 1 needs only the CLI; layers 2 and 3 need a tenant.

LayerCommandWhat it gates onHow it blocks
1. Scan exit coderelixq scan --exit-on <sev>Any finding at or above a severityProcess exit code 1 — works on every CI system
2. PR gaterelixq pr-comment --mode blockFindings in a SARIF file, with an optional base-versus-head score deltaGitHub check-run conclusion plus branch protection — not the exit code
3. QAST release gaterelixq pentest gate --mode blockNew HNDL exposures, net-new findings, score regression, and the governance policy decisionProcess exit code 1 on a fail verdict, plus a check-run conclusion

All three read the same scanner output and the same severity ladder (info < low < medium < high < critical), so a finding that trips layer 1 locally is the same finding that trips layer 2 on the pull request.

Quick start: GitHub Actions

A minimal, realistic workflow: scan the base ref and the head ref, upload SARIF to code scanning, and post the pull-request comment with a score delta.

yaml.github/workflows/relixq.yml
name: relixq-pqc-scan
on:
  pull_request:

permissions:
  contents: read
  pull-requests: write   # PR comment
  checks: write          # check run
  security-events: write # SARIF upload (optional)

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0   # needed to check out the base ref for the score delta

      - name: Install relixq
        run: |
          install -m 0755 ./tools/relixq /usr/local/bin/relixq
          relixq version

      - name: Scan base ref (for the score delta)
        run: |
          git worktree add /tmp/base "origin/${{ github.base_ref }}"
          relixq scan /tmp/base --format sarif --output base.sarif --quiet

      - name: Scan head ref
        # No --exit-on here: let the scan complete so SARIF upload and the PR
        # comment always run. Enforcement happens via the check-run conclusion.
        run: relixq scan . --format sarif --output relixq.sarif

      - name: Upload SARIF to GitHub code scanning (optional)
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: relixq.sarif
          category: relixq

      - name: Post PR comment + check run
        run: |
          relixq pr-comment \
            --sarif relixq.sarif \
            --base-sarif base.sarif \
            --mode block \
            --fail-on high
        env:
          GITHUB_TOKEN: ${{ github.token }}
  • GITHUB_REPOSITORY, GITHUB_SHA, and GITHUB_EVENT_PATH are set automatically; you only wire GITHUB_TOKEN.
  • Start with --mode warn for a rollout period, then flip to --mode block and add the check to required status checks.
  • Scanner configuration — paths, rule packs — is read from a relixq.yaml in the scanned directory. There is no --config flag.

Baselines and base-versus-head diffs

Adopting a scanner on a legacy codebase without drowning in the backlog is a two-mechanism story, and they compose.

The committed baseline file records every finding accepted at adoption time; a later scan with --baseline reports only findings absent from it. Matching uses a content fingerprint — rule plus file plus code snippet — so findings survive line-number drift.

Base-versus-head SARIF comparison decides "new" by comparing two scans instead of consulting a committed file. relixq pr-comment --base-sarif shows the score delta; relixq pentest gate --findings-sarif with --base-findings-sarif computes net-new findings, and only fingerprints absent from the base can trip the gate finding-severity and regression conditions.

The QAST release gate

relixq pentest gate --project <id> --run <id> computes one canonical decision and renders it identically to four surfaces — CLI JSON, a GitHub check run, an idempotent pull-request comment, and a forensic audit event — so the verdict in your CI logs can never disagree with the check run.

Eight named conditions feed the verdict. Each is pass, warn, fail, or not_evaluated — and not_evaluated always fails open: never a silent pass, never a fabricated one.

ConditionGates onCan block?
new-hndl-exposureNew HNDL exposures versus --base-run, at or above --fail-on relevanceYes
finding-severityNet-new static findings from the SARIF diff, at or above --fail-onYes
regressionAny net-new finding regression at allWarn-level
score-regressionLocal RelixQ Score drop between base and head findingsWarn by default; blocks when --fail-on-score-drop is set and met
policy-decisionThe governance service synchronous policy verdictYes
asset-coverage-regressionInventory coverage dropCurrently always `not_evaluated` — no real coverage endpoint exists yet, and the gate refuses to fabricate one
expired-exceptionPreviously granted exceptions that have lapsedWarn-level
ownerless-critical-remediationOpen critical remediation items with no assigned ownerWarn-level

The last three conditions need backend data, so they are evaluated only when a remote API and credential are available; offline they render not_evaluated.

Degradation, credentials and exit codes

  • Credential. Set RELIXQ_API_TOKEN in CI. With no usable credential — including the common "no OS keyring on a CI runner" case — the gate logs it and degrades to findings-only instead of failing: HNDL, policy, and backend conditions become not_evaluated, and SARIF-based conditions still run.
  • Side channels never change the verdict. The governance-exception fetch, the policy-decision fetch, and gate-history persistence are all non-fatal on failure.
  • Exit codes. 0 for a pass or warn verdict; 1 only for a fail verdict — which requires --mode block plus at least one failing condition — or an operational error.
consoleA typical block-mode invocation
relixq pentest gate \
  --project "$RELIXQ_PROJECT_ID" \
  --run "$HEAD_RUN_ID" \
  --base-run "$BASE_RUN_ID" \
  --findings-sarif relixq.sarif \
  --base-findings-sarif base.sarif \
  --mode block --fail-on high \
  --fail-on-score-drop 5 \
  --audit gate-audit.json

Use --dry-run first to see the full decision JSON without touching GitHub or disk.

The GitHub App: what it does today

Shipped and working:

  • Webhook receiver — HMAC-SHA256 signature verification with a constant-time compare, delivery-id replay protection, and fast-ack queueing.
  • Event processingpush to the default branch triggers a full platform scan; pull_request triggers a scan carrying the PR number; installation delete or suspend deactivates the installation; installation_repositories syncs repository connections into your workspace.
  • Installation token broker — signs App JWTs with a private key from the secret store, mints installation access tokens, and caches them for 50 minutes.

Designed but not shipped:

  • App-posted PR comments, check runs, and SARIF upload. The App does not write anything back to your pull request today. All PR-surface output comes from the CLI running inside your CI workflow. The CLI-based path is the shipped, supported path; results from App-triggered scans land in the dashboard, not on the pull request.
  • PR-diff-scoped platform scans. An App-triggered pull-request scan currently covers the whole connected repository rather than just the changed files. Use relixq scan --diff origin/<base-branch> in your workflow for diff-scoped scanning today.
  • A published App manifest and Marketplace listing.

Exit codes and modes reference

CommandExit `0`Exit `1`
relixq scan / scan deps / scan tlsScan completed; no finding at or above --exit-onA finding met --exit-on, or an operational error
relixq baselineBaseline writtenOperational error
relixq pr-commentComment and check run posted — regardless of findings or modeOperational error only
relixq pentest gateVerdict pass or warnVerdict fail (block mode plus a gating condition), or an operational error
ModeCheck-run conclusionCan block a merge?
observeneutralNever
warn (default)neutralNever — surfaces risk without blocking
blocksuccess when clean; failure when a gating condition tripsYes — via required status checks; pentest gate additionally exits 1

Other CI systems

Everything except the GitHub-specific publishing runs anywhere that can execute a static binary. relixq pentest gate --dry-run --json works anywhere too — the decision JSON is computed offline; parse the verdict field yourself if you want QAST-style gating on a non-GitHub system.

The check-run and PR-comment surfaces of pr-comment and pentest gate, without --dry-run or --no-publish, are GitHub-only since they require GITHUB_TOKEN, GITHUB_REPOSITORY, and GITHUB_SHA. On other CI systems use --dry-run with the scan exit code instead.

Related pages