Surveyor IT Documentation
How to install and run the spice command-line tool, with recorded sessions
of the real thing. Everything on this page works offline.
How Spice Labs works
Spice Labs builds an inventory of the cryptography in everything you ship, then tells you what needs to change for post-quantum migration and which fixes to make first. It works from built software (jars, wars, ears, container images, VM and device images), so it needs no source code, no agents in your applications and no changes to your builds.
The Surveyor and Topographer
The Surveyor is a command you run on your machines and in your CI. It is not an agent: it runs only when you run it, adds nothing to your applications, and leaves nothing running after a survey finishes. It reads every file in what you survey and computes a cryptographic fingerprint of each one.
The one exception is a runtime survey: it attaches a recorder to the single Java process you start under it, for as long as that process runs.
Topographer is the hosted service, used through the dashboard at dashboard.spicelabs.io, the REST API and AI assistants. It matches the fingerprints against a large graph of known open-source software and cryptographic libraries and builds the results:
- a component inventory, including copies of open source that appear in no manifest because they were vendored, renamed or bundled into other files;
- a cryptography inventory: the algorithms, keys and certificates found, each classified for post-quantum readiness, delivered as CycloneDX CBOMs;
- vulnerability findings for the components that are really present;
- a Portfolio that scores every application and ranks the fixes that improve the most applications at once.
What leaves your machine
A survey runs where your software is. The Surveyor builds an encrypted model of what it found and uploads only that model, using a Spice Pass that can upload to one project and do nothing else. Your files and source code never leave your machine.
Three kinds of survey
| Survey | Answers | Guide |
|---|---|---|
| Inventory | What cryptography and which components are present in this build? | Inventory surveys |
| Image | The same question, for a container image pulled by name | Image surveys |
| Runtime | Which cryptography does this Java application actually use when it runs? | Runtime surveys |
An inventory survey and a runtime survey of the same artifact combine into one CBOM that separates cryptography that is merely present from cryptography confirmed to run.
Reports and history
Each survey is kept. When Spice Labs’ knowledge of software and cryptography improves, existing reports are marked stale and can be regenerated from the stored survey, so you get better answers without surveying again. Surveying the same application over time shows findings going away as fixes land.
Next steps
Quick start
Survey one application and see what cryptography it contains, how ready it is for post-quantum migration, and what to fix first. It takes about ten minutes.
Before you start
- A Spice Labs account. Start a free trial or contact us. If someone invited you, use the link in your invitation email.
- Docker, installed and running. The Surveyor CLI runs in a container. Get Docker.
- Something to survey: a built Java application (a
.jar,.waror.earfile, or the folder your build writes them to), or a container image. Surveys read built artifacts, not source code.
Survey your first application
-
Sign in to Topographer
Go to dashboard.spicelabs.io and sign in with your email address (you get a sign-in link by email) or with Google. Open the project you want to survey into. If you only see sample data, create a project first: a sample project cannot have a Spice Pass.
-
Generate a Spice Pass
Open the menu (☰) and choose Survey setup. In step 1, Generate a Spice Pass, pick how long it should last under Expires in, select Generate, then Copy. The Spice Pass lets the CLI upload surveys to this project and nothing else.
-
Install the Surveyor CLI
curl -sSfL https://install.spicelabs.io | bashirm -UseBasicParsing -Uri https://install.spicelabs.io | iexIf the installer says its folder is not on your
PATH, follow its instructions and open a new terminal. Details are in Install the Surveyor CLI. -
Set your Spice Pass
export SPICE_PASS="<your Spice Pass>"$env:SPICE_PASS = "<your Spice Pass>" -
Run a survey
Every survey needs a subject: the name this application has in Spice Labs. You make the name up; it is what the Portfolio, reports and Data health show. Use the application’s real name, such as
payments-api, not a version, branch or date, and use exactly the same name every time you survey this application.spice survey inventory payments-api ./build/libs/payments-api.jarHere
payments-apiis the subject you chose, and./build/libs/payments-api.jaris the build to survey.To survey a container image from its registry instead, give the image with a
docker://prefix:spice survey inventory payments-api docker://<registry>/<image>:<tag>For an image, the subject is optional. Without it, the image name without its tag becomes the subject, so every version of the image lands on one subject.
The CLI surveys on your machine and uploads only an encrypted model of what it found. Your files and source code never leave your machine.
-
See the results
Back in the dashboard, open Portfolio in the top bar. Your subject appears with its scores once the survey has been analyzed, usually within a few minutes; Data health shows the survey’s progress until then. What to fix next lists the fixes that improve the most, and the subject’s post-quantum readiness report is under Reports in the menu.
If something goes wrong
spice: command not found: open a new terminal, and check that the folder the installer printed is on yourPATH.SPICE_PASS must be set via SPICE_PASS env var for upload: setSPICE_PASSin the same terminal you runspicein.- An upload is refused: your Spice Pass may have expired. Run
spice pass decodeto see its expiry, and generate a new one if needed.
More fixes are in Troubleshooting.
Next steps
Install the Surveyor CLI
The Surveyor CLI is the spice command. It surveys software on your own machines and uploads the encrypted results to your Topographer project. The installer puts a small wrapper script on your machine, and the wrapper runs the CLI in Docker.
Before you start
- Docker, installed and running. Get Docker. If you cannot use Docker, see Run without Docker.
- Linux containers. The CLI runs in a Linux container. On Windows, Docker Desktop runs Linux containers by default; if it was switched to Windows containers, switch it back (right-click the Docker icon, Switch to Linux containers). Windows Server’s Docker Engine runs only Windows containers.
- A Spice Pass, to upload surveys to your project. Generate one in the dashboard. A survey run with
--no-uploaddoes not need one.
The installer writes only to your home folder, so it needs no administrator rights.
Run the installer
curl -sSfL https://install.spicelabs.io | bashThe installer:
- Downloads the
spicewrapper script to~/.local/bin/spice. - Pulls the
spicelabs/spice-labs-cliimage and generates Bash and Zsh tab completion from it, in~/.local/share/spice/completions/. - Adds a line that loads tab completion to
~/.bashrcand~/.zshrc, when those files exist. - Warns you if
~/.local/binis not on yourPATH, ifSPICE_PASSis not set, or if Docker is missing. It does not change yourPATHitself.
Run this in PowerShell:
irm -UseBasicParsing -Uri https://install.spicelabs.io | iexThe installer:
- Downloads
spice.ps1and aspice.cmdshim to%USERPROFILE%\.spice\bin, sospiceworks from PowerShell and fromcmd.exe. - Pulls the
spicelabs/spice-labs-cliimage, generates PowerShell tab completion from it in%USERPROFILE%\.spice\completions, and loads it from your PowerShell profile. - Warns you if
%USERPROFILE%\.spice\binis not on yourPATH, ifSPICE_PASSis not set, or if Docker is missing. It does not change yourPATHitself.
If Docker is missing or the image cannot be pulled, the installer skips tab completion and prints the command that generates it later.
Set your PATH and Spice Pass
Add the install folder to your PATH and set SPICE_PASS to the Spice Pass you copied from the dashboard.
For the current terminal:
export PATH="$PATH:$HOME/.local/bin"
export SPICE_PASS="<your-spice-pass>"To keep them, add the same lines to your shell profile (~/.bashrc or ~/.zshrc) and open a new terminal.
For the current PowerShell session:
$env:Path += ';' + (Join-Path $HOME '.spice\bin')
$env:SPICE_PASS = '<your-spice-pass>'For the current cmd.exe session:
set "PATH=%PATH%;%USERPROFILE%\.spice\bin"
set "SPICE_PASS=<your-spice-pass>"To keep them for your user account, run this in PowerShell, then open a new terminal:
$add = Join-Path $HOME '.spice\bin'
$path = [Environment]::GetEnvironmentVariable('Path', 'User')
if (($path -split ';') -notcontains $add) {
[Environment]::SetEnvironmentVariable('Path', (($path, $add) -join ';').Trim(';'), 'User')
}
[Environment]::SetEnvironmentVariable('SPICE_PASS', '<your-spice-pass>', 'User')You can also search the Start menu for “environment variables”, choose Edit environment variables for your account, add %USERPROFILE%\.spice\bin to Path, and add a variable named SPICE_PASS.
Run the image Spice Labs gave you access to
The installer sets up the spice wrapper. To run your image rather than the default one, put --features it before every command, as in spice --features it survey inventory payments-api ./build/libs, or set SPICE_IMAGE to the image with its tag, such as ghcr.io/spice-labs-inc/spice-labs-cli-it:<tag>. See Configuration.
Check the installation
Run spice --version to confirm the wrapper can reach the CLI. The first run pulls the CLI image,
so it can take a minute. It prints the hash of the wrapper script, then the CLI version and the
commit it was built from. These are the lines to quote when asking for support.
spice --version~ ❯ spice --version
script version 499912f5957fb1cda5b582edd22ca611f393c531eecc06d894bfb5e062840320
1.8.6
https://github.com/spice-labs-inc/spice-labs-cli git commit: bbfd1217a1245da8d6e79498c2884ddd18dc9485
Surveyor IT (it): features: bulk, static-detect, inventory-surveys, runtime-surveys, static-surveys, cbom-materialOn Windows the first two lines are Powershell Script hash and the hash. Then check your Spice Pass:
spice pass decode
It prints a table of the pass’s claims, such as Project ID, Expires At and Upload Server, and ends with a Status line such as Valid (expires in ...). If the table includes Artifact Cutoff (only trial and demo passes have one), see Artifact cutoff.
Keep it up to date
There are two parts to keep current:
- The CLI image. Before each run, the wrapper pulls the latest image for its tag (
latestunless you choose a version). If the pull fails and you have a local copy, the run continues with it and printsUsing local copy. - The wrapper script. After the pull, the wrapper compares itself with the latest release. When yours is older, it prints
A newer version of this script is available. Run:followed by the install command. Run the installer again to update it.
To pin a CLI version instead of following latest, or to skip the pull, see Pin the CLI version.
Run without Docker
JVM mode runs the CLI with Java on your machine instead of in Docker. Set SPICE_LABS_CLI_USE_JVM=1 and point SPICE_LABS_CLI_JAR at the CLI jar. JVM mode needs Java 21 or later and a few tools the Docker image otherwise provides: see JVM mode. To build the jar yourself, see the spice-labs-cli repository.
Uninstall
rm ~/.local/bin/spice
rm -rf ~/.local/share/spice ~/.cache/spice
docker rmi spicelabs/spice-labs-cliThen delete the lines under # Spice CLI tab completion from ~/.bashrc and ~/.zshrc, and remove SPICE_PASS from your shell profile.
Remove-Item -Recurse -Force (Join-Path $HOME '.spice')
Remove-Item -Recurse -Force (Join-Path $env:LOCALAPPDATA 'spice') -ErrorAction SilentlyContinue
docker rmi spicelabs/spice-labs-cliThen remove %USERPROFILE%\.spice\bin from your Path, delete the SPICE_PASS variable, and delete the lines under # Spice CLI tab completion from your PowerShell profile (the file named by $PROFILE.CurrentUserAllHosts).
Uninstalling leaves your survey results alone: results you wrote with --output stay where you put them, and recordings kept with --keep-recording stay under ~/.spicelabs/runtime-survey or the --output folder you chose.
Next steps
Inventory surveys
- Surveyor IT
An inventory survey reads the files you point it at and records what is present: every file, the packages they belong to, and the cryptography they contain. Run one on your build output, a release archive or a folder of artifacts.
The results are uploaded to your Topographer project as encrypted Artifact Dependency Graphs (ADGs).
Run a survey
spice survey inventory <subject> <input>
<subject>is the name you choose for this application in Spice Labs. See Choose a subject.<input>is a folder or a single file: a jar, war or ear, a.tararchive, adocker savetarball, a disk image and so on. See Supported file types.
# Survey a build output folder and upload the results
spice survey inventory payments-api ./build/libs
# Survey a single archive
spice survey inventory payments-api ./dist/payments-api.tar
By default, the survey uploads its results to your Topographer project, which needs SPICE_PASS
set to a Spice Pass for that project. To survey without uploading, see Survey locally and upload
later.
To survey a container image straight from a registry, use an image survey.
What a survey finds
The survey opens archives, images and file systems inside your input, however deeply they are nested, and fingerprints every file it finds. From those files it identifies:
- Packages: Java archives, OS packages (such as Debian, RPM and Alpine packages), Go modules built into binaries, and dependencies declared in lockfiles, including npm, cargo and Python packages.
- Cryptography: algorithms, certificates, keys and cryptographic configuration.
The run log reports how many packages it found, for example Writing 290 Package URLs. Because the survey reads built artifacts rather than manifests, it also finds components that were copied, renamed or bundled into other files.
Choose a subject
The subject is the name you give an application in Spice Labs. Spice Labs does not read it from your build: whatever you type is what the Portfolio, reports and Data health show, and surveys with the same subject are compared over time.
- One subject per application, named the way your team names it, such as
payments-apiorcustomer-portal. - The same name on every run, from every machine and pipeline that surveys that application.
payments-apiandpayments_apiare two different applications. - No versions, build numbers, branch names or dates in the name. Each different name becomes a separate application with its own history. Record those details with
--tag-jsoninstead:
spice survey inventory payments-api ./build/libs --tag-json='{"version":"2.3.0","branch":"main"}'
Survey locally and upload later
Add --no-upload to survey on your machine only. Always pass --output with it, so the results land in a folder you choose:
spice survey inventory payments-api ./build/libs --no-upload --output ./spice-results
You do not need a Spice Pass for a --no-upload run.
This example surveys a small project directory,
my-app, and keeps the results on disk with--no-uploadand--output.ls my-app~ ❯ ls my-app Sample.java readme.txtThe input is an ordinary directory, here holding a source file and a text file. Any mix of jars, wheels, archives, images exported to disk and other files can be surveyed; the survey identifies each file by its content, not by its name.
spice survey inventory my-app ./my-app \ --no-upload --output ./out --log-level info~ ❯ spice survey inventory my-app ./my-app \ > --no-upload --output ./out --log-level info INFO 🌶️ Spice Labs Surveyor CLI v1.8.6 INFO 📦 Surveying artifacts... INFO Blocking build db until there's at least 1 item in the work queue INFO Found all files, count 2 INFO MIME pass complete: 2 total, 2 completed INFO Kicking off work queue consumer threads INFO Finished multi-thread consumer loop 1 at PT0.890625564S INFO Finished processing 2, vertices 14 at PT0.07769281S INFO Writing 0 Package URLs INFO Post-sort at PT0.010778482S INFO Post CBOR cache at PT0.033320473S INFO Wrote 14 entries in PT0.075401472S INFO Total build time for survey PT0.155444765S, write took PT0.077408219S INFO Done with runThe survey runs in the foreground and logs its progress: the files it found, how many it has classified by type, then the analysis and the writing of the results. This is the default
infolevel of detail, which the example also sets explicitly;--log-levelchanges it.find out -maxdepth 2 | head -20~ ❯ find out -maxdepth 2 | head -20 out out/surveyor out/surveyor/survey-9213057683804780998The results are written under the
--outputdirectory, insurveyor/survey-<id>. It can be uploaded later with--upload-only. Without--no-upload, and withSPICE_PASSset, the same command uploads the results to your project as soon as the analysis finishes.
Each run creates its own folder inside the one you name, so repeat runs never overwrite each other:
spice-results/
surveyor/
survey-<random-id>/
survey/ the ADGs
To upload those results later without surveying again, point --upload-only at the survey-<random-id> folder from that run:
spice survey inventory payments-api ./spice-results/surveyor/survey-<random-id> --upload-onlyYou cannot combine --no-upload and --upload-only in one run.
Tag a survey with metadata
Use --tag-json to attach your own labels, such as the environment, branch or owning team. The value must be a JSON object:
spice survey inventory payments-api ./build/libs --tag-json='{"env":"prod","team":"platform"}'
PowerShell 7 accepts the same syntax. Windows PowerShell 5.1 strips the outer quotes, so assign the JSON to a variable first:
$json = '{"env":"prod","team":"platform"}'
spice survey inventory payments-api C:\path\to\build --tag-json=$json
Tune large surveys
| Option | What it controls | Default |
|---|---|---|
--threads | Number of analysis threads (1 or more) | Half your CPU cores |
--max-records | Records processed per batch | 5000 |
--chunk-size sets the upload chunk size in MB; by default Spice Labs chooses it.
For example, to use fewer threads on a shared build machine:
spice survey inventory payments-api ./build/libs --threads=2
To use the same values on every run, put them in a configuration file instead.
Keep a log of the run
Use --log-level for more or less detail (error, warn, info, debug or trace) and --log-file to save a copy of everything the run prints:
spice survey inventory payments-api ./build/libs --log-level=debug --log-file=./spice.log
The log file is appended to, not replaced, so one file can hold several runs.
Artifact cutoff
A Spice Pass issued for a trial or a demo can carry an artifact cutoff: a date after which published artifacts are left out of the survey, together with anything that contains them. Spice Passes for paid projects have no cutoff and survey everything.
When your credential has a cutoff, the run says so:
Ignoring artifacts published after 2026-01-01T00:00:00Z
The cutoff comes from the credential, so no option or setting changes it. To check yours, run spice pass decode and look for Artifact Cutoff.
See the results
When the upload finishes, the survey appears in your project in the dashboard, and Topographer builds its reports from it, including the post-quantum readiness report and the CBOM. To correlate this survey with what your application does at run time, also run a runtime survey anchored to the same artifact.
Image surveys
- Surveyor IT
An image survey is an inventory survey of a container image’s contents. Give spice survey inventory the image with a docker:// prefix, and the CLI pulls it from its registry with oras, which is built into the CLI’s container, so there is no docker pull and no tar export. It surveys the image’s contents and handles the result like any inventory survey, with the same options (--no-upload, --output and the rest) and the same artifact cutoff.
Run a survey
spice survey inventory [<subject>] docker://<image>
<image>is the image reference, in the formname[:tag][@digest].oci://<image>means the same asdocker://<image>.<subject>is the name you choose for this application in Spice Labs; see Choose a subject. Without it, the image’s repository name, without its tag or digest, becomes the subject, so every version of the image lands on one subject. Set it when you want a different name.
Without the prefix, nginx:1.27 is read as a path. When no such file exists, the CLI stops and says to use docker://nginx:1.27.
Survey a public image:
spice survey inventory docker://nginx:1.27
spice survey inventory web-frontend docker://ghcr.io/myorg/web:2.3.0The first survey is labeled nginx; the second, web-frontend.
By default, the survey uploads its results to your Topographer project, which needs SPICE_PASS
set to a Spice Pass for that project. To survey without uploading, see Survey locally and upload
later.
How image names are expanded
spice expands short names to their full form. The default subject is the repository name, without the tag or digest:
| You type | spice surveys | Default subject |
|---|---|---|
docker://nginx | docker.io/library/nginx:latest | nginx |
docker://nginx:1.27 | docker.io/library/nginx:1.27 | nginx |
docker://myorg/app | docker.io/myorg/app:latest | myorg/app |
docker://ghcr.io/myorg/app:2.3.0 | Exactly what you typed | ghcr.io/myorg/app |
docker://ubuntu@sha256:<digest> | That exact image digest | ubuntu |
docker://registry.example.internal:5000/team/app:1.0 | Exactly what you typed | registry.example.internal:5000/team/app |
Pin a tag or a digest when you need repeatable results, because latest changes over time.
Survey a private image
spice uses the registry credentials already stored on your machine. Log in once with Docker, then survey as normal:
docker login ghcr.io
spice survey inventory docker://ghcr.io/myorg/app:2.3.0
The wrapper shares your Docker configuration folder with the CLI, read-only. It uses $DOCKER_CONFIG if you set it, and ~/.docker otherwise. If your credentials live somewhere else, set DOCKER_CONFIG to that folder before running spice.
Docker Desktop keeps logins in a credential helper (the macOS Keychain or Windows Credential Manager) rather than in that folder. When it does, the wrapper asks the helper for the login to that one registry and passes it to the CLI in a temporary file, which is deleted when the run ends. Without a login, public images still work and private ones fail with an authentication error.
Survey a saved image
An image saved as an OCI image layout folder, for example with oras cp --to-oci-layout, needs no prefix. Survey the folder like any other, with a subject:
spice survey inventory web-frontend ./saved-image
Survey locally and upload later
Add --no-upload to survey on your machine only. Always pass --output with it, so the results land in a folder you choose:
spice survey inventory web-frontend docker://nginx:1.27 --no-upload --output ./spice-results
Each run creates its own folder inside the one you name, with the same layout as an inventory survey. While the survey runs, the image itself is downloaded into a temporary folder under --output and removed afterwards, so point --output at a disk with room for the image.
You do not need a Spice Pass for a --no-upload run. To upload those results later without surveying again, use --upload-only, pointed at the survey-<random-id> folder from that run instead of the image, and with the same subject:
spice survey inventory web-frontend ./spice-results/surveyor/survey-<random-id> --upload-onlyYou cannot combine --no-upload and --upload-only in one run.
Other options
Image surveys accept the same options as other inventory surveys: --tag-json, --threads, --max-records, --log-level, --log-file and --analysis-args, and for the upload --chunk-size and --upload-args. See Inventory surveys for how to use them.
The older command
Before image surveys moved into spice survey inventory, they had a command of their own, spice survey image <image> [--subject=<subject>]. It still runs, but it is hidden from the help, prints a notice naming the new form, and will be removed. Its default subject now also drops the tag.
Runtime surveys
- Surveyor IT
A runtime survey runs your Java application, or its test suite, with recording turned on and captures the cryptographic operations it actually performs. An inventory survey tells you what cryptography is present in an artifact; a runtime survey tells you what that artifact used. Survey the same artifact both ways to get a combined view.
Run a survey
spice survey runtime <subject> --jfr -- <command>
<subject>is the name you choose for this application in Spice Labs. Use the same name as its inventory surveys; see Choose a subject.--jfrselects JDK Flight Recorder instrumentation. Always include it.--separates thespiceoptions from the command to run.<command>is the command that starts your Java application or tests.
# Run an application under the survey
spice survey runtime payments-api --jfr -- java -jar app.jar
# Run a test suite under the survey
spice survey runtime payments-api --jfr -- mvn test
The target application must run on JDK 11 or later.
By default, the survey uploads its results to your Topographer project, which needs SPICE_PASS
set to a Spice Pass for that project; see Upload the results. To survey
without uploading, see Survey locally without uploading.
What happens during a run
spiceprepares a working folder with the Spice Labs recording agent and the recording settings.- It downloads the probe configuration for the agent from Spice Labs.
- It runs your command on your own machine, not in a container, with recording turned on.
- When your command exits, it analyzes the recordings, prints a summary and uploads the results.
Because your command runs on your machine, it needs everything it normally needs there: the right Java version, your build tool and your project files. Exercise the parts of the application you care about, then stop it normally. The survey sees only cryptography that actually runs, and the recording is written when the JVM exits.
spice adds its settings to JAVA_TOOL_OPTIONS for your command, keeping any value you already had there. If your command fails (for example, a failing test), spice still collects and analyzes the recordings. It then exits with your command’s exit code, so your build still sees the failure.
Upload the results
A runtime survey uploads in the same run: when your command exits, spice analyzes the recordings and uploads the results to your Topographer project. Set SPICE_PASS to a Spice Pass for that project before you start:
export SPICE_PASS="<your Spice Pass>"
spice survey runtime payments-api --jfr -- java -jar app.jar$env:SPICE_PASS = "<your Spice Pass>"
spice survey runtime payments-api --jfr -- java -jar app.jarThere is no separate upload step, and no way to upload a runtime survey afterwards: a run with --no-upload is analyzed on your machine only. Once the upload is processed, the survey appears in your project; see See the results.
Run without the agent
--native-only records only the security events built into the JDK and skips the Spice Labs agent:
spice survey runtime payments-api --jfr --native-only -- java -jar app.jar
Use it if the agent conflicts with another agent in your application. It captures less detail.
spice also falls back to this mode on its own, with the warning Could not download probe config. Using native-only mode., when it cannot download the probe configuration.
Survey locally without uploading
Add --no-upload to analyze the recordings on your machine and print a summary, without uploading anything. You do not need a Spice Pass for this:
spice survey runtime payments-api --jfr --no-upload -- java -jar app.jarUnlike inventory and image surveys, a runtime survey cannot be uploaded later. To get its results into Topographer, run the survey again without --no-upload, with SPICE_PASS set.
The probe configuration download needs your Spice Pass. A --no-upload run without SPICE_PASS falls back to native-only mode with a warning; add --native-only to skip the download.
Keep the recordings
| Goal | Add |
|---|---|
Keep the .jfr recording files after the run | --keep-recording |
| Choose where the working files go | --output=<dir> |
spice survey runtime payments-api --jfr --keep-recording --output=./rec -- java -jar app.jar
Each run works in a survey-<random-id> folder under --output, by default ~/.spicelabs/runtime-survey. Without --keep-recording that folder is deleted at the end. With it, spice prints Recordings kept in: and the folder path.
Anchor the survey to an artifact
To correlate a runtime survey with an inventory survey of the same artifact, pass that jar, war or ear with --anchor:
spice survey runtime payments-api --anchor=./build/libs/app.jar --jfr -- java -jar ./build/libs/app.jar
spice fingerprints the anchor file so it can be matched to the inventory survey that indexed it, and the two combined into one CBOM. Without --anchor, the survey still produces its own results but is not correlated, and the run warns: No --anchor given; this survey will not correlate into a combined CBOM.
See the results
After the upload, the survey appears in your project. Its post-quantum readiness report also records the runtime environment and the TLS connections and certificates observed while the application ran. If no recordings appear, see Troubleshooting.
Registry sweeps and CBOMs
- Surveyor IT
A registry sweep starts from the artifacts your builds already publish. Point it at an artifact repository, such as Artifactory or Nexus, or at a folder of files, and it lists every package version there. Then it downloads each version, maps every file inside it, and writes a CBOM (a cryptographic bill of materials) for it, on this machine. No team has to change its build pipeline, and the sweep remembers what it has done, so the next run builds only the versions that are new.
A sweep takes three steps:
- Write a registry configuration file that says where the artifacts are.
- Run
spice registry discoverto list the package versions into a discovery file, and edit it to choose what to build. - Run
spice registry cbom --processto build the CBOMs.
Run every command from one folder that holds the configuration file, the discovery file and the output. The wrapper makes that folder, and every file you name on the command line, visible to the CLI’s container; paths inside the configuration file are read relative to that folder.
Write a registry configuration file
The file is TOML. Each [[repositories]] table is one repository: its format (the package type), the server’s url, the repository’s name on that server, and which product serves it. For a Maven repository on Sonatype Nexus:
[[repositories]]
id = "corp-maven" # your name for it, used in output
format = "maven"
url = "https://nexus.example.internal"
repository = "maven-releases"
[repositories.host]
kind = "nexus"
[repositories.host.credential]
type = "basic"
username = "survey-bot"
password = { file = "nexus-password.txt" }
Save it as registry.toml. The password is read from nexus-password.txt in the same folder, so the configuration file holds no secret. A secret can also be written in the file as a plain string, which is simplest to test with but is then in the file for anyone who can read it.
For JFrog Artifactory, url includes the context path, and Artifactory also accepts a bearer token:
[[repositories]]
id = "corp-libs"
format = "maven"
url = "https://artifactory.example.internal/artifactory"
repository = "libs-release-local"
[repositories.host]
kind = "artifactory"
[repositories.host.credential]
type = "bearer"
token = { file = "artifactory-token.txt" }
format can be maven, npm, pypi, nuget, helm, rubygems, cargo, apt, rpm, conda and others; kind can be nexus, artifactory, gitlab, standard (any server that speaks only the format’s own protocol) and others. A repository that needs no sign-in leaves out the credential table. Add one [[repositories]] table for each repository to sweep.
Sweep a folder of files
To sweep files you already have, such as firmware images, device images or compiled binaries copied from elsewhere, use format = "local" with a path:
[[repositories]]
id = "device-images"
format = "local"
path = "artifacts"
Every file under artifacts/ is a package with one version, named by the first twelve hex digits of its SHA-256. Keep the folder inside the one you run spice from.
Discover what is there
spice registry discover --config registry.toml --output discovery.toml
This lists every package and its versions into discovery.toml. Without --output, the list is printed instead; add --max 20 to try a few packages first. The discovery file is a snapshot: run discover again to refresh it.
To choose what to build, edit the rules near the top of the file. Each pattern is matched against the package URL (such as pkg:maven/com.example/payments-api); with no rules, everything is built:
[[include]]
pattern = "pkg:maven/com.example/"
[[exclude]]
pattern = "-test"
Do not edit the package list below the rules; discover rewrites it.
Build the CBOMs
spice registry cbom --process --config registry.toml --discovery discovery.toml --output cboms
For each selected version, this downloads the artifacts from the repository, maps every file inside them, and writes the results to cboms/<namespace>/<name>/<version>/. Each version’s folder holds a cbom folder with the CBOM of the cryptographic material found, such as certificates, keys and algorithms, in CycloneDX JSON, beside the inventory of every file.
When the run ends, it prints a summary: how many packages it processed, how many artifacts it downloaded, how many CBOM files it wrote, and any errors. --threads 4 processes four versions at once.
Run it again
The sweep keeps track of every version it has processed, in a state folder it creates beside the configuration file. Run the same --process command again after discover has found new versions, and it builds only those. To see or reset what it remembers:
# List the versions already processed
spice registry cbom --config registry.toml --export-processed
# Forget one package, or everything, so the next --process builds it again
spice registry cbom --config registry.toml --reset --purl pkg:maven/com.example/payments-api
spice registry cbom --config registry.toml --reset
spice registry status --config registry.toml summarizes what the sweeps have done so far.
To survey the selected versions and upload the results to Topographer instead of building CBOMs
locally, see spice registry run.
On Windows
The commands are the same in PowerShell. Run them from the folder that holds registry.toml, with Docker Desktop running:
spice registry discover --config registry.toml --output discovery.toml
spice registry cbom --process --config registry.toml --discovery discovery.toml --output cboms
GitHub Actions
The Spice Labs Surveyor action runs an inventory survey or an image survey in your workflow and uploads the results to your Topographer project. Add it after your build step so every push or release is surveyed.
Set up the action
-
Store your Spice Pass as a secret
Generate a Spice Pass for the project the surveys should go to, and choose an Expires in that suits a pipeline. In your repository, open Settings, then Secrets and variables, then Actions, and select New repository secret. Name it
SPICE_PASSand paste the Spice Pass as its value. -
Add the workflow
Create
.github/workflows/spice-survey.yml:name: Spice Labs survey on: push: branches: [main] permissions: contents: read jobs: survey: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build run: ./gradlew build - name: Survey build output uses: spice-labs-inc/action-spice-labs-surveyor@v5 with: subject: payments-api input: ./build/libs spice_pass: ${{ secrets.SPICE_PASS }}Replace the build step with your own, and point
inputat the folder your build writes its artifacts to. -
Commit and push
The next push to
mainruns the survey. The survey appears in your project when the upload finishes.
The action installs the Surveyor CLI and runs it in Docker, so use a Linux runner such as ubuntu-latest.
Inputs
| Input | Required | Default | Description |
|---|---|---|---|
subject | Yes, for files | The name you choose for this application in Spice Labs. Keep it the same on every run. With image, it defaults to the image name without its tag | |
spice_pass | Yes | Your Spice Pass. Pass it from a secret. If it is empty, the action stops before it surveys, with an error that names spice_pass. | |
input | No | . | Path to the files to survey. Ignored when image is set. |
image | No | Container image reference to survey instead of local files, for example ghcr.io/myorg/app:2.3.0 | |
log_level | No | info | debug, info, warn or error |
cli_image | No | spicelabs/spice-labs-cli | The CLI image to run. Add a tag or digest to pin a version, for example spicelabs/spice-labs-cli:1.9.3. |
The action has no outputs. It runs spice survey inventory, with docker://<image> as the input when image is set. For runtime surveys, --no-upload, --tag-json or other options, run the CLI directly.
Survey a container image
Set image to survey an image from its registry instead of local files. For a private registry, log in first:
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: spice-labs-inc/action-spice-labs-surveyor@v5
with:
subject: payments-api
image: ghcr.io/myorg/app:${{ github.sha }}
spice_pass: ${{ secrets.SPICE_PASS }}
The CLI uses the runner’s Docker credentials to pull the image. When you use GITHUB_TOKEN to read packages, give the job packages: read permission. See Image surveys.
Survey several components
Survey each component under its own subject, so each one has its own results in the dashboard:
jobs:
survey-frontend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: spice-labs-inc/action-spice-labs-surveyor@v5
with:
subject: frontend
input: ./frontend/dist
spice_pass: ${{ secrets.SPICE_PASS }}
survey-backend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: spice-labs-inc/action-spice-labs-surveyor@v5
with:
subject: backend
input: ./backend/build/libs
spice_pass: ${{ secrets.SPICE_PASS }}
Pin the CLI version
The action installs the newest wrapper script and, by default, runs the latest CLI image. To keep results repeatable between runs, name the CLI image with its version in cli_image:
with:
subject: payments-api
spice_pass: ${{ secrets.SPICE_PASS }}
cli_image: spicelabs/spice-labs-cli:1.9.3
To run the CLI from your own copy of the image, name that copy the same way, for example cli_image: registry.example.com/mirror/spice-labs-cli:1.9.3. A digest works in place of the tag.
Run the CLI directly
When you need an option the action does not expose, install the CLI in a step and run spice yourself:
jobs:
survey:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: ./gradlew build
- name: Install the Surveyor CLI
run: |
curl -sSfL https://install.spicelabs.io | bash
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Survey build output
env:
SPICE_PASS: ${{ secrets.SPICE_PASS }}
SPICE_IMAGE_TAG: "1.9.2"
run: spice survey inventory payments-api ./build/libs --tag-json='{"branch":"${{ github.ref_name }}"}'
SPICE_IMAGE_TAG pins the CLI version here. It applies only while SPICE_IMAGE is unset: if you set SPICE_IMAGE, put the tag in it (spicelabs/spice-labs-cli:1.9.2). On a self-hosted runner that already has the image, you can set SPICE_LABS_CLI_SKIP_PULL=1 to skip the pull and the update check. See Configuration.
Keep the pipeline secure
- Pass the Spice Pass only from a secret, never in the workflow file.
- Give the workflow the least permissions it needs, such as
contents: read. - A Spice Pass works for one project only. Use a separate secret for each project you survey into.
- When the Spice Pass expires, uploads stop working. Generate a new one and update the secret, or revoke the old one on the Credentials page if it leaks.
Configuration
Two kinds of settings control a survey. Host environment variables tell the spice wrapper how to run the CLI: which image, whether to pull it, and whether to use Docker at all. Survey settings, such as thread counts and log level, live in a configuration file and can be overridden by environment variables and options.
Credentials
SPICE_PASS holds your Spice Pass, which uploads need; generate one in the dashboard. It has no configuration file setting.
Host environment variables
The wrapper reads these on your machine before the CLI starts.
| Variable | Default | What it does |
|---|---|---|
SPICE_IMAGE | spicelabs/spice-labs-cli | The CLI image to run. When set, it is used exactly as written, including any tag. |
SPICE_IMAGE_TAG | latest | The image tag. Used only when SPICE_IMAGE is not set. |
SPICE_LABS_CLI_SKIP_PULL | 0 | 1 skips pulling the image and the wrapper’s update check. The image must already be on the machine. |
SPICE_DOCKER_NETWORK | host | The network the CLI’s container uses. none cuts it off from the network. |
SPICE_DOCKER_FLAGS | none | Extra options added to the docker run command, separated by spaces. Not applied to runtime surveys. |
DOCKER_CONFIG | ~/.docker | The Docker configuration folder whose registry credentials image surveys use, mounted read-only. |
SPICE_LABS_JVM_ARGS | -XX:MaxRAMPercentage=75 | Options for the JVM that runs the CLI. It is passed into the container when you set it. |
The wrapper also takes a --features option before the command: --features it, --features itpro or --features otpro runs ghcr.io/spice-labs-inc/spice-labs-cli-it, -itpro or -otpro
(tag latest) when SPICE_IMAGE is not set. The option is not passed on to the CLI.
Pin the CLI version
By default every run pulls spicelabs/spice-labs-cli:latest. To stay on one version, set the tag:
export SPICE_IMAGE_TAG=1.9.2$env:SPICE_IMAGE_TAG = "1.9.2"SPICE_IMAGE takes priority over everything else that picks an image. When it is set, spice runs exactly that reference: SPICE_IMAGE_TAG and the --features option are ignored, and an image named without a tag means latest. So if you set SPICE_IMAGE, put the tag in it:
export SPICE_IMAGE=spicelabs/spice-labs-cli:1.9.2$env:SPICE_IMAGE = "spicelabs/spice-labs-cli:1.9.2"Run without network access
To prove a survey makes no network calls, cut the container off from the network and skip the upload:
SPICE_DOCKER_NETWORK=none spice survey inventory payments-api ./build/libs --no-upload --output ./spice-results$env:SPICE_DOCKER_NETWORK = "none"
spice survey inventory payments-api .\build\libs --no-upload --output .\spice-results
Remove-Item Env:SPICE_DOCKER_NETWORKThe wrapper still pulls the image and checks for a newer wrapper script on your machine before the run. Set SPICE_LABS_CLI_SKIP_PULL=1 as well to skip both.
JVM mode (running without Docker)
With SPICE_LABS_CLI_USE_JVM=1, the wrapper runs the CLI jar with your local Java instead of in Docker. Paths are used as they are, and --features is ignored. Docker mode is the one these docs describe and the one to use when you can. JVM mode needs:
- Java 21 or later on your
PATH. - The CLI jar at the path in
SPICE_LABS_CLI_JAR. Build it from the spice-labs-cli repository. Plugins are loaded from aplugins/folder beside the jar. orason yourPATHfor image surveys.syfton yourPATHto identify OS packages, Go modules and lockfile dependencies. Without it the survey still runs but finds far fewer packages, and prints no warning.
The Docker image includes all of these. On Windows, set SPICE_LABS_JVM_ARGS to a single JVM option, such as -XX:MaxRAMPercentage=75, when you use JVM mode.
| Variable | Default | What it does |
|---|---|---|
SPICE_LABS_CLI_USE_JVM | 0 | 1 runs the CLI with Java on your machine instead of in Docker. |
SPICE_LABS_CLI_JAR | /opt/spice-labs-cli/spice-labs-cli.jar | The CLI jar used in JVM mode. |
Configuration file
Settings you use on every run can live in a TOML file instead of on the command line. spice reads one file per run.
Where the file is found
| Platform | Location, first match wins |
|---|---|
| Any | The file you name with spice --config <file> |
| macOS and Linux | $XDG_CONFIG_HOME/spice/config.toml (by default ~/.config/spice/config.toml), then spice/config.toml in each $XDG_CONFIG_DIRS folder (by default /etc/xdg) |
| Windows | %APPDATA%\spice\config.toml, then %PROGRAMDATA%\spice\config.toml |
The first file found is the only one used. Naming a file that does not exist with --config is an error, but finding no file at the default locations just means there is no configuration file. --config belongs to spice itself, so it goes before the subcommand:
spice --config ./ci.toml survey inventory payments-api ./build/libs
Settings
Settings are grouped by the job they describe:
[analysis]
threads = 16
max_records = 100000
[upload]
target_chunk_size = 64
[logging]
level = "debug"
| Group | Keys |
|---|---|
analysis | threads (default: half your CPU cores), max_records (default: 5000) |
upload | target_chunk_size (MB), encrypt_only, skip_key, comment, bundle_format_version |
logging | level: error, warn, info, debug or trace (default: info) |
A group at the top of the file applies to every command that reads it. To change one command only, write the group under that command’s name:
[analysis]
threads = 16
[survey.inventory.analysis]
threads = 4
The log file cannot be set in the configuration file. Use the --log-file option instead: the wrapper writes the log on your machine, and a path named anywhere else would be written inside the container and lost.
Other paths named in the configuration file are mounted into the container automatically, so they resolve to the same place as on your machine.
One setting, three names
Every setting has the same name in all three places:
| Form | Shape | Example |
|---|---|---|
| Configuration key | snake_case in a group | [analysis] max_records |
| Option | kebab-case | --max-records |
| Environment variable | SPICE_<GROUP>_<KEY> | SPICE_ANALYSIS_MAX_RECORDS |
Which value wins
defaults < [group] < [command.group] < environment < command line
When one source overrides another, the run logs it, for example analysis.threads = 8 (SPICE_ANALYSIS_THREADS) overrides 16 ([analysis] in ...).
Check what a run will use
spice config explain shows every setting a command would use and where each value came from, without running a survey. For example, with this configuration file at ~/.config/spice/config.toml:
[analysis]
threads = 4
[survey.inventory.analysis]
max_records = 20000
[logging]
level = "debug"
and the log level set from the environment:
SPICE_DOCKER_FLAGS="-e SPICE_LOGGING_LEVEL=warn" spice config explain survey inventory$env:SPICE_DOCKER_FLAGS = "-e SPICE_LOGGING_LEVEL=warn"
spice config explain survey inventory
Remove-Item Env:SPICE_DOCKER_FLAGSthe output names the source of each value, and logs the override:
# /home/you/.config/spice/config.toml
INFO ⚙️ logging.level = warn (SPICE_LOGGING_LEVEL) overrides debug ([logging] in /home/you/.config/spice/config.toml)
[analysis]
max_records = 20000 [survey.inventory.analysis] in /home/you/.config/spice/config.toml
threads = 4 [analysis] in /home/you/.config/spice/config.toml
[logging]
level = "warn" SPICE_LOGGING_LEVEL
A setting you have not set shows default. Without a configuration file, the first line reads # No configuration file; showing defaults and the environment. Add --group=<name> to show one group only.
If a group name is misspelled, or belongs to a plugin you do not have, the run warns and continues: No command reads [anaylsis] — check the spelling, or the plugin may not be installed.
Shell completion
The installers set up tab completion for spice in Bash, Zsh and PowerShell. The completion script is generated by the installed CLI, so it offers the commands your installation has. Choose your shell above the recording to see how it looks there.
Type
spiceand ask the shell for completions. Press Tab twice. Press Tab once.Zsh lists the top-level commands on the first Tab. On the second it starts inserting them in turn, so
configis now on the line. Keep pressing Tab to move through the list, or type a few letters to narrow it.Bash lists the top-level commands on the second Tab and leaves the line as it was, ready for you to type the first letters of the one you want.
PowerShell lists the top-level commands and options on the first Tab. That is its behavior on macOS and Linux; on Windows, Tab instead cycles through the candidates in place.
It works at every level:
spice surveyfollowed by Tab offers the survey types.The candidates come from
spice generate-completion, which reads the installed CLI, so they always match the commands and survey types you have.The candidates come from the script the installer generated with
spice generate-powershell-completion.
Set it up by hand
The installers do this for you. To do it yourself, for example after an upgrade:
- Bash. Write the script with
spice generate-completion > ~/.spice-completion.bash, and source that file from~/.bashrc. - Zsh. Use the same script, loaded through Bash compatibility. In
~/.zshrc, addautoload -U +X bashcompinit && bashcompinit, then source the file. - PowerShell. Write the script with
spice generate-powershell-completion > spice-completion.ps1, and dot-source it from your$PROFILE.
CLI reference
This page lists every command and option of spice. For how to use them, start with Inventory surveys; for environment variables and the configuration file, see Configuration.
The CLI prints the same information itself: run spice --help or spice <command> --help, or spice docs --commands for all of it at once.
- Start at the top:
spice --helplists the top-level commands. spice --help~ ❯ spice --help Usage: spice [-hV] [--config=FILE] [COMMAND] Spice Labs CLI: Surveyor IT --config=FILE TOML configuration file. Defaults to the standard config location. -h, --help Show this help message and exit. -V, --version Print version information and exit. Commands: survey Survey software artifacts pass Spice Pass utilities docs Show the user guide, or print the help for every command when this build has no guide. config Inspect the configuration this run would use registry Bulk-survey an artifact registry over time (allspice) Examples: spice survey inventory my-app ./build/libs spice survey inventory my-app ./app.jar --no-upload --output ./out spice survey runtime my-app --jfr -- java -jar app.jar spice pass decode Run 'spice <command> --help' for details on each subcommand.The main ones are
survey, which runs a survey, andconfig, which explains configuration.passworks with your Spice Pass.docsprints the help for every command. Plugins, when installed, add their commands here as well.spice survey --help~ ❯ spice survey --help Usage: spice survey [-hV] [COMMAND] Survey software artifacts -h, --help Show this help message and exit. -V, --version Print version information and exit. Commands: inventory Survey artifact inventory and upload ADGs to Spice Labs runtime Survey runtime crypto usage via JFR instrumentation image Survey an OCI or Docker image pulled by name Examples: spice survey inventory my-app ./build/libs spice survey image my-app nginx spice survey runtime my-app --jfr -- java -jar app.jar Run 'spice survey <type> --help' for per-subcommand details.spice survey --helplists the survey types, each with a one-line summary, followed by examples.spice survey inventory --help~ ❯ spice survey inventory --help Usage: spice survey inventory [-hV] [--no-upload] [--upload-only] [--chunk-size=<chunkSizeMB>] [--log-file=<logFile>] [--log-level=<logLevel>] [--max-records=<maxRecords>] [--output=<output>] [--tag-json=<tagJson>] [--threads=<threads>] [--analysis-args=<goatRodeoArgsRaw>[, <goatRodeoArgsRaw>...]]... [--upload-args=<gingerArgsRaw>[, <gingerArgsRaw>...]]... <subject> <input> Survey artifact inventory and upload ADGs to Spice Labs <subject> Label identifying the system being surveyed <input> Path to artifacts (directory or single file) --analysis-args=<goatRodeoArgsRaw>[,<goatRodeoArgsRaw>...] Additional analysis args in key=value format --chunk-size=<chunkSizeMB> Target chunk size in MB for uploads (default: 64) -h, --help Show this help message and exit. --log-file=<logFile> Path to log file (output appended to both console and file) --log-level=<logLevel> Log level: debug|info|warn|error (default: info) --max-records=<maxRecords> Max records to process per batch (default: 5000) --no-upload Survey only, skip upload --output=<output> Output directory for survey results --tag-json=<tagJson> Additional JSON metadata for tags --threads=<threads> Number of threads to use (default: half of available CPU cores) --upload-args=<gingerArgsRaw>[,<gingerArgsRaw>...] Additional upload args in key=value format --upload-only Upload previously-generated ADGs (skip survey) -V, --version Print version information and exit. Examples: # Survey a directory of artifacts and upload spice survey inventory my-app ./build/libs # Survey a single file, skip upload, write output to ./out spice survey inventory my-app ./app.jar --no-upload --output ./out # Attach JSON tag metadata spice survey inventory my-app ./dist --tag-json='{"env":"prod"}' # Upload previously-surveyed ADGs spice survey inventory my-app ./out --upload-only SPICE_PASS must be set in the environment for upload.Every survey type has its own
--helpwith the full option list.spice survey inventory --helpshows the arguments an inventory survey takes and every option, with its default.
Commands
| Command | What it does |
|---|---|
spice survey inventory | Survey files, folders and archives |
spice survey inventory docker://<image> | Survey a container image straight from its registry (docker:// or oci://) |
spice survey runtime | Record the cryptography a running Java application uses |
spice survey inventory --upload-only, --upload-args, --chunk-size | Upload inventory survey results to Topographer |
spice survey inventory docker://<image> --upload-args, --chunk-size | Upload image survey results to Topographer |
spice survey runtime --chunk-size | Upload runtime survey results to Topographer |
spice registry discover --output, --max | List the packages and versions in an artifact repository or a folder |
spice registry cbom --process, --discovery, --output, --threads, --reset, --export-processed | Build a CBOM of every package version a discovery file selects, on this machine |
spice registry run | Survey the versions a discovery file selects and upload the results |
spice registry status | Summarize what the registry runs have done |
spice pass decode | Show what your credential holds |
spice config explain | Show every setting a run would use and where each came from |
spice docs --commands, --json | Print the help for every command, or the commands as JSON |
spice --version | Print the version and, where a license applies, whether it is valid |
spice generate-completion | Print the tab completion script (generate-powershell-completion for PowerShell) |
Global options
These belong to spice itself, so they go before the subcommand.
| Option | Description |
|---|---|
--config=FILE | Use this TOML configuration file instead of the one found at the standard locations. See Configuration file. |
-h, --help | Show help and exit. Every command accepts it. |
-V, --version | Print the CLI version and the commit it was built from, and exit. Through the wrapper, it also prints the wrapper script’s hash. Every command accepts it. |
The wrapper also takes --features it, --features itpro or --features otpro before the
command, to choose the image to run when SPICE_IMAGE is not set. See
Configuration.
spice survey inventory
- Surveyor IT
spice survey inventory [options] <subject> <input>
spice survey inventory [options] [<subject>] docker://<image>Surveys artifacts and uploads the results to Spice Labs as encrypted Artifact Dependency Graphs
(ADGs). SPICE_PASS must be set to upload.
| Argument or option | Description | Default |
|---|---|---|
<subject> | Label identifying the system being surveyed. Required for a path; optional before an image | For an image, its repository name without tag or digest |
<input> | A folder (not empty) or a single file, or docker://<image> (or oci://<image>) for a container image in a registry. Required. | |
--output=<dir> | Folder for the results. Each run writes to <dir>/surveyor/survey-<random-id>/. | A folder inside the container, discarded after the run |
--tag-json=<json> | Your own metadata for the survey, as a JSON object | None |
--threads=<n> | Number of analysis threads, 1 or more | Half your CPU cores |
--max-records=<n> | Records processed per batch | 5000 |
--log-level=<level> | error, warn, info, debug or trace | info |
--log-file=<path> | Append everything the run prints to this file on your machine, without color codes | None |
--analysis-args=<key=value>[,...] | Extra settings passed to the analysis engine | None |
These control the upload:
- Surveyor IT
| Option | Description | Default |
|---|---|---|
--no-upload | Survey only, skip upload. No credential needed. | Off |
--upload-only | Upload a previous run’s results without surveying again. <input> is that run’s survey-<random-id> folder. Not with --no-upload or an image. | Off |
--chunk-size=<MB> | Target upload chunk size in MB | Chosen by Spice Labs |
--upload-args=<key=value>[,...] | Extra settings passed to the upload step | None |
The help text shows (default: 64) for --chunk-size, but when you leave the option unset, the CLI sends no chunk size and Spice Labs chooses one.
The help lists debug|info|warn|error for --log-level; trace is accepted too. Change --analysis-args only when Spice Labs support asks you to.
Most of these options also have a configuration file key and an environment variable, such as [analysis] threads and SPICE_ANALYSIS_THREADS. See One setting, three names.
Container images
- Surveyor IT
spice survey inventory [options] [<subject>] docker://<image>With docker://<image> as the input, the CLI pulls the image from its registry with oras, which is built into the CLI’s container, so there is no docker pull or tar export. It surveys the image’s contents and handles the result like any inventory survey. oci://<image> means the same thing. The image is name[:tag][@digest]; a bare name such as nginx is expanded to docker.io/library/nginx:latest.
Without a subject, the survey is labeled with the image’s repository name, without its tag or digest: docker://ghcr.io/acme/web:2.4.1 becomes ghcr.io/acme/web and docker://nginx:1.27 becomes nginx, so every version of an image lands on one subject. While the survey runs, the image is downloaded to a temporary folder, under --output when you set it, and removed afterwards.
All the options above apply, except --upload-only. See Image
surveys.
An image name without the prefix, such as nginx:1.27, is read as a path; when no such file exists, the CLI says to use docker://nginx:1.27. spice survey image <image>, the older form, still runs but is hidden from the help, prints a notice naming the new form, and will be removed.
spice survey runtime
- Surveyor IT
spice survey runtime [options] <subject> --jfr -- <command...>Runs <command> with Java Flight Recorder instrumentation, then analyzes the recordings and uploads the results. Everything after -- is the command to run. See Runtime surveys.
| Argument or option | Description | Default |
|---|---|---|
<subject> | Label identifying the system being surveyed. Required. | |
--jfr | Use Java Flight Recorder instrumentation. Required. | |
--anchor=<path> | The jar, war or ear this survey is of. It is fingerprinted so the survey can be correlated with an inventory survey that indexed it, producing a combined CBOM. | None |
--native-only | Record only the JDK’s built-in security events, without the Spice Labs agent | Off |
--keep-recording | Keep the .jfr files after the run | Off |
--output=<dir> | Folder for the working files | ~/.spicelabs/runtime-survey |
| Option | Description | Default |
|---|---|---|
--no-upload | Analyze locally and print a summary, don’t upload | Off |
--log-level=<level> | error, warn, info, debug or trace. Applied only in JVM mode, when running without Docker. | info |
--chunk-size=<MB> | Target upload chunk size in MB. Applied only in JVM mode, when running without Docker. | Chosen by Spice Labs |
The wrapper runs your command on your machine and uses the container only to prepare and analyze. --log-file works too.
spice registry
- Surveyor IT
Sweeps an artifact repository, or a folder of files, and builds CBOMs of what it holds. The settings (which repository, how to sign in to it) come from a registry configuration file that each command names with --config. See Registry sweeps and CBOMs for a walkthrough and the file’s format.
Every registry command takes:
| Option | Description |
|---|---|
--config=FILE | The registry configuration file |
--json | Machine-readable output |
This --config comes after registry <command>. The global --config, before registry, names spice’s own configuration file, which is a different file.
spice registry discover
spice registry discover --config=FILE [--output=FILE] [--max=N] [--json]Lists the packages and versions in the repository. With --output, writes them to a discovery file instead: TOML, or JSON when the name ends in .json. --max caps how many packages are listed.
spice registry cbom
- Surveyor IT
spice registry cbom --config=FILE --process --discovery=FILE --output=DIR [--threads=N]
spice registry cbom --config=FILE --export-processed
spice registry cbom --config=FILE --reset [--purl=PURL]Builds a CBOM of every package version the discovery file selects, on this machine. Nothing is uploaded.
| Option | Description |
|---|---|
--process | Download and analyze every selected version that has not been processed yet |
--discovery=FILE | The discovery file. Required with --process. |
--output=DIR | Where the CBOMs go, one folder per package version. Required with --process. |
--threads=N | How many package versions to process at once |
--export-processed | List the versions already processed |
--reset | Forget what has been processed, so the next --process starts again |
--purl=PURL | With --reset, forget only this package |
spice registry run
- Surveyor IT
spice registry run --config=FILE [--discovery=FILE] [--json]Surveys the versions the discovery file selects and uploads the results to Topographer.
spice registry status
- Surveyor IT
spice registry status --config=FILE [--json]Summarizes what the registry runs have done so far.
spice pass decode
- Surveyor IT
spice pass decode
Decodes and displays the Spice Pass in SPICE_PASS.
It prints:
- A first line naming the credential and its source, such as
Spice Pass (from.SPICE_PASS) - JWT Header: the signing algorithm and type.
- Claims: a table with the columns Name, Claim and Value. Times are shown as numbers with a readable date beside them.
- Status:
Valid (expires in ...),EXPIREDorNo expiration.
The claims it can show, in this order:
| Label | Claim | Label | Claim |
|---|---|---|---|
| Token Type | x-type | Organization ID | x-uuid-org |
| Token Version | x-version | User ID | x-uuid-user |
| JWT ID | jti | Project ID | x-uuid-project |
| Issued At | iat | Demo Project | demo |
| Expires At | exp | Upload Server | x-upload-server |
| Not Before | nbf | Challenge | x-challenge |
| Issuer | iss | Artifact Cutoff | x-cutoff |
| Subject | sub | Licensed Features | x-features |
| Audience | aud | Licensed Edition | x-edition |
Any other claim is shown with its claim name as the label. A public key in the credential is printed as a separate Public Key block. If Artifact Cutoff is present, inventory surveys leave out artifacts published after that date: see Artifact cutoff.
spice config explain
- Surveyor IT
spice config explain [--group=GROUP] [COMMAND...]
Shows every setting and where its value came from: a default, a group in the configuration file, the environment or an option. COMMAND is the command to explain, such as survey inventory; without it, every command’s settings are shown. --group restricts the output to one group, such as analysis. See Check what a run will use.
spice docs
- Surveyor IT
spice docs [--commands | --json]
Prints the help for every command, one after another. --json prints the commands and their options as JSON instead, for tools and AI agents; the same JSON is attached to each CLI release as cli-docs.json. --commands prints the help listing explicitly.
Tab completion
- Surveyor IT
The installer sets up tab completion for you. To generate it yourself:
# Bash and Zsh, for the current shell
source <(spice generate-completion)
In PowerShell, spice generate-powershell-completion prints the equivalent script. See Shell completion.
Exit status
spice exits with 0 when the command succeeds. On an error it prints the reason and exits with a non-zero status. The survey commands follow an error with Use --help for usage information. A runtime survey exits with your command’s exit code when that command failed, and otherwise with the survey’s own status. See Troubleshooting for the messages.
Supported file types
A survey fingerprints every file in its input, whatever the type. The formats below are the ones it also opens, so it can survey what is inside them, or recognizes as packages. Containers nest: a jar inside a tarball inside a disk image is opened all the way down.
Archives and containers
| Category | Formats | Examples |
|---|---|---|
| ZIP-family archives | ZIP, JAR, WAR, EAR and other Java archives, Android APK | app.zip, myapp.jar, webapp.war, app.ear, app.apk |
| Container images | docker save tarballs, OCI image layouts | myimage.tar (with manifest.json), a folder with oci-layout, index.json and blobs/ |
| Tar and other archives | tar, cpio, ar | backup.tar, initrd.cpio, libfoo.a |
| Compressed files | gzip, bzip2, xz, lzma, zstd | data.tar.gz, logs.tar.bz2, archive.xz, payload.zst |
| OS packages | RPM, Debian, Arch Linux (pacman), Alpine, FreeBSD and OpenBSD packages | nginx-1.24.0-1.el9.x86_64.rpm, curl_8.5.0_amd64.deb, musl-1.2.5-r0.apk |
| ISO 9660 images | ISO disc images | ubuntu-24.04-live-server-amd64.iso |
| Disk images | qcow2, VMDK, VHD, VHDX, VDI, raw, AMI, Google Cloud images | disk.qcow2, vm.vmdk, machine.vhdx, os.vdi, disk.raw, gcp-image.tar.gz |
| File systems in disk images | ext2, ext3, ext4, FAT32, exFAT, NTFS, XFS, Btrfs, SquashFS, HFS+, APFS, behind MBR or GPT partition tables and LVM | an ext4 partition inside a .qcow2, a SquashFS root file system in a firmware image |
| Firmware and binary containers | Linux kernel images, U-Boot FIT images, device tree blobs, ELF, Raspberry Pi firmware, Android boot images, WIM, DMG | zImage, fitImage, bcm2711-rpi-4-b.dtb, boot.img, install.wim, app.dmg |
Compressed files are decompressed first, and the result is then checked against the other formats. A disk image is read through every file system on it, including those in LVM volumes.
Packages
Besides opening files, a survey identifies the packages they belong to:
| Ecosystem | What it reads | Examples |
|---|---|---|
| Java | Jars and other Java archives, with their Maven metadata | commons-codec-1.17.0.jar |
| .NET | Managed assemblies | System.Data.dll, MyApp.exe |
| Installed OS packages | The package databases inside container images and file systems | Debian, RPM and Alpine packages in an image |
| Lockfiles | Declared dependencies, such as npm, cargo and Python requirements | package-lock.json, Cargo.lock, requirements.txt |
| Go | Modules built into Go binaries | a compiled Go service |
| Language package archives | npm, PyPI, Go modules, crates.io, RubyGems, Packagist, Conda, CocoaPods, CPAN, Hex and LuaRocks packages | lodash-4.17.21.tgz, requests-2.32.3-py3-none-any.whl, serde-1.0.200.crate, rails-7.1.3.gem |
Troubleshooting
Find the message spice printed, or the symptom that matches, and follow the fix. The headings quote each message without the symbol printed before it; <path> and similar stand for the value in your message.
For more detail on any run, add --log-level=debug and run it again, and add --log-file=spice.log to save a copy of the output. Messages marked JVM mode appear only when you run the CLI without Docker, in JVM mode (SPICE_LABS_CLI_USE_JVM=1). On Windows, the wrapper’s own messages use [X], [!] and [*] in place of the symbols, and a few differ slightly in wording.
Setup and Docker
Docker is not installed or not in PATH
The CLI runs in Docker by default. Install Docker from docs.docker.com/get-docker, make sure it is running, and open a new terminal. On Windows, install Docker Desktop and start it before you run spice. If you cannot use Docker, see JVM mode.
Docker on this machine runs Windows containers.
The CLI runs in a Linux container, and this machine’s Docker runs only Windows containers. In Docker Desktop, right-click the Docker icon and choose Switch to Linux containers, then run spice again. Windows Server’s Docker Engine cannot run Linux containers; use a machine whose Docker can.
Failed to pull <image>, then Image <image> not found locally either.
spice could not download the CLI image and has no local copy. Check that:
- Your machine can reach Docker Hub, or the registry you use.
- If you set
SPICE_IMAGEorSPICE_IMAGE_TAG, that image and tag exist. A tag must be inSPICE_IMAGEitself whenSPICE_IMAGEis set: see Pin the CLI version. - If you used
--features, your machine has access to that product’s private image.
If you see Failed to pull <image> followed by Using local copy., the run continues with the image you already have.
A newer version of this script is available. Run:
Your wrapper script is older than the latest release. Run the install command it prints, or see Install the Surveyor CLI.
The shell cannot find spice
- Open a new terminal after installing.
- Check that the install folder is on your
PATH:~/.local/binon macOS and Linux,%USERPROFILE%\.spice\binon Windows. See Set your PATH and Spice Pass.
Missing: <jar>
JVM mode. There is no CLI jar at the path in SPICE_LABS_CLI_JAR. Point it at your jar, or unset SPICE_LABS_CLI_USE_JVM to run in Docker.
Spice Pass
SPICE_PASS must be set via SPICE_PASS env var for upload. Use --no-upload to skip upload.
An inventory or image survey needs your Spice Pass to upload. Generate one and set SPICE_PASS, or add --no-upload to survey locally. A runtime survey reports the same problem as SPICE_PASS not set, or in JVM mode as SPICE_PASS must be set for upload. Use --no-upload to run locally.
The upload fails
- Run
spice pass decodeand check the Status line. If it saysEXPIRED, generate a new Spice Pass. - Check that Project ID is the project you expect. A Spice Pass works for one project only.
- Check that
SPICE_PASSholds the whole pass, with no extra spaces or line breaks. - Check your network connection and that your machine can reach the Upload Server shown by
spice pass decode, then run again.
Inventory and image surveys
Input path does not exist: <path> or Input directory is empty: <path>
Check the <input> argument of spice survey inventory. Relative paths are resolved from your current folder, and a folder must contain at least one file.
Cannot use both --no-upload and --upload-only
Pick one. Use --no-upload to survey without uploading, and --upload-only to upload the results of an earlier run.
--tag-json is not valid JSON or --tag-json must be a JSON object
The value must be a JSON object such as {"env":"dev"}. On Windows PowerShell 5.1, assign the JSON to a variable first, as the message suggests. See Tag a survey with metadata.
--threads must be at least 1, got: <n>
Use a whole number of 1 or more, or leave --threads off to use half your CPU cores.
Failed to pull image (<image>), oras exited <n>:
spice could not download the image you asked it to survey. The lines after the message are the download tool’s own output. Check the image name and tag, and for a private registry run docker login <registry> first. See Survey a private image.
The survey covered fewer artifacts than expected
If you are using a trial or demo Spice Pass, it may carry an artifact cutoff. Look for Ignoring artifacts published after <date> in the output, or run spice pass decode and look for Artifact Cutoff. See Artifact cutoff.
The local results are missing
Without --output, results are written inside the CLI’s container and discarded when the run ends. Run again with --output ./spice-results and look inside spice-results/surveyor/survey-<random-id>/.
Registry sweeps
--config is required, or repositories in the host's configuration
Name your registry configuration file with --config, after registry <command>. See Registry sweeps and CBOMs.
--discovery is required for --process or --output is required for --process
spice registry cbom --process needs both: the discovery file that selects what to build, and the folder the CBOMs go to.
Discovery file not found: <path>
Run spice registry discover --output first, and run spice from the folder that holds the discovery file, or give its full path.
no action: use --process, --reset or --export-processed
spice registry cbom needs one of those three options.
No packages to process
The discovery file’s include and exclude rules select nothing. Loosen them, or run spice registry discover --output again.
All <n> package(s) already processed
Every version the discovery file selects already has its CBOM. To build them again, run spice registry cbom --reset first, or --reset --purl=<purl> for one package.
Runtime surveys
No command specified after --
Put -- between the spice options and your command, for example spice survey runtime payments-api --jfr -- java -jar app.jar. In JVM mode the message is No command specified. Use -- to separate the command to execute.
No subject specified
Add a subject label before --, for example spice survey runtime payments-api --jfr -- mvn test.
Missing required option: '--jfr'
JVM mode. Add --jfr. Include it in every runtime survey, in Docker mode too.
No JFR recordings found in <folder>
Your command ran but produced no recordings. In JVM mode the message is No JFR recordings found. The target application may not have produced any. Check that:
- The command starts a Java process. A script that launches Java counts.
- The application exits normally after doing real work, so the recording is written.
- Your command’s output shows no Flight Recorder start-up errors.
Target command exited with code <n>. Still collecting recordings.
Your command failed, for example with a failing test. The survey continues with whatever was recorded and then exits with your command’s exit code.
JDK <n> is not supported by the JFR parser. JDK 11+ is required for the CLI to parse JFR recordings.
JVM mode. Run the application on JDK 11 or later.
Could not download probe config. Using native-only mode.
spice could not fetch the agent’s probe configuration, so it recorded the JDK’s built-in
security events only. The download needs your Spice Pass: set SPICE_PASS, check your network
connection, and run again for full detail. To skip the download on purpose, add --native-only.
In JVM mode the message is Failed to download probe config. Falling back to native-only mode.
No --anchor given; this survey will not correlate into a combined CBOM.
The survey still runs, but it cannot be combined with an inventory survey. Add --anchor=<path-to-jar>. See Anchor the survey to an artifact.
Total recording size exceeds 1GB. This may indicate excessive instrumentation.
JVM mode. The run was very long or recorded a lot. Survey a shorter run of the parts of the application you care about.
Configuration
No command reads [<group>] — check the spelling, or the plugin may not be installed
A group name in your configuration file is misspelled, or belongs to a plugin you do not have. The run continues without it. Fix the name and check with spice config explain.
[analysis] unknown key: <key>
A key in the [analysis] group is misspelled or not supported. See Settings for the keys.
[logging] level must be one of error, warn, info, debug, trace, got: <value>
Set level in [logging] to one of the values listed.
[logging] file was set in <source>, but it can only be given as --log-file
The log file can only be set with --log-file on the command line. Remove file from the group named in the message, or SPICE_LOGGING_FILE from your environment.
A setting is not taking effect
Run spice config explain <command>, for example spice config explain survey inventory, to see the value each setting will use and where it came from. Options beat environment variables, which beat the configuration file. In Docker mode, SPICE_<GROUP>_<KEY> variables do not reach the CLI unless you pass them through SPICE_DOCKER_FLAGS: see Which value wins.