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:
- a threshold-signed home attestation;
- a Merkle inclusion proof after the attestation is batched;
- peer-network cross-anchors over the batch root; and
- 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.