Image signing, SBOM and provenance
When an image passes the Build Security gate, Loha signs it with your workspace's own key and attaches two attestations next to it in your registry:
- SLSA provenance (v1) — which repository and commit it was built from, by which Loha builder, under which security gate decision. Loha targets SLSA Build Level 2: the build runs on a hosted platform and the platform signs the provenance.
- SBOM (CycloneDX) — the packages in the image, taken from the same managed scan that checked it for vulnerabilities.
Signatures and attestations are stored as standard cosign .sig / .att
artifacts in your registry, so you can verify them without Loha.
What gets signed
Loha signs an image only when all of these are true:
-
The build succeeded and pushed the image to a registry.
-
The workspace's security policy is in Enforce or Observe mode, and the image passed the policy:
- Enforce: the final gate's outcome for the build was passed.
- Observe: the gate does not wait for the scans, so the image first shows as Waiting for scans. When the scans finish, Loha checks the policy that was active for the build against the completed results, using the same evidence rules as Enforce (only Loha's own scans count). If it would pass, the image is signed and shows Signed under Observe.
An image the policy blocks is never signed, in either mode.
-
The image's registry is one Loha supports for managed scans, and the workspace has a registry credential for it (Settings → Registries). Loha signs with that credential's push rights.
A signature made under Observe says the same thing as one made under Enforce:
the image passed the policy's thresholds on scans Loha ran itself. The
difference is timing. Under Observe the build was not held while the scans ran,
so a target that pulls the image by tag can run it before it is signed. The
provenance records the mode (internalParameters.securityGate.policyMode).
The build's Security tab shows each image as Signed, Waiting to sign, Waiting for scans (Observe), Signing failed or Not signed, with the reason.
Signing happens one to two minutes after the build finishes. It never fails or delays the build.
Under Enforce, the tag you push moves to a signed image only after the gate passes; see Image promotion.
Verify an image
Download the workspace public key from Settings → Code security → Signing keys (or from the image's row on the build's Security tab), then:
cosign verify --key loha.pub --insecure-ignore-tlog=true registry.example.com/team/app@sha256:<digest>
cosign verify-attestation --key loha.pub --insecure-ignore-tlog=true --type slsaprovenance1 registry.example.com/team/app@sha256:<digest>
cosign verify-attestation --key loha.pub --insecure-ignore-tlog=true --type cyclonedx registry.example.com/team/app@sha256:<digest>
Use cosign v2. --insecure-ignore-tlog=true is required because Loha does
not publish your image digests to a public transparency log; the signature is
still checked against your workspace key.
With the Loha CLI, loha images verify registry.example.com/team/app@sha256:<digest>
fetches the key and runs the first two commands for you: it checks the
signature and the SLSA provenance. The CLI does not check the SBOM
attestation; run the third command yourself for that. If the image was signed
with a key that has since been revoked, the command fails; pass
--allow-revoked to accept it anyway (for example to inspect an old image).
Signing keys
Each workspace has one active ECDSA P-256 key. Loha stores the private key encrypted and uses it only inside the signing job; it is never shown or downloadable. Admins can rotate the key (with step-up verification): new images use the new key, and images signed with the old key stay verifiable with the old public key, which stays listed.
When admission policies are on for the workspace, a rotation is staged. The new key joins your admission policy at once and starts signing after 7 days, or when an admin activates it. See Key rotation.
If you believe a key is compromised, rotate and choose revoke. Images signed with a revoked key are marked on the Security tab. Loha keeps one signature per image digest, so an image that was signed with a revoked key stays signed with that key: to get a signature with the new key you need a new build that produces a new image digest. Loha does not re-sign an existing digest.
Requiring a signature before deployment
Set Require a Loha signature before Delivery in the security policy
(imageAttestations.required). The setting is refused in an Off policy. Under
Observe, a dispatch that reaches an image still Waiting for scans waits
until the image is signed, or until Loha decides not to sign it (the policy
blocks it, or the scans could not be relied on). Delivery then refuses to dispatch
a release whose image is not signed, whose signing failed or was skipped, or
that was signed with a revoked key. This applies to every dispatch, including
a rollback to an earlier release: a rollback to a release whose image is
unsigned is refused too.
You can prepare and approve a plan while the image is still being signed. If the rollout reaches dispatch before signing has finished, the dispatch waits: the rollout is deferred and retried until the image is signed (or signing fails). The wait ends when the plan expires (or after about two hours for a plan without an expiry), after which the rollout fails and you prepare a new plan. A rollout that is already in progress is not affected. Turning this off later is a relaxation and needs a second admin, like other policy relaxations.
What this does not do
- Loha does not stop a cluster from running an unsigned image by itself. With the admission policy Loha generates, your admission controller refuses images from your Loha-built repositories that Loha did not sign.
- Images your pipeline pushes to registries Loha does not manage scans for, or without a registry credential in Loha, are not signed.
- SBOMs larger than 20 MiB are not stored or attached; the image is still signed with provenance.
- SBOM downloads follow the security report retention (30 days). The attestation in your registry does not expire.