VEX
The English user guide is currently in beta preview. Most of the documents have been automatically translated from the Japanese version. Should you find any inaccuracies, please reach out to Flatt Security.
Takumi image vulnerability scans keep suspicious findings so that detection remains sensitive.
As a result, reports can include findings that are not actually applicable, findings that are already fixed, or findings whose vulnerability status is disputed.
VEX (Vulnerability Exploitability eXchange) publishes the decision and rationale for each finding in a machine-readable format.
Takumi Images publishes decisions in OpenVEX format with not_affected, fixed, and affected statuses.
The published VEX data is not just a suppression list.
It also includes affected statements so users can confirm which finding was reviewed, why it was classified that way, and which image the decision applies to.
Takumi Images is currently in beta. Specifications and behavior may change without prior notice.
Distribution paths
Takumi Images publishes the same VEX decisions through two paths.
Signed attestation
This is an OpenVEX attestation linked to the image digest. It can be verified with the same keyless signing identity as the SBOM and SLSA Provenance, and serves as evidence for the artifact you pulled. VEX is generally attached to the multi-arch index, not to architecture-specific child manifests.
Public VEX repository
The VEX repository is published at https://vex.images.flatt.tech.
It follows the VEX Repository Specification, so compatible scanners and audit tooling can consume it.
Use this path when you want to fetch VEX before pulling an image, or when you want to pass a combined OpenVEX document to grype.
Both paths are generated from vex/takumi.openvex.json in Takumi Images.
The source document uses Nix component-level pkg:nix/... products, and publication converts them into image-level pkg:oci/... products.
Public VEX repository structure
The production discovery manifest is published at:
https://vex.images.flatt.tech/.well-known/vex-repository.json
The manifest points to v1/vex-data.tar.gz as the repository data.
The repository client fetches and extracts that archive; it does not walk directories over HTTP.
The archive root contains index.json and pkg/... entries.
https://vex.images.flatt.tech/.well-known/vex-repository.json
https://vex.images.flatt.tech/v1/vex-data.tar.gz
https://vex.images.flatt.tech/v1/index.json
https://vex.images.flatt.tech/v1/pkg/oci/images.flatt.tech/takumi/<image>/vex.json
https://vex.images.flatt.tech/v1/all.openvex.json
The files have the following roles.
.well-known/vex-repository.json
The VEX repository discovery manifest.
It advertises spec_version: "0.1", the data archive URL, and update_interval: "24h".
v1/vex-data.tar.gz
The distribution archive for scanners.
It contains index.json and pkg/oci/.../vex.json.
v1/index.json
A mapping from PURLs to VEX document locations.
Because PURL strings are used as keys, / in repository_url is percent-encoded as %2F.
v1/pkg/oci/images.flatt.tech/takumi/<image>/vex.json
The per-image OpenVEX document. For example, the busybox VEX is available at:
https://vex.images.flatt.tech/v1/pkg/oci/images.flatt.tech/takumi/busybox/vex.json
v1/all.openvex.json
An aggregate OpenVEX document for all images. Use it when passing a local VEX file to grype or when auditing the whole catalog.
The data is served directly from a Cloudflare R2 public custom domain.
Daily publishing updates R2 objects; no worker deployment is required.
Objects are written in the order of per-image leaf data, aggregate, archive, index, and finally the .well-known manifest.
Updating the pointer last avoids exposing a state where the index exists but the referenced data is not yet present.
Statement contents
An OpenVEX statement records the vulnerability ID, target product, status, and rationale.
The vulnerability ID is not limited to CVE.
GHSA, OSV IDs, vendor advisory IDs, and other identifier formats are also represented in the same vulnerability.name field.
justification is the machine-readable category defined by OpenVEX, while impact_statement and action_statement explain the decision for readers.
The following is a shortened example from the published busybox VEX.
{
"vulnerability": {
"name": "CVE-2019-1010022",
"description": "glibc stack guard protection bypass -- DISPUTED by upstream."
},
"products": [
{
"@id": "pkg:oci/busybox@sha256:<index-digest>?repository_url=images.flatt.tech/takumi&tag=latest"
},
{
"@id": "pkg:oci/busybox?repository_url=images.flatt.tech/takumi"
}
],
"status": "not_affected",
"justification": "vulnerable_code_cannot_be_controlled_by_adversary",
"impact_statement": "Disputed by the glibc maintainers ..."
}
The example includes two product IDs in one statement for compatibility.
First: digest-pinned canonical ID
pkg:oci/<image>@sha256:<index-digest>?repository_url=images.flatt.tech/takumi&tag=latest
This is the audit-oriented canonical ID, aligned with the signed attestation.
It fixes the target image index by digest.
Because it does not rely only on the tag, the decision continues to apply to the same object even if latest later points to another digest.
Second: ID for grype matching
pkg:oci/<image>?repository_url=images.flatt.tech/takumi
grype generates OCI PURLs with the image name as the PURL name and the registry plus namespace in repository_url.
It also treats the per-architecture manifest digest as the version, so this ID omits the version.
tag=latest remains only on the canonical ID.
grype-generated PURLs do not include a tag qualifier, so matching with grype uses the second versionless ID.
Verify as an attestation
To verify the VEX linked to an image you pulled, retrieve the openvex attestation.
cosign verify-attestation \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
--certificate-identity-regexp 'https://github.com/flatt-security/takumi-images/.github/workflows/release.yml@.*' \
--type openvex \
images.flatt.tech/takumi/busybox:latest \
| jq -r '.payload' | base64 -d | jq '.predicate'
This verifies that the OpenVEX document was signed for the target digest and has not been modified since signing. See Attestation for the signing identity and general attestation verification flow.
Use with grype
With grype, fetch the aggregate document and pass it with --vex.
grype does not ignore findings just because it reads a VEX document, so configure ignore rules for not_affected and fixed.
Do not suppress affected.
# grype-vex.yaml
ignore:
- vex-status: not_affected
- vex-status: fixed
curl -fsSLo takumi.openvex.json \
https://vex.images.flatt.tech/v1/all.openvex.json
grype registry:images.flatt.tech/takumi/busybox:latest-amd64 \
--vex takumi.openvex.json \
--config grype-vex.yaml \
--show-suppressed
With this configuration, findings classified as not_affected or fixed by VEX are shown as suppressed.
Findings published as affected remain in the report and should stay in your response workflow.
Relationship with triage
VEX is not a post-processing step for making scan results look smaller. It records, in a verifiable form, the distinction between following upstream releases, applying package-level patches, and deciding that a finding does not actually apply. See Known Vulnerability Response for the triage model.