Specification

Compliance evidence attestation, version 1.

https://attestkeep.com/attestation/compliance-evidence/v1 is the predicateType of every attestation Attestkeep signs over a compliance report. If a document brought you to this URL, this page is its definition: the claim, every field, the envelope, the encoding that gets signed, and a real example you can verify.

The claim being made

In one sentence: this compliance report was produced by this installation, and it has not been edited since. That is the whole claim, and everything below follows from it.

The claim travels in the format the supply-chain world already uses for such things — an in-toto statement inside a DSSE envelope, signed with Ed25519 — rather than in a signature scheme of our own design. That choice is deliberate: a format only we could verify would make us a bottleneck, and the value of the document is precisely that an auditor can check it without us in the room. The signing key is generated inside the installation and never leaves it; what the world is asked to trust is only the public half.

The statement and its subject

The envelope's payload is an in-toto statement, version 1. Its outer fields:

FieldTypeMeaning
_typestring Always https://in-toto.io/Statement/v1.
subjectarray Exactly one resource descriptor naming the report the claim is about: name is the report id, and digest.sha256 is the SHA-256 of the exact bytes that were generated, as 64 lowercase hex characters with no sha256: prefix.
predicateTypestring Always https://attestkeep.com/attestation/compliance-evidence/v1 — the address of this page.
predicateobject The claim itself; every field is defined in the next section.

The statement is about a document, so the document is the subject. The report itself is not carried inside the attestation, and that is deliberate: the report is large, and a reader may hold it as a PDF or a CSV rendering. What has to be provable is that the copy in their hands is the one that was signed, and a digest answers that for any copy of the bytes — an inline duplicate would only answer it for the duplicate.

The framework and the audit period sit inside the signature, next to the subject. The same period assessed against two frameworks yields two different documents, and a reader must not be able to relabel one as the other. Nothing in the statement — subject, digest, period, framework, timestamps — can be edited after signing without verification failing.

Predicate fields

Optional fields are omitted from the JSON when empty, never written as placeholders. A timestamp the installation does not have is written as an empty string, never as an invented date.

FieldTypeMeaning
report_idstring The document's identity, the same id printed on the report itself, so the attestation in your hands can be matched at a glance to the report in your hands. It repeats the subject's name.
frameworkstring The compliance mapping the report was assembled against, for example soc2. Inside the signature so one framework's report cannot be relabelled as another's.
periodobject The audit window the report covers.
period.start, period.endstring RFC 3339 timestamps, normalised to UTC. Strings on the wire, because the exact bytes are what gets signed.
installationobject Identifies the signer without describing the cluster.
installation.fingerprintstring The opaque cluster identifier the licence is bound to — a hash carrying no topology, no names, nothing a customer would object to putting in a document that leaves their building.
installation.license_idstring, optional Lets the issuer confirm the installation was licensed at the time — the link between "this file is untouched" and "it came from a real installation".
installation.binding_idstring, optional The licence binding this installation activated under; serves the same confirmation.
generated_atstring When the installation produced the report, as it understood the time — RFC 3339 UTC. An assertion by the signer, not a proof: nothing binds the document to a trusted clock, and a reader who needs that must get it elsewhere.
generated_bystring, optional The operator who asked for the report, as the report itself records them.
generatorobject The software that produced the report.
generator.name, generator.versionstring Which release's compliance mappings were in force. Mappings change as frameworks are read more carefully; evidence that does not say which reading it used cannot be re-examined later.

The DSSE envelope

The statement travels inside a DSSE envelope with this shape:

{
  "payload":     "<standard base64 of the statement JSON>",
  "payloadType": "application/vnd.in-toto+json",
  "signatures": [
    { "keyid": "ak-…", "sig": "<base64 of an Ed25519 signature>" }
  ]
}
  • payload is standard base64, with padding, of the exact marshalled statement bytes. The statement is serialised once: the bytes that are signed are the bytes that are transmitted.
  • payloadType is part of what gets signed (see the encoding below), so a signature over an in-toto statement can never be presented as a signature over the same bytes read as something else.
  • sig is an Ed25519 signature (RFC 8032) over PAE(payloadType, payload) — over the pre-authentication encoding, not over the base64 string.
  • keyid is derived from the public key: "ak-" followed by the first 16 hex characters of the SHA-256 of the 32-byte key. Derived, so it cannot disagree with the key it names; and a hint only — a verifier must check against a key it already trusts, never against a key the envelope told it about.
  • An envelope may carry several signatures. A verifier tries each entry against its trusted key and accepts the envelope if any entry verifies.

The PAE encoding

What is signed is the pre-authentication encoding of the payload together with its type:

PAE(type, body) = "DSSEv1" SP LEN(type) SP type SP LEN(body) SP body

where SP is a single space and LEN is the byte length in decimal ASCII. Lengths count bytes, never characters. For example, PAE("http://example.com/HelloWorld", "hello world") is the byte string DSSEv1 29 http://example.com/HelloWorld 11 hello world.

The whole of DSSE's security is in this one idea. Signing the payload alone would let a signature made over an in-toto statement be presented as a signature over something else that happens to have the same bytes under a different payloadType. PAE binds the two together, length-prefixed so neither can be made to look like part of the other.

The certificate chain

The envelope's signature proves the report is unchanged and names a key. It cannot, by itself, prove whose key that is: a verifier that takes the public key from whoever brought the document proves only self-consistency. The licence certificate closes that gap. It is a document Attestkeep signs for every licensed installation, and it carries the installation's evidence signing key inside the signature — so a reader who trusts one Attestkeep key can verify a report from any installation:

Attestkeep root key
  └─ signs → licence certificate (compact JWT)
                └─ cnf.jwk names → installation's evidence key
                                      └─ signs → DSSE envelope
                                                    └─ subject digest pins → report

The root key. This value, obtained from this page over TLS, is the chain's only trust anchor:

kid:     ak1
ed25519: e287aabe4712197cfa5b6fdf9ea8f2d7f0333ce16ca69aac7e4a3f1e84c04179

The certificate is a compact JWT (RFC 7515): base64url(header) "." base64url(payload) "." base64url(signature), with unpadded base64url (RFC 4648 section 5 — no =). The header is {"alg":"EdDSA","typ":"JWT","kid":"ak1"}; a verifier must refuse any alg other than EdDSA, which keeps the classic JWT downgrade attacks (alg=none, algorithm confusion) out by construction. The signature is Ed25519 over the ASCII bytes of header_b64 "." payload_b64.

The payload fields a chain verifier reads (unknown fields must be ignored — the issuer may add claims):

FieldTypeMeaning
substring The licence id the certificate was issued to.
fprstring The cluster fingerprint the licence is bound to — the value the predicate's installation.fingerprint must equal.
planstring The plan code, informational for a chain verifier.
iat, expnumber Issued-at and expiry, unix seconds. exp of 0 means the certificate does not expire.
jtistring The certificate's serial.
cnf.jwkstring RFC 7800's confirmation claim: the installation's Ed25519 evidence public key, as exactly 64 hex characters. Not a nested JWK object — the raw 32 bytes, hex encoded. A certificate without it vouches for no key and cannot anchor a chain.

To verify a report through the chain:

  1. Split the certificate on . into exactly three parts; decode the header and refuse it unless alg is EdDSA.
  2. Verify the Ed25519 signature over the ASCII bytes of header_b64 "." payload_b64 with the root key above.
  3. Decode the payload and read cnf.jwk — the installation's evidence key.
  4. Verify the DSSE envelope with that key, exactly as the sections above describe.
  5. Cross-check: the statement's predicate.installation.fingerprint must equal the certificate's fpr. Without this step a genuine certificate could be presented next to a genuine envelope from a different installation. If installation.license_id is present and differs from sub, warn the reader.

Expiry is information, not a rejection. Certificates rotate every thirty days, and a report from last quarter is vouched for by the certificate that was current then — refusing expired certificates would make every old report unverifiable. A verifier should report an expired certificate's date and leave the judgement to the reader.

A complete example

This envelope was produced by the reference signer. It is signed with a deliberately public test key — the Ed25519 seed is the bytes 0x01 through 0x20 — so it demonstrates the format, not the provenance of any real report. Real attestations are signed with a key generated inside each installation, which never leaves it.

{
  "payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoicnB0LTlmMmM3YTQxZDBiODNlNTYiLCJkaWdlc3QiOnsic2hhMjU2IjoiNzQ3YjgzZWNkMTY2OTEyNjk2NmVlNjAyZTM3MjI0NTE2Y2M5YmMyZGQ5NWFhZWJlNDIwZWQxY2IzNGVkZWMwYyJ9fV0sInByZWRpY2F0ZVR5cGUiOiJodHRwczovL2F0dGVzdGtlZXAuY29tL2F0dGVzdGF0aW9uL2NvbXBsaWFuY2UtZXZpZGVuY2UvdjEiLCJwcmVkaWNhdGUiOnsicmVwb3J0X2lkIjoicnB0LTlmMmM3YTQxZDBiODNlNTYiLCJmcmFtZXdvcmsiOiJzb2MyIiwicGVyaW9kIjp7InN0YXJ0IjoiMjAyNi0wNy0wMVQwMDowMDowMFoiLCJlbmQiOiIyMDI2LTA3LTMxVDIzOjU5OjU5WiJ9LCJpbnN0YWxsYXRpb24iOnsiZmluZ2VycHJpbnQiOiIzZjdiMWE5ZTVjMmQ4ZjA0YTZiM2UxYzdkOTA1MmU4YTFiNGY2YzNkN2U5YTBiMmM0ZDZlOGYwYTFiM2M1ZDdlIiwibGljZW5zZV9pZCI6ImxpYy03YzMxZjBkMiIsImJpbmRpbmdfaWQiOiJiaW5kLWE5NGUyYzE3In0sImdlbmVyYXRlZF9hdCI6IjIwMjYtMDgtMDFUMDY6MDA6MDBaIiwiZ2VuZXJhdGVkX2J5IjoiYXVkaXRvckBleGFtcGxlLmNvbSIsImdlbmVyYXRvciI6eyJuYW1lIjoiYXR0ZXN0a2VlcCIsInZlcnNpb24iOiIwLjEuMCJ9fX0=",
  "payloadType": "application/vnd.in-toto+json",
  "signatures": [
    {
      "keyid": "ak-65b60673d6ed884b",
      "sig": "68F6kgLmLYYMYa2wHzdOZMZFQN7NJ5prDRfR7pcpwI+kqYl9ARr+VpfOoyiJmnh01N8HG6ze/1bExrUgTugTBg=="
    }
  ]
}

The payload decodes to this statement:

{
  "_type": "https://in-toto.io/Statement/v1",
  "subject": [
    {
      "name": "rpt-9f2c7a41d0b83e56",
      "digest": {
        "sha256": "747b83ecd1669126966ee602e37224516cc9bc2dd95aaebe420ed1cb34edec0c"
      }
    }
  ],
  "predicateType": "https://attestkeep.com/attestation/compliance-evidence/v1",
  "predicate": {
    "report_id": "rpt-9f2c7a41d0b83e56",
    "framework": "soc2",
    "period": {
      "start": "2026-07-01T00:00:00Z",
      "end": "2026-07-31T23:59:59Z"
    },
    "installation": {
      "fingerprint": "3f7b1a9e5c2d8f04a6b3e1c7d9052e8a1b4f6c3d7e9a0b2c4d6e8f0a1b3c5d7e",
      "license_id": "lic-7c31f0d2",
      "binding_id": "bind-a94e2c17"
    },
    "generated_at": "2026-08-01T06:00:00Z",
    "generated_by": "auditor@example.com",
    "generator": {
      "name": "attestkeep",
      "version": "0.1.0"
    }
  }
}

The public key that verifies it, as the verifier expects it:

79b5562e8fe654f94078b112e8a98ba7901f853ae695bed7e0e3910bad049664

Paste the envelope and the key into the browser verifier and it passes; change any single character of the payload and it fails.

Verifying an attestation

Three ways, in increasing order of independence:

  • the verification page on this site, which runs entirely in your browser and sends nothing anywhere;
  • any tool that speaks DSSE and in-toto — the format carries nothing proprietary;
  • your own dozen lines in any language: verify the licence certificate with the root key, take the installation's key from its cnf.jwk, base64-decode the payload, build the PAE, verify the Ed25519 signature with that key, and only then read the statement.

Whichever you choose, the trust anchor is the same: Attestkeep's root key, which vouches for the installation's key through the licence certificate as described above — never a key taken from the document itself, whose keyid is a label rather than a credential, and never a key taken on trust from whoever delivered the report. A hand-delivered key still verifies an envelope, but it proves only self-consistency, not origin. And the limits are the same too: a valid chain proves the document is unchanged and which installation signed it. It is not a legal electronic signature, and it does not prove when the signing happened — generated_at and the period are the installation's own declaration. Both limits are spelled out on the verification page.

Last updated: 29 August 2026