---
title: "Signed x402 evidence: ed25519 report attestations | Settlens"
description: "Sign Settlens reports with ed25519 so customers and auditors can verify offline that a report is intact, who signed it and which digest was attested."
url: https://settlens.com/product/evidence
last_updated: 2026-10-13
---

# Evidence anyone can check offline

> **In short:** Every Settlens JSON report carries a SHA-256 digest of its canonical content. settlens evidence attest signs that digest, the report id and a timestamp with your ed25519 key, so a customer or auditor can verify offline that the report is intact and which key attested it.

Every Settlens JSON report carries a SHA-256 digest of its own content. Sign that digest with an ed25519 key and a customer, partner or auditor can confirm, with no network access, that the report is intact and was attested by the key you published.

[Request a pilot](https://settlens.com/contact) [Evidence docs](https://settlens.com/docs/evidence)

Real output from an attested scan of the demo target.

**settlens evidence verify**

```text
$ settlens evidence verify attestation.json \
    --report reports/settlens-cb29c023.json
VALID evidence attestation
Signature valid: true
Report matches:  true
Public key:      0x07275b21a0307d196228262603b7b16844487c1db1ce1dd0244c5ab9c9612fed
```

## Two layers of proof

The integrity digest comes with every JSON report. The attestation is optional: it adds a signer and, because the digest is signed, makes deliberate edits detectable.

_Integrity digest and signed attestation_

| Property | Integrity digest | Signed attestation |
| --- | --- | --- |
| Detects accidental changes to the JSON report | Yes | Yes |
| Detects a deliberate edit | No: whoever edits the report can recompute it | Yes, with --report: the signed digest no longer matches |
| Says who vouches for the report | No | The holder of the signing key |
| Carries a time | generatedAt, inside the report | attestedAt, signed |
| Needs a key to create | No | An ed25519 private key |
| Verifiable offline | Yes | Yes |
| Command | verify-report | evidence verify |

## How it works

_Diagram: Producing evidence: the JSON report is canonicalized and hashed with SHA-256 into its integrity block; the attestation carries that digest, the report id, a timestamp and the public key, and is signed with ed25519. Verifying offline: recompute the report digest and compare it with the integrity block, compare it with the attestation's reportDigest, then check the ed25519 signature and compare the public key with the one you expect._

_The signer keeps the private key. The verifier needs only the attestation, the report and the expected public key._

1. **Scan**

   Any scan or `check` writes a JSON report. Settlens serializes it with sorted keys (`settlens-stable-json-v2`), hashes it with SHA-256 and stores the result in an `integrity` block.

2. **Generate a key once**

   `evidence keygen` creates an ed25519 key pair with Node's built-in crypto. With `--out`, it writes the private key to a new file readable only by its owner, and refuses to overwrite a file that already exists. Publish the public key where your verifiers can find it.

3. **Attest**

   `evidence attest` works offline: it signs the report's digest, id, product and protocol versions, subject URLs, summary, a timestamp and your public key. It signs the digest recorded in the report without recomputing it, so run `verify-report` first.

4. **Share and verify**

   Send the report and `attestation.json`. `evidence verify` checks the signature, with `--report` that the report is intact and is the one attested, and with `--expect-key` that your key signed it.

**Producing evidence**

```text
$ settlens verify-report reports/settlens-cb29c023.json
VALID Report content matches its SHA-256 evidence digest.
Expected: 3c101d12204a09a63dcbb2605ee160b40f09e6444e49a7ecb7fee2738bcde126
Actual:   3c101d12204a09a63dcbb2605ee160b40f09e6444e49a7ecb7fee2738bcde126
$ settlens evidence keygen --out settlens-evidence.key
Private key written to settlens-evidence.key (keep it secret).
Public key: 0x07275b21a0307d196228262603b7b16844487c1db1ce1dd0244c5ab9c9612fed
$ settlens evidence attest reports/settlens-cb29c023.json \
    --key settlens-evidence.key --out attestation.json
Attestation written to attestation.json
```

## What an attestation contains

**attestation.json**

```json
{
  "kind": "settlens-evidence-attestation",
  "version": 1,
  "canonicalization": "settlens-stable-json-v2",
  "reportId": "cb29c023",
  "productVersion": "0.5.0",
  "protocolVersion": 2,
  "subjects": [
    "http://127.0.0.1:4402/secure"
  ],
  "summary": {
    "pass": 23,
    "fail": 0,
    "warn": 0,
    "skip": 0
  },
  "reportDigest": "3c101d12204a09a63dcbb2605ee160b40f09e6444e49a7ecb7fee2738bcde126",
  "attestedAt": "2026-10-13T11:12:36.225Z",
  "publicKey": "0x07275b21a0307d196228262603b7b16844487c1db1ce1dd0244c5ab9c9612fed",
  "signature": "0xda54e746e5adabc6a4726ca3c794b488823fa4bcece60df585a17e8791c51bbc668ba1dba5a5aa5a7b009067b74d7bfc87a982f47cc7d95602557df88402f905"
}
```

_Attestation fields_

| Field | Meaning |
| --- | --- |
| `reportDigest` | The report's SHA-256 integrity digest: the link to the exact report. |
| `canonicalization` | The canonical JSON form that was signed, settlens-stable-json-v2. |
| `reportId` | The report's id, also part of its file name. |
| `subjects` | Target URLs from the report, as sanitized in it. |
| `summary` | Pass, fail, warn and skip counts. |
| `productVersion`, `protocolVersion` | Settlens version and x402 version of the report. |
| `attestedAt` | When the signer attested, from the signer's clock. |
| `publicKey` | The raw 32-byte ed25519 public key, hex. |
| `signature` | ed25519 signature over every other field, in sorted-key JSON. |

## What an auditor can check offline

_Questions an auditor can answer with the files alone_

| Question | How | Command |
| --- | --- | --- |
| Is the report internally consistent? | Recompute the canonical SHA-256 digest and compare it with the integrity block. This catches corruption and careless edits, not someone who recomputes the digest. | `settlens verify-report report.json` |
| Is this the report that was attested, unchanged? | The report's digest must equal the attestation's reportDigest, which the signature protects. | `settlens evidence verify attestation.json --report report.json` |
| Was the attestation changed? | The ed25519 signature must verify over every attested field. | `settlens evidence verify attestation.json` |
| Who attested it? | Pass the key the signer published, obtained through a channel you trust. An attestation signed by any other key is INVALID. | `settlens evidence verify attestation.json --expect-key signer.pub` |
| Did the assessment move funds or use keys? | The report scope declares `settlementCalls: 0` and `privateKeysUsed: 0`. These are fixed by design, since Settlens has no code path that settles or signs a payment, not measured during the run. | Read the report |
| Was an active probe authorized? | Active targets record `mode: active-authorized` and the approval reference. | Read the report |

## What tampering looks like

Each layer catches a different edit. All three transcripts are real.

**Editing the report**

```text
# one check message edited in the report
$ settlens verify-report tampered.json
INVALID Report content was modified after the evidence digest was created.
Expected: 3c101d12204a09a63dcbb2605ee160b40f09e6444e49a7ecb7fee2738bcde126
Actual:   59171055343d2f48b64a85efe2fd4b490da979add667ef5762880572c7192014
$ settlens evidence verify attestation.json --report tampered.json
INVALID evidence attestation
Signature valid: true
Report matches:  false
Public key:      0x07275b21a0307d196228262603b7b16844487c1db1ce1dd0244c5ab9c9612fed
Reasons:
  - The provided report failed its own integrity check.
```

**Editing the attestation**

```text
# the subject URL edited in the attestation
$ settlens evidence verify attestation-edited.json --report reports/settlens-cb29c023.json
INVALID evidence attestation
Signature valid: false
Report matches:  true
Public key:      0x07275b21a0307d196228262603b7b16844487c1db1ce1dd0244c5ab9c9612fed
Reasons:
  - The attestation signature is invalid.
```

**A valid signature from someone else**

```text
# the same report, attested with a different key
$ settlens evidence verify attestation-other.json --report reports/settlens-cb29c023.json
VALID evidence attestation
Signature valid: true
Report matches:  true
Public key:      0x272957df36966f378990ec667b94757c5a08939c28cc63c2336437159d05ee1f
# the same check, pinned to the key you published
$ settlens evidence verify attestation-other.json --report reports/settlens-cb29c023.json \
    --expect-key ours.pub
INVALID evidence attestation
Signature valid: true
Report matches:  true
Expected signer: false
Public key:      0x272957df36966f378990ec667b94757c5a08939c28cc63c2336437159d05ee1f
Reasons:
  - The attestation is signed by 0x272957df36966f378990ec667b94757c5a08939c28cc63c2336437159d05ee1f, not by the expected key 0x07275b21a0307d196228262603b7b16844487c1db1ce1dd0244c5ab9c9612fed.
```

> **Always check the public key**
>
> The public key travels inside the attestation, so anyone can attest any report with their own key and the signature will verify. What makes an attestation yours is that its `publicKey` matches the key you published. Verifiers should pass that key with `--expect-key`, as the last command shows, or compare it by hand.

## What the report records, and what it leaves out

The digest covers the whole JSON report: scope, summary, every target and every check with its message and evidence. Reports are designed to be shared:

- In target URLs, credentials are removed and common secret query parameters (tokens, keys, signatures) are redacted.
- Request headers are recorded by name only, never by value.
- The paid resource's response body is never stored: only its size and SHA-256 digest. Facilitator capabilities read from `/supported` are kept as evidence.
- The scope records how many targets were probed actively and declares zero settlement calls and zero private keys used.

Read more in the [report reference](https://settlens.com/docs/reports) and the [methodology](https://settlens.com/docs/methodology).

**settlens-cb29c023.json (top level, targets elided)**

```json
{
  "schemaVersion": 1,
  "reportId": "cb29c023",
  "product": "Settlens",
  "productVersion": "0.5.0",
  "protocol": "x402",
  "protocolVersion": 2,
  "integrations": { "officialX402Core": "2.28.0" },
  "generatedAt": "2026-10-13T11:12:36.003Z",
  "scope": {
    "targetCount": 1,
    "activeTargetCount": 0,
    "settlementCalls": 0,
    "privateKeysUsed": 0
  },
  "metadata": { "source": "cli" },
  "summary": { "pass": 23, "fail": 0, "warn": 0, "skip": 0 },
  "targets": [ … ],
  "integrity": {
    "algorithm": "sha256",
    "canonicalization": "settlens-stable-json-v2",
    "digest": "3c101d12204a09a63dcbb2605ee160b40f09e6444e49a7ecb7fee2738bcde126"
  }
}
```

## Verification from an agent

The [Settlens MCP server](https://settlens.com/mcp) exposes the same checks as offline tools: `verify_report` recomputes a report's digest and `verify_attestation` checks an attestation's signature, given the report that it is the one attested, and given `expected_public_key` that the expected key signed it. An agent can confirm a counterparty's evidence before relying on it, with no network access and no key.

## What evidence does not prove

- **Identity is a key, not a name.** There is no certificate authority, registry or revocation. Publish your public key somewhere verifiers trust, and rotate it yourself if it leaks.
- **The time is the signer's.** `attestedAt` comes from the signing machine's clock. It is signed, but it is not a trusted timestamp.
- **JSON only.** The digest covers the JSON report. The HTML and SARIF files are renderings of the same run and are not signed.
- **Observed behavior, not certification.** An attestation says what Settlens observed during the run. It is not a security audit, a compliance certificate or legal advice.
- **Your key, your custody.** Settlens never stores evidence keys. Keep the private key out of repositories and CI logs; a leaked key can attest anything.

## Questions about evidence

**Does verification need Settlens servers or a network connection?**

No. verify-report and evidence verify run locally on the files. The signature check uses Node's built-in ed25519 implementation.

**What exactly is signed?**

A JSON object with sorted keys containing the canonicalization name, the report digest, report id, product and protocol versions, subject URLs, summary counts, attestedAt and the public key. The signature covers all of it.

**Can I verify an attestation without the report?**

Yes, the signature can be checked alone. You then know the attestation is intact, but not whether a given report is the one attested; pass --report for that.

**Where should I publish my public key?**

Anywhere your verifiers already trust: your website, a security page, a signed email or a contract annex. Verifiers pass it to evidence verify with --expect-key, or compare it with the publicKey field.

**Is the integrity digest enough on its own?**

No. It catches corruption and careless edits, but anyone who edits the report can recompute it, and it does not say who produced the report. An attestation signs the digest, so verifying the report against it detects edits and identifies the signer.

## Related

- **[Assess](https://settlens.com/product/assess)**

  Passive conformance and policy scan, plus the authorized fail-closed probe.

- **[CI gate](https://settlens.com/product/ci)**

  GitHub composite Action with a fail-on threshold, SARIF and evidence artifacts.

- **[Verify](https://settlens.com/product/verify)**

  Offline EIP-3009 and Solana signature checks, and facilitator checks.

- **[Monitor](https://settlens.com/product/monitor)**

  Scheduled passive scans compared to a baseline, with regression alerts.

Reference: [Evidence guide](https://settlens.com/docs/evidence) · [report formats](https://settlens.com/docs/reports) · [security model](https://settlens.com/docs/security) · [why signed evidence matters](https://settlens.com/blog/signed-evidence-for-payment-assessments) · [Settlens for security teams](https://settlens.com/use-cases/security-teams).

## Get a report you can hand to a partner

A pilot ends with a signed assessment of your endpoint that your customers and auditors can verify on their own machines.

[Request a pilot](https://settlens.com/contact) [See how Assess works](https://settlens.com/product/assess)

## Sources

1. [RFC 8032: Edwards-Curve Digital Signature Algorithm (EdDSA)](https://www.rfc-editor.org/rfc/rfc8032.html) — IETF
2. [FIPS 180-4: Secure Hash Standard (SHS)](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf) — NIST
