To run Safeguard on a Linux server, install the CLI with the curl line, sign in with safeguard login --no-browser, then start the headless runner with safeguard runner start --workspace and your repository path. The runner polls the platform over outbound HTTPS, runs scan jobs on the server and uploads only SBOMs and findings, never source.
The assistant on that server can use the AI you already pay for. 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. You can also connect Claude or ChatGPT to the Safeguard MCP server and ask about the server's findings from the chat you already use.
This guide is for build boxes, shared development servers and private network hosts that nobody sits in front of. If you want the general Linux install first, read installing the Safeguard CLI on Linux.
What you will have at the end
- A dedicated service account on the server with the Safeguard CLI installed, pinned to a release you chose.
- A sign-in made with a device code from your laptop or phone, with no browser on the server.
- The headless runner picking up scan jobs for your repository, and a service that brings it back after a reboot.
- Shell commands for scans, SBOMs, container images and infrastructure files that you can put in cron.
- Approvals for server work from your phone or the desktop app, an approval mode that suits a shared machine, and an update policy you control.
How the headless runner works
The local runner is part of the safeguard CLI, not a separate product. Safeguard's runner docs describe three steps:
- The runner sends heartbeats and polls the platform for jobs assigned to the machine. All traffic is outbound HTTPS opened by the runner. Nothing connects in.
- It runs a claimed job with its own local scan server.
- It uploads the SBOM and findings and reports job status. Source code stays on the machine.
That makes it a good fit for three cases the docs call out: private repositories you will not give cloud OAuth access to, restricted networks that allow only outbound HTTPS, and fixing vulnerabilities in code you have not pushed yet.
The runner command groups everything a server needs. These descriptions come from the CLI's own help:
| Command | What it does |
|---|---|
safeguard runner | Opens the interactive terminal for the runner; /login starts polling |
safeguard runner start | Starts the machine runner and serves turns in their selected workspaces |
safeguard runner status | Shows current runner status |
safeguard runner stop | Drains in-flight work, then stops a headless runner |
safeguard runner scan-jobs | Runs the platform's scans on this machine, headless |
safeguard runner install | Keeps this machine reachable from your other devices, starting at login |
safeguard runner uninstall | Stops and removes the login service that runner install created |
safeguard runner slash | Runs terminal slash commands without a terminal |
There is no Homebrew formula, winget package or container image for the runner. You install the CLI with the script.
Prepare the server, step by step
-
Check the architecture. The CLI ships for Linux x86_64 and arm64 as a single static binary. The docs say the operating system and architecture matter, not the distribution.
uname -m # x86_64 -> linux_amd64 build # aarch64 -> linux_arm64 buildPlan for about 40 MB for the binary plus the scanners it downloads on first use.
-
Create a service account. Run the runner as its own user, not as root and not as your personal account. That keeps its token, logs and scanner cache separate.
sudo useradd --create-home --shell /bin/bash sgrunner sudo mkdir -p /srv/payments-api sudo chown sgrunner:sgrunner /srv/payments-api -
Check outbound HTTPS. The runner needs outbound port 443 and nothing inbound. Behind a proxy, set
HTTP_PROXYandHTTPS_PROXYfor the service account. If your network inspects TLS, pointSSL_CERT_FILEat the company CA, or add it to the system store and runsudo update-ca-certificateson Ubuntu or Debian. A quick test:curl -I https://api.safeguard.sh/auth -
Put the code in place with its dependencies. Clone the repository as the service account and install dependencies so lockfiles exist. The troubleshooting docs list missing lockfiles and uninstalled dependencies as the cause of an incomplete SBOM.
sudo -iu sgrunner git clone https://github.com/your-org/payments-api.git /srv/payments-api cd /srv/payments-api && npm ci
Install and sign in without a browser
Stay in the sgrunner shell for these steps.
-
Read the installer, then install into the account's own folder, pinned. Pinning keeps the server on a release you tested. The current release is published in
latest.txton the releases site.curl -fsSL https://cli.safeguard.sh/install -o install.sh && less install.sh SAFEGUARD_INSTALL_DIR="$HOME/.local/bin" SAFEGUARD_VERSION="<version>" bash install.shThe shell installer verifies the archive against the published checksums and aborts if the checksum file is missing.
SAFEGUARD_SKIP_CHECKSUM=1exists for air-gapped mirrors; leave it unset otherwise. -
Put the folder on the PATH and confirm.
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc source ~/.bashrc safeguard --version which -a safeguard -
Finish first run setup. Run
safeguardonce over SSH. The interactive terminal downloads syft, grype and trivy into atoolsfolder next to the binary, plus the vulnerability database. Then type/quit. Skipping this is the usual cause of "syft binary not found", becausesafeguard updateonly refreshes scanners you already have. -
Sign in with a device code.
safeguard login --no-browserThe CLI uses the OAuth device authorization flow: it prints a URL and a user code, you open the URL on your laptop or phone, enter the code and approve, and the CLI saves the tokens.
safeguard whoamishows the account and token expiry.safeguard login --forcesigns in again, which is also how you switch accounts. SetSAFEGUARD_NO_BROWSERin~/.bashrcso the interactive terminal never tries to open a browser on this machine. -
Know where the token lives. By default it is
~/.scanner/token.json, readable only by the owner (mode0600).SAFEGUARD_CREDENTIAL_STORE=nativeuses the OS store, which on Linux means Secret Service; most headless servers do not run one, so the file default is the normal choice here. There is no API-key variable for interactive sign-in. -
Run the health check.
safeguard doctorIt checks the environment, sign-in, tools and policy, prints what is wrong in the order that matters, and changes nothing. Its exit code is
0whenever the checks ran, so read the lines, not the status.
Start the headless runner
With the account signed in, start the runner against the repository:
safeguard runner start --workspace /srv/payments-api
From a second SSH session as the same user:
safeguard runner status
safeguard runner stop
runner stop drains in-flight work before stopping, so a scan in progress finishes first.
Two options matter on servers:
- Polling.
runner startlong-polls by default. On networks that forbid held connections, add--poll-wait 0to poll at a fixed interval instead.SAFEGUARD_POLL_INTERVALsets the interval in seconds, default 10. - Permission mode.
--permission-modecontrols how much the assistant may do without asking when the runner serves turns, fromsupervisedtoautonomous. On a shared server, start at the supervised end and loosen it only for specific repositories.
Keep it running after you log out
A runner started in your SSH session stops when the session ends. Pick one of these, depending on how permanent the machine is:
| Option | Survives SSH disconnect | Survives reboot | Best for |
|---|---|---|---|
| Foreground in your SSH session | No | No | A first test |
| Inside tmux | Yes | No | A dev box you visit often |
safeguard runner install | Yes | Starts at login | Keeping a machine reachable from your other devices |
| A systemd unit you write | Yes | Yes | Build boxes and shared servers |
tmux. Start a named session, run the runner, and detach with C-b d. tmux keeps the session and the programs inside it running in the background, and protects them from dropped connections.
tmux new -s safeguard
safeguard runner start --workspace /srv/payments-api
# press C-b then d to detach
tmux ls
tmux attach -t safeguard
runner install. This is the CLI's own route. It keeps the machine reachable from your other devices and starts at login. safeguard runner uninstall stops and removes the login service.
A systemd unit. For a server that must come back after a reboot without anyone logging in, a system unit that runs as the service account is the familiar choice. Run the runner in a terminal first and confirm it stays in the foreground, which is what a service manager expects. Then create /etc/systemd/system/safeguard-runner.service:
[Unit]
Description=Safeguard headless runner
Wants=network-online.target
After=network-online.target
[Service]
Type=exec
User=sgrunner
Environment=HOME=/home/sgrunner
Environment=SAFEGUARD_NO_BROWSER=1
WorkingDirectory=/srv/payments-api
ExecStart=/home/sgrunner/.local/bin/safeguard runner start --workspace /srv/payments-api
ExecStop=/home/sgrunner/.local/bin/safeguard runner stop
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
Then enable it and follow its output:
sudo systemctl daemon-reload
sudo systemctl enable --now safeguard-runner
journalctl -u safeguard-runner -f
Why these settings: with Type=exec, systemd treats the unit as started only once the binary has actually run, so a wrong path shows up as a failure. Restart=on-failure restarts it after a crash but not after systemctl stop. ExecStop should block until the service has exited, and runner stop drains work before it returns. The CLI also writes its own log to ~/.sg/logs/safeguard.log unless LOG_DIR points elsewhere.
If you prefer a user unit under the service account, sudo loginctl enable-linger sgrunner starts that user's service manager at boot and keeps it after logout, so user services run without anyone logging in.
Scans and checks from the shell
The runner handles jobs the platform schedules. The same binary also runs scans you start yourself, which suits cron and maintenance scripts.
# Dependencies in a folder, results in a directory the account owns
safeguard scan --dir /srv/payments-api --output /home/sgrunner/results
# A container image, private registry included
safeguard container-scan --image registry.example.com/team/api:1.8
# An SBOM only, written to a file you choose
safeguard sbom --dir /srv/payments-api --output payments-api-sbom.json
# Terraform, Kubernetes manifests, Helm, Dockerfiles and more
safeguard iac-scan --target /srv/payments-api/infra
# Check SBOMs and VEX documents you received or produced
safeguard validate ./sboms --recursive --strict
# A local look with nothing uploaded and no project registered
safeguard findings sca --dir /srv/payments-api --json
safeguard scan writes a folder per scan with vuln.json, license.json, collection.json and others. It returns 0 when the scan ran, 1 when the command failed and 2 when results were written but the vulnerability scan did not run. It never fails because of findings; gating on findings is done by policies and gates in the platform.
A nightly cron entry for the service account (crontab -e) can use those exit codes:
15 2 * * * cd /srv/payments-api && git pull -q && safeguard scan --dir . --output /home/sgrunner/results >> /home/sgrunner/scan.log 2>&1 || safeguard doctor >> /home/sgrunner/scan.log 2>&1
On a machine with no internet access, load a vulnerability database you downloaded elsewhere with safeguard update --scanner grype --import followed by the archive path.
Do the work properly with the Safeguard CLI
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. On a server that means the fix happens where the code already is, with your approval.
Install
curl -fsSL https://cli.safeguard.sh/install | bash on Linux and macOS; irm https://cli.safeguard.sh/install.ps1 | iex on Windows. One static binary, no Node, Python or JVM.
First session
Over SSH, run safeguard in the repository. /scan . scans the folder, /status shows user, tenant, runner, job counts and approval mode, and /jobs shows recent scans. @ attaches a file from the folder you started in, and /resume picks up your last session in that folder.
Writing and fixing code
The assistant has tools for your local files and your Safeguard data. It can find and read files, edit code and run commands, with your approval. This recorded session on the sample project payments-api shows the whole loop: install with the curl command (checksum verified), safeguard --version, open the interactive terminal and ask "fix the lodash CVE in this repo". The assistant reads the finding, the files and the advisory, then asks before each command (press y or n), 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 while pushing nothing. It ends with /permissions, /model and /scan ..
Click through it at your own pace, or open it full screen.
Before you commit, safeguard review reviews the working-tree changes for correctness and security, and --base diffs against a git ref instead. Files the assistant edits are snapshotted first, so /rewind or safeguard checkpoints restore with --dry-run shows and undoes a change. For a wider review routine, see reviewing AI-generated code before merge.
Using your Claude, ChatGPT or Codex plan
The plan you already pay for works inside the CLI on the server too, with no extra Safeguard charge and within your plan's own limits and terms. /model shows or switches the model; for connecting your own plan, see the CLI docs and the /model picker.
Approvals
/permissions sets the interactive mode:
| Mode | What runs without asking | On a server |
|---|---|---|
| Ask | Reads; edits and commands ask first | Shared servers |
| Accept edits | Edits in this folder and read-only commands | A repository you own |
| Plan | Read and plan only | Investigating production-adjacent hosts |
| Auto (default) | Read-only commands and what the policy allows | Your own dev box |
| Strict | Pre-approved actions only | CI and cron |
The --yolo flag approves everything. Keep it for throwaway sandboxes, never a server that holds deploy keys.
CI with safeguard exec
safeguard exec runs one non-interactive assistant turn and exits. Its default 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 unless you pass --skip-git-repo-check. Two flags are worth knowing on Linux. --sandbox confines every write to the workspace at the kernel level using Landlock, and refuses to start where Landlock is unavailable. --restricted gives a read-only tool set with no writes, no shell and no network.
safeguard exec --sandbox --output-format json \
"summarise the critical findings in this repository" > summary.json
To check whether the kernel has Landlock, the kernel docs suggest dmesg | grep landlock as root and looking for "landlock: Up and running".
Pipeline scans use a different command. safeguard ci scan scans the commit as a new version of its Safeguard project, authenticates with a secret key stored as SAFEGUARD_API_KEY, and the docs' templates run it with --wait --fail-on critical. More in running the Safeguard CLI in CI with safeguard exec.
Approve server work from your phone or desktop
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. From the desktop or phone app you pick the computer running the CLI and the folder, and then approve each command and file edit. So the server does the work and you hold the approvals, wherever you are.
This recorded Android session shows it. 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.
From the server side, safeguard devices lists your signed-in devices, and safeguard send pushes a note to one of them:
safeguard send --to "Pixel" --text "payments-api nightly scan: exit 0, see results/"
tail -n 20 /home/sgrunner/scan.log | safeguard send --to "Pixel" --stdin
safeguard inbox shows what your other devices sent to this CLI. Android installs from Google Play, with a direct APK for phones without it; iPhone and iPad install from the App Store. Setup is in Safeguard on Android phones and tablets.
Keep it updated and under control
Binary updates. safeguard self-update --check reports whether a release is available and installs nothing. safeguard self-update reads the release feed over HTTPS, verifies a signed release manifest against a key built into the binary, checks the archive against the digest in that manifest, runs the new binary to prove it works and only then swaps it in. It keeps the old one as safeguard.previous, refuses older versions, refuses anything it cannot verify, and will not cross a major version without --allow-major.
Golden images and managed fleets. SAFEGUARD_DISABLE_SELF_UPDATE=1 turns binary updates off entirely, and SAFEGUARD_NO_UPDATE_CHECK=1 skips the background release check. In settings, "update": {"check": false} does the same for the check. Bake a pinned SAFEGUARD_VERSION into the image and roll forward deliberately.
Databases. safeguard update refreshes the vulnerability databases, separately from the binary. Run it on a schedule, or import an archive on offline hosts.
Retiring a server. Stop the runner, then sign out and clean up as the service account: safeguard logout --yes, remove the binary, and delete ~/.sg, ~/.scanner and ~/.cli. Platform data is not affected.
Troubleshooting and common mistakes
"Not authenticated" or HTTP 401. Run safeguard whoami to see the account and expiry, then safeguard logout and safeguard login --force --no-browser.
"Connection refused" or "Network error". Check outbound 443, the proxy variables for the service account, and the auth endpoint with curl -I.
Certificate errors. Set SSL_CERT_FILE or install the company CA. Never disable certificate checks.
"syft binary not found" or exit code 2. First run setup did not finish for this account. Run safeguard interactively once as sgrunner.
"Permission denied" writing results. Point --output at a folder the service account owns.
Mixed text in your JSON. Logs go to stderr. Redirect stdout alone.
"Out of memory" on a monorepo. Scan package folders separately.
Common mistakes. Running the runner as root or as your own user. Signing in as one user and starting the service as another, so the token is in the wrong home. Forgetting that a WSL distribution or a tmux session does not survive a reboot. Using --yolo on a shared host. Setting SAFEGUARD_SKIP_CHECKSUM to get past a failed download. Expecting safeguard scan to fail a build on findings, when gates live in the platform.
If you are still stuck, collect safeguard --version and safeguard doctor output before you contact support.
FAQ
Does the runner need any inbound ports?
No. All traffic is outbound HTTPS opened by the runner, and nothing connects to it.
Can I keep using my Claude, ChatGPT or Codex plan on the server?
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. You can also connect Claude or ChatGPT to the Safeguard MCP server. See the CLI docs or /model for connecting a model.
Does source code leave the server?
When the runner runs platform scan jobs, only SBOMs and findings are uploaded. safeguard findings uploads nothing and registers no project.
Which distributions are supported?
The CLI is a static binary, so the docs say the operating system and architecture matter, not the distribution. Linux x86_64 and arm64 are published.
Is there a container image for the runner?
No. The runner docs say there is no Homebrew formula, winget package or container image. Install with the script, pinned with SAFEGUARD_VERSION.
How do I sign in on a server with no browser?
safeguard login --no-browser, then open the printed URL on any device, enter the code and approve.
Can the assistant change files on the server without asking?
Only in the modes you choose. In Ask and Auto, edits and commands beyond read-only or policy-allowed actions wait for approval, from the terminal, the desktop app or your phone. Plan only reads and plans, and safeguard exec defaults to strict.
How do I update a fleet without surprises?
Pin SAFEGUARD_VERSION in your image, set SAFEGUARD_DISABLE_SELF_UPDATE=1, and roll forward by building a new image. Run safeguard update for databases on its own schedule.
Try it yourself
This walkthrough covers installing and starting the CLI: set 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.
Ready for a real server? Download Safeguard and keep the CLI docs and the runner docs open while you set up.
Quick check
What does safeguard runner stop do with a scan that is still running?
It drains in-flight work first, then stops the runner.
Your network does not allow held connections. How do you keep the runner polling?
Start it with the poll-wait option set to 0. It then polls at a fixed interval set by SAFEGUARD_POLL_INTERVAL, which defaults to 10 seconds.
Which exec flag confines writes to the workspace at the kernel level on Linux?
The sandbox flag. It uses Landlock and refuses to start on a kernel where Landlock is unavailable.
Sources
- Safeguard CLI documentation
- Safeguard local runner
- Safeguard CLI installation
- Safeguard CLI support matrix
- Safeguard CLI usage
- Safeguard CLI system configuration
- Safeguard CLI troubleshooting
- Safeguard CI/CD integration
- Safeguard downloads
- systemd.service manual
- loginctl manual
- journalctl manual
- tmux: Getting Started
- Linux kernel: Landlock
Last reviewed: 21 October 2026.