Skip to content

Infrastructure

Reference document for the infrastructure of Quorum (quorum-sec-scan), v0.8.3. Revision: 2026-07-04. Written in English (primary language of the site), enterprise standard.

Quorum is a CLI/Docker consensus security scanning tool. It has no runtime infrastructure of its own: there is no hosted service, cluster, database, queue, load balancer or exposed API. The product runs on the consumer's machine/pipeline — a CI runner, a developer workstation or an orchestration job — and terminates the process at the end of the scan. All of the project's "infrastructure" is therefore build and distribution infra (the software supply chain), not continuous operation.

This document describes the real infra (Docker images, GHCR registry, GitHub Actions as the build platform, native binary distribution and the supply-chain posture), and explicitly declares as N/A the classic runtime-infrastructure components that do not apply to this product model.

Important note: Quorum scans Kubernetes and IaC targets (Terraform, CloudFormation, K8s manifests, Dockerfiles, etc.). That is product capability, not Quorum's runtime infrastructure. Do not conflate "Quorum analyzes K8s" with "Quorum runs on K8s".


1. Deployment model

flowchart LR
    subgraph build["BUILD platform (Quorum CI)"]
        GHA["GitHub Actions<br/>release.yml"]
        GR["GoReleaser"]
        BX["Docker Buildx + QEMU"]
    end

    subgraph dist["DISTRIBUTION (signed artifacts)"]
        GHCR[("GHCR<br/>ghcr.io/martinez1991/<br/>quorum-sec-scan")]
        REL[("GitHub Releases<br/>binaries + checksums")]
    end

    subgraph runtime["RUNTIME (CONSUMER environment)"]
        CI["CI runner<br/>(GitHub/GitLab/etc.)"]
        DEV["Dev workstation<br/>(Linux/macOS/Win)"]
    end

    GHA --> BX --> GHCR
    GHA --> GR --> REL
    GHCR -. docker pull .-> CI
    GHCR -. docker pull .-> DEV
    REL  -. binary download .-> CI
    REL  -. binary download .-> DEV
Layer Owner What exists Note
Build Quorum project GitHub Actions, GoReleaser, Buildx Ephemeral; ubuntu-latest runners
Distribution Quorum project GHCR (images), GitHub Releases (binaries) Immutable, signed artifacts
Runtime Consumer No resource provisioned by Quorum Short-lived process

2. RUNTIME infrastructure — N/A (declarations)

Quorum operates none of the components below. Each item is declared N/A with a technical justification.

Component Status Justification
Cloud (AWS/GCP/Azure) N/A There is no hosted service. The binary/image runs in the consumer's environment.
Kubernetes (its own runtime) N/A Quorum is a short-lived CLI process; there is no product Deployment, Pod or Operator. (It scans K8s manifests/clusters — that is a product function.)
Ingress / Gateway N/A There is no inbound HTTP service — no REST API and no frontend.
Load Balancer N/A There is no request traffic to balance; execution is single-process.
CDN N/A Artifact distribution is delegated to GHCR/GitHub Releases (which already have their own edge). The project operates no CDN.
WAF N/A No exposed HTTP surface to protect.
Relational database N/A State is ephemeral; correlation/consensus happen in memory per run. The only persisted "state" is a local alias cache at ~/.cache/quorum/aliases.json (a file with 0600 perms and a schemaVersion, not a DB).
Queue / messaging N/A Scanner fan-out is via goroutines in the same process (orchestrator), not an external broker.
Runtime authentication / IAM N/A There are no user accounts nor a protected API.
Runtime observability (APM/metrics/tracing) N/A No long-running service; diagnostics are via stderr logs (--log-format text\|json) and per-scanner status (ran/skipped/unavailable/error/timeout). The --metrics <file> flag emits a Prometheus textfile per run — to be scraped by the consumer's node_exporter, not an endpoint of its own.
Runtime secrets N/A Normal execution requires no secrets. OSV.dev is queried without credentials; --offline disables it. The opt-in advisory layer's remote provider is the only path that takes a key (QUORUM_ADVICE_API_KEY) and is off by default — see §8.

Future proposal (clearly separated — NOT implemented)

Per DESIGN.md §13, there is the idea of a separate runtime module (Falco or Tetragon, host OpenSCAP) as a standalone product, with a streaming model. If that were built, runtime infra (agents, continuous collection) would then exist — but that is not part of Quorum v0.8.x and is out of scope for this document.


3. Docker images (real distribution infra)

Quorum publishes two image variants, with distinct goals. Both use a multi-stage build from golang:1.26-alpine (compilation) and alpine:3.20 (runtime), both pinned by @sha256 digest (see §3.2). Both variants also bundle the advisory knowledge pack (knowledge//opt/quorum/knowledge: Phase-0 remediation templates + the digest-pinned OWASP corpus), so the opt-in advisory layer works offline out of the box.

Variant Dockerfile Contents Platforms Size/use
:slim Dockerfile Orchestrator only (quorum binary) + crosswalks + knowledge pack linux/amd64, linux/arm64 Small. BYO-scanners: scanners must be on PATH (mounted in or present in the runner).
:full Dockerfile.full Orchestrator + all 12 scanners + pre-cached grype DB + knowledge pack linux/amd64 only Self-contained for CI. Large — the jump from 6 to 12 scanners (Checkov's Python/venv, KICS assets, the grype DB and six more IaC/K8s binaries) grew the size relative to v0.2.x. The bundled scanner binaries are amd64, hence the architecture restriction.

3.1 Published tags

Generated in release.yml (Resolve version and image name step):

Variant Tags
full :full, :<version>, :<version>-full, :latest
slim :slim, :<version>-slim

The :latest tag points at full. For reproducibility, prefer a digest pin (@sha256:...) in production — see §6.

3.2 Base images and image runtime dependencies

In v0.8.3 every base layer and every scanner source is pinned by digest or verified by checksum — there is no longer a mutable base tag (the alpine:3.20 gap from v0.2.x is closed).

Image Base / source Pinning
Build stage (both) golang:1.26-alpine@sha256:3ad57304… Pinned by @sha256 digest (build stage, discarded)
Runtime stage (both) alpine:3.20@sha256:d9e853e8… Pinned by @sha256 digest
Trivy (full) aquasec/trivy:0.71.2 Pinned by @sha256 digest (tag kept for readability)
KICS (full) checkmarx/kics:v2.1.3-alpine Pinned by @sha256 digest; copies binary + ./queries assets side by side
Grype + Syft (full) official anchore installer (install.sh) Version fixed via ARG; checksum verified by the script
Dockle (full) GitHub release tarball SHA-256 checksum verified (via dockle_..._checksums.txt)
Kubescape (full) GitHub release (kubescape-ubuntu-latest binary) Pinned by KUBESCAPE_SHA256sha256sum -c, no curl \| bash fallback
Polaris (full) GitHub release tarball SHA-256 checksum verified (via checksums.txt)
kube-score (full) GitHub release tarball SHA-256 checksum verified (via checksums.txt)
tfsec (full) GitHub release (tfsec-linux-amd64 binary) SHA-256 checksum verified (via tfsec_checksums.txt)
Terrascan (full) GitHub release tarball SHA-256 checksum verified (via checksums.txt); terrascan init at build (offline policies)
Regula (full) GitHub release tarball SHA-256 checksum verified (via checksums.txt)
Conftest (full) GitHub release tarball SHA-256 checksum verified (via checksums.txt)
Checkov (full) pip install checkov==3.3.6 in an isolated venv (/opt/checkov) Version pinned (pip version pin)

System packages on :full (via apk): ca-certificates bash curl tar python3 py3-pip git docker-cli. :slim installs only ca-certificates.

3.3 Pre-cached grype DB (:full)

:full runs grype db update && grype db status at build time and freezes the vulnerability database into the image (GRYPE_DB_CACHE_DIR=/opt/grype/db, GRYPE_DB_AUTO_UPDATE=false). This ensures the first scan works offline and does not fail with "database does not exist". In addition, GRYPE_DB_VALIDATE_AGE=false is set: without it, grype refuses a DB older than 5 days (db.max-allowed-built-age) and hard-fails on every scan once the image ages a few days — we accept a baked (possibly stale) DB by design and document the rebuild cadence. The DB is frozen at build time — to update it, rebuild the image or set GRYPE_DB_AUTO_UPDATE=true at runtime (requires network).

3.4 Bundled scanner versions (Dockerfile.full)

The 12 scanners cover SCA/image (Trivy, Grype, Dockle), IaC/MISCONFIG (Checkov, KICS, tfsec, Terrascan, Regula), K8S_POSTURE (Kubescape, Polaris, kube-score) and policy-as-code (Conftest, which runs your Rego from ./policy).

Scanner Version ARG Category
Trivy TRIVY_VERSION=0.71.2 SCA / image / IaC (emits AVD)
Grype GRYPE_VERSION=v0.114.0 SCA
Syft (Grype support) SYFT_VERSION=v1.11.0 SBOM (Grype dependency)
Dockle DOCKLE_VERSION=0.4.14 Image / Dockerfile
KICS KICS_VERSION=v2.1.3 IaC / MISCONFIG
Kubescape KUBESCAPE_VERSION=v3.0.8 K8S_POSTURE (crosswalk C-#### hub)
Checkov CHECKOV_VERSION=3.3.6 IaC / MISCONFIG
Polaris POLARIS_VERSION=10.2.0 K8S_POSTURE
kube-score KUBE_SCORE_VERSION=1.20.0 K8S_POSTURE
tfsec TFSEC_VERSION=1.28.14 IaC / Terraform (emits AVD; auto-correlates with Trivy)
Terrascan TERRASCAN_VERSION=1.19.9 IaC / MISCONFIG
Regula REGULA_VERSION=3.2.1 IaC / MISCONFIG (OPA, built-in rules)
Conftest CONFTEST_VERSION=0.68.2 Policy-as-code (OPA; consumer's Rego in ./policy)

tfsec is deprecated upstream (folded into Trivy) but is kept as a distinct engine: because it emits native AVD ids, its findings auto-correlate with Trivy without a dedicated crosswalk.

Terrascan offline: the build runs terrascan init to bake the policies into the image, so the scan does not depend on network on the first run (same philosophy as the grype DB).


4. Registry — GHCR

Item Value
Registry ghcr.io (GitHub Container Registry)
Image repository ghcr.io/martinez1991/quorum-sec-scan (owner/repo lowercased)
Push authentication docker/login-action@v3 with ${{ secrets.GITHUB_TOKEN }} (packages: write scope)
Pull authentication Public (read) for consumers; no credential needed

The push is done by docker/build-push-action@v6 (push: true), which also generates native Buildx provenance and SBOM (provenance: true, sbom: true). In addition, the release attests a first-class SPDX SBOM (actions/attest-sbom, generated by anchore/sbom-action in spdx-json) and pushes it to GHCR alongside the cosign signature — see §5.1 and §6.


5. GitHub Actions as the build platform

There is no dedicated build server. The "build infra" is GitHub Actions with ephemeral ubuntu-latest runners. Relevant workflows:

Workflow Trigger Function
ci.yml push on main, PRs go vet, go test -race, coverage, build, smoke (list-scanners)
e2e.yml (end-to-end consensus) Consensus validation
release.yml semver tag v[0-9]+.[0-9]+.[0-9]+; workflow_dispatch Build/publish/sign/attest images, binaries and the knowledge pack
tag-major.yml after a semver release Advances the moving v0 tag (Action composite pin) automatically

5.1 Release pipeline (release.yml)

flowchart TD
    TAG["git tag vX.Y.Z<br/>(strict semver)"] --> J1

    subgraph J1["job: images (matrix full|slim)"]
        A["actions/checkout"] --> B["QEMU + Buildx"]
        B --> C["login GHCR (GITHUB_TOKEN)"]
        C --> D["build-push-action<br/>provenance + sbom (BuildKit)"]
        D --> E["cosign sign (keyless OIDC, with retry)<br/>signs the manifest digest"]
        E --> F["attest-build-provenance<br/>(SLSA v1) push to GHCR"]
        F --> G["gh attestation verify<br/>(fails the release if broken)"]
        G --> M["syft SBOM (SPDX) + attest-sbom<br/>(attested SBOM, push to GHCR)"]
    end

    TAG --> J2

    subgraph J2["job: binaries (tag only)"]
        H["install syft<br/>(GoReleaser SBOMs)"] --> I["GoReleaser release --clean"]
        I --> K["attest-build-provenance<br/>(checksums.txt)"]
        K --> L["gh attestation verify<br/>(spot-check 1 artifact)"]
    end

    TAG --> J3

    subgraph J3["job: knowledge"]
        N["actions/checkout"] --> O["compute knowledge.sha256<br/>(sorted templates + OWASP corpus)"]
        O --> P["attest-build-provenance<br/>(subject-checksums)"]
        P --> Q["gh attestation verify<br/>knowledge/owasp/corpus.yaml"]
    end

The moving v0 tag (used to pin the GitHub Action composite) does NOT trigger a release — the trigger is restricted to full semver vX.Y.Z; tag-major.yml advances it after the release. See action.yml and §7.

5.2 Release workflow permissions

permissions:
  contents: read        # (binaries job elevates to write — creates the release and uploads assets)
  packages: write       # push to GHCR
  id-token: write       # cosign keyless (Sigstore OIDC)
  attestations: write   # SLSA build-provenance and SBOM attestations

The knowledge job runs with contents: read + id-token: write + attestations: write (keyless SLSA attestation over the pack files).


6. Supply chain and pinning (DESIGN §12)

Project principle: a bundled scanner is part of YOUR trust boundary. The v0.8.3 supply-chain posture is:

  • Bases pinned by @sha256 (golang:1.26-alpine, alpine:3.20) — no longer a mutable tag.
  • Scanner images pinned by @sha256 digest (Trivy and KICS).
  • Kubescape pinned by release SHA256 (sha256sum -c, no curl | bash fallback).
  • Checksums verified at build for Dockle, Polaris, kube-score, tfsec, Terrascan, Regula and Conftest (each against its respective checksums.txt/*_checksums.txt); the anchore installers (Grype/Syft) validate the checksum internally.
  • Fixed versions by ARG/pip pin for every scanner.
  • cosign keyless signing (identity via GitHub OIDC — no keys to manage), with retry (the OIDC endpoint + Sigstore/Fulcio/Rekor occasionally expire the token mid-sign on long builds).
  • SLSA build-provenance attestation (actions/attest-build-provenance), verified in the release itself (gh attestation verify), so a broken attestation fails the build.
  • Attested SPDX SBOM (actions/attest-sbom), first-class and verifiable, in addition to the native Buildx SBOM (sbom: true); GoReleaser also generates per-binary SBOMs (syft).
  • Attested advisory knowledge pack: the knowledge job computes a sorted knowledge.sha256 over the remediation templates + the digest-pinned OWASP corpus (knowledge/** + crosswalk/** YAML) and produces a SLSA build-provenance attestation for it, verified in the release. The pack ships inside both images (/opt/quorum/knowledge) but is also a standalone data artifact a consumer can verify independently.
  • THIRD_PARTY_NOTICES.md published with third-party licenses.

6.1 Consumer-side verification

# Image (multi-arch manifest) — cosign keyless signature
cosign verify ghcr.io/martinez1991/quorum-sec-scan:slim \
  --certificate-identity-regexp \
    "https://github.com/Martinez1991/quorum-sec-scan/.github/workflows/release.yml@.*" \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

# Image SLSA build-provenance attestation
gh attestation verify "oci://ghcr.io/martinez1991/quorum-sec-scan@sha256:<digest>" \
  --repo Martinez1991/quorum-sec-scan

# Attested SPDX SBOM for the image (same verification flow)
gh attestation verify "oci://ghcr.io/martinez1991/quorum-sec-scan@sha256:<digest>" \
  --repo Martinez1991/quorum-sec-scan --predicate-type https://spdx.dev/Document

# Native binary — provenance attestation
gh attestation verify quorum_<version>_linux_amd64.tar.gz \
  --repo Martinez1991/quorum-sec-scan

# Advisory knowledge pack (OWASP corpus) — provenance attestation
gh attestation verify knowledge/owasp/corpus.yaml \
  --repo Martinez1991/quorum-sec-scan

6.2 Image hardening checklist (DESIGN §12)

  • [x] Trivy/KICS scanner images pinned by @sha256.
  • [x] golang:1.26-alpine and alpine:3.20 bases pinned by @sha256v0.2.x gap closed.
  • [x] Kubescape pinned by release SHA256 (no curl | bash fallback).
  • [x] Dockle, Polaris, kube-score, tfsec, Terrascan, Regula and Conftest verified by SHA-256 checksum.
  • [x] Scanner versions pinned by ARG/pip pin.
  • [x] cosign keyless signature over the manifest digest (with retry).
  • [x] SLSA build-provenance attestation verified in the release (fails if broken).
  • [x] Attested SPDX SBOM (attest-sbom) in addition to the native Buildx SBOM.
  • [x] Advisory knowledge pack SLSA-attested (knowledge job, verified against corpus.yaml).
  • [x] Pre-cached grype DB (and GRYPE_DB_VALIDATE_AGE=false) for offline first-scan operation.
  • [x] Terrascan with terrascan init baked in for offline policies.
  • [ ] Grype/Syft pinned by release @sha256 — today via the anchore installer with a fixed version and a checksum validated by the script, but not an immutable digest. Suggested additional hardening.

Production recommendation: the consumer should pin the Quorum image by @sha256 (the action's image input documents this) instead of using :full/:latest.


7. GitHub Action composite (action.yml)

The repository exposes a composite Action that wraps the :full image, allowing uses: instead of hand-writing docker run. By default it cosign-verifies the image before running it (input verify: true).

- uses: Martinez1991/quorum-sec-scan@v0   # moving v0 tag (advanced by tag-major.yml)
  with:
    target: .
    type: repo
    fail-on: high
    image: ghcr.io/martinez1991/quorum-sec-scan:full  # pin by @sha256 in production
    scanner-args: ""        # per-scanner passthrough (e.g. an API key)
    docker-socket: ""       # optional; in type=image the socket is auto-mounted
    # ── advisory layer (opt-in, off by default) ──
    advice: "false"                 # attach remediation templates + OWASP references
    advice-provider: "none"         # none | local | remote
    advice-endpoint: ""             # OpenAI-compatible endpoint (local provider)
    advice-model: ""                # recommendation model
    advice-embed-model: ""          # embedding model (semantic RAG)
    advice-max: ""                  # cap advisory attachments
    advice-cache: ""                # on-disk advice cache path
    advice-allow-egress: "false"    # required consent for the remote provider
    advice-api-key: ""              # forwarded as QUORUM_ADVICE_API_KEY (remote)
    fix: "off"                      # off | suggest (verify-the-fix patch)

Internally the action: (1) installs cosign if absent; (2) verifies the keyless signature against the release.yml OIDC identity; (3) runs docker run --rm -v <workdir>:/work -w /work <image> scan <target> ...; (4) in type: image, auto-mounts /var/run/docker.sock to avoid a false-zero when scanning a local image; (5) propagates Quorum's exit code (0 ok, 1 gate tripped, 2 error) and exposes output-file. For advice-provider: local it auto-adds --add-host host-gateway so the container can reach an on-host endpoint (e.g. Ollama); for remote it forwards the API key via env.

Per-scanner passthrough is also available via the QUORUM_<SCANNER>_ARGS env (e.g. QUORUM_CHECKOV_ARGS="--bc-api-key ..." unlocks Prisma policies in Checkov). See §8.


8. Secrets

Context Secret Use
Runtime (consumer) None (required) The scan requires no secrets. OSV.dev is public (no key); --offline disables network lookups. Optional keys (e.g. Checkov's bc-api-key) can be passed via QUORUM_<SCANNER>_ARGS. The opt-in remote advisory provider takes QUORUM_ADVICE_API_KEY — off by default, gated on --advice-allow-egress, and blocked by --offline.
CI/build (Quorum) GITHUB_TOKEN (ephemeral, from GitHub) GHCR login, release creation, gh attestation verify.
Signing No key cosign keyless — the identity comes from the Actions OIDC token; nothing to store/rotate.

There is no private signing key, cloud credential or long-lived secret in the project. This is a direct property of the CLI/keyless model. Secrets that Trivy happens to detect in the target have their Match redacted in Quorum's output (redaction). The remote advisory provider sends only the normalized finding — never source code — and refuses --fix.


9. Native binary distribution (GoReleaser)

Binaries generated by .goreleaser.yaml in the release binaries job (tag only).

Item Value
OS linux, darwin, windows
Arch amd64, arm64
Build CGO_ENABLED=0, -trimpath, -ldflags "-s -w -X main.version=..."
Archive quorum_<version>_<os>_<arch>.tar.gz (.zip on Windows)
Archive contents binary + README.md, README.pt-BR.md, LICENSE, crosswalk/ directory
Checksums checksums.txt (sha256 name format)
SBOM Per-binary SBOMs generated by GoReleaser (syft installed in the job)
Signature cosign sign-blob over checksums.txt (.sig + .pem)
Attestation SLSA build-provenance over dist/checksums.txt (covers every listed artifact)
Publication GitHub Releases (Martinez1991/quorum-sec-scan), prerelease: auto

The archive bundles the crosswalk/ directory. At runtime, the --crosswalk flag defaults to ./crosswalk with an automatic fallback to /opt/quorum/crosswalk (the path used in the Docker images). The advisory knowledge pack ships in the Docker images (/opt/quorum/knowledge) and as a separately attested data artifact, not inside the native binary archive.


10. Sizing, cost and capacity

Since there is no hosted runtime, there is no operational infra sizing (no instances, replicas, autoscaling or product cloud cost). The only capacity considerations are:

  • Build: GitHub Actions minutes on ubuntu-latest runners (project cost, not the consumer's). :full bundles 12 scanners + grype DB, so it is the heaviest build job.
  • Distribution: package storage on GHCR and assets in GitHub Releases. :full is larger than in v0.2.x due to the doubled scanner count (Checkov's Python/venv, KICS assets, the grype DB and six additional IaC/K8s binaries).
  • Consumer runtime: CPU/RAM of the runner or workstation where the scan runs. The orchestrator fans out in parallel (goroutines) with a per-scanner timeout (default 5m) and a 60s version probe; consumption is dominated by the bundled scanners (especially Trivy/Grype with the DB).
  • DoS guardrails: output-size caps (QUORUM_MAX_OUTPUT_BYTES, default 512MiB) and target caps (QUORUM_MAX_TARGET_BYTES, default 20GiB) prevent memory/disk blowups on pathological scans.

11. Disaster recovery / continuity

Scenario Posture
Runtime loss N/A — there is no runtime state to recover; each run is independent.
GHCR unavailability Consumer uses an already-pulled/digest-pinned image, or falls back to the native binary.
OSV.dev unavailability Graceful degradation: local scanner aliases + cache (~/.cache/quorum/aliases.json); --offline avoids the network entirely.
IaC/K8s target without network The grype DB and Terrascan policies are pre-cached in :full, so the first scan works offline.
Advisory model unreachable Graceful degradation: the report ships without AI advice and the scan never fails; deterministic Phase-0/Phase-2 templates and OWASP references still attach (they ship in the image).
Corrupted release attestation/signature The release job fails (gh attestation verify — images, binaries and the knowledge pack), blocking a bad publish.

Assumptions

  • The upstream repository and GHCR owner are Martinez1991/quorum-sec-scan / ghcr.io/martinez1991/quorum-sec-scan, per action.yml and release.yml. Forks will use a different owner.
  • "Infrastructure" was interpreted as build + distribution + supply-chain infra, since the product has no hosted runtime. Classic runtime components were declared N/A with justification, per the task.
  • Image sizes are not measured in the repository; descriptions like "small/large" are qualitative, derived from the Dockerfile contents (:full bundles 12 scanners + grype DB + Terrascan policies + the knowledge pack; :slim, only the binary + crosswalks + knowledge pack). The claim that :full grew relative to v0.2.x derives from the increase from 6 to 12 bundled scanners, not from a measurement.
  • The base @sha256 digests are cited truncated; the full values are in Dockerfile/Dockerfile.full and must be re-resolved when bumping a version.
  • The e2e.yml job is cited from its known trigger/purpose; its detailed contents are out of scope for this infrastructure document.
  • The claim that :full's first scan works offline assumes a target compatible with the grype DB frozen and the Terrascan policies baked in at image build time.
  • The advisory layer is opt-in and off by default: without --advice the output is byte-identical and no AI/egress code path runs. The deterministic core (correlationKey, fingerprint, confidence, aggregated severity, fail-on gate) is untouched by it.