How it works
The end-to-end flow: audits, signed logs, policies, and how a consumer verifies a project.
This page walks through the mechanics: how an audit comes into existence, how it gets distributed, how a consumer subscribes to it, and how a project’s dependencies get gated against a policy. It is the “understand-the-machine” companion to What is OpenVet.
The audit #
An OpenVet audit is the atomic unit. Each audit is a signed
statement, by one or more authors, about a specific package version
identified by the subject tuple (registry, name, version, variant, hash). The audit’s content — summary, report, source-anchored
annotations, structured findings, and machine-readable claims; see
What is OpenVet for the full breakdown
— is bundled, hashed, and signed by the author(s).
Two properties follow from “audit = signed, content-addressed object”: audits are immutable (editing an audit produces a different hash, and therefore a different audit), and they are standalone — anyone with the audit bytes and the author’s public key can verify it without consulting any server.
The log #
Every author or organisation owns one log: an append-only chain of
commits, where each commit records one change to the log’s state. The
most common change is adding or updating an audit, but commits also
carry keyset changes (e.g. adding a new signing key). A log lives at a
URL — https://openvet.org/alice on the public registry, or any URL
for a self-hosted log.
Three properties define the trust shape of a log:
- Append-only. Older commits stay; new commits get appended. Nothing in the log is rewritten after the fact.
- Content-addressed. Every commit and every audit is referenced by its content hash. Tampering with any byte changes the hash; downstream references stop matching.
- Owner-signed. Every commit is signed by a key from the log’s keyset, so each commit is cryptographically tied to the log’s owner.
Alice's log
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ commit 0 │ ← │ commit 1 │ ← │ commit 2 │ ← │ commit 3 │ ← head
└──────────┘ └──────────┘ └──────────┘ └──────────┘
+audit +audit +keyset +audit
libfoo@1.0 libbar@2.0 (new key) libbaz@0.1
sig: alice sig: alice sig: alice sig: alice
Under the hood, audits within a log are indexed by a content-addressable
Merkle Search Tree, which makes audit lookup efficient. Commits are
linked with a skipchain, which makes verifying a new head against your
pinned one fast — O(log n) instead of O(n). And because everything
in a log is content-addressed, the data is highly cacheable. The net
result: openvet check finishes in milliseconds, even on projects with
a thousand-plus dependencies. The wire details live in the
specification; for day-to-day use you do
not need them.
Publishing an audit #
To publish, the author drafts the audit, signs it with their key, and appends it to their log as a new commit. The CLI handles the publishing handshake; the server validates the commit (signature shape, keyset authority, audit well-formedness, tree-state transitions) and persists it. Once accepted, the new commit’s hash becomes the log’s head.
From a consumer’s perspective, nothing happens automatically. The consumer pulls in the new commit (and the new audit it carries) the next time they fetch the log.
Subscribing to a log #
On the consumer side, you declare which logs you trust in your
project’s openvet.toml:
[log.alice]
url = "https://openvet.org/alice"
The first time the CLI fetches the log, it pulls the current head,
verifies it independently (signature + commit shape), and pins the
hash in openvet.lock. This first fetch is trust-on-first-use —
there is no prior state to compare against. Subsequent fetches pull
the new head and verify that it is a descendant of the pinned head,
walking the skipchain back to confirm. If the verification succeeds,
the pin advances. If it fails — a commit you previously pinned is no
longer present, suggesting the log was rewritten — the fetch aborts
and surfaces the problem.
This pin-and-verify pattern is what makes the server hosting the log unnecessary to trust. The server’s job is to serve bytes; the cryptographic verification happens on the consumer’s machine.
The policy #
A policy is a set of named requirements, declared in openvet.toml:
[requirement]
safe-to-deploy = "is-benign and (not uses-unsafe or unsafe-safe)"
Each requirement is a boolean expression over claim names. When the expression is evaluated against an audit, each atom is looked up in the audit’s claims map and resolves to one of three values:
True— the audit asserts the claim is true.False— the audit asserts the claim is false.Unknown— the audit doesn’t mention the claim.
The expression then composes under Kleene’s three-valued logic with
and, or, not, and implies.
For a given subject and requirement, the verdict collapses across all relevant audits:
A requirement passes for a subject iff at least one audit evaluates to
Trueand no audit evaluates toFalse.
The “no False” rule means a single contradicting audit can sink a
requirement that would otherwise pass. This is intentional: if you do
not want a particular auditor’s veto, you stop subscribing to their
log (or alias the disagreeing claim). Per-subject overrides
([[override]]) let you patch the effective requirement set for
specific packages.
The check #
openvet check composes everything above into a verdict.
For each lockfile listed in openvet.toml, the CLI extracts the
project’s dependencies as subject tuples. For each subject, it looks
up the matching audits across every subscribed log. Each requirement
is then evaluated against those audits using the rules above. A
subject passes iff every applicable requirement passes; the project
passes iff every subject passes. A failure means a non-zero exit and
per-subject failure reasons printed to stderr.
Lockfile Subscribed logs Policy Verdict
-------- --------------- ------ -------
libfoo@1.0 ──→ alice: audit(libfoo)
bob: audit(libfoo) safe-to-deploy PASS
libbar@2.0 ──→ alice: audit(libbar) safe-to-deploy PASS
libbaz@0.1 ──→ (no audit) safe-to-deploy FAIL
(no audit)
The verdict is deterministic: given the same openvet.toml, the same
openvet.lock, and the same project lockfile, every machine produces
the same result. That is what makes openvet check safe to run in CI.
Further reading #
This page covered the mechanics. Deeper questions — who you have to trust (and who you don’t), what happens if a signing key is compromised, what guarantees mirroring gives — live in Trust model. The exact wire formats and field-level details live in the Specification.