> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sengol.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Auditor or examiner

> Verify an evidence pack offline, without Sengol and without trusting the vendor.

You check that the evidence is what it claims to be. You need two things
from the bank: the **evidence pack** (a JSON file) and the appliance's
**public key**, delivered through a channel you trust. You do not need a
Sengol install or a licence.

**1. Receive the pack and the key**

Separately, from people you trust.

**2. Install the verifier**

`sengol-verify`, Apache-2.0, two dependencies.

**3. Verify**

Every check runs on your laptop.

**4. Check completeness**

Confirm the pack covers the period you asked for.

## 1. Receive the pack and the key

The bank's operator produces the pack:

```bash theme={null}
sengol export --agent gic-advisor --out pack.json --wait-for-countersign
```

They also give you the appliance's Ed25519 public key as a JSON map of key
id to PEM, which is the format of `--trusted-keys-file`. Ask for it through
a different channel from the pack: whoever produced the pack could also have
produced the key embedded in it.

```json trusted-keys.json theme={null}
{"c5d2e5892a3fd29e": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA...\n-----END PUBLIC KEY-----\n"}
```

In this walkthrough we export the pack and fetch the key ourselves, from a
running appliance:

```bash theme={null}
sengol export --agent gic-advisor --out pack.json --wait-for-countersign
curl -s "$SENGOL_API_URL/v1/audit/verification-keys" \
  | python -c 'import sys, json; json.dump({k: v["public_key"] for k, v in json.load(sys.stdin)["keys"].items()}, open("trusted-keys.json", "w"))'
```

## 2. Install the verifier

`sengol-verify` is open source, has no dependency on Sengol, and makes no
network calls:

```bash theme={null}
pip install "git+https://github.com/sengol-io/sengol-verify.git"
sengol-verify --version
```

## 3. Verify

```bash theme={null}
sengol-verify pack.json --trusted-keys-file trusted-keys.json
```

```text theme={null}
sengol-verify: pack.json
  [PASS        ] canonical_payload (84 records)
  [PASS        ] payload_hash_integrity (84 records)
  [UNVERIFIABLE] hmac_verify (all_records)
               HMAC key material absent for 84 records (key material is not exported for security)
  [PASS        ] chain_continuity (all_partitions)
  [PASS        ] ed25519_countersig (all_countersigs)
  [UNVERIFIABLE] anchor_coverage (0 anchors)
  [PASS        ] field_coverage (84 records)
  [PASS        ] signed_field_presence (84 records)

Verdict: UNVERIFIABLE
```

The command exits `0`. That is the result you want.

### What each check proves

| Check | Proves |
| - | - |
| `canonical_payload` | Each record re-serializes to the exact bytes that were signed. |
| `payload_hash_integrity` | No record changed after its countersignature. |
| `hmac_verify` | The appliance's HMAC. Always `UNVERIFIABLE` offline: the HMAC key never leaves the appliance. The bank can run this check online with `sengol audit verify`. |
| `chain_continuity` | Each record links to the hash of the one before it. Nothing in the run was removed, inserted or reordered. |
| `ed25519_countersig` | The countersignatures were made by the key you trust. This is what ties the pack to the bank's appliance. |
| `anchor_coverage` | Records were anchored to an external timestamp authority. `UNVERIFIABLE` when the appliance does not anchor. |
| `field_coverage` | Lists the few fields outside the signature. These are unsigned by design. |

### Exit codes

The exit code follows the countersignature check, not the overall verdict:

| Code | Meaning |
| - | - |
| `0` | Nothing failed, and the countersignatures verify against a key you trust. |
| `1` | A check **failed**. Treat the pack as altered. |
| `2` | Usage error: unreadable pack or key file. |
| `3` | Nothing failed, but trust is not established: no trusted key given, or the pack has no countersignatures. |

Add `--json` for machine-readable output with `verdict`,
`trusted_key_supplied` and `countersignature_trust_established`.

### What tampering looks like

Flip one verdict in one record:

```python theme={null}
import json

pack = json.load(open("pack.json"))
pack["records"][0]["eval_result"]["scores"][0]["passed"] = False
json.dump(pack, open("tampered.json", "w"))
```

```bash theme={null}
sengol-verify tampered.json --trusted-keys-file trusted-keys.json
```

```text theme={null}
  [FAIL        ] payload_hash_integrity (84 records)
  [FAIL        ] chain_continuity (all_partitions)
  [FAIL        ] ed25519_countersig (all_countersigs)
Verdict: FAIL
```

## 4. Check completeness

The chain proves the records in the pack link together. It does **not**
prove the pack holds every record. A pack that starts mid-sequence still
passes, and an **empty pack passes every check**. Confirm the scope yourself:

```python theme={null}
import json

pack = json.load(open("pack.json"))
records = pack["records"]
seq = sorted(r["sequence_number"] for r in records)
times = sorted(r["timestamp"] for r in records)
print("agent:", pack["agent_id"], "| records:", len(records))
print("sequence:", seq[0], "to", seq[-1], "| gaps:", len(seq) != seq[-1] - seq[0] + 1)
print("period:", times[0], "to", times[-1])
print("countersignatures pending at export:", pack["pending_countersignatures"])
```

```text theme={null}
agent: gic-advisor | records: 84
sequence: 1 to 84 | gaps: False
period: 2026-09-21T... to 2026-10-05T...
countersignatures pending at export: 0
```

Ask the bank to explain any of these:

* a first sequence number other than `1`, or gaps
* a period that does not cover the one under review
* pending countersignatures (re-export later; they are usually just not finished)

## Reading the rest of the evidence

* **Compliance reports** map the records to the bank's obligations. A report
  generated in the Console or through the API carries an HMAC signature. One
  generated with the CLI does not. Ask for the Console version.
* **Review decisions** made in the Console are labels, not signed evidence.
* **Account administration** (users, roles, break-glass sign-ins) writes logs,
  not signed records.

## The same checks, from the Sengol CLI

If you have the `sengol` package, the same offline checks run with:

```bash theme={null}
sengol verify --offline pack.json --trusted-keys-file trusted-keys.json
```

With read access to the appliance, the online check also verifies every
HMAC:

```bash theme={null}
sengol audit verify --chain --agent-id gic-advisor
```

```text theme={null}
Chain  : 84 records  (0 pre-chain, not verified for completeness)
PASS — chain intact (84 records).
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.