Attestree detonation attestation, v1
The predicate of the signed in-toto statement Attestree writes for each installer a real-sandbox detonation verifies: what the run observed about that installer, and what the platform concluded when it signed. A version can have several.
- TYPE URI
- https://attestree.com/attestation/detonation/v1
- KIND
- in-toto predicate type
- USED AS
- predicateType of an in-toto Statement v1, in a DSSE envelope (<packageId>_<version>.intoto.jsonl)
- SIGNED BY
- The deployment’s catalog-attest key: ECDSA P-256 with SHA-256
- EMITTED SINCE
- Platform 0.240.0
- REPLACES
- None. It is new. The legacy https://wep.dev/detonation/v1 was the catalog evidence bundle’s predicate; see evidence-bundle/v1.
- PAGE UPDATED
- 2026-10-14
The envelope
One DSSE envelope with payloadType application/vnd.in-toto+json and a
single signature. The payload is an in-toto Statement v1. The signature is ECDSA P-256 over
SHA-256 of the DSSE pre-authentication encoding (PAE:
DSSEv1 <len(type)> <type> <len(payload)> <payload>), encoded
as Base64 of the DER signature. keyid is the lowercase hex SHA-256 of the signing
key’s SubjectPublicKeyInfo.
The platform stores the envelope byte for byte and serves exactly those bytes. A re-sign under a new key keeps the payload unchanged, so the statement digest stays the same.
The subject
Exactly one. Its name is <manager>:<PackageId>@<Version>;
its digest.sha256 is the SHA-256 of the installer bytes the run executed and the feed’s
manifest pins. For a derived MSI, that is the derived artifact, and installer.parentSha256
names the bundle it was extracted from. You can check the subject against the winget manifest’s
InstallerSha256 and against an installer you downloaded yourself.
The predicate
Every field is always present. A value the run does not know is written as null,
never as an empty string and never left out, so a reader can tell “unknown” from “not in this
version of the shape”. The shape, with placeholder values:
{
"_type": "https://in-toto.io/Statement/v1",
"subject": [
{ "name": "winget:[email protected]", "digest": { "sha256": "<installer SHA-256>" } }
],
"predicateType": "https://attestree.com/attestation/detonation/v1",
"predicate": {
"package": { "manager": "winget", "id": "Contoso.App", "version": "1.0" },
"installer": {
"sha256": "…", "manifestSha256": "…",
"parentSha256": null, "derivationStage": null
},
"run": {
"resultId": "…", "origin": "real-sandbox", "engineVersion": "…",
"goldenImageDigest": null, "isolationStrength": "…",
"networkPosture": "offline", "mitmCaSki": null, "detonatedAt": "…"
},
"evidence": {
"fingerprintDigest": "…", "probeDigest": "…", "sbomProjectionDigest": "…"
},
"sbom": {
"mediaType": "application/vnd.cyclonedx+json", "specVersion": "…",
"digest": { "sha256": "…" }, "componentCount": 0,
"summary": { "digest": { "sha256": "…" }, "viewVersion": 1 },
"absent": null
},
"verdict": { "attestationTier": "…" },
"signing": { "mode": "at-ingest" },
"tenant": "…",
"attestedAt": "…"
}
}
| Field | Meaning |
|---|---|
package | The catalog row’s manager (for example winget), the package id and the version. |
installer.sha256 | The subject digest: the bytes the run executed and the feed’s manifest pins. |
installer.manifestSha256 | The manifest’s InstallerSha256: the bundle for a derived MSI, otherwise the same as sha256. |
installer.parentSha256 | For a derived stage, the bundle it was extracted from; otherwise null. |
installer.derivationStage | derived-msi, or null for a vendor installer. |
run.resultId | The detonation result the statement covers. |
run.origin | Always real-sandbox: only detonation-backed attestations are signed. |
run.engineVersion | The detonation engine version, when the run recorded one. |
run.goldenImageDigest | The sandbox image digest. No run records one yet, so it is null today. |
run.isolationStrength | For example standard-user-interactive; null on the headless path. |
run.networkPosture | offline, mitm or unknown. |
run.mitmCaSki | The subject key identifier of the CA that captured a MITM pass, or null. |
run.detonatedAt | When the run’s result was recorded. |
evidence | The run’s digests as its result records them: the install footprint, the probe set, and the name-and-version projection of the SBOM. |
sbom | The run’s stored CycloneDX document: media type, specVersion, the SHA-256 of the document byte for byte, its component count, and the digest and rule version of its component summary. |
sbom.absent | null when a document is stored; otherwise why not: no-install-location, no-footprint, unparseable, too-large (its digest is still kept), not-retained or not-produced. |
verdict.attestationTier | The composed attestation tier at signing. |
signing.mode | Always at-ingest: the statement was signed when the version was attested. No statement is backfilled. |
tenant | The tenant the version belongs to. |
attestedAt | When the platform signed the attestation. |
What the signature means
The deployment that holds this key ran this installer in a real sandbox, recorded these evidence
digests, and concluded this tier, at attestedAt. That is all it says.
- It is not SLSA provenance. Attestree does not build a vendor’s installer, so it makes no claim about how the installer was built.
- It does not say the installer is safe. The tier is the platform’s conclusion at signing, and a later re-detonation can change the live tier without changing this statement.
- It does not name the installer’s architecture or install scope, or the architecture of the sandbox that ran it. The console shows those; the statement does not prove them.
- The SBOM it names is a third-party SBOM: Syft’s scan of what the installer put on disk, not the vendor’s own. The component summary is not a top-level-dependency view; a scan of installed files cannot tell top-level from transitive.
Which installers have one
Each installer a real-sandbox detonation verifies from platform 0.240.0 on, so a version can have several, one per installer. An installer verified before then has none until its version is re-tested. A version attested through a waiver has none. An installer vouched for by hand is not attested: on Community Edition it is an unverified-installer acceptance, with no statement, and a dedicated instance has no hand vouch. The version’s page says which of these applies.
Where to get it
-
The version’s page in the console, under Signed statement: the statement, the
third-party SBOM, the component summary and
keys.json, with the verify line filled in. -
GET /api/v1/catalog/packages/{packageId}/{version}/attestation, which serves the stored envelope byte for byte. Add?subject=<installer SHA-256>for one installer’s statement when a version has several; without it, the route serves the statement the version’s page shows. -
Inside a catalog evidence bundle, at
evidence/<id>@<version>/statement.intoto.jsonl(see evidence-bundle/v1).
How to verify
With the verifier that ships with each platform release:
wep-bundle-verify attestation <packageId>_<version>.intoto.jsonl \
--key keys.json --expected-fingerprint <sha256> \
[--artifact <installer>] [--sbom <packageId>_<version>.cdx.json]
--key is required: a certificate inside an envelope never vouches for itself. Only
catalog-attest keys in the key file count. Take the fingerprint you pin from a
channel you trust, such as the version’s page in the operator’s own console, not from the files
you are checking:
keys.json is a convenience copy, never a trust anchor. The output names the
predicate type, the subject, the key with its status, the signing mode and the statement digest,
and prints one Not checked: line for each check you did not ask for. A retired key
passes with a warning. Non-zero exits include 4 (bad signature), 7 (malformed input or an unknown
predicate type), 10 (pin mismatch), 11 (key not in the set), 12 (revoked key), 13 (subject
mismatch) and 14 (SBOM mismatch).
Or with the 35-line Python sample, whose one dependency is cryptography:
python verify_attestation.py <packageId>_<version>.intoto.jsonl keys.json [installer]
It prints the SPKI SHA-256 of the key that actually verified, and refuses a statement whose
keyid is not that key, or a key the key file marks revoked.
By hand: decode the payload, rebuild the PAE, verify the signature with a P-256 public key you
trust from outside the envelope, check that keyid is that key’s SPKI SHA-256, check
_type, predicateType and the single subject, then compare the subject
digest with the installer.
Transparency log
An operator can switch on a Sigstore Rekor entry per attestation; the entry hashes the PAE bytes. It is off by default and has not yet been proven against the public log, so treat the statement and your pinned key as the evidence, not a log entry.