Skip to content

Verification model

Witness is designed so a client can verify a result without trusting the gateway's verification answer. witness-core is the shared implementation of the signature, Merkle, log, and proof-bundle checks.

Trust anchor

Verification requires a secret-free NetworkVerificationConfig containing the network ID, witness IDs and public keys, threshold, signature scheme, and public federation policy. It contains no witness bearer tokens or private keys.

signed object + pinned public config
                 |
                 v
           witness-core
          /     |      \
       sig    Merkle    log / bundle
                 |
                 v
        local accept / reject

GET /v1/network is a convenient source for a public configuration. Fetching it from the gateway under test is trust-on-first-use. For a stronger anchor, pin or independently review a configuration derived from an operator's network.json. GET /v1/config is informational and is insufficient for cryptographic verification.

Signed attestations

The verifier recomputes the canonical attestation bytes and checks the signature representation against the configured scheme:

  • Ed25519 requires unique known witness IDs and individually valid signatures.
  • BLS requires unique known signer IDs, aggregates the corresponding public keys, and verifies the aggregate.

Both paths require the configured threshold. A signature count in a JSON object is not trusted merely because it is large; IDs, keys, scheme, message, and threshold are checked together.

Proof-bundle levels

A ProofBundle can contain four layers:

  1. a threshold-signed home attestation;
  2. a Merkle inclusion proof after the attestation is batched;
  3. peer-network cross-anchors over the batch root; and
  4. external-anchor records.

The verifier returns per-layer results and a highest achieved level. A bundle can be valid at the signature layer before batching; missing batch inclusion is not treated as a bad signature. A cross-anchor counts only when its peer configuration is supplied and its batch/root/network links are valid. External anchors are counted as present evidence, not fully verified provider proofs.

RFC 9162 log

The gateway's global log uses RFC 9162 Merkle Tree Hash rules: leaves are SHA-256(0x00 || leaf), internal nodes are SHA-256(0x01 || left || right), and left/right position is significant. An unbalanced tree splits at the largest power of two strictly below its size.

An STH commits to network ID, tree size, timestamp, and root hash. The gateway turns its STH digest into a synthetic attestation and threshold-signs it with the normal witness flow. A client can verify an STH signature, an inclusion proof against an STH, and an RFC 9162 consistency proof between two STHs. A valid consistency chain demonstrates that the old tree is a prefix of the new tree.

CLI, SDK, WASM, and remote checks

The witness CLI and witness-client SDK run verification locally. They can fetch objects online and then verify them locally, or accept a bundle and public configurations from files for fully offline operation. The WASM crate exports verification-only functions for browser and Node SDK use.

POST /v1/verify is intentionally different: it asks the gateway to perform a check and returns the gateway's opinion. It is useful as an API operation, but it is not an independent trust decision.

Auditor

witness-auditor independently polls the gateway's latest STH, verifies the threshold signature and each RFC 9162 consistency proof, and stores accepted STHs and anomalies in its own SQLite database. It detects tree-size regression, same-size root changes, invalid STH signatures, and invalid consistency links.

The auditor has no signing keys. Its public configuration is still a trust anchor: fetching that configuration from the audited gateway is a TOFU path, so operators should consider supplying an independently obtained configuration.