SPECIFICATIONS IN-TOTO PREDICATE TYPE

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": "…"
  }
}
FieldMeaning
packageThe catalog row’s manager (for example winget), the package id and the version.
installer.sha256The subject digest: the bytes the run executed and the feed’s manifest pins.
installer.manifestSha256The manifest’s InstallerSha256: the bundle for a derived MSI, otherwise the same as sha256.
installer.parentSha256For a derived stage, the bundle it was extracted from; otherwise null.
installer.derivationStagederived-msi, or null for a vendor installer.
run.resultIdThe detonation result the statement covers.
run.originAlways real-sandbox: only detonation-backed attestations are signed.
run.engineVersionThe detonation engine version, when the run recorded one.
run.goldenImageDigestThe sandbox image digest. No run records one yet, so it is null today.
run.isolationStrengthFor example standard-user-interactive; null on the headless path.
run.networkPostureoffline, mitm or unknown.
run.mitmCaSkiThe subject key identifier of the CA that captured a MITM pass, or null.
run.detonatedAtWhen the run’s result was recorded.
evidenceThe run’s digests as its result records them: the install footprint, the probe set, and the name-and-version projection of the SBOM.
sbomThe 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.absentnull 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.attestationTierThe composed attestation tier at signing.
signing.modeAlways at-ingest: the statement was signed when the version was attested. No statement is backfilled.
tenantThe tenant the version belongs to.
attestedAtWhen 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.