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:
| Field | Type | Meaning |
|---|---|---|
_type | string | Always https://in-toto.io/Statement/v1. |
subject | array | 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. |
predicateType | string | Always https://attestkeep.com/attestation/compliance-evidence/v1 —
the address of this page. |
predicate | object | 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.
| Field | Type | Meaning |
|---|---|---|
report_id | string | 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. |
framework | string | 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. |
period | object | The audit window the report covers. |
period.start, period.end | string | RFC 3339 timestamps, normalised to UTC. Strings on the wire, because the exact bytes are what gets signed. |
installation | object | Identifies the signer without describing the cluster. |
installation.fingerprint | string | 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_id | string, 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_id | string, optional | The licence binding this installation activated under; serves the same confirmation. |
generated_at | string | 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_by | string, optional | The operator who asked for the report, as the report itself records them. |
generator | object | The software that produced the report. |
generator.name, generator.version | string | 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>" }
]
}
payloadis 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.payloadTypeis 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.sigis an Ed25519 signature (RFC 8032) overPAE(payloadType, payload)— over the pre-authentication encoding, not over the base64 string.keyidis 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):
| Field | Type | Meaning |
|---|---|---|
sub | string | The licence id the certificate was issued to. |
fpr | string | The cluster fingerprint the licence is bound to — the value the predicate's
installation.fingerprint must equal. |
plan | string | The plan code, informational for a chain verifier. |
iat, exp | number | Issued-at and expiry, unix seconds. exp of 0 means the certificate
does not expire. |
jti | string | The certificate's serial. |
cnf.jwk | string | 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:
- Split the certificate on
.into exactly three parts; decode the header and refuse it unlessalgisEdDSA. - Verify the Ed25519 signature over the ASCII bytes of
header_b64 "." payload_b64with the root key above. - Decode the payload and read
cnf.jwk— the installation's evidence key. - Verify the DSSE envelope with that key, exactly as the sections above describe.
- Cross-check: the statement's
predicate.installation.fingerprintmust equal the certificate'sfpr. Without this step a genuine certificate could be presented next to a genuine envelope from a different installation. Ifinstallation.license_idis present and differs fromsub, 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