CLI Client
SolidPing ships a command-line client, sp, for managing your monitoring from the terminal or from scripts and CI pipelines. It talks to the same REST API as the dashboard, so anything you can do in the UI you can also automate.
Installing
Every release publishes a prebuilt sp for macOS, Linux and Windows, on Intel and ARM (plus RISC-V on Linux), compressed only — .gz for macOS/Linux, .zip for Windows — under version-free names (the same stable URLs the server uses), plus a checksum file:
# Pick your platform: darwin-amd64, darwin-arm64, linux-amd64, linux-arm64, linux-riscv64
curl -sSL -o sp.gz \
"https://github.com/fclairamb/solidping/releases/latest/download/sp-linux-amd64.gz"
gunzip -c sp.gz > sp
chmod +x sp
sudo mv sp /usr/local/bin/
sp --version
sp --version prints the client version alone. sp version prints the client and the server side by side, with their commits and build times, which shows at a glance whether the two are on the same release. Add --client to skip the server, -o json for a script:
$ sp --url https://solidping.acme.com version
Client:
Version: 0.44.0
Commit: 1a2b3c4d
Git Time: 2026-11-01T10:00:00Z
Server (https://solidping.acme.com):
Version: 0.43.0
Commit: 5e6f7a8b
Git Time: 2026-10-31T10:30:01Z
When the server can't be reached, sp version still prints the client half, then the error, and exits 1.
Or pin a specific version by pointing the same file name at that release's tag:
VERSION=0.31.0
curl -sSL -o sp.gz \
"https://github.com/fclairamb/solidping/releases/download/v${VERSION}/sp-linux-amd64.gz"
gunzip -c sp.gz > sp
chmod +x sp
On Windows, download sp-windows-amd64.zip and extract it — the archive contains a single, already-named sp.exe:
Invoke-WebRequest -Uri "https://github.com/fclairamb/solidping/releases/latest/download/sp-windows-amd64.zip" -OutFile "sp-windows-amd64.zip"
Expand-Archive -Path "sp-windows-amd64.zip" -DestinationPath "."
There is no bare binary to download — every asset ships compressed. sp links only the config-only check registry (no client-go, go-ora, grpc, chromedp, ...), so the binary itself is ~32 MB and the .gz/.zip archive around ~12 MB.
In CI, or anywhere you would rather not manage a binary, use the image:
docker run --rm -v "$PWD:/w" -w /w ghcr.io/fclairamb/solidping/sp \
checks validate config.yaml
The image is sp alone — it does not carry the server — so it pulls in seconds.
Authentication
Log in once and the client stores your session:
sp auth login # device flow: approve a one-time code in any browser
sp auth login --with-password # classic email + password
sp auth login --token pat_... # save a Personal Access Token created in the dashboard
sp auth me # show the current user
sp auth switch-org # change active organization (session logins only)
sp auth logout
A Personal Access Token, including the one the device flow mints, is bound to
the organization it was created in. It cannot switch organization or change how
your account signs in (password, email, 2FA, passkeys): sp auth switch-org on
a token login says so. Run sp auth login again and pick the other
organization on the consent page, use a token created in that organization, or
log in with sp auth login --with-password to get a session that can switch.
See
API tokens are bound to one organization.
sp auth login uses the OAuth 2.0 Device Authorization Grant (RFC 8628): it
prints a short one-time code and a verification URL, tries to open your browser
at that URL, and waits while you approve the login in any browser where you are
already signed in — including your phone. Because nothing has to come back to
the machine running the CLI, this works over SSH, inside containers and on
headless servers.
On the consent page you pick which organization the login is for (when you belong to more than one); approving mints a named Personal Access Token scoped to that organization, valid for 90 days, which you can review and revoke from Account → Tokens.
The consent page shows when the request was opened, from which IP address and
with which client software. Only approve a code you just got from sp auth login yourself. Never approve a code someone sent you: anyone can open a
request and name it after the CLI. When the code arrives pre-filled in the
link, the page asks you to tick "I started this sign-in myself" first. Each
approval also sends you an email naming the client, the organization and the
requester IP, with a link to revoke the token.
Common Commands
| Command | Description |
|---|---|
sp checks list | List checks |
sp checks get <uid> | Show a check |
sp checks add / update / upsert | Create or modify checks |
sp checks deps | Manage check dependencies |
sp checks remove <uid> | Delete a check |
sp checks export / import / diff / validate | Config-as-code: see below |
sp checks import --from uptime-kuma-db <kuma.db> | Migrate from Uptime Kuma 2.x: see below |
sp results list | List check results (with filters) |
sp incidents list / get | Inspect incidents and their events |
sp events list | Browse the audit event log |
sp tokens list / create / revoke | Manage API tokens |
sp members list / add / update / remove | Manage organization members |
sp jobs list / get / create / cancel | Manage background jobs |
sp system get / set / delete | Read and write system parameters |
sp version | Print the client version and the version of the server it talks to (--client skips the server) |
sp server health / version | Check server status |
sp mcp | Serve the MCP endpoint over stdin/stdout for command-based MCP clients (MCP Server) |
Config as Code
sp checks supports a full export → edit → validate → dry-run → import → re-export loop for managing a whole organization's checks as one tracked YAML (or JSON) file — the same shape the dashboard's export produces ({version, exportedAt, organization, secrets, defaults, checks}).
# 1. Export the current state (writes YAML because of the .yaml extension;
# pass --format explicitly to override, or write .json for the raw JSON).
sp checks export --file config.yaml
# 2. Edit config.yaml by hand (or with a script) — add/rename/tune checks.
# 3. Validate the file offline: no token, no network call. Checks document
# shape, slug/config formats, and the dependency graph.
sp checks validate config.yaml
# 4. Preview what would change against the live organization.
sp checks import config.yaml --dry-run
# 5. Apply it for real.
sp checks import config.yaml
# 6. Re-export and commit, so the tracked file matches live state again.
sp checks export --file config.yaml
sp checks validate is the validator for a SolidPing config file. It runs the server's own document rules offline — no token, no network — so it cannot fall behind the server the way a re-implementation of those rules must. It reports every problem at once, each on a line of the form [slug] CODE field: message, where CODE is a stable identifier your pipeline can allow-list (REGION_FORMAT, UNKNOWN_TYPE, INLINED_CREDENTIAL, DUPLICATE_SLUG, …). Exit 0 when the file is valid, 1 when it is not, 2+ when it could not be read. The one rule it cannot check offline is whether a ${param:…} reference exists for your organization — post the file to POST /api/v1/orgs/:org/checks/validate for that, which any member token can do.
sp checks diff config.yaml reports whether the tracked file has drifted from what SolidPing currently holds — useful as a CI check after every merge. It asks the server for the reconcile plan (a dry run that changes nothing) and prints one row per check: create, update with the fields that move, unchanged, delete, unmanaged. It exits 0 when there's no drift, 1 when the file and the live org disagree, and 2+ on errors (missing file, auth failure, ...). --text prints a plain textual diff instead, which is also what happens automatically when your token cannot compute a plan; there the exportedAt timestamp is ignored on both sides since it always differs.
sp checks import config.yaml --dry-run answers the same question in counts: 0 created, 0 updated, 0 deleted with N unchanged means the file matches the instance. Region spellings, expectedStatus vs expectedStatusCodes and document defaults are normalized before the comparison, so a difference in the plan is a real difference.
Import never deletes. sp checks import is an idempotent upsert keyed on each check's slug: a check present in SolidPing but absent from the file is left untouched. If a check was removed from the file on purpose, delete it explicitly with sp checks remove, or use sp apply --prune (a separate, declarative-reconcile command) for delete-by-absence semantics. Always start from a fresh sp checks export before hand-editing so the file reflects live state.
sp checks import --from <source> <file> converts a third-party file instead of a SolidPing export document — today the only source is uptime-kuma-db, for migrating off Uptime Kuma. The file is read and converted on your machine; only the resulting check definitions are sent, never the source file itself. Unlike the plain import, --from previews by default (the input is a file another product wrote, and the warning list is the point) — pass --apply to write:
sp checks import --from uptime-kuma-db ./data/kuma.db # preview only
sp checks import --from uptime-kuma-db ./data/kuma.db --apply # create/update
sp checks export picks its output format from --format yaml|json, defaulting to the --file extension (.yaml/.yml → YAML, everything else including stdout → JSON). YAML output preserves the document's field order — two exports of unchanged live state produce byte-identical files, so diffs in version control only ever show real changes.
Secrets: parameters and references
A tracked config file must not carry passwords. Anywhere a check config takes a string, write a reference instead — ${param:KEY} for a value SolidPing stores for your organization, ${env:NAME} for one the machine running the check holds in its environment:
checks:
- slug: sso-login
name: SSO login
type: http
config:
url: https://sso.acme.com/token
method: POST
body: "grant_type=password&username=probe&password=${param:sso-authtest-password}"
Manage the parameters the file references with sp params:
sp params set sso-authtest-password 'hunter2' # secret by default
sp params set region-label paris --public # a plain, readable setting
sp params list # secret values are never shown
sp params get sso-authtest-password # key, flag and timestamp — no value
sp params delete sso-authtest-password
A secret parameter is write-only: nothing reads the value back, so rotating one is simply setting it again under the same key — every check referencing it keeps working with no file change. The value is resolved when the check runs, never stored in the check: sp checks export gives you back the reference you wrote, so the loop above round-trips unchanged.
If a reference cannot be resolved, sp checks import and sp apply refuse the whole file (including on --dry-run) rather than storing it, and a parameter deleted later turns the check red with unresolved secret reference: param:… instead of quietly probing with the literal text.
${env:} resolves on whichever process executes the check — for a check pinned to a private location, that is the agent's own environment, not the server's. That is useful for per-region credentials, and worth remembering when a value seems to be missing.
${env:} only reads allowlisted variables
${env:NAME} resolves only for a name the operator listed in SP_CHECK_ENV_ALLOWLIST (comma-separated) on the process that runs the check. The list is empty by default, so ${env:} resolves nothing until you set it:
SP_CHECK_ENV_ALLOWLIST=SP_ACME_API_TOKEN,SP_ACME_DB_PASSWORD
Without the list, anyone allowed to write a check config could make a check read the server's own secrets (its JWT secret, its database URL) and send them to a target of their choice.
- Saving a check (create, update, clone,
sp checks import,sp apply, including--dry-run) refuses a${env:NAME}whose name the server does not list, withVALIDATION_ERROR. Create, update and clone check the name only, so a listed name is accepted even if the server does not set it (it may be set on the agent that runs the check).sp checks importandsp applyalso resolve the reference on the server, and refuse a listed but unset variable exactly like an unlisted one, so their error never tells you whether a variable exists. - At run time, a name the executing process does not list turns the check red with
unresolved secret reference: env:NAME. The literal reference is never sent to the target. - A check pinned to a private location needs the name listed on the server (so it can be saved) and on that agent (so it resolves there). Each agent reads its own
SP_CHECK_ENV_ALLOWLIST. - Prefer
${param:…}for anything an organization should manage itself: parameters are scoped to the organization and need no server change. - For how this shows up in an
sp applyworkflow, see Config as code.
Existing checks keep their ${env:NAME} references, but those stop resolving until the name is listed: each such check goes red with unresolved secret reference: env:NAME on its next run. If you use ${env:}, set SP_CHECK_ENV_ALLOWLIST on the server and on every private-location agent before or right after upgrading. No migration is needed.
On a deployment where untrusted users could create checks (open signup), the server's own environment was reachable through ${env:} before this change: consider rotating SP_AUTH_JWT_SECRET, the database credentials and the encryption master key.
Output Formats
The client can print human-readable tables or machine-readable output for scripting:
sp checks list --output json
sp results list --output jsonl
Configuration
The client is configured through SOLIDPING_-prefixed environment variables:
| Variable | Default | Description |
|---|---|---|
SOLIDPING_CONFIG | ~/.config/solidping/settings.json | Config / session file path |
SOLIDPING_URL | - | Server URL override |
SOLIDPING_ORG | - | Organization override |
SOLIDPING_OUTPUT | text | Output format: text, json, jsonl |
SOLIDPING_VERBOSE | false | Verbose logging |
SOLIDPING_EMAIL | - | Email for non-interactive auth login |
SOLIDPING_PASSWORD | - | Password for non-interactive auth login |
This makes the client convenient in CI: set SOLIDPING_URL, SOLIDPING_EMAIL, and SOLIDPING_PASSWORD (or use a token) and run sp commands directly.