Skip to content
VERASPEC
Repository
The conformance validatorstable

Python API

python
from ver_validator import validate_record, validate_file, CODES
report = validate_record(record, schema="1.0.1") # or schema="1.0.0"
report = validate_file("record.json", level="L2") # force the effective level
if not report.ok:
for finding in report.errors:
print(finding.code, finding.json_pointer, finding.message)
python
validate_record(record: dict, *, schema: str = "1.0.1", # or "1.0.0", "1.1-draft"
level: str | None = None, # None => auto (claimed, else L0)
spaces_registry: dict | None = None,
max_bytes: int = 64 * 1024 * 1024,
signature_key: str | bytes | None = None,
registry_snapshot: dict | None = None, # 1.1-draft: VER1205/VER1206
lineage_peers: Sequence[dict] | None = None, # 1.1-draft: VER1604
profile: str = "default") -> VeraValidationReport # or "high-trust"
validate_file(path: str | Path, **same_kwargs) -> VeraValidationReport

VeraValidationReport (frozen pydantic v2 model) carries profile, schema_version, level_claimed, level_effective, issues, ok, status, ok_after_policy_exceptions and ignored_codes, plus the errors, warnings, codes, kept_issues and effective_ok conveniences. VeraValidationIssue carries code, severity, json_pointer (RFC 6901), message and remediation.

status is the third answer. ok is a boolean and a boolean has two states, but a validation run has three outcomes:

statusokmeaning
conformanttrueevery rule ran and none found an error
nonconformantfalseevery rule ran and at least one found an error
indeterminatefalsea rule did not finish (VER001); the question was not settled

ok == (status == "conformant") always, so nothing that already reads ok is broken by the addition — and nothing that reads it can mistake an unsettled run for a clean one. A VER001 anywhere outranks every other finding: when a rule crashed, the validator does not know what that rule would have said, so neither "conformant" nor "nonconformant" is a claim it is entitled to make.

json_pointer is "" for a finding about the document as a whole (VER102, VER103) and a pointer into the record otherwise. It is null for exactly two codes — VER101, where there is no parsed document to point into, and VER001, which is about this software and so has no location in any record. Nothing else emits null.

Both entry points raise ValueError rather than returning a report when the invocation is wrong: an unknown level or profile, or a signature_key that is not a parseable PEM public key. The CLI turns all of them into exit 2. None is a property of the record, and reporting one as a finding would accuse the producer of the operator's mistake.

report.profile names the rule set that ran: vera-profile/1.0 for a 1.0.x run and vera-profile/1.1-draft for a 1.1-draft one. It is not affected by the profile= argument, which selects strictness within a run rather than a different published profile.

CODES maps every code to its {stage, severity, title}.

Conformance level inference

A record's conformance_level is its claim, and the profile holds it to that claim. A record that declares nothing is validated at L0 and told so (VER301) — the level is never guessed upward from the fields a record happens to carry, because inferring a level from evidence would let a producer earn a claim it never made.

level= (CLI --level) overrides the effective level: it decides what is audited. It does not edit the record, so the claim is read anyway and both values appear in every report — as level_claimed and level_effective, and as [schema …, level L0, claimed L3] in the text renderer. Read a composite report accordingly, because the two override directions mean opposite things:

  • Auditing downward (--level L0 on a record claiming L3) checks only the L0 obligations. A clean report then says "its L0 obligations hold" and says **nothing about L3** — the L3 gates were not run. level_claimed: "L3" sitting beside ok: true is not an endorsement of the claim.
  • Auditing upward (--level L3 on a record claiming L0) holds the record to a level it never claimed. Findings from that run describe the audit, not a broken promise.

No issue code is allocated for a divergence between claim and audit. The divergence is a property of the invocation, and a code would report the operator's own instruction back to them as a finding. Consumers that need the distinction have both fields; a report consumed as report.codes alone should be produced without --level.