Platform and DevOps teams use Safeguard in three places: the Safeguard CLI on engineers' laptops, safeguard ci scan in every pipeline, and a runner on build hosts you control. Scans run on your machines and only results go to the platform. Policies and gates are evaluated in the platform, and your pipeline fails when a gate fails.
This guide assumes you own the pipelines, base images and build hosts. Every command below comes from the CLI's own help output (CLI v11.11.0, checked 21 October 2026).
Bring the Claude, ChatGPT or Codex plan you already pay for and use it inside the Safeguard CLI. Safeguard adds no extra charge for that usage; your plan's own usage limits and terms still apply. Your engineers can also connect Claude or ChatGPT to the Safeguard MCP server and ask about findings from the chat they already use.
What you will have at the end
Work through the sections in order and you will end up with:
- The Safeguard CLI installed the same way on macOS, Linux and Windows machines, with a health check you can run anywhere.
- A pipeline step in GitHub Actions or GitLab CI that scans every commit and fails the build on a severity threshold or on a named gate.
- A container image check and an SBOM for every image you ship, validated before anyone downloads it.
- An infrastructure as code scan over your Terraform, Helm, Kubernetes and Dockerfile folders.
- A runner on a build host that takes the platform's scan jobs without your source leaving that host.
- A way for the on-call engineer to approve a fix from the desktop app or a phone, with every command and file edit approved one at a time.
You can stop after any step and still have something useful.
Do the work properly with the Safeguard CLI
Everything here runs on one tool. The Safeguard CLI scans code, container images and SBOMs on your own machine, sends only the results to the platform, and includes an assistant that can fix what it finds. It writes code, not only scans it, and it already knows your vulnerabilities.
Install it the same way everywhere
On macOS and Linux:
curl -fsSL https://cli.safeguard.sh/install | bash
On Windows, in PowerShell:
irm https://cli.safeguard.sh/install.ps1 | iex
The CLI is a single static binary. There is no Node, Python or JVM to install next to it, which matters when you bake it into CI images. It runs on macOS, Linux and Windows, on x86_64 or arm64 (Windows ships x86_64 only). The download page lists prebuilt archives for macOS Apple Silicon and Intel, Linux x86_64 and arm64, and Windows x86_64. The Linux guide at /resources/blog/install-safeguard-cli-linux covers headless hosts in more detail.
Then check the install:
safeguard --version
safeguard doctor
safeguard doctor checks the environment, sign in, profile, policy and tools, prints what is wrong in the order that matters, and changes nothing.
Control updates on shared machines
Engineers can keep laptops current with safeguard self-update. It verifies a signed release manifest and the download's digest, runs the new binary to prove it works, and keeps the old one as safeguard.previous. It refuses older versions and will not cross a major version without --allow-major. On CI images and build hosts, change the version only when you decide.
# See what a new release would change, install nothing
safeguard self-update --check
# On images you pin: no background check, no updating at all
export SAFEGUARD_NO_UPDATE_CHECK=1
export SAFEGUARD_DISABLE_SELF_UPDATE=1
Rebuild the image to move every pipeline to a new version at once.
First session on your own repository
Open a terminal in a repository you own and run safeguard with no arguments. That opens the interactive terminal. Type /login to sign in through the browser, then /scan . to scan the current folder. You can then ask in plain language: "how many critical vulns do we have", or "fix the lodash CVE in this repo".
The assistant has tools for your local files and for your Safeguard data, and picks whichever the question needs. Tools that change things, such as writing a file, editing code or running a command, ask before they act. That makes it a general terminal assistant for platform work too: find and read files, edit a Dockerfile or a workflow, run a command, all with your approval. Type @ to attach a file from the folder you started in, /model to see or switch the model, and /resume to pick up the last session in that folder.
Here is that loop in a real recorded session on a sample project. It installs with the curl command (checksum verified), runs safeguard --version, opens the interactive terminal and asks "fix the lodash CVE in this repo". The assistant reads the finding, the files and the advisory, then asks before each command. It edits package.json to lodash 4.17.21, runs npm install and npm test with approval, and prints a summary with a suggested commit message. It pushes nothing. The session ends with /permissions, /model and /scan ..
Click through it at your own pace, or open it full screen.
Pick the approval mode for the job
/permissions in the interactive CLI offers five modes. Platform teams tend to use all of them, for different jobs:
| Mode | What runs without asking | Where it fits |
|---|---|---|
| Ask | Reads. Edits and commands ask first. | Production config, shared infra repos |
| Accept edits | Edits in this folder and read only commands. Other commands ask. | Refactoring a pipeline file you will review as a diff |
| Plan | Nothing changes. It reads and plans only. | Scoping a migration before anyone touches it |
| Auto (default) | Read only commands and what the policy allows. Anything else asks. | Day to day work on your own repos |
| Strict | Pre-approved actions only | CI |
There is also a --yolo flag that approves everything. Use it only in throwaway sandboxes that you will delete afterwards, never on a build host or a laptop with credentials on it.
Standardise the agent setup across teams
safeguard import claude or safeguard import codex converts an existing Claude Code or Codex setup into Safeguard configuration. By default the CLI also reads Claude Code, Codex and OpenCode configuration below its own; safeguard other-agents off limits it to Safeguard's files. safeguard mcp sync pulls the tenant's MCP connectors managed under Connectors. For repeatable CI runs, --safe-mode ignores every local customisation (settings, hooks, skills, plugins and SAFEGUARD.md), so a stray file in one repository cannot change how the pipeline behaves.
Gate every pipeline with safeguard ci scan
safeguard ci is built for Jenkins, GitHub Actions, GitLab CI, Bitbucket Pipelines, Azure Pipelines, CircleCI or anything else that can run a command. It authenticates with a secret key from Settings, Secret Keys that has the scans:trigger scope, plus scans:read if you use --wait. Store it as the CI secret SAFEGUARD_API_KEY.
Choose platform mode or local mode
safeguard ci scan has two ways to run, and the difference decides where your source goes.
- Platform mode (the default) starts a scan of the project's repository on this build's branch. The scan runs on the Safeguard platform and is recorded as version branch plus short commit SHA. A repository that is not imported yet is imported on its first run under the connected GitHub, GitLab, Bitbucket or Azure DevOps integration that covers it.
- Local mode (
--local) scans the checkout on the CI runner itself and uploads only the results, never the source. It needs no integration and works for private and self-hosted repositories. It scans dependencies: SBOM and vulnerabilities.
If your rule is that source never leaves your runners, use --local. Branch, commit and repository URL are read from the CI system's environment; flags such as --branch, --commit and --project override them.
Step by step: GitHub Actions
- Create a secret key in Safeguard with the
scans:triggerandscans:readscopes. - In GitHub, open the repository's Settings, then Secrets and variables, then Actions, and add a repository secret named
SAFEGUARD_API_KEY. With the GitHub CLI,gh secret set SAFEGUARD_API_KEYdoes the same. - Add the workflow below as
.github/workflows/safeguard.yml. - Open a pull request and watch the step.
name: safeguard
on:
pull_request:
branches: [main]
push:
branches: [main]
permissions:
contents: read
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Install the Safeguard CLI
run: curl -fsSL https://cli.safeguard.sh/install | bash
- name: Scan this commit on the runner
env:
SAFEGUARD_API_KEY: ${{ secrets.SAFEGUARD_API_KEY }}
run: safeguard ci scan --local --wait --fail-on high
--wait polls until the scan finishes and exits non zero if it failed or did not finish within --timeout (30 minutes by default). --fail-on high fails the step when the scan has a vulnerability at high or above; the other values are critical, medium and low.
Keep the permissions block. GitHub's docs say that once you specify any permission, every one you did not list is set to none.
Step by step: GitLab CI
- In GitLab, open Settings, then CI/CD, expand Variables and add
SAFEGUARD_API_KEY. - Leave it masked. GitLab's docs note masking hides the value in job logs but is not a guarantee against someone determined to read it, so keep the key's scopes narrow.
- Decide on Protected. A protected variable is only available to pipelines on protected branches or tags, so merge request pipelines from feature branches will not see it.
- Add the job:
safeguard_scan:
stage: test
image: ubuntu:24.04
script:
- apt-get update && apt-get install -y curl ca-certificates
- curl -fsSL https://cli.safeguard.sh/install | bash
- safeguard ci scan --local --wait --fail-on high
Fail on a gate instead of a number
A severity threshold is a blunt rule. Gates let your security team decide what blocks a release, in the platform, without you editing every pipeline. --fail-on-gate waits for the gate's policies to be evaluated against this scan and fails the step when the gate is Failed (a policy with the block action failed), Error or Not evaluated. Warning, where only warn policies failed, and Passed both pass the step, and failed warn and notify policies are printed in the log.
safeguard ci scan --wait --fail-on-gate Release
You can name the gate, use its id, or pass all. This needs the policies outcomes feature on your tenant. Security owns the policy, you own the pipeline step.
Add an assistant step with safeguard exec
safeguard exec runs one non interactive assistant turn, which is useful for a summary in the job log or a pull request comment. Its default permission mode is strict: nothing is asked, and anything the policy file has not pre-approved is denied. It refuses to run outside a git repository, because there would be no way to undo what it changes.
safeguard exec --output-format json \
"summarise the critical findings in this repository" > safeguard-summary.json
# Pre-approve exactly one command: go test
safeguard exec --allowedTools 'Bash(go test:*)' \
"run go test ./... and summarise any failures"
# Read only: no writes, no shell, no network
safeguard exec --restricted "explain why the last scan failed"
The walkthrough at /resources/blog/safeguard-cli-in-ci-with-safeguard-exec goes further on prompts and output schemas.
Scan images and infrastructure before they ship
The image adds a base layer, OS packages and whatever the Dockerfile installs, so scan it as well as the repository.
Container images and their SBOMs
- Build and tag the image. With Docker,
-tnames the image and--loadputs a single platform build into your local image store. - Scan it.
safeguard container-scangenerates an SBOM and runs the vulnerability scan; its output is identical tosafeguard scan --image. - Write the SBOM to a file you will publish with the release.
- Validate the SBOM before anyone downloads it.
docker buildx build -t registry.example.com/payments-api:1.8.0 --load .
safeguard container-scan --image registry.example.com/payments-api:1.8.0
safeguard sbom --image registry.example.com/payments-api:1.8.0 \
--output payments-api-1.8.0.spdx.json
safeguard validate payments-api-1.8.0.spdx.json --strict
For a private registry, add --registry-username and --registry-password to the scan or SBOM command, and pass them from your CI secret store, not the command line history. Scan results land under ./output as JSON files such as vuln.json and license.json, so archive that folder as a build artifact.
safeguard sbom writes SPDX JSON using the same path the scanner uses internally. safeguard validate checks CycloneDX (JSON or XML), SPDX (JSON, YAML, tag value or RDF) and VEX (CSAF or OpenVEX). --strict treats warnings as errors, --recursive walks a folder of SBOMs, and -o report.json writes the result for your release notes.
Infrastructure as code
safeguard iac-scan runs a misconfiguration scan over a folder using the trivy config engine. It covers Terraform, CloudFormation, Helm, Kubernetes manifests, Dockerfiles and ARM or Bicep, maps the results onto the platform's finding format and writes the standard output files.
safeguard iac-scan --target ./infra
Quick local checks that upload nothing
To test a new base image on your laptop before anything is recorded, use safeguard findings. It runs one scanner on your machine and prints a single normalised list: id, severity, title, CVE or rule, package and version, fixed version or file and line, description and remediation. Nothing leaves the machine, even when the CLI is signed in.
safeguard findings sca --dir .
safeguard findings iac --dir ./infra --json
safeguard findings container --image node:22-slim --json
Run scans and fixes on machines you control
A runner for the platform's scan jobs
To let the platform schedule scans without ever holding the source, use safeguard runner scan-jobs. It takes the repository, image, directory and SBOM scans the platform routes to a machine, runs them there and uploads only the results. All traffic is outbound HTTPS; nothing listens on a network port. Run it on a build host inside your network:
safeguard login
safeguard runner scan-jobs
Each event prints as a line, and Ctrl+C or SIGTERM stops it, so it fits under a process supervisor.
A machine your team can send work to
safeguard runner install registers safeguard runner start as a per user login service: a systemd user unit on Linux, a LaunchAgent on macOS and a logon scheduled task on Windows. From then on your phone, the desktop app or the browser can send turns to that machine from any network for as long as it is on. Again, the runner only opens outbound HTTPS.
safeguard login
safeguard runner install --workspace ~/src/payments-api
safeguard runner status --json
--workspace sets the folder the runner works in, and safeguard runner uninstall removes the service. If your proxy forbids held connections, safeguard runner start --poll-wait 0 switches from long polling to interval polling.
A CLI session is live. It shows in the web app, the desktop app and the phone app, and other devices can send work to it. That is how an on-call engineer approves a dependency fix at 2am without opening a laptop. In this recorded Android session the same conversations appear on the phone. "Work on my computer" picks a computer running the CLI and a folder, the command waits for Allow once, Allow for turn or Deny on the phone, and the answer shows the tests passed.
Click through it at your own pace, or open it full screen.
The desktop app (3.2.2) does the same from a native window on Windows, macOS and Linux; the Linux AppImage needs FUSE and the Safeguard CLI on your PATH. The app guide at /resources/blog/safeguard-desktop-app-guide walks through it. For quick handoffs, safeguard devices lists your signed in devices and safeguard send --to pixel --link https://example.com/runbook sends a link or up to 8,000 characters of text to one of them. safeguard inbox shows what your other devices sent back.
Fix and review before merge
Two commands help when a platform change touches dependencies:
# Preview dependency fixes at high severity and above, change nothing
safeguard fix --dry-run --severity high
# Review the working tree against main for correctness and security
safeguard review --base origin/main
safeguard fix can also open a pull request with --create-pr, and it keeps to minor and patch upgrades unless you allow major ones. In the platform, autonomous remediation can open fix PRs for you, and it ships switched off. For changes written by any coding agent, the habit in /resources/blog/review-ai-generated-code-before-merge applies here too.
If the assistant edits something you did not want, safeguard checkpoints list --session ID shows the snapshots taken before each file change, and safeguard checkpoints restore CHECKPOINT --session ID --dry-run shows what a restore would change as a diff. These are the same checkpoints /rewind uses. Files the agent writes or edits are snapshotted; anything a shell command writes is not.
Troubleshooting and common mistakes
Troubleshooting
safeguard: command not found in the next CI step. The install ran in one step and the binary's folder is not on the job's PATH in the next. Add the install folder to PATH, or install and scan in the same step.
The scan step fails on pull requests from forks. GitHub does not pass secrets to the runner when a workflow is triggered from a forked repository, and secrets are not available to Dependabot triggered workflows either. Decide whether those runs should skip the scan or run it later on the merge.
SAFEGUARD_API_KEY is empty on GitLab feature branches. The variable is probably Protected, so only protected branches and tags see it.
--fail-on-gate fails with Not evaluated. Not evaluated fails the step by design. Check the gate name or id, and that the policies outcomes feature is on for your tenant.
safeguard exec refuses to start. It will not run outside a git repository. Run it inside the checkout, or pass --skip-git-repo-check if you accept that changes cannot be undone with git.
The runner does not show up on the phone. Run safeguard runner status --json and safeguard doctor. Check the machine can make outbound HTTPS calls, and try --poll-wait 0 behind strict proxies.
Common mistakes
- Running
--yoloon a real machine. It approves everything. Keep it for sandboxes you throw away. - Treating
doctoras a pass or fail check. Its exit code is 0 whenever the checks ran. Read the lines, not the status. - Expecting
findingsresults in the platform.findingsuploads nothing. Usescan,container-scanoriac-scanwhen the result should be recorded. - One broad key for every pipeline. Give CI keys only
scans:triggerandscans:read, and one per pipeline group so you can revoke one without breaking all. - Expecting
execto run your tests by default. Strict mode denies anything not pre-approved. Allow the exact command with--allowedTools. - Moving policy into YAML. Keep thresholds that change often in platform gates, not in fifty copies of
--fail-on.
FAQ
Does our source code leave our machines?
Not with safeguard ci scan --local, safeguard runner scan-jobs or the interactive CLI. Those scan on the machine and upload SBOMs and findings, never source. In platform mode, safeguard ci scan scans the repository on the platform through your connected integration, so choose the mode per repository.
Can engineers use the AI plan they already have?
Yes. Bring the Claude, ChatGPT or Codex plan you already pay for and use it inside the Safeguard CLI. Safeguard adds no extra charge for that usage; your plan's own usage limits and terms still apply. See the CLI docs or /model for connecting a model. Claude and ChatGPT can also connect to the Safeguard MCP server.
Which CI systems work?
The CLI's help names Jenkins, GitHub Actions, GitLab CI, Bitbucket Pipelines, Azure Pipelines and CircleCI, plus anything that can run a command. You need the binary and the SAFEGUARD_API_KEY secret.
Where do policies and gates live?
In the platform. The CLI does not evaluate them. --fail-on-gate waits for the platform's verdict on the scan and passes or fails the step from it.
How do we stop the assistant changing things in CI?
safeguard exec defaults to strict mode, so anything not pre-approved is denied. Add --restricted for a read only tool set with no writes, no shell and no network.
Can we scan IaC and images with the same binary?
Yes. safeguard iac-scan covers Terraform, CloudFormation, Helm, Kubernetes, Dockerfiles and ARM or Bicep, and safeguard container-scan covers images from Docker, ECR, ACR and GCR. Runners work on macOS (Apple Silicon or Intel), Linux (x86_64 or arm64) and Windows (x86_64).
Can the on-call engineer approve fixes from a phone?
Yes. With a runner installed on a machine, the iOS or Android app can send work to it, and every command waits for Allow once, Allow for turn or Deny.
Try it yourself
This demo sets up the runner on your own machine: pick the platform, start it, approve it with its device code, then use the same CLI to scan from the terminal.
Click through it at your own pace, or open it full screen.
Get the CLI, the desktop app and the phone apps from safeguard.sh/download and keep the CLI docs open. Start with one pipeline, add the gate, then the image scan.
Quick check
Which ci scan option keeps your source code on the CI runner?
--local. It scans the checkout on the runner and uploads only the results. It needs no integration and works for private and self-hosted repositories.
Your gate reports Warning. Does the pipeline step fail?
No. With --fail-on-gate, Warning and Passed pass the step. Failed, Error and Not evaluated fail it, and failed warn and notify policies are printed in the log.
What is the default permission mode for safeguard exec?
Strict. Nothing is asked, and anything the policy file has not pre-approved is denied.
Sources
- Safeguard CLI documentation
- Safeguard CLI interactive terminal
- Safeguard download page
- GitHub Docs: Using secrets in GitHub Actions
- GitHub Docs: Workflow syntax for GitHub Actions
- actions/checkout repository
- GitLab Docs: CI/CD variables
- Docker Docs: docker buildx build
- Trivy Docs: Misconfiguration scanning
Last reviewed: 21 October 2026.