OSPulse CLI
The OSPulse CLI is the command-line front end to the same API the web app uses. It exists so that a build agent — or you, at a terminal, inside a project directory — can scan a dependency tree and turn the result into a pass or fail decision without writing any client code.
This page describes shape, not an exhaustive command list
The CLI evolves faster than prose. Everything below reflects the documented shape of the tool —
the workflows, the flags that matter, and how it behaves in CI. For the authoritative,
version-accurate command list, run ospulse --help, or ospulse <command> --help for a
specific command. Where this page and --help disagree, --help wins.
What it is for
Three jobs, in rough order of how often people use them:
Scanning a projectworkflowOptionalPoint the CLI at a directory, it discovers manifests and lockfiles for the supported ecosystems, resolves the dependency set, and submits it for analysis. You get health scores, known vulnerabilities, and drift for everything you actually depend on — including transitives.
Gating a buildworkflowOptionalEvaluate your policies against a scan result and exit non-zero when they fail. This is the whole point of running it in CI: a merge that pulls in an abandoned or compromised package fails before it reaches your default branch.
Machine-readable outputworkflowOptionalEmit JSON so another tool — a PR comment bot, a dashboard, a security review pipeline — can consume the result rather than parsing human-formatted text.
If you need something the CLI does not do, everything it can do is reachable over the REST API, which is a supported and equally first-class path.
Authenticate
The CLI authenticates with the same API tokens as everything else. The
supported way to supply one is the OSPULSE_TOKEN environment variable — that keeps the token
out of your shell history, out of ps output, and out of any command you might paste into an
issue.
export OSPULSE_TOKEN="$(op read op://engineering/ospulse/token)" # or your secret manager
ospulse scan .Never pass a token as a command-line argument
Arguments are visible to every other process on the machine and are frequently captured in CI logs. Use the environment variable. If a token is ever printed, revoke it immediately — see Authentication.
Create the token with the narrowest scope that works, and give CI its own token rather than sharing yours. A CI token that is revoked mid-incident should not also lock you out of your laptop.
Typical commands
Scan a project directory
The core command takes a path — the repository root, or a subdirectory if you only want part of a monorepo — discovers the manifests beneath it, and reports what it finds.
ospulse scan .Useful things to reach for: restrict the scan to specific ecosystems when a monorepo mixes
several, and choose an output format (see below). Run ospulse scan --help for the flags this
version supports.
Watch dependencies over time
A one-off scan tells you where you are today; watching tells you when a dependency you already shipped goes bad. The watch workflow registers packages for continuous monitoring so that a maintainer walking away, a suspicious release, or a new CVE raises an alert rather than waiting for your next build.
ospulse watch .This is the CLI equivalent of the watchlist in the app, and it is the right thing to run once per repository rather than on every commit. See Monitor packages continuously for the wider workflow.
Evaluate policy and gate the build
Evaluating policy is what makes the CLI useful in CI. It takes the scan result, applies the policies defined for your tenant, and reports every violation — then exits non-zero if any violation is at or above the severity you are gating on.
ospulse policy evaluate .Author and test policies first — see Enforce policy gates. A gate you have not tested against a known-bad dependency is a gate you do not know works.
Emit JSON for other tools
Human output is for humans. Anything downstream of the CLI should consume JSON:
ospulse scan . --format json > ospulse-scan.json
jq '[.violations[] | select(.severity == "High")] | length' ospulse-scan.jsonUpload that file as a build artefact. When someone asks in three months why a release was blocked, the artefact answers it.
Exit codes and CI gating
The CLI follows the standard Unix convention: 0 means the command succeeded and your gate
passed; any non-zero exit means it did not. That single rule is all a pipeline needs — most CI
systems fail a step automatically on a non-zero exit, so a gate is often just the command
itself with nothing around it.
What matters more in practice is telling apart two very different failures:
- The gate failed — the CLI ran fine and your policy said no. This is a real finding and the build should stop.
- The CLI could not run — bad token, no network, an unparseable manifest, the API returning
5xx. This is an operational problem, and failing the build on it may be right or may be noise, depending on how much you trust your runners.
Rather than branching on specific numeric codes — which can change between versions — branch on the JSON output, which is stable and self-describing:
set +e
ospulse policy evaluate . --format json > result.json
status=$?
set -e
if [ "$status" -ne 0 ] && [ ! -s result.json ]; then
echo "::warning::OSPulse could not complete — treating as infrastructure failure"
exit 0 # or exit 1, if you want hard failures on tooling problems
fi
exit "$status"Start in report-only mode
On the first week, run the gate but do not fail the build — log the result and let the team see what would have been blocked. Turn on enforcement once the noise is down. A gate everybody learns to bypass is worse than no gate.
In a pipeline
The CLI is designed to be a single step in an existing pipeline, not a pipeline of its own. A minimal GitHub Actions job:
name: OSPulse
on:
pull_request:
push:
branches: [main]
jobs:
supply-chain:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Scan and gate
env:
OSPULSE_TOKEN: ${{ secrets.OSPULSE_TOKEN }}
run: |
ospulse scan . --format json > ospulse-scan.json
ospulse policy evaluate . --format json > ospulse-policy.json
- name: Upload results
if: always()
uses: actions/upload-artifact@v4
with:
name: ospulse
path: ospulse-*.jsonNote if: always() on the upload — you want the evidence most when the gate failed.
Scan in CI/CD covers the equivalents for GitLab CI, Azure Pipelines, and Jenkins, plus how to scope gates so pull requests are strict and nightly builds are informational.
Keeping runners current
A pinned CLI version makes builds reproducible; a pinned CLI version that nobody ever bumps makes them stale. The API exposes the current released version so you can automate the check:
curl -s https://api.ospulse.app/api/v1/cli-version{
"version": "1.4.2"
}That endpoint needs no special privileges, which makes it easy to wire into a scheduled job:
latest="$(curl -s https://api.ospulse.app/api/v1/cli-version | jq -r '.version')"
current="$(ospulse --version | awk '{print $NF}')"
if [ "$latest" != "$current" ]; then
echo "OSPulse CLI $current is behind $latest — update the runner image"
fiPin in CI, float locally
Pin an explicit version in your CI image so a release never changes a build's behaviour
without a commit, and run a weekly job that compares the pin against /api/v1/cli-version and
opens a bump PR. Locally, staying on the latest version is fine and gets you fixes sooner.
When to use the API instead
The CLI is the right tool when the thing you want is a decision in a pipeline. Reach for the API directly when you need to:
- Query historical data — score history, trends, or audit entries — rather than the current state.
- Build a bespoke integration such as a Slack bot or an internal dashboard.
- Drive workflows the CLI does not cover, like SBOM export, CRA evidence, or warehouse export.
The two share tokens, tenancy, and policy definitions, so mixing them is normal — gate with the CLI, report with the API.
