---
title: "Reports: JSON, HTML, SARIF and integrity | Settlens"
description: "Settlens report formats: the JSON report schema, the HTML report, SARIF for code scanning, and the SHA-256 integrity digest checked by verify-report."
url: https://settlens.com/docs/reports
last_updated: 2026-10-13
---

# Reports

> **In short:** Each scan, facilitator or check run writes up to three files: a JSON report whose integrity block holds a SHA-256 digest of its canonical form, a self-contained HTML report for people and a SARIF 2.1.0 file for code scanning. verify-report recomputes the digest offline.

What a Settlens run writes, field by field, and how the SHA-256 integrity digest lets anyone detect a modified report.

Every `scan`, `facilitator` and `check` run builds one report object and writes it in up to three formats, chosen with `--format` (all three by default), into the `--out` directory (`reports` by default). Files are named `settlens-<reportId>.json`, `.html` and `.sarif`. `--no-write` skips the files and prints the console summary only.

_Report formats_

| Format | For | Contents |
| --- | --- | --- |
| JSON | Evidence, automation, verify-report, evidence attest | The complete report, with the integrity block. |
| HTML | People: engineers, partners, auditors | One self-contained file: inline styles, no scripts, nothing loaded from the network. |
| SARIF | Code scanning and SARIF viewers | Failing and warning checks as SARIF 2.1.0 results. |

## JSON report

The JSON report follows the schema in `schemas/settlens-report.schema.json`, from which the tables below are generated. This excerpt keeps one of the 13 checks of a passive scan of the demo endpoint:

**settlens-613cdc9b.json (excerpt)**

```json
{
  "schemaVersion": 1,
  "reportId": "613cdc9b",
  "product": "Settlens",
  "productVersion": "0.5.0",
  "protocol": "x402",
  "protocolVersion": 2,
  "integrations": { "officialX402Core": "2.28.0" },
  "generatedAt": "2026-10-13T11:14:32.642Z",
  "scope": { "targetCount": 1, "activeTargetCount": 0, "settlementCalls": 0, "privateKeysUsed": 0 },
  "metadata": { "source": "cli" },
  "summary": { "pass": 13, "fail": 0, "warn": 0, "skip": 0 },
  "targets": [
    {
      "id": "23c370f9",
      "name": "127.0.0.1",
      "target": { "url": "http://127.0.0.1:4402/secure", "method": "GET", "requestHeaderNames": [] },
      "mode": "passive",
      "request": {
        "status": 402,
        "durationMs": 12,
        "responseBytes": 28,
        "responseDigest": "bd3559c1cc8ba756860ad6f192140e6ecd6bc00fe6becbccdb6f14bc23d0b319",
        "responseBodyTruncated": false,
        "responseHeaderNames": ["connection", "content-type", "date", "keep-alive", "payment-required", "transfer-encoding"]
      },
      "checks": [
        {
          "id": "binding.resource_url",
          "title": "Payment requirement is bound to the requested resource",
          "status": "pass",
          "severity": "high",
          "message": "resource.url exactly matches the protected request URL.",
          "evidence": {
            "requested": "http://127.0.0.1:4402/secure",
            "advertised": "http://127.0.0.1:4402/secure"
          }
        }
      ]
    }
  ],
  "integrity": {
    "algorithm": "sha256",
    "canonicalization": "settlens-stable-json-v2",
    "digest": "cb591281b5875d2bb9a27c4d2ca290dd5da43c3ec60a9ef49cfa7878a6a39cbd"
  }
}
```

_Top-level fields_

| Field | Type and rules | Description |
| --- | --- | --- |
| schemaVersion | 1 (constant), required | Version of the report format. |
| reportId | string, required; 8 lowercase hex characters | Random identifier of the report, also used in the file names. |
| product | "Settlens" (constant), required | Always Settlens. |
| productVersion | string, required | Version of the CLI that produced the report. |
| protocol | "x402" (constant), required | Always x402. |
| protocolVersion | 2 (constant), required | x402 protocol version assessed. |
| generatedAt | string, required | ISO 8601 time at which the report was assembled. |
| integrations | object, required | Versions of external components used during the run. |
| integrations.officialX402Core | string, required | Pinned @x402/core version used for SDK schema validation. |
| scope | object, required | What the run touched. |
| scope.targetCount | integer, required; ≥ 1 | Number of targets in the report. |
| scope.activeTargetCount | integer, required; ≥ 0 | Targets that received the authorized active probe. |
| scope.settlementCalls | 0 (constant), required | Calls to a facilitator's /settle. Always 0. |
| scope.privateKeysUsed | 0 (constant), required | Private keys used. Always 0. |
| metadata | object, required | How the run started: {"source":"cli"}, {"source":"config","configName":"settlens.config.json"}, {"source":"cli","assessmentType":"facilitator"} or {"source":"settlens-web-api"}. |
| summary | object (see [summary](https://settlens.com/docs/reports#summary-object)), required | Result counts across every target. |
| targets | array of object (see [targetResult](https://settlens.com/docs/reports#target-result)), required; 1 to any number of items | One result per assessed target. |
| integrity | object, required | Digest of the report without this block. See Integrity below. |
| integrity.algorithm | "sha256" (constant), required | Hash function. |
| integrity.canonicalization | one of settlens-stable-json-v1, settlens-stable-json-v2, required | How the report is serialized before hashing. |
| integrity.digest | string, required; 64 lowercase hex characters | SHA-256 of the canonical report, in hex. |

### Target result

_Fields of each entry in targets_

| Field | Type and rules | Description |
| --- | --- | --- |
| id | string, required | Random identifier of this target result. |
| name | string, required | Target label (config name, --name or the hostname). |
| target | object, required | The request: sanitized url, method and requestHeaderNames (names only, never values). |
| mode | string, required | passive, active-authorized (the probe ran) or passive-facilitator (facilitator command). |
| startedAt | string, required | ISO 8601 start time. |
| completedAt | string, required | ISO 8601 end time. |
| summary | object (see [summary](https://settlens.com/docs/reports#summary-object)), required | Result counts for this target. |
| request | object or null | First response: status, durationMs, responseBytes, responseDigest (SHA-256 of the body read), responseBodyTruncated and responseHeaderNames. null when the endpoint could not be reached. |
| checks | array of object (see [check](https://settlens.com/docs/reports#check-object)), required | Every check result for this target. |
| supported | object or null | Facilitator command only: the /supported answer as parsed. |
| integrations | object | When a facilitator was cross-checked: its URL and the number of kinds it supports. |
| authorization | object | Only when the active probe ran: { confirmed: true, reference } from --authorization-ref. |

### Check result

_Fields of each entry in checks_

| Field | Type and rules | Description |
| --- | --- | --- |
| id | string, required; matches ^\[a-z0-9\_.-\]+$ | Check id, documented in the check catalog. |
| title | string, required | Short name of the property checked. |
| status | one of pass, fail, warn, skip, required | Outcome of the check. |
| severity | one of info, low, medium, high, critical, required | Impact if the check fails. |
| message | string, required | What was observed in this run. |
| evidence | any JSON value | Check-specific observations, such as expected and actual status codes. Values come from the assessed server. |
| recommendation | string | How to fix it, on results that did not pass. |

What each id means, and how to fix a failure, is in the [check catalog](https://settlens.com/docs/checks).

### Summary

_Fields of summary objects_

| Field | Type and rules | Description |
| --- | --- | --- |
| pass | integer, required; ≥ 0 | Checks that passed. |
| fail | integer, required; ≥ 0 | Checks that failed. |
| warn | integer, required; ≥ 0 | Checks that raised a warning. |
| skip | integer, required; ≥ 0 | Checks that could not run. |

### What reports leave out

- Values of the request headers you configure: only their names are kept.
- Response bodies: only their size and SHA-256 digest.
- Credentials in URLs, and the values of common secret query parameters such as `token`, `api_key` or `signature`.

Evidence fields do contain what the assessed server sent, for example the payment terms it advertised. Treat them as data from that server, not as instructions.

## Integrity

The JSON report ends with an `integrity` block. Its `digest` is the SHA-256 of the whole report except that block, serialized with the canonicalization named in `integrity.canonicalization`. New reports use `settlens-stable-json-v2`: object keys sorted by UTF-16 code unit, arrays kept in order, values serialized as JSON, no whitespace. Reports written by Settlens 0.4.0 and earlier name `settlens-stable-json-v1` and still verify. Pretty-printing or reordering keys in the file does not change the digest; changing any value does.

_Diagram: The report minus its integrity block is serialized as canonical JSON (settlens-stable-json-v2), hashed with SHA-256, and the hex digest is written to integrity.digest. verify-report and the MCP verify_report tool recompute it from the received file and compare: VALID or INVALID._

_The digest is computed once when the report is created and recomputed by anyone who receives the file._

The digest proves that the content is unchanged since it was computed. It does not say who produced the report or when: anyone can edit a report and compute a new digest. To bind a report to a signing identity and a time, sign it with [Settlens Evidence](https://settlens.com/docs/evidence). Each run also gets a new `reportId` and timestamps, so two scans of the same endpoint never share a digest.

## Verify a report

**settlens verify-report (exit 0, then exit 1 for an edited copy)**

```text
$ settlens verify-report reports/settlens-613cdc9b.json
VALID Report content matches its SHA-256 evidence digest.
Expected: cb591281b5875d2bb9a27c4d2ca290dd5da43c3ec60a9ef49cfa7878a6a39cbd
Actual:   cb591281b5875d2bb9a27c4d2ca290dd5da43c3ec60a9ef49cfa7878a6a39cbd
$ settlens verify-report tampered.json
INVALID Report content was modified after the evidence digest was created.
Expected: cb591281b5875d2bb9a27c4d2ca290dd5da43c3ec60a9ef49cfa7878a6a39cbd
Actual:   8db417669d0df8842b0478668786774a7b17cf8a98e377a23b28575cf67c66bf
```

The same verification is available offline to AI agents through the `verify_report` tool of the [MCP server](https://settlens.com/docs/mcp). Without the CLI, these lines of Node.js reproduce it; run against the report above, they print `VALID` and the same digest:

**verify-digest.mjs (node verify-digest.mjs report.json)**

```js
import { createHash } from "node:crypto";
import { readFileSync } from "node:fs";

// settlens-stable-json-v2: object keys sorted by UTF-16 code unit (the default sort),
// arrays kept in order, other values serialized with JSON.stringify, no whitespace.
// Reports whose integrity.canonicalization is settlens-stable-json-v1 sort keys with
// (a, b) => a.localeCompare(b, "en") instead.
function stable(value) {
  if (Array.isArray(value)) return `[${value.map(stable).join(",")}]`;
  if (value !== null && typeof value === "object") {
    const keys = Object.keys(value).sort();
    return `{${keys.map((key) => `${JSON.stringify(key)}:${stable(value[key])}`).join(",")}}`;
  }
  return JSON.stringify(value);
}

const report = JSON.parse(readFileSync(process.argv[2], "utf8"));
const { integrity, ...content } = report;
const digest = createHash("sha256").update(stable(content)).digest("hex");
console.log(digest === integrity.digest ? "VALID" : "INVALID", digest);
```

> **Key order is UTF-16 code-unit order**
>
> `settlens-stable-json-v2` sorts keys the way JavaScript's default `sort()` does, by UTF-16 code unit, with no locale involved. For ASCII keys this is byte order; a reimplementation in another language must compare UTF-16 code units, not code points, for characters outside the Basic Multilingual Plane. The older `settlens-stable-json-v1` sorted with `localeCompare` under the `en` collation, which puts `_` before letters and compares letters case-insensitively first. When in doubt, verify with `settlens verify-report`.

## HTML report

The HTML file is meant for people. It shows the digest, the pass, fail and warning counts, then each target with its checks sorted by status (fail, warn, pass, skip) and severity. Each check shows its id, severity, message, recommendation and a collapsible evidence block. The file has inline styles only, no scripts and a `noindex` robots tag, so it can be attached to a ticket or opened offline. Its footer states that the report describes observed behavior and is not a guarantee of security or legal compliance.

## SARIF

The SARIF file (version 2.1.0) lists failing and warning checks as results, with one rule per check id. Passing and skipped checks are left out.

_How check results map to SARIF_

| Check result | SARIF level | security-severity |
| --- | --- | --- |
| fail, critical | error | 9.5 |
| fail, high | error | 8.0 |
| fail, medium | warning | 5.0 |
| fail, low | warning | 2.0 |
| warn (any severity) | warning | rule severity: 9.5, 8.0, 5.0, 2.0 or 0.1 (info) |

**settlens-56940334.sarif (excerpt: one rule, one result)**

```json
{
  "version": "2.1.0",
  "$schema": "https://json.schemastore.org/sarif-2.1.0.json",
  "runs": [
    {
      "tool": {
        "driver": {
          "name": "Settlens",
          "version": "0.5.0",
          "informationUri": "https://settlens.com",
          "rules": [
            {
              "id": "policy.network",
              "name": "policy_network",
              "shortDescription": { "text": "Networks match the local policy" },
              "fullDescription": { "text": "Unexpected networks: eip155:84532." },
              "help": { "text": "Restore the approved networks or update the reviewed Settlens policy intentionally." },
              "properties": { "tags": ["security", "x402", "agentic-payments"], "security-severity": "8.0" }
            }
          ]
        }
      },
      "automationDetails": { "id": "settlens/56940334" },
      "invocations": [
        { "executionSuccessful": true, "properties": { "reportDigest": "d5576349e7b85404e354b15b17635e2f31cd2a3d63a42569ca5c0ac957fa2252" } }
      ],
      "results": [
        {
          "ruleId": "policy.network",
          "level": "error",
          "message": { "text": "Unexpected networks: eip155:84532. Target: GET http://127.0.0.1:4402/drift" },
          "locations": [
            {
              "physicalLocation": {
                "artifactLocation": { "uri": "settlens.config.json", "uriBaseId": "%SRCROOT%" },
                "region": { "startLine": 1 }
              }
            }
          ],
          "partialFingerprints": { "primaryLocationLineHash": "651267a8258e3446:1" },
          "properties": {
            "status": "fail",
            "severity": "high",
            "target": "http://127.0.0.1:4402/drift",
            "recommendation": "Restore the approved networks or update the reviewed Settlens policy intentionally.",
            "reportId": "56940334"
          }
        }
      ]
    }
  ]
}
```

Findings concern a URL, not a line of code, but code scanning only displays results that have a location. Each result is therefore anchored at line 1 of the configuration file that declared the target: the `--config` path relative to the working directory for `check`, and `settlens.config.json` for an ad hoc `scan` such as this one. `partialFingerprints.primaryLocationLineHash` is computed from the check id and the target, so the same rule failing on two targets stays two alerts. The target URL is in the message and in `properties.target`, and the run's `invocations[0].properties.reportDigest` ties the SARIF file to its JSON report. How to upload it from a GitHub workflow is covered on the [GitHub Action](https://settlens.com/docs/github-action#sarif) page.

## Console summary

Every run also prints a summary: the report id, the scope counters, then for each target its counts and every check that did not pass, failures first. Colors are used only on a terminal and never when `NO_COLOR` is set. Examples are on the [CLI reference](https://settlens.com/docs/cli#scan).

## Sources

1. [Static Analysis Results Interchange Format (SARIF) Version 2.1.0](https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html) — OASIS
2. [SARIF support for code scanning](https://docs.github.com/en/code-security/reference/code-scanning/sarif-files/sarif-support) — GitHub Docs
3. [FIPS 180-4: Secure Hash Standard (SHS)](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf) — NIST
