Skip to content

Command line (synapse-cli)

Documentation home · Previous: Configuration · Next: Architecture

synapse-cli runs the same SCA pipeline as the server, from the command line. It is built for CI gating. It creates an ephemeral, scope-checked engagement covering the target path, so scope enforcement is exercised, not bypassed. Nothing is persisted.

Build it with make build. The binary lands at ./bin/synapse-cli.

The benchmark-only operator binaries synapse-sca-bench, synapse-sca-cycle, and synapse-sca-prepare are intentionally outside product composition. The cycle binary runs the fixed same-SBOM accuracy workflow; the prepare binary validates pinned inputs before capture. Their input and artifact contracts are documented in SCA accuracy benchmark.

synapse-reachability-cycle current-go-binary-scorecard assembles the hosted Go-binary regression result from the API and worker binding reports. It requires Linux/amd64 and SYNAPSE_GOBIN_BINDING_REPORT_DIR pointing to a directory with api.json and worker.json from the binding tests. It writes a sanitized scorecard under the process temp directory; missing, mismatched, or stale report identities and a failed ratchet exit nonzero. The fixture uses the root go.mod and go.sum pins, so prepare dependencies with go mod download before an offline local run. The hosted workflow uploads the scorecard as an artifact when generated and fails the gate unless it passes.

Assessment lifecycle administration

These are separate operator binaries, not synapse-cli subcommands. make build places them in bin/; the production image includes them in /opt/synapse/. Follow Assessment lifecycle rollout operations for migration order, dry-run examples, checkpoints, approval and rollback gates.

Binary Purpose
synapse-assessment-backfill Create historical singleton Cycles with durable checkpoints.
synapse-assessment-snapshot-backfill Append immutable legacy Snapshots after Cycle backfill.
synapse-finding-lineage-backfill Project source Findings into Identities, Observations, review candidates or explicit skips.
synapse-assessment-integrity Verify membership, boundaries and source reconciliation without repairing source data.
synapse-assessment-comparison-backfill Queue root-to-selected-head comparisons and optionally repair failed artifacts.
synapse-assessment-rollout-gate Evaluate supplied JSON rollout evidence; it does not enable feature flags or collect metrics.

The first five commands require SYNAPSE_DB_DSN and a non-superuser role that cannot bypass RLS. They do not run schema migrations. Integrity verification persists its run/checkpoints/findings but never changes the assessed source data.

Flag Applies to / behavior
--tenants All database commands; required comma-separated list of one to four unique tenant IDs.
--actor All database commands; audit actor, 1–256 characters, defaults to the command-specific service actor.
--dry-run All backfills; default false, so pass it explicitly to preview. Integrity defaults to true and rejects false.
--batch-size All database commands; 1–2000 rows/items per batch. Default 500, except comparison uses SYNAPSE_ASSESSMENT_MIGRATION_BATCH_SIZE.
--timeout All database commands; positive duration. Default 30m, or 2h for comparison backfill.
--lease-duration Cycle/Snapshot/Lineage backfill and integrity; positive per-tenant lease, default 10m.
--resume-after Cycle/Snapshot backfill: initial Assessment ID; Lineage: initial Finding ID. Exactly one tenant required. Durable existing runs resume their own checkpoint.
--producers Lineage only; optional comma-separated producer filter. See the operations guide for supported names and deferred matcher skips.
--repair-failed Comparison only; default false. Repair one deterministic failed batch before queueing missing comparisons.
--backlog-warning Comparison only; default from configuration (500), allowed 1–500.
--backlog-hard-limit Comparison only; default from configuration (1000), between the warning threshold and 1000.
--oldest-active-limit Comparison only; positive age gate for queued/generating artifacts, default 15m.
--after-updated-at, --after-cycle-id Comparison only; paired RFC3339 timestamp / Cycle ID checkpoint, exactly one tenant required.
--phase Rollout gate only; required internal_canary, opt_in_canary, read_cutover, ui_default or rollback_drill.
--input Rollout gate only; JSON file or - for stdin (default), at most 1 MiB, exactly one object with no unknown fields.

Successful runs exit 0; invalid input, runtime errors, integrity findings or a rejected rollout gate exit 1 and explain the failure on stderr. The rollout gate also emits the evaluated decision JSON on stdout when policy rejects valid input. Do not use process success alone as production approval: the operations guide requires retained evidence and explicit operator/security sign-off.

RulePack release commands

The rulepack command group verifies signed detection-content artifacts, replays their deterministic fixtures, and evaluates attested release evidence. The RulePack content-signing key and gate-evidence producer key are supplied independently as standard-base64 text containing raw 32-byte Ed25519 public keys; neither artifact is allowed to self-authorize an embedded trust key.

synapse-cli rulepack verify \
  --artifact rulepack.signed.json \
  --public-key rulepack-release.pub

synapse-cli rulepack replay \
  --artifact rulepack.signed.json \
  --public-key rulepack-release.pub

synapse-cli rulepack gate \
  --artifact rulepack.signed.json \
  --public-key rulepack-release.pub \
  --evidence rulepack-gate-evidence.json \
  --evidence-public-key rulepack-evidence.pub \
  --phase promotion
Subcommand / flag Description
rulepack verify Recompute the canonical digest and verify the RulePack signature against --public-key. Prints the verified pack identity as JSON.
rulepack replay Verify the artifact, then run positive fixtures before negative fixtures in deterministic order. Prints replay results as JSON and exits non-zero on any mismatch.
rulepack gate Verify both the RulePack and the attested gate-evidence envelope, emit the deterministic gate report, and exit non-zero when the selected lifecycle phase is not eligible.
--artifact <file> Signed RulePack JSON. Required by all three RulePack subcommands.
--public-key <file> Externally trusted RulePack Ed25519 public key. Required by all three subcommands.
--evidence <file> Attested SignedGateEvidence JSON. Required by rulepack gate. Raw/unattested gate input is rejected.
--evidence-public-key <file> Externally trusted Ed25519 key for the evidence producer. Required by rulepack gate and intentionally separate from the RulePack content key.
--phase pre-canary\|canary\|promotion Which lifecycle threshold to enforce. Default: promotion. pre-canary requires deployment state candidate; canary and promotion require state canary.

Successful verification/replay/gating exits 0; a signature, provenance, replay, lifecycle-state, or gate failure exits non-zero and prints the reason on stderr. rulepack gate writes its deterministic report to stdout after provenance verification even when the requested gate subsequently fails, so CI can retain the failure evidence. See RulePack release lifecycle for the signed artifact schema, retro-hunt selector provenance, release-stage ordering, metrics, and rollback semantics.

Doctor

synapse-cli doctor [path] [--json]

doctor is an offline pre-scan readiness check. It does not run a scan, install tools, or call the network. It reports optional toolchain availability, dependency markers found in the target tree, and whether SCA, SAST, secret, misconfig, and code-quality coverage is full, partial, or unavailable.

# preview what Synapse can analyze before scanning the current tree
synapse-cli doctor .

# emit structured output for CI or wrapper scripts
synapse-cli doctor . --json

Scan

synapse-cli scan <path|image-ref> [flags]
Flag Description
--mode full\|vulnerabilities\|licenses What to scan. Default is full.
--fail-on critical\|high\|medium\|low\|info Exit non-zero if a finding at or above this severity is present. Default is high.
--image Treat the argument as a container image reference, pulled daemonlessly in-process, instead of a local path.
--offline Make the scan run without network egress. Detection uses the local sources only, the owned advisory store, plus Grype's pre-synced database when SYNAPSE_DETECTION_SOURCES names it (Grype is not in the default set), and every network-capable resolver and enricher is switched off: npm, composer, poetry, Bundler, Maven, Gradle, the Maven Central JAR SHA-1 lookup, KEV/EPSS, online NVD CVSS backfill, deps.dev and PyPI license metadata, and AI false-positive triage. SYNAPSE_OFFLINE=true does the same. Recall drops in exchange; the run makes no outbound request. Target acquisition is the one step outside the flag: a registry --image reference or a remote git URL is still fetched, so on an air-gapped runner point the scan at a local path or a local OCI layout.
--ignore-unfixed Ignore vulnerabilities that have no fix available.
--min-confidence low\|medium\|high\|very_high Drop findings below this confidence. Findings that carry no confidence (SAST/misconfig) are kept. Useful to cut lower-signal secret matches.
--base <ref> Scope line-anchored findings (SAST, secret, misconfig) to code changed vs this git ref (Clean-as-You-Code), so a repo with a backlog gates the pipeline only on what a change introduces. Dependency/license findings are not line-attributable and are kept, baseline those with .synapseignore. Local git repos only (not --image).
--detection-priority comprehensive\|precise comprehensive (default) reports every match. precise moves single-source, non-KEV findings into a needs-verify queue that does not trip --fail-on.
--include-test Also gate on findings in test, fixture, and example paths. They are reported but gate-exempt by default.
--verify-secrets Actively confirm each detected credential is live by making one minimal read-only API call to its provider's public host (GitHub, GitLab, OpenAI), stamping the finding verified/unverified/unknown. Opt-in and off by default, and IGNORED under --offline (which forbids network egress). It sends the raw leaked secret over the network to the public provider host, so use it only against credentials you are authorized to test; a self-managed-instance token is still sent to the public host and yields unknown. The secret is never logged or written to output; a verified live credential is raised to very_high confidence, and an unverified or unknown verdict never removes a finding.
--json Print the full scan result as JSON to stdout, for machine consumption in CI.
--sarif Print a SARIF 2.1.0 report to stdout, ready to upload to GitHub code scanning. Covers every finding kind; SAST, secret and misconfig findings carry a file and line so the platform annotates the exact source line. Findings exempted from the CI gate by verified AI consensus remain present and carry an external suppression with the policy version and reason. --fail-on still sets the exit code.
--sarif-out <file> Write the same SARIF report to <file> and keep the human report on stdout. Prefer this in a pipeline: --sarif owns stdout, so redirecting it to a file leaves the job log with nothing but the exit code.
--sbom Print the generated CycloneDX SBOM to stdout instead of a findings report.
--server <url> --project <key> Record the result on a Synapse server as that project's next analysis. The token comes from SYNAPSE_API_TOKEN. https is required unless the host is loopback. See Push results to the console.
--engagement <id> Record the scan's security findings on that engagement, through the server's own SARIF ingest, so a pipeline scan reaches the console the way a code-quality analysis does. Independent of --project: give either, or both to record one run in both places. The server deduplicates against existing first-party findings and reports what it refused.
--asset <id> Bind the findings ingested by --engagement to a business asset. Needs --engagement.
--coverage <file> Record test coverage with the project analysis. lcov, cobertura and jacoco are auto-detected, by the same parser synapse-cli coverage and gate --coverage use. The analysis payload has always carried a line_coverage field and the scan had no way to fill it, so a pushed analysis showed no coverage and a managed gate could not evaluate a coverage condition. Needs --project.
--push-sbom Import the generated CycloneDX SBOM into the engagement, which is how an image scan's component inventory becomes visible on the console beside its findings. The server keeps one active imported SBOM per engagement, so this replaces any SBOM imported by hand. Needs --engagement.
--push-source Upload the scanned tree for the analysis --project creates, so the console's Code view can show the source. Without it a CI-pushed analysis reports source as unavailable with the reason not_retained, because the CLI pushes results and not files. Best-effort: a failed upload warns and names the publish-source command that retries it, and never fails the build. Needs --project.
--insecure-http Accept a plain-http --server that is not loopback. The API token then travels in the clear; use it only on a network you trust.
--branch <ref>, --run-url <url>, --ci-provider <name> What the pipeline says about itself, shown on the analysis in the console. On GitHub Actions, GitLab CI and Jenkins these are read from the provider's variables when not given.

--json, --sarif, and --sbom each take over stdout completely, so they are mutually exclusive. Passing more than one exits 2 rather than silently honoring the last flag.

Suppressing findings with .synapseignore

Drop known/accepted findings by committing a .synapseignore file at the scan root. Every suppression must carry a reason and an expires date (YYYY-MM-DD), after the expiry the suppression stops applying and the finding reappears (with a warning), so suppressions are periodically re-justified instead of rotting. Each entry matches by rule (a rule key or advisory id), by path (a file glob), or both:

suppress:
  - rule: generic-secret
    path: "testdata/**"
    reason: "test fixtures, not real credentials"
    expires: "2026-12-31"
  - rule: CVE-2024-1234
    reason: "not exploitable in our configuration; tracked in JIRA-123"
    expires: "2026-09-30"

A malformed entry (missing reason/expiry, or no matcher) fails the scan rather than silently ignoring nothing. Suppressed counts and any expired entries are printed to stderr.

Examples

# fail a build on any high-or-critical vulnerability
synapse-cli scan . --fail-on high

# licenses only
synapse-cli scan . --mode licenses

# scan a container image, offline
synapse-cli scan alpine:3.19 --image --offline

The exit code is 0 when no finding meets the --fail-on threshold. See Exit codes for the full contract, which distinguishes a gate result from a usage error.

Push results to the console

Without --server, synapse-cli scan is a self-contained gate: it prints a report, sets the exit code, and nothing is persisted. With --server, the same scan is also recorded on a Synapse server as the named project's next analysis:

export SYNAPSE_API_TOKEN="$CI_SECRET_SYNAPSE_TOKEN"
synapse-cli scan . --fail-on high --server https://synapse.example.com --project payments-api

What happens on the server is exactly what happens for an analysis the server runs itself. The result goes through the same recorder, so it takes its place in the project's history, moves the trend on the Activity page, is evaluated against the project's managed quality gate, and carries ratings, issues and hotspots. The analysis is marked origin: ci and shows the branch, the run and the actor the pipeline reported, with a link back to the run.

Two things are different from a server-run analysis, and the console says so. The branch and commit are the pipeline's own account, since the server did not clone anything. And the gate that decides is the server's: the CLI's --fail-on threshold still sets this process's exit code for the pipeline, and the server's managed gate verdict is printed alongside it. If they disagree, both are right about what they measure, and the server's is the one the console records.

A push that fails is an error whatever the local gate says. A pipeline that asked for its result to be recorded must not go green because the record silently did not happen.

--project records a full source analysis, so it cannot be combined with --image or with a --mode other than full: measures, ratings and a code-quality report have no meaning for an image. --engagement has no such limit, because an ingest is findings and an image scan produces those as well as a source scan does. An image pipeline therefore records with --engagement, optionally adding --push-sbom so the image's package inventory lands on the console too, and the findings appear under the engagement's Imported tab with the tool and rule that produced each one.

Recording a result needs the operate permission. Give the pipeline its own user with that role rather than the bootstrap operator token.

CI pull / merge-request identity

When a scan is pushed with --server, Synapse also captures provider-neutral pull/merge-request identity for later PR decoration. Explicit CLI CI fields still win; the variables below fill only missing values. The forge head SHA is the source commit for the change, not GitHub's synthetic merge commit.

Provider Variables used
GitHub Actions GITHUB_HEAD_REF, GITHUB_BASE_REF, GITHUB_REPOSITORY, GITHUB_REF, and the pull_request payload in GITHUB_EVENT_PATH (including head.sha).
GitLab CI CI_MERGE_REQUEST_IID, CI_MERGE_REQUEST_TARGET_BRANCH_NAME, CI_PROJECT_PATH, CI_MERGE_REQUEST_SOURCE_BRANCH_SHA (falling back to CI_COMMIT_SHA).
Bitbucket Pipelines BITBUCKET_PR_ID, BITBUCKET_PR_DESTINATION_BRANCH, BITBUCKET_REPO_FULL_NAME, BITBUCKET_COMMIT.
Jenkins multibranch CHANGE_ID, CHANGE_TARGET, GIT_COMMIT; set SYNAPSE_REPO_SLUG when Jenkins cannot infer the repository slug.

Provider-independent overrides are SYNAPSE_PR_NUMBER, SYNAPSE_PR_TARGET_BRANCH, SYNAPSE_REPO_SLUG, and SYNAPSE_PR_HEAD_SHA. Decoration is skipped unless all four identity fields are present.

False-positive gate

A scan of a real repository surfaces findings in test files and deliberately-insecure fixtures. Synapse handles this in two layers, and neither ever deletes a finding, both are retain-and-mark (the finding stays in the report, it is only held back from the --fail-on gate).

  1. Deterministic test scope. Findings in test/fixture/example/benchmark/docs paths, including the foo_test.go, test_foo.py, foo.test.ts, foo_spec.rb file conventions where the test sits beside its source, are classified as background scope and are exempt from the gate by default. Pass --include-test to gate on them too. This alone removes the bulk of the noise.

  2. AI critique (opt-in). Set SYNAPSE_FP_TRIAGE_ENABLED=true with an LLM endpoint configured (SYNAPSE_LLM_BASE_URL, SYNAPSE_LLM_API_KEY, and SYNAPSE_FP_TRIAGE_MODEL or SYNAPSE_LLM_MODEL). After the deterministic pass, the model adjudicates the remaining production-scope first-party source findings (SAST/misconfig; secret findings are never sent to the LLM) and returns a typed verdict, refuted (suspected false positive), sound, or uncertain, with a confidence. The proposer only advises: single-model output can never change the gate. Set SYNAPSE_VERIFIER_MODEL to a different model family to enable consensus. The verifier may use its own SYNAPSE_VERIFIER_BASE_URL, SYNAPSE_VERIFIER_API_KEY, and explicit SYNAPSE_VERIFIER_PROVIDER; the proposer provider is SYNAPSE_FP_TRIAGE_PROVIDER (defaulting to SYNAPSE_LLM_PROVIDER). The verifier runs first with only the finding and source context; it never sees the proposer verdict. Provider prefixes, dated aliases, and Amazon Bedrock geographic/global inference-profile IDs are canonicalized fail-closed so one model family cannot verify itself under two names. Set SYNAPSE_FP_TRIAGE_INDEPENDENCE=provider to require both a different provider and a different model family; missing/unknown identity metadata leaves triage advisory-only. Provider/model-family/policy metadata is retained in the scan evidence. The rollout mode defaults to SYNAPSE_FP_TRIAGE_MODE=shadow: Synapse stores would_gate_exempt for measurement, always forces gate_exempt=false, and keeps the finding gating. Set the mode explicitly to enforce only after the evaluation threshold is approved. In enforced mode, a finding is gate-exempt only when both models independently refute it at/above the bar and the deterministic human-review floor permits it. High/critical findings, secrets, and dangerous injection/auth/access- control/SSRF/traversal/upload/deserialization CWEs always stay gating.

Every finding remains in JSON/SARIF/compliance. The ai_triage JSON separates suspected_fp, verified, gate_exempt, and review_required, and carries model/prompt/policy metadata. These fields are sealed into the scan evidence hash-chain when a ledger is configured. The standalone CLI currently has no evidence vault, so AI triage there is advisory-only and never exempts the gate. Model, verifier, or evidence availability failure leaves the gate unchanged.

Per scan, Synapse attempts at most SYNAPSE_FP_TRIAGE_MAX_FINDINGS=100 eligible findings with SYNAPSE_FP_TRIAGE_CONCURRENCY=6 simultaneous assessments by default. A distinct verifier can make at most two provider calls per attempted finding. If the cap is reached, selection is deterministic, every skipped finding remains reported and gating, and ai_triage_budget plus the CLI warning expose eligible, attempted, and skipped counts. Accepted ranges are 1..1000 findings and 1..32 concurrent assessments; zero, negative, malformed, and over-limit values restore the safe finite defaults. A second reservation guard defaults to SYNAPSE_FP_TRIAGE_MAX_TOKENS=1000000. Optional micro-USD pricing and SYNAPSE_FP_TRIAGE_MAX_COST_MICRO_USD add a strict cost ceiling. Reservations happen before either model is contacted, so a finding that does not fit receives no partial verifier call and remains gating. Repeated provider or invalid-output failures open a bounded circuit and keep the remaining findings advisory-only until its cooldown probe succeeds. API deployments expose the resulting request, latency, timeout, parse, token/cost, disagreement, exemption and alert views at /api/v1/ai-triage/observability. The same response carries normalized language/CWE/project distributions for offline drift checks:

go run ./cmd/synapse-fptriage-drift \
  --baseline ai-triage-drift-baseline.json \
  --observed ai-triage-observability.json \
  --output ai-triage-drift-report.json

The baseline owns its human approval, minimum sample size, and maximum total-variation distance. The command writes deterministic evidence before returning a non-zero drift alert; it never changes runtime gate behavior. See AI triage evaluation.

export SYNAPSE_LLM_BASE_URL=http://localhost:8081/v1
export SYNAPSE_LLM_API_KEY=…
SYNAPSE_FP_TRIAGE_ENABLED=true SYNAPSE_FP_TRIAGE_MODE=shadow SYNAPSE_FP_TRIAGE_MODEL=<model> \
  synapse-cli scan . --fail-on high --json

To evaluate a model/prompt/policy combination against the repository's versioned non-production golden dataset, run synapse-fptriage-eval. It emits deterministic JSON with precision, recall, false-negative escape rate, disagreement, coverage, language/kind/CWE/severity/framework/adversarial breakdowns, and pairwise adversarial invariance evidence. The bundled v2 dataset pairs a clean control with a semantically equivalent prompt-injection challenge; the v3 report records proposer, verifier, consensus, and policy flips without copying source into the robustness summary:

SYNAPSE_FP_TRIAGE_MODEL=<proposer> SYNAPSE_VERIFIER_MODEL=<verifier> \
  go run ./cmd/synapse-fptriage-eval --output ai-triage-eval.json

The evaluator always invokes the server policy in shadow mode. A report containing gate_exempt=true is rejected, so an evaluation run can never authorize a production quality gate.

Before reviewing a new model or prompt for promotion, compare its shadow report with the approved baseline on the same dataset and policy:

go run ./cmd/synapse-fptriage-compare \
  --baseline ai-triage-baseline.json \
  --candidate ai-triage-candidate.json \
  --output ai-triage-comparison.json

The command exits non-zero on a quality regression or adversarial flip but writes the deterministic comparison evidence first. The default policy requires complete counterfactual coverage, complete verifier coverage whenever a pair reaches the refuted branch, and zero proposer/verifier/consensus/policy flips. A passing result is still review_required; it never changes runtime AI configuration.

After that result, use synapse-fptriage-release to bind the baseline, candidate, comparison, unique release version, and independent PM/Security approvals into a hash-chained ledger. Rollback appends another approved decision targeting initial or a previous decision. The command writes a new ledger file for every event and never changes live AI-triage or gate configuration. See AI triage evaluation.

# First print the exact digest PM and Security must approve.
go run ./cmd/synapse-fptriage-release \
  --manifest ai-triage-release.json \
  --comparison ai-triage-comparison.json \
  --baseline ai-triage-baseline.json \
  --candidate ai-triage-candidate.json \
  --print-review-digest

# After both approvals are added to the manifest, create a new ledger artifact.
go run ./cmd/synapse-fptriage-release \
  --manifest ai-triage-release-approved.json \
  --comparison ai-triage-comparison.json \
  --baseline ai-triage-baseline.json \
  --candidate ai-triage-candidate.json \
  --output ai-triage-release-ledger.json

The AI critique reads the target's own source into the prompt, so an untrusted PR can still try prompt injection through comments or strings. Distinct consensus and the human-review floor bound the risk, and the finding always remains in SARIF/JSON, but treat AI triage as advisory for untrusted contributor code.

Exit codes

The exit code is a CI contract. A pipeline that branches on it can distinguish a real finding from a misconfigured invocation:

Code Meaning
0 The command succeeded and no gate threshold was crossed.
1 A runtime failure, or a gate result: a finding at or above --fail-on, a failed quality gate, coverage below its threshold, or a rating below --fail-below.
2 Invalid usage: an unknown or incomplete flag, a missing required argument, or an invalid enum value such as --fail-on none. Nothing was analyzed.

Treat 2 as "fix the pipeline definition" and 1 as "inspect the findings". Retrying an exit 2 unchanged will always fail again.

Container image (Docker)

The current release workflow does not publish a container image. Build one locally when a containerized CLI is needed:

docker build -t synapse:full --target full -f deploy/Dockerfile .
docker run --rm -v "$PWD:/scan:ro" synapse:full synapse-cli scan /scan --fail-on high

The full target bundles pinned Syft and Grype and covers the pure-Go scan path: SBOM, OSV/Grype vulnerabilities, licenses, SAST, secrets, and IaC misconfiguration. Sandboxed execution and JVM-from-source resolution need a Linux host with bubblewrap and a JDK/Maven/Gradle, so run those on a host install or the full Compose stack.

Advisory sync (optional owned store)

For detection independence you can maintain an owned advisory store and ingest feeds into it. This requires a database via SYNAPSE_DB_DSN.

# ingest a local OSV dump directory
synapse-cli sync-advisories <dir>

# fetch and ingest application ecosystems from the OSV bulk source
synapse-cli sync-advisories --remote

# fetch and ingest OS-package advisories (large)
synapse-cli sync-advisories --remote-distros

# ingest a local CSAF 2.0 advisory dump
synapse-cli sync-advisories --csaf <dir>

# ingest a local yum/dnf updateinfo dump (Amazon Linux ALAS and Fedora: repodata updateinfo.xml[.gz/.bz2/.zst])
synapse-cli sync-advisories --updateinfo <dir>

# ingest a local RESF/Apollo OSV list dump (Rocky Linux RLSA: apollo.build.resf.org OSV JSON pages)
synapse-cli sync-advisories --rocky <dir>

# ingest a local apk secdb dump (Alpine secdb.alpinelinux.org, Wolfi packages.wolfi.dev, Chainguard)
synapse-cli sync-advisories --secdb <dir>

Unsigned local OVAL is not accepted by sync-advisories because it cannot safely enter durable advisory storage. Configure an authenticated API-managed OVAL source instead, with either a pinned OpenPGP key or trusted provider metadata. The only exception is the exact SLES 15 SP6 SUSE HTTPS-origin source documented in Vulnerability intelligence: it is not a local import, generic unsigned OVAL support, or OpenPGP verification. Local OVAL data remains suitable for ownadvisory parser and scabench benchmark fixtures, which do not import it into durable advisory storage.

Enable the store at scan time with SYNAPSE_OWNED_ADVISORY=true, then it runs alongside the live and offline sources.

GitHub Action

The reusable action installs the released synapse-cli and runs the gate, so a whole scan step is three lines. v1 is the action's INTERFACE version and moves with every release; pin a release tag such as @v0.2.4 instead when you want the action itself frozen.

- uses: KKloudTarus/synapse-ce@v1
  with:
    fail-on: high        # critical | high | medium | low | info (default: high)
    path: .              # what to scan (default: .)
    version: latest      # a released tag like v0.1.0, or latest (default)

Emit SARIF and upload it to the Security tab, while still failing the build on high findings:

- id: synapse
  uses: KKloudTarus/synapse-ce@v1
  with:
    fail-on: high
    sarif: true
  continue-on-error: true          # let the upload run even when the gate fails
- name: Upload SARIF
  if: always()
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: ${{ steps.synapse.outputs.sarif-file }}

Set offline: true to run against the bundled offline databases only (no network egress).

From source

Without the action you can install the tools and build the CLI yourself:

- name: SCA scan
  run: |
    make tools
    make build
    ./bin/synapse-cli scan . --fail-on high

Or emit SARIF and upload it to the GitHub Security tab, while still failing the build on high findings:

- name: Synapse scan
  run: ./bin/synapse-cli scan . --sarif --fail-on high > synapse.sarif
  continue-on-error: true            # let the upload run even when the gate fails the step
- name: Upload SARIF
  if: always()
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: synapse.sarif

The report lands in the repository's Code scanning alerts, with each SAST, secret and misconfig finding annotated on its exact source line.

GitLab CI

The same gate as a GitLab job. make tools installs syft and grype, make build produces ./bin/synapse-cli, and a non-zero exit from the scan fails the pipeline:

synapse-scan:
  stage: test
  image: golang:1.26
  script:
    - make tools
    - make build
    - ./bin/synapse-cli scan . --fail-on high

Keep the SARIF report as a downloadable build artifact (works on every GitLab tier):

synapse-scan:
  stage: test
  image: golang:1.26
  script:
    - make tools
    - make build
    - ./bin/synapse-cli scan . --sarif --fail-on high > synapse.sarif
  artifacts:
    when: always
    paths:
      - synapse.sarif

Note on native GitLab ingestion. artifacts:reports:sast does not accept SARIF; it takes GitLab's own report schema (a gl-sast-report.json), so pointing reports: sast: at a SARIF file does nothing. GitLab ingests SARIF only through artifacts:reports:sarif, and that (with the merge-request security widget and the Vulnerability Report) is a GitLab Ultimate feature. On Free/Premium, download the artifact above or upload the SARIF to your own tooling.

Jenkins

A declarative pipeline stage. The scan's exit code fails the stage on a finding at or above the threshold:

pipeline {
  agent { docker { image 'golang:1.26' } }
  stages {
    stage('Synapse scan') {
      steps {
        sh 'make tools'
        sh 'make build'
        sh './bin/synapse-cli scan . --fail-on high'
      }
    }
  }
}

To keep the SARIF report as a build artifact (for a platform or plugin that ingests SARIF), let the scan step record its exit code, archive the report, then fail the build explicitly:

stage('Synapse scan') {
  steps {
    sh 'make tools && make build'
    script {
      def rc = sh(returnStatus: true, script: './bin/synapse-cli scan . --sarif --fail-on high > synapse.sarif')
      archiveArtifacts artifacts: 'synapse.sarif', allowEmptyArchive: true
      if (rc != 0) { error("Synapse found a finding at or above the fail-on threshold") }
    }
  }
}

Code quality gate (Clean as You Code)

Beyond security, synapse-cli measures code health and gates on it. The quality gate can score the whole codebase or, with --new-code-only, just the lines a branch changed, so a legacy repo can adopt the gate without fixing all pre-existing debt first.

# fail the build if new code introduces a critical/high issue, a new secret, or drops below A ratings
synapse-cli gate . --new-code-only --base origin/main

# feed a coverage report (lcov / Cobertura / JaCoCo / Go -coverprofile, auto-detected); a .synapse-gate.yaml can then
# require e.g. `coverage >= 80` on new code
synapse-cli gate . --new-code-only --base origin/main --coverage coverage.info

With --new-code-only the gate also measures new_coverage (line coverage over the added lines the report knows about) and new_duplication (the share of added lines inside a duplicated block). Each is written only when it could be measured: a condition on coverage, new_coverage, or new_duplication with no measurement fails as no data rather than being judged against a 0 nobody computed, so a new_duplication condition without --new-code-only, or with a diff no report line matches, fails rather than silently passing.

The gate also builds a first-party dependency graph for Go and JavaScript/TypeScript source. Managed or local gates can cap max_efferent_coupling and max_instability; if graph collection is incomplete, those conditions are reported as no data and fail closed. synapse-cli quality prints the same maxima for architectural feedback without executing project code or package managers.

synapse-cli scan --server also records a commit-pinned Behavioral Hotspots snapshot when the source is a clean Git worktree and enough first-parent history is already present. It scores each file as cyclomatic complexity multiplied by the number of commits that touched its current path. Collection is read-only and performs no network fetch; unavailable or shallow evidence stays explicit and does not create findings or affect the gate.

When the server records successive analyses, its Project Measures response also includes complexity rollups and signed per-path deltas. These are server-side history facts, not a CLI quality-gate input: the current cyclomatic/cognitive totals are summed from parsed functions, while Δ preserves both increases and reductions against a compatible baseline. The response includes measured/eligible file coverage and the baseline analysis provenance; unsupported languages, parser failures, legacy snapshots, and unknown branches are reported as unavailable rather than as zeroes.

Flag Default Description
--new-code-only off Score only lines changed against --base instead of the whole tree.
--base <ref> origin/main Git reference the new-code diff is computed against.
--gate <file> <path>/.synapse-gate.yaml Gate definition to apply.
--rules <file> <path>/.synapse-rules.yaml Rule enable/disable and severity overrides.
--coverage <file> none Coverage report (lcov, Cobertura, JaCoCo, or a Go -coverprofile, auto-detected) so gate conditions can require a coverage floor. A Go profile names files by import path; the CLI reads the module directive from <dir>/go.mod and strips it so lines key on repo-relative paths. Without a go.mod at the scan root the import paths are kept as-is and will not match the tree.
--format text\|markdown text Output format. markdown prints a ready-to-post PR summary.
--decorate off Post the gate result back to the pull/merge request on the CI's forge (commit status, check/report, and a PR/MR comment). The forge is the CI provider (SYNAPSE_CI_PROVIDER, auto-detected on GitHub/GitLab/Bitbucket); the token is SYNAPSE_DECORATION_TOKEN. A decoration error never fails the gate.
--dry-run off With --decorate, print the resolved decoration target and gate verdict without any forge write, and without needing a token. Use it to confirm decoration will land before provisioning a credential.

A .synapse-gate.yaml overrides the built-in gate, and a .synapse-rules.yaml enables/disables rules or overrides severities. Use --gate and --rules to point at files outside the scanned tree:

# .synapse-gate.yaml
conditions:
  - metric: new_critical
    op: "<="
    threshold: 0
  - metric: coverage
    op: ">="
    threshold: 80
  - metric: max_efferent_coupling
    op: "<="
    threshold: 12
  - metric: max_instability
    op: "<="
    threshold: 0.8

Inspect coverage on its own:

synapse-cli coverage coverage.info --fail-below 80

Code-health commands

These commands run the same analyzers the gate composes, but each reports one dimension on its own. None of them needs a database or a server; all are safe in CI. Complexity and structural analysis use the synapse-ast sidecar, and degrade to Go-only counts when it is unavailable rather than failing.

synapse-cli inventory <path>
synapse-cli metrics <path> [--fail-on-complexity N] [--top N]
synapse-cli duplication <path> [--min-tokens N] [--fail-on-duplication PCT] [--top N]
synapse-cli quality <path> [--fail-on SEV] [--min-complexity N] [--include-test-smells] [--sarif]
synapse-cli rating <path> [--json] [--fail-below GRADE]
Command Reports Gate flag
inventory Languages, files, and lines of code none
metrics Per-function cyclomatic and cognitive complexity --fail-on-complexity N exits 1 when any function exceeds N
duplication Duplicated blocks, lines, and density --fail-on-duplication PCT exits 1 when density exceeds PCT
quality Maintainability and reliability findings, duplication and complexity bridges, and module coupling maxima --fail-on SEV accepts critical\|high\|medium\|low\|info
rating A–E security, reliability, and maintainability grades with technical debt --fail-below GRADE exits 1 when any grade falls below it

--top N limits how many entries are printed. quality --sarif writes a SARIF report, and quality --include-test-smells adds test-code smells that are otherwise suppressed.

# gate a build on complexity and duplication without a server
synapse-cli metrics . --fail-on-complexity 15
synapse-cli duplication . --fail-on-duplication 3

# publish code-quality findings to GitHub code scanning
synapse-cli quality . --sarif > quality.sarif

Validate a third-party SARIF report

synapse-cli validate-sarif <engagement-id> <report.sarif>|- [--actor <id>] [--fail-on-refusal]

validate-sarif runs a third-party report through the same use case as the server ingest endpoint and reports what would be accepted or refused. It writes nothing: the JSON output carries "persisted": false, and no audit entry is recorded. To actually ingest a report, post it to /api/v1/engagements/{id}/sarif, where the ingesting actor comes from the authenticated principal.

import-sarif remains an alias for the same command and the same non-persisting contract. Pass - to read the report from stdin. --actor is a local label only. Exit 1 covers both a report whose results were all refused and, with --fail-on-refusal, any partial refusal, so a pipeline can insist every result be attributable.

Publish analysis source

synapse-cli publish-source [dir] --server <url> --project <key> --analysis <id>

Uploads the source files a completed server-side analysis listed as retainable, so the dashboard can show annotated code. It requires SYNAPSE_API_TOKEN, and --server defaults to SYNAPSE_API_URL. Only files in the analysis inventory are sent; the server returns a digest-verified source manifest.

Build an offline CVSS database

synapse-cli build-cvss-db <out.jsonl[.gz]> <nvd-*.json[.gz]...>

Converts NVD JSON feeds into a compact local database. Point SYNAPSE_NVD_CVSS_DB at the output to backfill CVSS scores with no network access and no API rate limit, which suits air-gapped CI.

PR decoration

--decorate posts the gate result natively to the pull/merge request on the CI's forge: a commit status, a check run (GitHub) or Code Insights report (Bitbucket) or Code Quality report (GitLab), and one summary comment. It is idempotent (a rerun updates the same status and comment in place) and fail-soft (a forge error never fails the gate). The forge is chosen from the CI provider, which is auto-detected on GitHub Actions, GitLab CI, and Bitbucket Pipelines (override with SYNAPSE_CI_PROVIDER), and the PR identity comes from the CI environment. Confirm the wiring first with --dry-run, which prints the resolved target without any network write:

./bin/synapse-cli gate . --new-code-only --base "origin/main" --decorate --dry-run

Token scopes. SYNAPSE_DECORATION_TOKEN must be able to write back to the change. Grant only what decoration needs and prefer a short-lived CI-provided token:

  • GitHub: a token with statuses: write, checks: write, and pull-requests: write.
  • GitLab: a project access token with the api scope (commit status + MR note), presented as PRIVATE-TOKEN.
  • Bitbucket: an access token or app password with repository:write and pullrequest:write, over Basic auth (SYNAPSE_DECORATION_USERNAME pairs with an app password; access tokens default to x-token-auth).
- name: Synapse quality gate
  run: |
    make tools && make build
    ./bin/synapse-cli gate . --new-code-only --base "origin/${{ github.base_ref }}" \
      --coverage coverage.info --decorate
  env:
    SYNAPSE_DECORATION_TOKEN: ${{ secrets.GITHUB_TOKEN }}

The server decorates automatically too: a project that opts in (PUT /api/v1/projects/{key}/decoration {"enabled": true}) has every PR-ref analysis decorated using the tenant's configured SCM connector, so a CI push through the import route needs no --decorate flag. Decoration is off for every project by default, so no project performs an outward forge write until it opts in.

Next: Architecture