First run
An empty inventory is a fair first impression and a useless demonstration. Load the invented portfolio, run the whole loop against it, and then throw it away.
Load the demo
docker run --rm -v "$PWD/data:/data" --user "$(id -u):$(id -g)" \
ghcr.io/oscarvalenzuelab/crane:latest demo
loaded 140 rows: 8 products, 8 components, 5 SBOM records
SBOM files written to /data/demo-sboms
Next: crane validate sbom && crane worker run --once && crane assess cra
Eight invented products, chosen to show the situations this exists for: a component shared by several products, one with no build at all, SBOMs that pass and SBOMs that fail, a document that cannot be read, and a product nobody has scoped.
demo refuses to run against a database that already has anything in it, so it cannot be
mistaken for real inventory. --force overrides that, and you should not need it.
The demo portfolio is fiction. Do not build a real deployment on top of it. Start over with an empty database instead, which is one file to delete.
Run the loop
The working loop is four commands, and they are the same four whether the portfolio is invented or real.
crane validate sbom --requested-by you # queue conformance checks. Returns immediately
crane worker run --once # actually execute them
crane assess cra # score, and store the reasoning
crane report leadership # a document for somebody who did not run this
Queueing and executing are separate on purpose. validate never runs a tool; it registers
work and returns. That is what makes it safe in cron and safe over an API, and it is why a
deployment with no shell can still ask for validation.
What you will see on the plain image
claimed 15, succeeded 0, failed 15, retrying 0
All fifteen failed, and the exit code is 5. This is correct and it is not a
misconfiguration: the plain image carries no validator. Ask a failed job why:
crane job list --status failed --json
{
"definition_key": "ossbomer.validate",
"error": "the SBOM file does not exist at /data/demo-sboms/cirrus-sensor-missing.spdx.json",
"attempts": 1,
"max_attempts": 3
}
Two different failures are mixed together here, and telling them apart is the point of the exercise:
- The validator is not installed. Use the
-ossbomerimage, or install ossbomer next to the worker. - The SBOM file genuinely is not there. One demo product ships a deliberately missing document, because a source you cannot read is a real state and the report has to distinguish it from a source nobody asked about.
The score
crane assess cra
obligation: cra v2
lens: cra.regulatory
denominator:
in scope 6
scope undecided 1
out of scope 1
products total 8
bands:
amber 4
red 2
average score 59.2
checks across the population:
composition.known 2 pass, 4 warn
owner.accountable 4 pass, 2 warn
sbom.conformance 6 insufficient evidence
sbom.present 1 fail, 4 pass, 1 source inaccessible
scope.decided 6 pass
support.period.declared 2 fail, 4 pass
vuln.contact.published 3 fail, 3 pass
Read the denominator before the score. Eight products exist; six are in scope under this lens; one has no scope decision and is counted neither as passing nor as failing, and one is out of scope. A number that does not say which population it measured is not an answer.
Note the three distinct non-passing states, which most tools collapse into one:
| State | Means |
|---|---|
fail |
Checked, and it does not hold |
warn |
Checked, and it is weak rather than absent |
insufficient evidence |
Nobody could check. Here, because no validator ran |
source inaccessible |
The document exists in the inventory and could not be read |
insufficient evidence is not a failing grade. Nothing is scored as failing on account of
a tool that was never installed.
Look at it
docker run --rm -p 127.0.0.1:8000:8000 -v "$PWD/data:/data" \
--user "$(id -u):$(id -g)" -e CRANE_ALLOW_UNAUTHENTICATED_BIND=1 \
ghcr.io/oscarvalenzuelab/crane:latest serve --host 0.0.0.0 --port 8000 --worker
| Page | What it answers |
|---|---|
/ |
Every obligation side by side. The only consolidated view |
/attention |
What to do next, ranked. The page to open first on a working day |
/products, /components |
The inventory, filterable, with a CSV at /products.csv |
/graph |
How they interact: repositories feed components, components compose products. Anything nothing relates to is drawn as an island |
/scope |
Scope decisions per lens, and what is undecided |
/findings, /exceptions |
What failed, and what has been formally excused |
/sbom |
Evidence, its content hashes, and its validation state |
/jobs |
Queued and finished work, with stdout and stderr per run |
/health |
Whether the build itself is coherent |
/reports |
The three reports, rendered |
/api/docs |
The HTTP API, browsable and complete |
Every page except / works one obligation at a time, chosen in the header and carried in
the URL as ?o=, so a filtered view is a link you can send someone.
--worker runs queued tools in the same process. Without it the interface will happily
queue validations that nothing ever executes.
Starting over
The database is one file. Deleting it is the reset:
rm -rf data/crane.db data/demo-sboms
docker run --rm -v "$PWD/data:/data" --user "$(id -u):$(id -g)" \
ghcr.io/oscarvalenzuelab/crane:latest init
Now go to Populate and put your own portfolio in.