Attach a scan result
houba does not run scanners. A scan is produced upstream (your CI, a registry-native scanner, or a scan service); houba ingests its report and stamps it as a portable OCI referrer on the image's digest. This makes "which images carry a critical CVE?" one referrers query at incident time.
Don't confuse this with the SBOM houba generates on every image it places — copy and rebuild alike: the SBOM is the package inventory (what's inside), produced by houba after placement; a scan is a vulnerability assessment (what's wrong with those packages), produced by a scanner houba does not run and merely ingests here. Building an inventory is not scanning.
1. Produce a report upstream (example: Trivy emitting SARIF)
trivy image --format sarif --output scan.sarif.json harbor.corp/lib/redis:7.2.0
2. Attach it
houba attach harbor.corp/lib/redis:7.2.0 --report scan.sarif.json
# attached sarif scan (trivy 0.50.1) → harbor.corp/lib/redis@sha256:ref…
# subject=sha256:abc… vuln.critical=1 vuln.high=0 vuln.medium=1 vuln.low=0 vuln.unknown=0
The referrer manifest carries the summary annotations (io.houba.scan.*) and the raw
SARIF as its blob. Re-running attach after a fresh scan adds a new referrer (history).
sample.sarif.json is a runnable example report (1 critical, 1 medium).
3. Sign it (verifiable scan provenance)
With a signer configured, houba attach also emits a signed in-toto attestation
(https://houba.dev/predicate/scan/v1) over the image digest — additive to the raw report
referrer above. This is what lets an admission controller require a signed scan.
export HOUBA_ATTEST_SIGNER=keyless # or kms | key
export HOUBA_ATTEST_BUILDER_ID=houba://ci # identifies this houba attester
houba attach harbor.corp/lib/redis:7.2.0 --report scan.sarif.json
# attached sarif scan (trivy 0.50.1) → harbor.corp/lib/redis@sha256:ref…
# subject=sha256:abc… vuln.critical=1 …
# signed: https://houba.dev/predicate/scan/v1 → sha256:att…
Off by default: with no HOUBA_ATTEST_SIGNER, only the unsigned referrer is attached.
4. Gate CI on severity (--fail-on)
attach is observational by default (exits 0). Pass --fail-on <severity> to turn it into a
CI gate: if the ingested scan has any finding at or above the threshold, attach exits 1.
Severity order (highest to lowest): critical > high > medium > low > unknown.
# Fail CI when any high or critical finding is present
houba attach registry.example.com/lib/redis:7.2.0 --report scan.sarif.json --fail-on high
# attached sarif scan (trivy 0.50.1) → registry.example.com/lib/redis@sha256:ref…
# subject=sha256:abc… vuln.critical=1 vuln.high=0 vuln.medium=1 vuln.low=0 vuln.unknown=0
# gating: scan has a finding at or above high (--fail-on)
# exit 1
The raw referrer is always attached first; the gate only controls the exit code. With no
findings at or above the threshold the command exits 0 (gate passes). Use --fail-on low or
--fail-on unknown to catch every finding including unknowns.
5. Roster-driven authentication
houba attach authenticates against the HOUBA_REGISTRIES roster — the same roster that
reconcile and audit use — so credentials are declared once and applied consistently.
5a. Host-match (default)
Set the roster and run attach as usual. houba parses the host from the image ref
(harbor.corp in the example below), finds the matching roster entry, and logs in before
touching the registry. No extra flag needed.
export HOUBA_REGISTRIES='{
"prod": {
"host": "harbor.corp",
"username": "robot$houba",
"password": "s3cr3t"
}
}'
houba attach harbor.corp/lib/redis:7.2.0 --report scan.sarif.json
# houba matches harbor.corp → roster entry "prod", logs in, then attaches.
5b. --registry override
Use --registry <name> to force a specific roster entry regardless of the ref's host — useful
when the image is behind a pull-through proxy or the ref's host differs from the roster name:
houba attach harbor.corp/lib/redis:7.2.0 --report scan.sarif.json --registry prod
--registry with a name that is not in the roster exits 3 (ConfigError), consistent with the
rest of the CLI.
5c. Silent fallback
When no --registry is given and the ref's host is not in any roster entry (for example a
public image or an empty roster), attach configures nothing and falls back to ambient regctl
config — exactly today's behaviour. No flag and no roster entry required for public registries.
Posture reports (governance verdicts, not vulnerabilities)
A SARIF report is not always a vulnerability scan. Policy / posture analyzers that emit SARIF
(license, EOL, best-practice, or compliance tools) report evaluation outcomes — each result
carries an explicit SARIF kind (pass / fail / …) marking a verdict rather than a finding.
houba attach keys on that standard SARIF signal: a result with an explicit kind is a governance
verdict and wins over any CVSS security-severity it carries, so it is summarized in the
policy.* space — never inflating the vuln.* counts:
uv run houba attach --format sarif posture.sarif.json harbor.corp/lib/redis:7.2.0
# attached sarif scan (posture-analyzer 1.x) → harbor.corp/lib/redis@sha256:ref…
The stamp then carries io.houba.scan.policy.<severity> (a failed verdict bucketed by its severity,
e.g. policy.critical) and io.houba.scan.policy.passed, alongside the io.houba.scan.vuln.*
buckets. houba keys on the SARIF kind, never on the tool name — so any analyzer emitting kind
gets governance bucketing for free. The --fail-on <severity> gate acts on vuln.* only — policy
verdicts are reported in the stamp, not gated.
End-to-end: any SARIF analyzer at the front door
The same path works for any analyzer that emits SARIF — a vulnerability scanner, or a policy-as-code engine that also covers licenses, EOL, and best-practices beyond CVEs. The analyzer reads the placed, hardened front-door image itself; houba does not invoke it. You run your analyzer, then publish its verdict as portable provenance:
# any SARIF-emitting analyzer: read the placed image, apply your policy
<analyzer> registry.example.com/lib/redis@sha256:abc… -o sarif > findings.sarif.json
# houba: bind the verdict to the digest as a signed referrer
houba attach registry.example.com/lib/redis@sha256:abc… --report findings.sarif.json
That verdict is now a signed referrer on the digest, read by digest by an admission controller (Kyverno), Dependency-Track, or an audit — the same join key as the stamp and SBOM. The division of labour: the analyzer decides, houba carries, the admission controller enforces (signed-scan + max-age, ADR 0032). Read at the front door: a registry that strips OCI referrers on replication (e.g. Harbor) won't carry them downstream — follow the digest back to where houba attached them.
houba's only requirement is SARIF: it stays analyzer-agnostic (the SarifMapper handles
CVSS-scored vulnerabilities and rule pass/fail alike), so swapping or adding analyzers never
touches houba.