<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>OpenVet Blog</title>
    <subtitle>Notes from building OpenVet — a public registry for software supply-chain audits.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://blog.openvet.org/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://blog.openvet.org"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-06-01T00:00:00+00:00</updated>
    <id>https://blog.openvet.org/atom.xml</id>
    <entry xml:lang="en">
        <title>Reactive vs proactive supply-chain security</title>
        <published>2026-06-01T00:00:00+00:00</published>
        <updated>2026-06-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.openvet.org/posts/reactive-vs-proactive/"/>
        <id>https://blog.openvet.org/posts/reactive-vs-proactive/</id>
        
        <content type="html" xml:base="https://blog.openvet.org/posts/reactive-vs-proactive/">&lt;p&gt;Software supply-chain security is currently top-of-mind for many people,
because the previous months have seen a lot of attacks. However, this is not a
new issue. It is something that I have been thinking about for several years
now. My concerns about it were strengthened when the xz-utils attack happened,
and current events provide even more evidence that we need to re-think how we
approach it.&lt;&#x2F;p&gt;
&lt;p&gt;As a community, we have adopted a working pattern where we place implicit trust
in software packages published on registries like NPM, PyPI, Cargo or even on
random GitHub repositories. And I would like to live in a world where that is
okay, but real world experience shows us that this is not a sustainable path.&lt;&#x2F;p&gt;
&lt;p&gt;People have suggested different “solutions” for this. In the previous months I
have read a plethora of blog posts arguing for different ways to improve
security. However, in my opinion, none of the suggestions are actually the
right way to solve this. They are all &lt;em&gt;reactive&lt;&#x2F;em&gt;: they wait for something to
go wrong, then tell you about it. Real security has to be &lt;em&gt;proactive&lt;&#x2F;em&gt;. It has to
surface problems &lt;em&gt;before&lt;&#x2F;em&gt; they reach you in the first place.&lt;&#x2F;p&gt;
&lt;p&gt;I think we can only solve this problem if we re-think the way that we use
dependencies. Part of the reason proactive security isn’t pushed more often,
in my view, is that it asks us to change how we think — and the tooling to make
that practical does not yet exist.&lt;&#x2F;p&gt;
&lt;p&gt;In this article, I want to walk through some suggestions that I have read
about, and why I think they are not good solutions. Finally, I will explain
what OpenVet’s philosophy is, why I am building it the way I’m building it,
and how OpenVet tries to solve these problems differently, and properly.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;just-don-t-use-dependencies&quot;&gt;Just don’t use dependencies
&lt;a class=&quot;zola-anchor&quot; href=&quot;#just-don-t-use-dependencies&quot; aria-label=&quot;Anchor link for: just-don-t-use-dependencies&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;A few of the articles I have read seem to argue that you just should not use
dependencies. Implement things yourself, or copy code. In fact, Go even spells
the latter out as a proverb:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;A little copying is better than a little dependency.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Let me untangle this a bit. I think a part of this thinking applies to
&lt;em&gt;microdependencies&lt;&#x2F;em&gt;, those are dependencies that do a tiny thing. For example,
&lt;code&gt;left-pad&lt;&#x2F;code&gt; was a small package published on NPM that left-padded a string. When
the maintainer deleted it, it broke a lot of other people’s code. The article
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bvisness.me&#x2F;microlibraries&#x2F;&quot;&gt;Micro-libraries need to die already&lt;&#x2F;a&gt;
talks about cases like this.&lt;&#x2F;p&gt;
&lt;p&gt;However, I don’t agree with the blanket idea that you should copy code. If a
lot of people are using a dependency that does one tiny thing, that is a signal
that whatever the library does should become part of the language’s standard
library. There’s no signal stronger to add something like &lt;code&gt;left-pad&lt;&#x2F;code&gt; to your
language’s standard library than someone publishing a library that does only
that, and has hundreds of millions of users. There are cases where copying is
fine, but that should not be the go-to way that software is developed.&lt;&#x2F;p&gt;
&lt;p&gt;For larger libraries, I also don’t think copying is the right move. A
language’s usefulness is compounded by having a set of robust libraries. It’s
the ecosystem.  Instead of declaring that an ecosystem is &lt;em&gt;bad&lt;&#x2F;em&gt; and that people
should go back to implementing things by themselves, we should work together to
make sure that there are strong libraries, and that those libraries are safe.&lt;&#x2F;p&gt;
&lt;p&gt;Having dependencies is not bad. Having dependencies &lt;em&gt;and not checking them&lt;&#x2F;em&gt;
is bad.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;vulnerability-scanners-don-t-protect-you&quot;&gt;Vulnerability scanners don’t protect you
&lt;a class=&quot;zola-anchor&quot; href=&quot;#vulnerability-scanners-don-t-protect-you&quot; aria-label=&quot;Anchor link for: vulnerability-scanners-don-t-protect-you&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Many people are pushing for the use of vulnerability scanners. I don’t think
that using these is bad — it’s better than not using them. But fundamentally,
vulnerability scanners can’t protect you.&lt;&#x2F;p&gt;
&lt;p&gt;Vulnerability scanners are, by definition, reactive. If there is an active
attack (a malicious version of a software package published), someone might
notice it, investigate it, make a report, that report is then checked, once
verified it lands in a vulnerability database and that is where your dependency
scanner picks it up. But that might be a considerable time later. This is
reactive security: by the time your dependency scanner flags something, your
secrets, API tokens and access to your infrastructure may already be
exfiltrated. Modern supply-chain attacks move fast.&lt;&#x2F;p&gt;
&lt;p&gt;The time it takes for a malicious package to be discovered also depends on how
popular it is. Today, most attacks target dependencies that are popular, which
means that the damage they can do is greater (more users), but it also means
that they are discovered more quickly. What if someone attacks a smaller, niche
dependency? Who looks into those?&lt;&#x2F;p&gt;
&lt;p&gt;The data also undercuts the assumption that the CVE feed is even complete.
Sonatype’s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.sonatype.com&#x2F;state-of-the-software-supply-chain&quot;&gt;2026 State of the Software Supply Chain&lt;&#x2F;a&gt; report puts the
catalog coverage of recent open-source CVEs at roughly 35%. The other 65% of
issues are real, but not in any database your scanner is querying. The scanner
is also not going to flag the next Shai-Hulud worm an hour after it is
published, because there is no signature to match against yet.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;sboms-answer-a-different-question&quot;&gt;SBOMs answer a different question
&lt;a class=&quot;zola-anchor&quot; href=&quot;#sboms-answer-a-different-question&quot; aria-label=&quot;Anchor link for: sboms-answer-a-different-question&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;A &lt;em&gt;Software Bill of Materials&lt;&#x2F;em&gt; is a list of what is in your build. It is
documentation. It does not contain any assertion about whether the listed
components are &lt;em&gt;safe&lt;&#x2F;em&gt; — it just enumerates them. SBOMs are useful when you need
to answer “do I have a dependency on log4j 2.14?” after a CVE drops. They do
not help you decide whether to take a new dependency in the first place.&lt;&#x2F;p&gt;
&lt;p&gt;The empirical picture is also bleak. Williams et al.’s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doi.org&#x2F;10.1145&#x2F;3696630.3734200&quot;&gt;&lt;em&gt;Rising
Tide&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; study of supply-chain practices across nine large companies
found SBOM consumption at three percent adoption. That is not “early days”
three percent: that is “the machinery to act on SBOMs basically doesn’t exist
on the consumer side” three percent. SBOMs are being produced because
regulators are asking for them, not because consumers have a workflow that
ingests them.&lt;&#x2F;p&gt;
&lt;p&gt;Scanners on top of SBOMs do not close the gap either. Dietrich et al. looked at
727 confirmed vulnerable shaded clones in Maven Central across 29 CVEs; the
dominant SCA tools (Dependabot, Snyk, OWASP Dependency-Check, Grype)
collectively flagged 20.5% of them. Even catalogued issues evade catalog-based
detection when the code reappears under a slightly different name. These
would have been caught if someone had looked into those dependencies.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;sigstore-and-provenance-answer-yet-another-question&quot;&gt;Sigstore and provenance answer yet another question
&lt;a class=&quot;zola-anchor&quot; href=&quot;#sigstore-and-provenance-answer-yet-another-question&quot; aria-label=&quot;Anchor link for: sigstore-and-provenance-answer-yet-another-question&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Sigstore, npm provenance, PyPI’s Sigstore-bound uploads, and Go’s checksum
database are all answering the question &lt;em&gt;did this artifact come from where it
claims to?&lt;&#x2F;em&gt;. They are good, important answers to that question. I want them to
exist, and OpenVet’s data structures are explicitly designed to compose with
them rather than replace them.&lt;&#x2F;p&gt;
&lt;p&gt;But “came from where it claims” is not the same as “is safe to run”. The
dominant supply-chain compromises of the last two years all produced signed,
attested, provenance-verified malicious releases:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.openwall.com&#x2F;lists&#x2F;oss-security&#x2F;2024&#x2F;03&#x2F;29&#x2F;4&quot;&gt;xz-utils backdoor&lt;&#x2F;a&gt; was introduced by a maintainer who was, at the
time, a legitimate maintainer of the project. Every release in the campaign
was signed by a real key held by a real human with push rights.&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.bleepingcomputer.com&#x2F;news&#x2F;security&#x2F;polyfillio-javascript-supply-chain-attack-impacts-over-100k-sites&#x2F;&quot;&gt;polyfill.io takeover&lt;&#x2F;a&gt; happened because the attacker bought the domain and
the GitHub organisation of the dependency. There was no credential compromise
to detect — they &lt;em&gt;were&lt;&#x2F;em&gt; the new owners.&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.cisa.gov&#x2F;news-events&#x2F;alerts&#x2F;2025&#x2F;09&#x2F;23&#x2F;widespread-supply-chain-compromise-impacting-npm-ecosystem&quot;&gt;Shai-Hulud worm&lt;&#x2F;a&gt; propagated via &lt;code&gt;postinstall&lt;&#x2F;code&gt; scripts published under
stolen but otherwise valid maintainer tokens. Every infected release was
provenance-attested to a real CI job, run from a real repo, under a real
account.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;There is also a deeper limit on what attestations &lt;em&gt;can&lt;&#x2F;em&gt; prove, even in their
richest form. in-toto, the spec behind most modern attestation schemes, only
constrains the chain from source to artifact: it records which steps ran, in
what order, and signed by whom. It is not an auditing tool: it does not check
whether the source code is correct. A maintainer with commit and signing rights
(or one whose tokens have been stolen) can put malicious code straight into the
git tree, and the entire chain will verify end-to-end, even with in-toto.&lt;&#x2F;p&gt;
&lt;p&gt;A perfectly attested malicious release is still malicious. Provenance gates
lower the rate at which &lt;em&gt;unauthorised&lt;&#x2F;em&gt; compromises succeed; they do not lower
the rate at which &lt;em&gt;authorised&lt;&#x2F;em&gt; compromises succeed, and “authorised” turns out
to cover a lot of the interesting attacks.&lt;&#x2F;p&gt;
&lt;p&gt;For example, the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blog.pypi.org&#x2F;posts&#x2F;2024-12-11-ultralytics-attack-analysis&#x2F;&quot;&gt;Ultralytics&lt;&#x2F;a&gt; GitHub project was hijacked and
published a malicious release that went through the legitimate GitHub Actions
pipeline and was properly attested in Sigstore. Every signature was real, every
check passed, and the release was still malicious.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dependency-freezes-shift-the-problem-in-time&quot;&gt;Dependency freezes shift the problem in time
&lt;a class=&quot;zola-anchor&quot; href=&quot;#dependency-freezes-shift-the-problem-in-time&quot; aria-label=&quot;Anchor link for: dependency-freezes-shift-the-problem-in-time&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blog.yossarian.net&#x2F;2025&#x2F;11&#x2F;21&#x2F;We-should-all-be-using-dependency-cooldowns&quot;&gt;Dependency cooldowns&lt;&#x2F;a&gt; and the related “turn Dependabot off, pin
everything, update on your own cadence” school
(&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;words.filippo.io&#x2F;dependabot&#x2F;&quot;&gt;Valsorda&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;benhoyt.com&#x2F;writings&#x2F;dependencies&#x2F;&quot;&gt;Hoyt&lt;&#x2F;a&gt;) start from a real observation:
most malicious package versions are detected within days of publication, so a
consumer who waits N days before adopting a new release dodges a lot of attacks
for free.&lt;&#x2F;p&gt;
&lt;p&gt;The problem, as Cal Paterson &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;calpaterson.com&#x2F;deps.html&quot;&gt;pointed out&lt;&#x2F;a&gt;, is that this works
precisely because someone else absorbs the attack during the cooldown window.
The mechanism is structurally a free-rider on the ecosystem’s early adopters.
If &lt;em&gt;everyone&lt;&#x2F;em&gt; froze for 7 days, the detection clock would simply move 7 days
later, because most detections happen when the malicious code reaches consumers
and one of them notices. The aggregate amount of exploitation does not go down;
it just shifts to a slightly later cohort.&lt;&#x2F;p&gt;
&lt;p&gt;Cooldowns do nothing against slow campaigns. xz-utils was prepared over
multiple years. A 7-day cooldown is invisible to that attack. Anywhere a
determined adversary is willing to wait, time-based defenses degrade to zero.&lt;&#x2F;p&gt;
&lt;p&gt;There is also a second axis of dependency risk that cooldowns do not engage
with at all. Cooldowns are a defense against malicious code — code deliberately
placed in a release to harm consumers. They do nothing about ordinary
vulnerabilities: bugs in legitimate code, undiscovered at release time, waiting
to be found. The longer you stay on an old version, the more time attackers
have to find exploitable bugs in the code you are still running, and the longer
you remain on a version that has accumulated known issues since release.
Cooldowns trade exposure to malicious new releases for exposure to
slowly-accumulating vulnerabilities in old ones.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-common-pattern&quot;&gt;The common pattern
&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-common-pattern&quot; aria-label=&quot;Anchor link for: the-common-pattern&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Each of these defenses is a real thing, addressing a real layer of the threat
surface. CVEs answer “is this known broken?”. SBOMs answer “what is in my
build?”. Provenance answers “did this come from where it claims?”. Cooldowns
answer “has anyone else been hit yet?”. Pinning answers “is this still the
same code I ran last week?”.&lt;&#x2F;p&gt;
&lt;p&gt;None of them answer the question that actually matters when you take a
dependency: &lt;em&gt;has anyone competent looked at this code?&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;That question is the one we have been collectively avoiding for two decades. We
use other people’s code at a scale that has no historical precedent — the
median application now pulls in around 180 transitive dependencies — and we do
it almost entirely without reading what we are running. The popularity
heuristic (“if a lot of people use it, someone must have looked at it”) is the
implicit fallback, and it is exactly the heuristic that maintainer-handoff,
typosquatting, and account-takeover attacks are designed to defeat.&lt;&#x2F;p&gt;
&lt;p&gt;Sammak et al.’s 2024 interview study of 18 industry developers across 11
countries is sobering on this point: all 18 reported assessing popularity, 15
of 18 verified the recency of the last update, and 13 of 18 named dependency
auditing as one of the most challenging security measures to implement at all.
SBOMs, signing, and SLSA were essentially absent from the actual decisions
developers reported making.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;so-why-don-t-we-just-read-our-dependencies&quot;&gt;So why don’t we just read our dependencies?
&lt;a class=&quot;zola-anchor&quot; href=&quot;#so-why-don-t-we-just-read-our-dependencies&quot; aria-label=&quot;Anchor link for: so-why-don-t-we-just-read-our-dependencies&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Because there is too much code. One personal data point: my average Rust pet
project pulls in around 400 transitive dependencies, summing to about 3.5
million lines of code, as measured with &lt;code&gt;cargo vendor&lt;&#x2F;code&gt; and &lt;code&gt;tokei&lt;&#x2F;code&gt;. Reading
that, even once, is the work of months. Doing it again on every update is not a
job a human can hold.&lt;&#x2F;p&gt;
&lt;p&gt;And the trajectory is wrong. AI coding agents are generating code at
unprecedented speed, and recent benchmarks suggest only around 10% of
AI-generated code meets both functional and security bars. The volume of code
entering projects without anyone reading it is growing faster, not slower.&lt;&#x2F;p&gt;
&lt;p&gt;This is the part of the argument that I think is most often missed. When
somebody says “just audit your dependencies”, they are either:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;proposing that every consumer privately performs millions of lines of review
per project, on rolling updates, forever — which is not going to happen; or&lt;&#x2F;li&gt;
&lt;li&gt;proposing the same private audit at company scale, which is what Google,
Mozilla, and the Bytecode Alliance actually do, and which produces real
audit artifacts that the rest of the world cannot see&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;; or&lt;&#x2F;li&gt;
&lt;li&gt;handwaving.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Option 1 is unrealistic at the individual level. Option 2 happens, but the
artifacts are locked inside each organisation. Option 3 is what the “you should
audit your dependencies” discourse usually collapses to in practice.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-proactive-bet-read-once-share-widely&quot;&gt;The proactive bet: read once, share widely
&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-proactive-bet-read-once-share-widely&quot; aria-label=&quot;Anchor link for: the-proactive-bet-read-once-share-widely&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;OpenVet’s argument is that doing the actual code-reading is the only thing that
actually answers the question — and that the only way to make it sustainable is
to do it in the open. If you read a dependency and publish a signed,
machine-readable audit of what you read, then &lt;em&gt;everyone who trusts you&lt;&#x2F;em&gt; gets to
stand on that work without re-reading the code themselves. The marginal cost of
adding another audited consumer drops to zero.&lt;&#x2F;p&gt;
&lt;p&gt;The trust model is the load-bearing piece. You don’t have to trust every audit;
you choose whose audits you trust. Maybe you trust Mozilla, the Bytecode
Alliance, some well-known cryptography researchers, and three of your friends.
Maybe you also trust a vendor you work with. A dependency passes your policy
when the union of audits from people you trust covers it. If nobody you trust
has audited it, you have a clear, actionable signal: either you do the audit
yourself (which OpenVet tries to make as easy as possible), or you find a
dependency that someone you trust has reviewed.&lt;&#x2F;p&gt;
&lt;p&gt;This is proactive in the specific sense that it answers the question &lt;em&gt;before&lt;&#x2F;em&gt;
you ship the dependency, not after the incident response. You read the code
while you are depending on it, not three months later when the CVE rolls in.
And because the work is shared, you do not have to read all 3.5 million lines
yourself: you only have to audit the slice that nobody you trust has audited
yet.&lt;&#x2F;p&gt;
&lt;p&gt;And there is one aspect that none of the other approaches cover at all. OpenVet
is a tool you can use to detect malicious code, but it does not end there. What
about correctness? Vulnerability databases focus on code that is exploitable,
but not on code that is just broken. By auditing your dependencies, you can not
only surface malicious behaviour, but you can also check for other properties,
such as correctness, test coverage, and overall implementation quality.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;audits-don-t-need-maintainer-cooperation&quot;&gt;Audits don’t need maintainer cooperation
&lt;a class=&quot;zola-anchor&quot; href=&quot;#audits-don-t-need-maintainer-cooperation&quot; aria-label=&quot;Anchor link for: audits-don-t-need-maintainer-cooperation&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;There is one more structural property of the audit channel that the
attestation-based defenses do not have: it does not require the maintainer to
do anything. Every signing scheme — npm provenance, PyPI attestations, in-toto
layouts, Sigstore-bound publishes — depends on the &lt;em&gt;publisher&lt;&#x2F;em&gt; of a package
opting in. That turns ecosystem-wide adoption into a coordination problem
across millions of independent maintainers, and the numbers show it is going
slowly. Williams et al. put attestation production at 0.17 across major
industry adopters and provenance delivery at 0.08. Your security model cannot
realistically be “I will contact every maintainer in my dependency tree and
convince them to emit attestations”.&lt;&#x2F;p&gt;
&lt;p&gt;OpenVet sits on the other side of that boundary. An audit is something &lt;em&gt;anyone
who can read the code&lt;&#x2F;em&gt; publishes about a package, with zero cooperation
required from the upstream maintainer. If a crate’s maintainer has never heard
of signing, an auditor can still publish a useful audit of it. If a package’s
maintainer is unreachable, an auditor can still review it. The bootstrap
problem is much smaller: you do not have to convince every npm publisher in
the world to flip a switch. You just need a community of reviewers willing to
read code, and a way to share their work. It is also why this is a thing you
can deploy &lt;em&gt;today&lt;&#x2F;em&gt;, without waiting on the rest of the ecosystem to catch up.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;reactive-defenses-still-matter&quot;&gt;Reactive defenses still matter
&lt;a class=&quot;zola-anchor&quot; href=&quot;#reactive-defenses-still-matter&quot; aria-label=&quot;Anchor link for: reactive-defenses-still-matter&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;I want to be clear that I am not arguing against the reactive layer.
Provenance, signing, SBOMs, and CVE feeds are all genuinely useful and OpenVet
is designed to compose with them, not displace them.&lt;&#x2F;p&gt;
&lt;p&gt;Andrew Nesbitt makes the honest version of this case in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nesbitt.io&#x2F;2026&#x2F;05&#x2F;24&#x2F;signing-is-for-the-bad-days.html&quot;&gt;Signing is for the
bad days&lt;&#x2F;a&gt;: the value of signing infrastructure shows up &lt;em&gt;when&lt;&#x2F;em&gt;
something has already gone wrong — forensic clarity, identity binding, the
ability to tell whether a malicious release came from a stolen token or a
poisoned build cache. He is right about that. The reactive layer is the thing
that helps you survive a bad day. It just should not be the layer that decides
whether to install the dependency in the first place.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Provenance is useful to capture where binary artifacts came from. You typically
can’t meaningfully audit binary artifacts, so provenance doesn’t tell you
whether they are safe — but it does at least tell you where they came from,
which is often the best you can do.&lt;&#x2F;li&gt;
&lt;li&gt;SBOMs give you the inventory you need to ask, after a disclosure, “do I have
this thing in my build?”. They also give you legal compliance, in jurisdiction
where that is required.&lt;&#x2F;li&gt;
&lt;li&gt;CVE feeds tell you when somebody else has caught an issue you missed. One
feature I am working on with OpenVet is re-publishing data from CVEs and
various vulnerabilities as OpenVet audits, so the tooling can ingest those as
well.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These should be present. They just should not be the front line. A defense
layered entirely on “what the rest of the ecosystem has already learned” is a
defense that accepts being late as a feature. The front line should be
“somebody I trust read this code”, and the reactive feeds should sit behind
that as a backstop for the cases the audits did not catch.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-openvet-is-doing-about-it&quot;&gt;What OpenVet is doing about it
&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-openvet-is-doing-about-it&quot; aria-label=&quot;Anchor link for: what-openvet-is-doing-about-it&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Concretely, OpenVet’s design follows from this position in a few specific ways:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Audits are first-class artifacts.&lt;&#x2F;strong&gt; They are signed, structured, and
machine-readable. They carry claims, findings, source-anchored annotations,
and a structured report. They are not a side effect of some other workflow.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Audits are publicly shareable by default.&lt;&#x2F;strong&gt; The registry hosts them under
permissive licenses (&lt;a href=&quot;https:&#x2F;&#x2F;blog.openvet.org&#x2F;posts&#x2F;licensing-of-audits&#x2F;&quot;&gt;CC0 or CC-BY-4.0&lt;&#x2F;a&gt;) so that consumers can
actually use, redistribute, and build on them.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Trust is rooted in publishers, not in the platform.&lt;&#x2F;strong&gt; Consumers pick whose
logs they trust. The registry holds the bytes, the signatures and the
cryptographic chain hold the trust.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The tooling is built around lowering authoring cost.&lt;&#x2F;strong&gt; Creating and
publishing your first audit should take minutes, not days. If the
marginal cost of publishing one more audit is high, the ecosystem will not
scale.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The bet is that, if we can get the cost-sharing right, an ecosystem where most
of the code most people depend on has been read by &lt;em&gt;somebody they trust&lt;&#x2F;em&gt; is
actually reachable. It is not reachable by asking every consumer to read
everything. It is not reachable by hoping a CVE feed catches the bad stuff in
time. It is reachable by splitting the work and making the result legible
across the network of people who already trust each other to some degree.&lt;&#x2F;p&gt;
&lt;p&gt;That is the part of the problem none of the reactive defenses are trying to
solve, and it is the part OpenVet is built around.&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;1&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;Google, Mozilla and some other companies are auditing their dependencies.
They publish &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;google&#x2F;rust-crate-audits&quot;&gt;some of these
audits&lt;&#x2F;a&gt; in the form of &lt;code&gt;cargo-vet&lt;&#x2F;code&gt;
audits. But these published audits only cover Rust libraries, and don’t seem to
be updated very often.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>OpenVet 0.6.0 Release</title>
        <published>2026-05-31T00:00:00+00:00</published>
        <updated>2026-05-31T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.openvet.org/announcements/openvet-0-6-0/"/>
        <id>https://blog.openvet.org/announcements/openvet-0-6-0/</id>
        
        <content type="html" xml:base="https://blog.openvet.org/announcements/openvet-0-6-0/">&lt;p&gt;&lt;strong&gt;OpenVet 0.6.0 is out.&lt;&#x2F;strong&gt; I managed to sneak some breaking changes into the
data structures again. Two important commands were included in this release:
&lt;code&gt;openvet info&lt;&#x2F;code&gt; allows you to query audit data about the dependencies in your
project, and &lt;code&gt;openvet guard&lt;&#x2F;code&gt; is my first attempt at integrating the OpenVet
checks into existing developer flows.&lt;&#x2F;p&gt;
&lt;p&gt;I also took the time to audit every single dependency of OpenVet with OpenVet
itself, some dogfooding that allows me to test how this all fits together.&lt;&#x2F;p&gt;
&lt;p&gt;The rest of this post walks through the changes that matter from a user’s
perspective: the guard wrapper, the dependency lookup, the new per-dependency
narrative on audits, the new &lt;code&gt;--project&lt;&#x2F;code&gt; flag for root auto-discovery, and the
wire-format breaks to be aware of if you have audits authored against 0.5.0.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;all-direct-dependencies-of-openvet-are-audited&quot;&gt;All direct dependencies of OpenVet are audited
&lt;a class=&quot;zola-anchor&quot; href=&quot;#all-direct-dependencies-of-openvet-are-audited&quot; aria-label=&quot;Anchor link for: all-direct-dependencies-of-openvet-are-audited&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;The 0.5.0 release post ended with 111 audits in the OpenVet log. In 0.6.0 that
grew to 572, covering every direct dependency the OpenVet workspace pulls in,
with the project’s own &lt;code&gt;openvet.toml&lt;&#x2F;code&gt; requirements satisfied end-to-end.
Running &lt;code&gt;openvet check&lt;&#x2F;code&gt; against the workspace now passes cleanly:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CDD6F4); background-color: light-dark(#EFF1F5, #1E1E2E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ openvet check&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[..]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Summary: 572 passed, 0 failed, 0 unaudited, 14 skipped&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is the first project that has a full audited-dependencies workflow.  All
audits are hosted in my log at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;openvet.org&#x2F;xfbs&quot;&gt;openvet.org&#x2F;xfbs&lt;&#x2F;a&gt;.
You can take a look at them if you are curious. Note that most of them are
generated with the help of an LLM: My goal here was not to produce the most
perfect audits, but to exercise every part of the data structures and to test
the flow end-to-end. For some heavy dependencies, I only did a light audit,
which did not evaluate all claims. And I have had to add some overrides.
This models a reasonable real-world usage of OpenVet.&lt;&#x2F;p&gt;
&lt;p&gt;Notably, many of the design decisions (content-addressing, caching, compression)
seem to be working well. Once you’ve populated the cache on the first run, checking
takes only a couple hundred milliseconds:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CDD6F4); background-color: light-dark(#EFF1F5, #1E1E2E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ time openvet check&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[..]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;openvet check  0.10s user 0.02s system 84% cpu 0.139 total&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Running it with an empty cache is reasonable. This will do about ~600 network
requests. It is fast because the OpenVet Registry has a CDN, will transparently
serve pre-compressed objects, and network requests are issued in parallel.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CDD6F4); background-color: light-dark(#EFF1F5, #1E1E2E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ rm ~&#x2F;.cache&#x2F;openvet&#x2F;cache.db&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ openvet check&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[..]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;openvet check  0.44s user 0.34s system 20% cpu 3.752 total&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The cache size is reasonable:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CDD6F4); background-color: light-dark(#EFF1F5, #1E1E2E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ ls -lah ~&#x2F;.cache&#x2F;openvet&#x2F;cache.db&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-rw-r--r-- 1 patrick  patrick   6.6M May 31 14:49 &#x2F;home&#x2F;patrick&#x2F;.cache&#x2F;openvet&#x2F;cache.db&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There is currently no garbage collection for the cache. If the cache grows too
large, you would need to just delete it. That is something that is on my
roadmap. But given that the size of it is in the megabytes, I am not too
worried about that yet.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;openvet-guard-block-a-build-until-policy-passes&quot;&gt;&lt;code&gt;openvet guard&lt;&#x2F;code&gt; — block a build until policy passes
&lt;a class=&quot;zola-anchor&quot; href=&quot;#openvet-guard-block-a-build-until-policy-passes&quot; aria-label=&quot;Anchor link for: openvet-guard-block-a-build-until-policy-passes&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;OpenVet is not useful if it’s only advisory: the way that OpenVet becomes
useful is by integrating it into developer flows, so that it blocks you from
executing code that does not match your requirements. Doing that is not
trivial, and there are different ways that I am exploring. What I built into
this release is &lt;code&gt;openvet guard&lt;&#x2F;code&gt;, which is intended to be used as a kind of
proxy for running commands. It takes a command that you want to run, runs
&lt;code&gt;openvet check&lt;&#x2F;code&gt; quietly, only executing the command if the check passes. The
intended use is a shell alias:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CDD6F4); background-color: light-dark(#EFF1F5, #1E1E2E);&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#8839EF, #CBA6F7);&quot;&gt;alias&lt;&#x2F;span&gt;&lt;span&gt; cargo&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #94E2D5);&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;openvet guard cargo&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With that in place, every &lt;code&gt;cargo build&lt;&#x2F;code&gt;, &lt;code&gt;cargo test&lt;&#x2F;code&gt;, &lt;code&gt;cargo run&lt;&#x2F;code&gt; in the
project first checks that all dependencies match the project’s &lt;code&gt;openvet.toml&lt;&#x2F;code&gt;
requirements, and only proceeds when they do. Forget to audit a new dependency
you just added? &lt;code&gt;cargo build&lt;&#x2F;code&gt; won’t run. Add a dependency whose existing audit
contradicts a requirement? Same.&lt;&#x2F;p&gt;
&lt;p&gt;On block, you get the same per-subject failure trees that &lt;code&gt;openvet check&lt;&#x2F;code&gt; prints, followed by an error summary so the reason is
unmistakable:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CDD6F4); background-color: light-dark(#EFF1F5, #1E1E2E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ cargo build&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;✘ cargo:some-new-crate@0.3.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;└─ requires: is-benign&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   └─ no audit asserts is-benign&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;error: 1 subject(s) did not pass policy (run `openvet check` for details)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;On pass, guard is silent, so the wrapped command’s output starts immediately.
The exec is in-place, so signals and exit codes flow through unchanged.
Hyphenated arguments after the command are forwarded verbatim (&lt;code&gt;openvet guard cargo build --release --features foo&lt;&#x2F;code&gt; works as expected).&lt;&#x2F;p&gt;
&lt;p&gt;Outside an OpenVet project, guard’s behaviour is configurable via a new
&lt;code&gt;[guard]&lt;&#x2F;code&gt; section in the user config. The default is &lt;code&gt;strict = false&lt;&#x2F;code&gt;, which
runs the wrapped command transparently — so aliasing &lt;code&gt;cargo&lt;&#x2F;code&gt; globally doesn’t
break anything in scratch directories. Flip to &lt;code&gt;strict = true&lt;&#x2F;code&gt; and guard
refuses to run the wrapped command anywhere that isn’t an OpenVet project,
except for paths you’ve explicitly excluded:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CDD6F4); background-color: light-dark(#EFF1F5, #1E1E2E);&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #89B4FA);font-style: italic;&quot;&gt;openvet&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; config&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; guard.strict&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;-set&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#FE640B, #FAB387);&quot;&gt; true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #89B4FA);font-style: italic;&quot;&gt;openvet&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; config&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; guard.exclude&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;-set&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;~&#x2F;Projects&#x2F;alacritty,~&#x2F;scratch&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Unix-only for now.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;openvet-info-a-man-page-for-a-project-dependency&quot;&gt;&lt;code&gt;openvet info&lt;&#x2F;code&gt; — a man-page for a project dependency
&lt;a class=&quot;zola-anchor&quot; href=&quot;#openvet-info-a-man-page-for-a-project-dependency&quot; aria-label=&quot;Anchor link for: openvet-info-a-man-page-for-a-project-dependency&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;openvet info &amp;lt;registry&amp;gt;:&amp;lt;package&amp;gt;[@&amp;lt;version&amp;gt;]&lt;&#x2F;code&gt; finds every matching
subject in the project’s lockfiles and renders each matching audit as
a flat, reflowed document. The default view is short — claims and
summary — with longer sections opt-in via flags:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-r&lt;&#x2F;code&gt;, &lt;code&gt;--report&lt;&#x2F;code&gt; long-form report&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;-f&lt;&#x2F;code&gt;, &lt;code&gt;--findings&lt;&#x2F;code&gt; findings (with &lt;code&gt;--severity&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;--class&lt;&#x2F;code&gt; to triage)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;-n&lt;&#x2F;code&gt;, &lt;code&gt;--annotations&lt;&#x2F;code&gt; source annotations&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;-d&lt;&#x2F;code&gt;, &lt;code&gt;--deps&lt;&#x2F;code&gt;  declared direct dependencies (with the new per-dependency
narratives, see below)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;-u&lt;&#x2F;code&gt;, &lt;code&gt;--users&lt;&#x2F;code&gt; reverse dependencies (which of your audited packages declare
this one as a dep)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;-a&lt;&#x2F;code&gt;, &lt;code&gt;--all&lt;&#x2F;code&gt; everything at once&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Short examples:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CDD6F4); background-color: light-dark(#EFF1F5, #1E1E2E);&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #89B4FA);font-style: italic;&quot;&gt;openvet&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; info&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; cargo:serde&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #9399B2);font-style: italic;&quot;&gt;                  #&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #9399B2);font-style: italic;&quot;&gt; claims + summary, paged&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #89B4FA);font-style: italic;&quot;&gt;openvet&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; info&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; cargo:serde@1.0.210&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #9399B2);font-style: italic;&quot;&gt;          #&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #9399B2);font-style: italic;&quot;&gt; pin a version&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #89B4FA);font-style: italic;&quot;&gt;openvet&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; info&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;rfd&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; cargo:serde&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #9399B2);font-style: italic;&quot;&gt;             #&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #9399B2);font-style: italic;&quot;&gt; report, findings, deps&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #89B4FA);font-style: italic;&quot;&gt;openvet&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; info&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; cargo:serde&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #9399B2);font-style: italic;&quot;&gt;               #&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #9399B2);font-style: italic;&quot;&gt; the whole picture&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #89B4FA);font-style: italic;&quot;&gt;openvet&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; info&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; cargo:serde&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;-severity&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; high&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #9399B2);font-style: italic;&quot;&gt;  #&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #9399B2);font-style: italic;&quot;&gt; only high-severity findings&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #89B4FA);font-style: italic;&quot;&gt;openvet&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; info&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;u&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; cargo:serde&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #9399B2);font-style: italic;&quot;&gt;               #&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #9399B2);font-style: italic;&quot;&gt; who in my project uses it&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The reverse-dependency mode (&lt;code&gt;-u&lt;&#x2F;code&gt;) is probably the most interesting feature.
If you have a dependency that has a soundness or safety issue, or some security
findings, you need to make a decision: is this dependency okay to use? There
are cases where dependencies have an issue that only surfaces if you use a
specific API, or if you use them on a specific platform.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s say you want to check package &lt;em&gt;frobnication&lt;&#x2F;em&gt;. With the reverse-dependency
mode, OpenVet will scan all of the &lt;em&gt;other&lt;&#x2F;em&gt; dependencies in your project to see
if any of those use &lt;em&gt;frobnication&lt;&#x2F;em&gt;, and surface that to you, with a description
of &lt;em&gt;how&lt;&#x2F;em&gt; the &lt;em&gt;frobnication&lt;&#x2F;em&gt; package is used. This should give you some data to
help you decide if it is safe to use &lt;em&gt;frobnication&lt;&#x2F;em&gt;, even if it has a known
issue.&lt;&#x2F;p&gt;
&lt;p&gt;Here’s an example of what the output looks like, this is the output of &lt;code&gt;openvet info -u cargo:sha1&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;cargo:sha1@0.11.0#sha256:aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Audit 57cbe92225e8 (from xfbs)&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;crypto-impl-correct=true, crypto-impl-safe=true, crypto-impl-tested=true, has-binaries=false,
has-build-exec=false, has-fuzz-tests=false, has-install-exec=false, has-integration-tests=true,
has-property-tests=false, has-unit-tests=false, impl-algorithm=false, impl-concurrency=false,
impl-crypto=true, impl-datastructure=false, impl-interpreter=false, impl-jit=false,
impl-parser=false, impl-protocol=false, is-benign=true, unsafe-documented=true, unsafe-minimal=true,
unsafe-safe=true, unsafe-tested=true, uses-concurrency=false, uses-crypto=false,
uses-environment=false, uses-exec=false, uses-filesystem=false, uses-interpreter=false,
uses-jit=false, uses-network=false, uses-unsafe=true&lt;&#x2F;p&gt;
&lt;p&gt;sha1 0.11.0 is the RustCrypto SHA-1 (FIPS 180-4) implementation with four backends (portable soft,
x86 SHA-NI, ARMv8 SHA1, LoongArch64 asm); the source matches upstream, unsafe is minimal and
properly gated, and KAT vectors plus a 16 MiB feed test exercise all backends. One high-severity
finding: SHA-1 itself is cryptographically broken — unsuitable for any new collision-sensitive use;
the crate exists for legacy interop.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Reverse Dependencies&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;cargo:axum@0.8.9 -&amp;gt; cargo:sha1@0.10&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Optional (ws feature). SHA-1 digest used to compute the Sec-WebSocket-Accept handshake header value
as specified by RFC 6455. Not used for security, only for protocol compliance.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;cargo:rsa@0.9.10 -&amp;gt; cargo:sha1@0.10.5&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Optional dependency, used as a hash function for PKCS#1 v1.5 and OAEP operations in examples and
tests. Not included in the default feature set.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;cargo:ssh-key@0.6.7 -&amp;gt; cargo:sha1@0.10&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Optional dependency, gated behind the sha1 feature. Provides SHA-1 used by the legacy Md5&#x2F;Sha1
fingerprint formats and by the known_hosts HMAC-SHA1 host-name hashing scheme.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;cargo:zip@8.6.0 -&amp;gt; cargo:sha1@0.11&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Optional dependency behind the aes-crypto feature. Provides the SHA-1 hash function as the primitive
for PBKDF2 and HMAC in the WinZip AES key derivation and authentication scheme.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Output paginates through a git-style pager (&lt;code&gt;OPENVET_PAGER&lt;&#x2F;code&gt;, then &lt;code&gt;$PAGER&lt;&#x2F;code&gt;,
then &lt;code&gt;less -FRX&lt;&#x2F;code&gt;) when stdout is a terminal. &lt;code&gt;--no-pager&lt;&#x2F;code&gt; turns it off.&lt;&#x2F;p&gt;
&lt;p&gt;Because the reverse-dependencies mode needs to walk all audits for all
dependencies in your project, there is a slight delay (~100ms) when using that
mode. I am considering using caching to make this faster, but for now I am okay
with the slight inefficiency.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;per-direct-dependency-narratives-on-audits&quot;&gt;Per-direct-dependency narratives on audits
&lt;a class=&quot;zola-anchor&quot; href=&quot;#per-direct-dependency-narratives-on-audits&quot; aria-label=&quot;Anchor link for: per-direct-dependency-narratives-on-audits&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Closely tied to &lt;code&gt;info -u&lt;&#x2F;code&gt; above. Audits now carry an &lt;code&gt;Audit.dependencies&lt;&#x2F;code&gt; field
with the auditor’s prose answer to &lt;em&gt;why does this package need this dep&lt;&#x2F;em&gt; and
&lt;em&gt;how does it use it&lt;&#x2F;em&gt; for each declared direct dependency. The audit body is
what reaches the consumer alongside the claims and findings. So a downstream
consumer triaging a disclosure against dep &lt;code&gt;X&lt;&#x2F;code&gt; can read the entries on his
audited packages that depend on &lt;code&gt;X&lt;&#x2F;code&gt; to judge whether the weakness is reachable
in his composition.&lt;&#x2F;p&gt;
&lt;p&gt;The authoring side is automated where it can be:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;openvet audit new&lt;&#x2F;code&gt; auto-seeds the list from a new &lt;code&gt;openvet-package&lt;&#x2F;code&gt;
extractor — one entry per resolved dependency with an empty description for
the auditor to fill in. The full extractor output (including the entries it
skipped, like git URLs and path deps) is written to &lt;code&gt;dependencies.json&lt;&#x2F;code&gt; next to
&lt;code&gt;audit.pb&lt;&#x2F;code&gt; as an auditor-local sidecar, never signed, never published.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;A new &lt;code&gt;openvet audit dependency&lt;&#x2F;code&gt; subcommand manages the list at authoring
time, letting you list, show, edit dependency metadata in audits.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The linter enforces a non-empty description on every entry, and warns inside a
workspace when the extractor sees a dep the audit doesn’t carry. The server’s
publish handler runs the same description check at publish time, so an audit
with empty descriptions cannot enter a log even by bypassing the CLI.&lt;&#x2F;p&gt;
&lt;p&gt;The extractor itself is a meaningful addition on its own: every shipping
registry adapter (cargo, npm, pypi, rubygems, go) now parses its native
manifest and surfaces direct dependencies with their registry, package,
requirement, scope (runtime vs build), and an &lt;code&gt;optional&lt;&#x2F;code&gt; flag.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;vcs-provenance-on-audits&quot;&gt;VCS provenance on audits
&lt;a class=&quot;zola-anchor&quot; href=&quot;#vcs-provenance-on-audits&quot; aria-label=&quot;Anchor link for: vcs-provenance-on-audits&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Sibling to the dependency narratives, audits also gained an optional
&lt;code&gt;Audit.vcs&lt;&#x2F;code&gt; field that records where the audited package’s source is hosted in
version control: clone URL, optional commit hash, optional subdirectory for
monorepo packages. Registry-published VCS metadata is sometimes missing,
sometimes wrong, and sometimes unreachable; this field captures what &lt;em&gt;actually
worked&lt;&#x2F;em&gt; so a downstream consumer can navigate to the same source the auditor
reviewed.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;openvet audit new&lt;&#x2F;code&gt; records what cloned successfully. It has some retry and
fixup logic, rewriting some &lt;code&gt;git:&#x2F;&#x2F;&lt;&#x2F;code&gt; and &lt;code&gt;ssh:&#x2F;&#x2F;&lt;&#x2F;code&gt; URLs to &lt;code&gt;https:&#x2F;&#x2F;&lt;&#x2F;code&gt;. Many
registries don’t validate the git URLs, and some of them are broken. We fix
them on a best-effort basis, but sometimes it needs manual work to discover the
proper VCS URL.&lt;&#x2F;p&gt;
&lt;p&gt;A new &lt;code&gt;openvet audit vcs&lt;&#x2F;code&gt; subcommand inspects, sets, or clears the field.
&lt;code&gt;--commit&lt;&#x2F;code&gt; accepts only 40-character SHA-1 or 64-character SHA-256 hex strings:
tag names and branch names are rejected, because a tag-pinned audit can
silently start covering different source if someone moves the tag.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;openvet audit check&lt;&#x2F;code&gt; warns when a recorded provenance has no commit. A Git URL
without a pinned revision means the repo HEAD could move under the audit.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;project-root-auto-discovery&quot;&gt;Project root auto-discovery
&lt;a class=&quot;zola-anchor&quot; href=&quot;#project-root-auto-discovery&quot; aria-label=&quot;Anchor link for: project-root-auto-discovery&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;OpenVet now discovers the project root by walking up the filesystem and
locating the &lt;code&gt;openvet.toml&lt;&#x2F;code&gt; file, similar to how &lt;code&gt;git&lt;&#x2F;code&gt; and &lt;code&gt;cargo&lt;&#x2F;code&gt; operate.&lt;&#x2F;p&gt;
&lt;p&gt;Previously, commands only worked in the root folder of projects, and I had
separate &lt;code&gt;--config&lt;&#x2F;code&gt; and &lt;code&gt;--lockfile&lt;&#x2F;code&gt; flags. Not because that is the right way
to build it, but I needed a way to override these for unit tests.  I’ve now
done it properly, with either the parent-traversal for automatic discovery, or
a &lt;code&gt;--project&lt;&#x2F;code&gt; flag that lets you point at the project root. The
&lt;code&gt;OPENVET_PROJECT&lt;&#x2F;code&gt; environment variable can also be used to override this.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;other-things-worth-a-quick-mention&quot;&gt;Other things worth a quick mention
&lt;a class=&quot;zola-anchor&quot; href=&quot;#other-things-worth-a-quick-mention&quot; aria-label=&quot;Anchor link for: other-things-worth-a-quick-mention&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;openvet check&lt;&#x2F;code&gt; runs ~3.5× faster on a cold cache.&lt;&#x2F;strong&gt; Per-subject audit
lookups and the up-front head prefetch now fan out across logs and subjects
with bounded 16-way concurrency. Output order is preserved, so the listing
stays diff-stable across runs.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;openvet check&lt;&#x2F;code&gt; explains not-asserted requirements.&lt;&#x2F;strong&gt; When a requirement
neither passes nor is contradicted because the claims it rests on were never
asserted, &lt;code&gt;check&lt;&#x2F;code&gt; now prints the same per-audit expression tree it already
shows for contradictions. The unevaluated claim is visible in the tree rather
than collapsed to a bare &lt;code&gt;not asserted&lt;&#x2F;code&gt; line.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;openvet update &amp;lt;log&amp;gt;&lt;&#x2F;code&gt; accepts a single log name.&lt;&#x2F;strong&gt; Refreshes only the
named log’s pin and leaves every other entry in &lt;code&gt;openvet.lock&lt;&#x2F;code&gt; untouched. The
bare &lt;code&gt;openvet update&lt;&#x2F;code&gt; still refreshes every configured log.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;File annotations carry a per-file content hash.&lt;&#x2F;strong&gt; A new required
&lt;code&gt;FileTarget.content_hash&lt;&#x2F;code&gt; field (32-byte BLAKE2b-256) binds each file
annotation’s body and per-line ranges to the exact byte sequence the auditor
reviewed at that path. The audit’s &lt;code&gt;subject.hash&lt;&#x2F;code&gt; already commits to the
archive as a whole, but per-annotation hashes let a consumer detect (and
&lt;code&gt;openvet audit check&lt;&#x2F;code&gt; flag) annotations whose target file has drifted under
them without re-deriving the manifest.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Audit subject binding is now enforced at publish.&lt;&#x2F;strong&gt; A new spec invariant
requires that &lt;code&gt;InsertAudit&lt;&#x2F;code&gt; and &lt;code&gt;UpdateAudit&lt;&#x2F;code&gt; operations carry the same
&lt;code&gt;subject&lt;&#x2F;code&gt; as the embedded audit’s &lt;code&gt;audit.subject&lt;&#x2F;code&gt;. This closes a hole where a
hostile client could put an audit for one subject under a different tree key,
misleading any consumer querying for that key.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Commit timestamp monotonicity and admission window.&lt;&#x2F;strong&gt; Every published
commit’s &lt;code&gt;timestamp&lt;&#x2F;code&gt; must be greater than or equal to its predecessor’s, and
the server rejects commits whose &lt;code&gt;timestamp&lt;&#x2F;code&gt; is more than 15 minutes away from
its wall clock. Publishers whose system clock is badly off will see a &lt;code&gt;400 Bad Request&lt;&#x2F;code&gt;, fix by running NTP. This makes KeyState revocation more easily
verifiable: monotonic time means commits cannot be backdated, which allows
key expiry to work correctly.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;wire-format-breaks&quot;&gt;Wire-format breaks
&lt;a class=&quot;zola-anchor&quot; href=&quot;#wire-format-breaks&quot; aria-label=&quot;Anchor link for: wire-format-breaks&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;0.6.0 is another breaking pre-1.0 release. If you have audits
authored against 0.5.0, the things to know:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;FileTarget.content_hash&lt;&#x2F;code&gt; is now required. Audits produced by earlier
releases (where file annotations had no &lt;code&gt;content_hash&lt;&#x2F;code&gt;) fail to decode. The
path forward is to re-run &lt;code&gt;openvet audit annotate &amp;lt;path&amp;gt;&lt;&#x2F;code&gt; over each file
annotation, which reads the manifest, validates against the on-disk contents,
and stamps the content hash onto the annotation.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;New optional fields (&lt;code&gt;Audit.dependencies&lt;&#x2F;code&gt;, &lt;code&gt;Audit.vcs&lt;&#x2F;code&gt;) decode as empty &#x2F;
&lt;code&gt;None&lt;&#x2F;code&gt; on older blobs and are ignored by older readers, so they don’t break
wire compatibility.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;A handful of source-level renames in the protocol crates (&lt;code&gt;CommitSigned&lt;&#x2F;code&gt; →
&lt;code&gt;CommitEnvelope&lt;&#x2F;code&gt;, &lt;code&gt;AuditSigned&lt;&#x2F;code&gt; → &lt;code&gt;AuditEnvelope&lt;&#x2F;code&gt;, &lt;code&gt;Publish&lt;&#x2F;code&gt; → &lt;code&gt;Bundle&lt;&#x2F;code&gt;,
&lt;code&gt;apply&lt;&#x2F;code&gt; → &lt;code&gt;build_commit&lt;&#x2F;code&gt;, etc.). Wire bytes are unchanged for these; only Rust
crate consumers see the rename. If you depend on &lt;code&gt;openvet-proto&lt;&#x2F;code&gt; or
&lt;code&gt;openvet-log&lt;&#x2F;code&gt; directly, your imports need updating.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The full per-bullet migration notes live in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;openvet-org&#x2F;openvet&#x2F;-&#x2F;blob&#x2F;master&#x2F;CHANGELOG.md&quot;&gt;&lt;code&gt;CHANGELOG.md&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; in the
CLI repo.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-i-m-working-on-next&quot;&gt;What I’m working on next
&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-i-m-working-on-next&quot; aria-label=&quot;Anchor link for: what-i-m-working-on-next&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Some of the ideas that I listed in the 0.5.0 release I still haven’t gotten to,
but they are still on the roadmap. There is a long list of features I still
need to design, implement and&#x2F;or test.&lt;&#x2F;p&gt;
&lt;p&gt;One feedback that I got from talking to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tmpfs&quot;&gt;@muji&lt;&#x2F;a&gt;
(thanks for taking the time!) was that the per-project config that OpenVet uses
may not work well for all users. He suggested that it should support a way to
use OpenVet as a global proxy for package registry operations. This is
something that I need to think through, design and test. There are different
ways this could be implemented: a proxy running on your local machine, or a
hosted proxy that organizations can use to configure an organization-wide
policy and overrides.&lt;&#x2F;p&gt;
&lt;p&gt;I see different mechanisms with different goals here:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;An organization-wide or system-wide proxy allows you to set a policy that
applies to &lt;em&gt;all&lt;&#x2F;em&gt; projects. You can use this to enforce an organization-wide
policy, like a baseline that all projects need to use, or for individual
users it could be used to set a system-wide policy with a minimum set of
requirements.&lt;&#x2F;li&gt;
&lt;li&gt;A project configuration can be used to set more specific requirements. You
may not need this at all. Or this can be used to set more granular
requirements, depending on the needs of a project.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The over-arching goal of OpenVet is to make it as easy as possible for both
individuals and organizations to adopt it. So getting this right matters a lot,
and it requires me to do a lot of prototyping, testing, and talking to people
to get some outside perspectives.&lt;&#x2F;p&gt;
&lt;p&gt;There are some alternative implementation approaches that I want to test out
for the OpenVet guard mode that don’t involve an alias, so that tooling (IDEs)
can use them too. I know roughly what those would look like, but I haven’t
implemented them yet.&lt;&#x2F;p&gt;
&lt;p&gt;I put together some thoughts on the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;blog.openvet.org&#x2F;posts&#x2F;licensing-of-audits&#x2F;&quot;&gt;Licensing of audits hosted on the
OpenVet registry&lt;&#x2F;a&gt;,
which I have not implemented in this version yet, but will land in the
next version.&lt;&#x2F;p&gt;
&lt;p&gt;I’ll keep posting here as these land.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;find-it-report-it&quot;&gt;Find it &#x2F; report it
&lt;a class=&quot;zola-anchor&quot; href=&quot;#find-it-report-it&quot; aria-label=&quot;Anchor link for: find-it-report-it&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Install:&lt;&#x2F;strong&gt; &lt;code&gt;cargo install openvet&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Source:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;openvet-org&#x2F;openvet&quot;&gt;gitlab.com&#x2F;openvet-org&#x2F;openvet&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Docs:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.openvet.org&quot;&gt;docs.openvet.org&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Changelog:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;openvet-org&#x2F;openvet&#x2F;-&#x2F;blob&#x2F;master&#x2F;CHANGELOG.md&quot;&gt;&lt;code&gt;CHANGELOG.md&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Issues:&lt;&#x2F;strong&gt; the GitLab tracker on the CLI repo. Issues and MRs
are very much welcome — this is still a one-person project and
any extra eyes help.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you’d rather reach me directly, my GitHub profile has the
contact info.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Licensing of audits hosted on the OpenVet Registry</title>
        <published>2026-05-30T00:00:00+00:00</published>
        <updated>2026-05-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.openvet.org/posts/licensing-of-audits/"/>
        <id>https://blog.openvet.org/posts/licensing-of-audits/</id>
        
        <content type="html" xml:base="https://blog.openvet.org/posts/licensing-of-audits/">&lt;p&gt;The OpenVet Registry hosts audits that its users produce, and the whole point
is that those audits are freely accessible to anyone who wants to consume them.
The whole reason I am building the OpenVet Registry, that I am hosting it, and
that I am licensing it as AGPL is that I think audits should be a public good,
and supply-chain security should not be a subscription model.&lt;&#x2F;p&gt;
&lt;p&gt;“Freely accessible” sounds nice in a marketing sentence, but it is not a
legally meaningful statement. If we want consumers to actually be able to &lt;em&gt;use&lt;&#x2F;em&gt;
the audits they download — read them, depend on them, redistribute them, build
on top of them — we need to encode what “freely accessible” means in terms that
copyright law understands. That means licensing.&lt;&#x2F;p&gt;
&lt;p&gt;This post is about how I’m planning to approach that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;audits-are-original-work&quot;&gt;Audits are original work
&lt;a class=&quot;zola-anchor&quot; href=&quot;#audits-are-original-work&quot; aria-label=&quot;Anchor link for: audits-are-original-work&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;One thing that makes the licensing question tractable is that audits, as
OpenVet defines them, are completely original work. They do not embed the
code they describe. An audit can annotate a piece of code, but the annotation
refers to the source by file path and line number — it does not include the
source itself. Findings, claims, and the report text are all written by the
auditor.&lt;&#x2F;p&gt;
&lt;p&gt;This matters because it means a single audit can be covered by a single
license. There is no embedded third-party material that needs to be tracked
or licensed separately. The auditor wrote the audit, the auditor owns the
copyright, and the auditor gets to pick the license.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-license-attribute&quot;&gt;The license attribute
&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-license-attribute&quot; aria-label=&quot;Anchor link for: the-license-attribute&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Audits already have a mechanism for carrying meta-data about the audit
itself (as opposed to data about the thing being audited). We call these
&lt;em&gt;attributes&lt;&#x2F;em&gt;. The natural place to encode the license is as one of these
attributes, set to an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;spdx.org&#x2F;licenses&#x2F;&quot;&gt;SPDX&lt;&#x2F;a&gt; license identifier.&lt;&#x2F;p&gt;
&lt;p&gt;That answers the &lt;em&gt;how&lt;&#x2F;em&gt;. The harder questions are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;What licenses are reasonable for audits to be published under?&lt;&#x2F;li&gt;
&lt;li&gt;Should the registry require specific licenses, or accept anything?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;what-licenses-make-sense&quot;&gt;What licenses make sense
&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-licenses-make-sense&quot; aria-label=&quot;Anchor link for: what-licenses-make-sense&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;The registry only exists because audits get more useful when they are
shared. If somebody publishes an audit under a license that prevents others
from using it — anything non-commercial, anything that forbids
redistribution, anything bespoke that consumers need a lawyer to read —
there is no point in hosting that audit on a public registry. The whole
value proposition collapses.&lt;&#x2F;p&gt;
&lt;p&gt;So the set of acceptable licenses needs to be narrow enough that consumers
can use audits without thinking about it, and wide enough to cover the
reasonable preferences authors have. My argument is that, at least for now,
that set should be exactly two licenses: &lt;strong&gt;CC0&lt;&#x2F;strong&gt; and &lt;strong&gt;CC-BY-4.0&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Both are well-understood, both are permissive enough to be useful, and they
differ in exactly one meaningful way:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CC0&lt;&#x2F;strong&gt; is effectively a public-domain dedication. The author gives up
their rights and the work can be used by anyone for any purpose with no
obligations attached.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;CC-BY-4.0&lt;&#x2F;strong&gt; allows the same uses, but requires attribution: if you
redistribute or build on the work, you have to credit the original author.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Either license makes audits useful in the way I want them to be. A consumer
can pick up an audit, improve it, and re-publish the improved version. If
the original was CC0, no strings attached. If it was CC-BY-4.0, the
improved version needs to credit the original author. Both of these are
reasonable workflows for a collaborative auditing ecosystem.&lt;&#x2F;p&gt;
&lt;p&gt;This is not a novel choice. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rustsec.org&#x2F;&quot;&gt;RustSec&lt;&#x2F;a&gt; uses the same pair: their own
original advisory content is published under CC0, and they also ingest
GitHub advisories, which are CC-BY-4.0. The intersection of “useful for a
public security database” and “common enough that people will actually
pick it” lands in roughly the same place for both of us.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-enforce-it-and-why-now&quot;&gt;Why enforce it, and why now
&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-enforce-it-and-why-now&quot; aria-label=&quot;Anchor link for: why-enforce-it-and-why-now&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;The registry could accept any license and let consumers sort it out. I
think that would be a mistake.&lt;&#x2F;p&gt;
&lt;p&gt;If we do not enforce a license attribute from the start, we end up with a
collection of audits with unclear licensing. Some will have no license at
all (which, under default copyright law, means consumers cannot legally
redistribute them). Some will have hand-rolled licenses that consumers
have to read individually. Cleaning that up after the fact is much harder
than getting it right from day one — you have to track down every author
and ask them to re-license, and any author who has gone silent leaves a
permanent hole in the dataset.&lt;&#x2F;p&gt;
&lt;p&gt;It is always possible to &lt;em&gt;relax&lt;&#x2F;em&gt; the requirement later. If, in a year, it
turns out that there is a strong case for adding a third license to the
allow-list, that is a one-line registry change and existing audits remain
valid. Going the other direction — tightening the rules after the fact —
would mean breaking or removing existing audits, which is the kind of
thing that erodes trust in a registry.&lt;&#x2F;p&gt;
&lt;p&gt;So: enforce a license attribute, and limit it to CC0 or CC-BY-4.0.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;cross-posting-from-other-sources&quot;&gt;Cross-posting from other sources
&lt;a class=&quot;zola-anchor&quot; href=&quot;#cross-posting-from-other-sources&quot; aria-label=&quot;Anchor link for: cross-posting-from-other-sources&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;One of the use cases I want this scheme to support is &lt;em&gt;cross-posting&lt;&#x2F;em&gt;
existing vulnerability data into the audit format. I would like to be able
to write a tool that ingests, say, RustSec advisories, and generates
(either automatically or with human review) an audit whose findings and
claims encode what that advisory says. The same goes for GitHub Security
Advisories, and for CVE data.&lt;&#x2F;p&gt;
&lt;p&gt;The CC0 &#x2F; CC-BY-4.0 allow-list covers this cleanly for the sources I care
about most:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;RustSec&lt;&#x2F;strong&gt; advisories are CC0, so they can be republished under either
CC0 or CC-BY-4.0 with no friction.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;GHSA&lt;&#x2F;strong&gt; entries are CC-BY-4.0, so a cross-posted audit can carry that
license and the attribution requirement is satisfied by crediting the
original advisory.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;CVE&lt;&#x2F;strong&gt; data is more nuanced. The structured facts (affected versions,
identifiers, references) are not copyrightable on their own, so summarizing
them in our own words produces an original audit that we can license
freely. Verbatim copying of CVE descriptions would fall under the CVE
program’s terms of use, which is a special case I do not want to add to
the allow-list.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In other words, the policy is “audits in the registry are CC0 or
CC-BY-4.0”, and the cross-posting workflow has to produce audits that fit
inside that policy. Any source whose terms do not fit gets summarized in
the audit author’s own words, or it does not get cross-posted.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;implementation&quot;&gt;Implementation
&lt;a class=&quot;zola-anchor&quot; href=&quot;#implementation&quot; aria-label=&quot;Anchor link for: implementation&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;The default workflow to create audits uses the command-line interface of
OpenVet. I am changing this tooling to insert a default license of &lt;strong&gt;CC0&lt;&#x2F;strong&gt;,
with a message stating it has done so. The attribute can be overridden using
the audit commands, like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CDD6F4); background-color: light-dark(#EFF1F5, #1E1E2E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;openvet audit attribute license --set CC-BY-4.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In the registry, I am implementing a check to require a license attribute to be
present, and for it to be one of the licenses in the allow-list. OpenVet
does not force you to use any of these licenses. Only the registry requires
&lt;em&gt;published&lt;&#x2F;em&gt; audits to have one of these two licenses.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary
&lt;a class=&quot;zola-anchor&quot; href=&quot;#summary&quot; aria-label=&quot;Anchor link for: summary&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Audits are original work, so a single license per audit is enough.&lt;&#x2F;li&gt;
&lt;li&gt;The license will be encoded as a &lt;code&gt;license&lt;&#x2F;code&gt; attribute on the audit, using an
SPDX identifier.&lt;&#x2F;li&gt;
&lt;li&gt;The registry will only accept CC0 or CC-BY-4.0.&lt;&#x2F;li&gt;
&lt;li&gt;Enforcing this from the start avoids a permanent licensing-mess in the
dataset; the allow-list can always be relaxed later.&lt;&#x2F;li&gt;
&lt;li&gt;The same allow-list happens to make it straightforward to cross-post
audits derived from RustSec, GHSA, and (with care) CVE data.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Supply-Chain Update #1</title>
        <published>2026-05-23T00:00:00+00:00</published>
        <updated>2026-05-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.openvet.org/posts/supply-chain-update-1/"/>
        <id>https://blog.openvet.org/posts/supply-chain-update-1/</id>
        
        <content type="html" xml:base="https://blog.openvet.org/posts/supply-chain-update-1/">&lt;p&gt;Updates on supply-chain security, vulnerabilities, attacks and mitigations.
I try to summarize interesting articles I come across, and post these,
targeting a biweekly cadence.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;vulnerabilities&quot;&gt;Vulnerabilities
&lt;a class=&quot;zola-anchor&quot; href=&quot;#vulnerabilities&quot; aria-label=&quot;Anchor link for: vulnerabilities&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;cve-2026-46529-10-year-old-rce-in-linux-pdf-viewer-xreader-evince-atril&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;medeiros.zip&#x2F;posts&#x2F;CVE-2026-46529-evince&quot;&gt;CVE-2026-46529: 10-year-old RCE in Linux PDF Viewer (XReader&#x2F;Evince&#x2F;Atril)&lt;&#x2F;a&gt;
&lt;a class=&quot;zola-anchor&quot; href=&quot;#cve-2026-46529-10-year-old-rce-in-linux-pdf-viewer-xreader-evince-atril&quot; aria-label=&quot;Anchor link for: cve-2026-46529-10-year-old-rce-in-linux-pdf-viewer-xreader-evince-atril&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Claude Code helped find a bug in several Linux PDF viewers
(Evince&#x2F;Atril&#x2F;XReader) that use the Evince codebase for rendering PDFs. The
author tried fuzzing, which did not yield results. Looking through the codebase
for suspicious code revealed the &lt;code&gt;ev_spawn&lt;&#x2F;code&gt; function, which is used to
implement the &lt;code&gt;&#x2F;GoToR&lt;&#x2F;code&gt; (go-to-remote) PDF function, which is used to open
external documents. This function is implemented using an &lt;code&gt;exec()&lt;&#x2F;code&gt; call and is
susceptible to injection attacks, because injected parameters are not properly
shell-escaped.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;supply-chain-attacks&quot;&gt;Supply-Chain Attacks
&lt;a class=&quot;zola-anchor&quot; href=&quot;#supply-chain-attacks&quot; aria-label=&quot;Anchor link for: supply-chain-attacks&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;investigating-unauthorized-access-to-github-owned-repositories&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.blog&#x2F;security&#x2F;investigating-unauthorized-access-to-githubs-internal-repositories&#x2F;&quot;&gt;Investigating unauthorized access to GitHub-owned repositories&lt;&#x2F;a&gt;
&lt;a class=&quot;zola-anchor&quot; href=&quot;#investigating-unauthorized-access-to-github-owned-repositories&quot; aria-label=&quot;Anchor link for: investigating-unauthorized-access-to-github-owned-repositories&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;GitHub was hit with a supply-chain attack involving a malicious VS Code
extension. The attackers were able to exfiltrate the contents of 3800 internal
git repositories used by GitHub.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;megalodon-mass-github-repo-backdooring-via-ci-workflows&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;safedep.io&#x2F;megalodon-mass-github-repo-backdooring-ci-workflows&#x2F;&quot;&gt;Megalodon: Mass GitHub Repo Backdooring via CI Workflows&lt;&#x2F;a&gt;
&lt;a class=&quot;zola-anchor&quot; href=&quot;#megalodon-mass-github-repo-backdooring-via-ci-workflows&quot; aria-label=&quot;Anchor link for: megalodon-mass-github-repo-backdooring-via-ci-workflows&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;5,561 GitHub repositories received malicious workflow commits which exfiltrate
CI environment variables and various credentials (including AWS, GCP, SSH,
databases, APIs, GitHub Actions). The commits which added the malicious GitHub
workflow configurations were pushed straight to master with no pull-request,
likely using a compromised personal access token or deploy key.&lt;&#x2F;p&gt;
&lt;p&gt;This attack does not affect downstream consumers of the affected packages, it
only works to exfiltrate secrets from the CI pipelines, which would later give
attackers the means to further compromise those affected packages.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;teampcp-s-mini-shai-hulud-is-back-a-self-spreading-supply-chain-attack-compromises-tanstack-npm-packages&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.stepsecurity.io&#x2F;blog&#x2F;mini-shai-hulud-is-back-a-self-spreading-supply-chain-attack-hits-the-npm-ecosystem&quot;&gt;TeamPCP’s Mini Shai-Hulud Is Back: A Self-Spreading Supply Chain Attack Compromises TanStack npm Packages&lt;&#x2F;a&gt;
&lt;a class=&quot;zola-anchor&quot; href=&quot;#teampcp-s-mini-shai-hulud-is-back-a-self-spreading-supply-chain-attack-compromises-tanstack-npm-packages&quot; aria-label=&quot;Anchor link for: teampcp-s-mini-shai-hulud-is-back-a-self-spreading-supply-chain-attack-compromises-tanstack-npm-packages&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;To quote from the article:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mini Shai-Hulud is a true worm: after stealing credentials from one CI&#x2F;CD
pipeline, it enumerates every package that maintainer controls and publishes
infected versions of each. The 2.3 MB obfuscated payload reads GitHub Actions
runner process memory to extract every secret, harvests credentials from over
100 file paths spanning cloud providers, cryptocurrency wallets, AI tools,
and messaging apps, and installs persistence hooks in Claude Code, VS Code,
and OS-level services that survive reboots. Stolen data is encrypted and
exfiltrated through the Session Protocol CDN and GitHub’s own GraphQL API,
where dead-drop commits are authored as claude@users.noreply.github.com and
disguised with Dependabot-style branch names drawn from Frank Herbert’s Dune
universe.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;grafana-labs-security-update-latest-on-tanstack-npm-supply-chain-ransomware-incident&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;grafana.com&#x2F;blog&#x2F;grafana-labs-security-update-latest-on-tanstack-npm-supply-chain-ransomware-incident&#x2F;&quot;&gt;Grafana Labs security update: Latest on TanStack npm supply chain ransomware incident&lt;&#x2F;a&gt;
&lt;a class=&quot;zola-anchor&quot; href=&quot;#grafana-labs-security-update-latest-on-tanstack-npm-supply-chain-ransomware-incident&quot; aria-label=&quot;Anchor link for: grafana-labs-security-update-latest-on-tanstack-npm-supply-chain-ransomware-incident&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Grafana was affected by the &lt;em&gt;TanStack&lt;&#x2F;em&gt; NPM supply-chain ransomware event (also
called the &lt;em&gt;Mini Shai-Hulud&lt;&#x2F;em&gt; attack). According to them, the attack did not
affect customer production systems or the Grafana Cloud platform. Business
contact names and email addresses that would be exchanged in a professional
relationship context were leaked. Their reaction is to harden their CI and CD
pipelines.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;reverse-engineering-android-malware-with-claude-code&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;zanestjohn.com&#x2F;blog&#x2F;reing-with-claude-code&quot;&gt;Reverse engineering android malware with claude code&lt;&#x2F;a&gt;
&lt;a class=&quot;zola-anchor&quot; href=&quot;#reverse-engineering-android-malware-with-claude-code&quot; aria-label=&quot;Anchor link for: reverse-engineering-android-malware-with-claude-code&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Not really &lt;em&gt;software&lt;&#x2F;em&gt; supply-chain security, but nevertheless interesting:
cheap gadgets bought from Chinese vendors come pre-loaded with malware.
Oftentimes, such hardware is used to implement &lt;em&gt;residential VPNs&lt;&#x2F;em&gt;: they allow
other people to use your internet connection (and thus your IP address) to
access the internet. These types of VPNs are popular because residential IP
addresses often bypass detection systems for automated requests. This article
contains a writeup examining an LED projector, figuring out how the malware is
pre-loaded, and analyzing how it works.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;security&quot;&gt;Security
&lt;a class=&quot;zola-anchor&quot; href=&quot;#security&quot; aria-label=&quot;Anchor link for: security&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;apple-a-blueprint-for-formal-verification-of-apple-corecrypto&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;security.apple.com&#x2F;blog&#x2F;formal-verification-corecrypto&#x2F;&quot;&gt;Apple: A blueprint for formal verification of Apple corecrypto&lt;&#x2F;a&gt;
&lt;a class=&quot;zola-anchor&quot; href=&quot;#apple-a-blueprint-for-formal-verification-of-apple-corecrypto&quot; aria-label=&quot;Anchor link for: apple-a-blueprint-for-formal-verification-of-apple-corecrypto&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Apple is formally verifying their CoreCrypto framework, which implements post-quantum
cryptography. They are using Cryptol and Isabelle to do so. Their verification efforts
have surfaced real correctness issues.&lt;&#x2F;p&gt;
&lt;p&gt;CoreCrypto ships on billions of Apple devices, so a verified implementation
removes a class of supply-chain risk from every system that depends on it.
Verifying post-quantum primitives is particularly high-leverage: the algorithms
are new and structurally complex, and implementation bugs are easy to miss in
normal review or testing. The most useful signal from Apple’s writeup is that
the work already surfaced real correctness issues — i.e. formal methods caught
things conventional QA didn’t. It also fits a broader pattern this issue
touches on (AWS’s s2n-tls, Jane Street’s growing interest per the Minsky talk
below): formal verification is moving from “academic” to “practical at
production scale.”&lt;&#x2F;p&gt;
&lt;h3 id=&quot;cloudflare-project-glasswing-what-mythos-showed-us&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blog.cloudflare.com&#x2F;cyber-frontier-models&#x2F;&quot;&gt;Cloudflare: Project Glasswing: what Mythos showed us&lt;&#x2F;a&gt;
&lt;a class=&quot;zola-anchor&quot; href=&quot;#cloudflare-project-glasswing-what-mythos-showed-us&quot; aria-label=&quot;Anchor link for: cloudflare-project-glasswing-what-mythos-showed-us&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Cloudflare talks about finding security issues with Mythos in their
participation in Project Glasswing. They explain that in general, LLMs are
useful at finding issues in code, but they work best when given direction and
context. They also explain that the output can be very noisy, with
false-positives being included in the output, and with the models not being
able to express accurate certainty (hedging). Cloudflare explains the harness
they use to refine the output.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-first-cve-wave-signs-that-ai-assisted-vulnerability-discovery-is-reshaping-disclosure-volumes&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.vulncheck.com&#x2F;blog&#x2F;ai-assisted-vulnerability-discovery&quot;&gt;The First CVE Wave: Signs That AI-Assisted Vulnerability Discovery Is Reshaping Disclosure Volumes&lt;&#x2F;a&gt;
&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-first-cve-wave-signs-that-ai-assisted-vulnerability-discovery-is-reshaping-disclosure-volumes&quot; aria-label=&quot;Anchor link for: the-first-cve-wave-signs-that-ai-assisted-vulnerability-discovery-is-reshaping-disclosure-volumes&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;The article claims that CVE disclosure volumes are up sharply year-to-date
(YTD) across several software suppliers, ranging from 100% to 500% increase
compared to last year. Much of the growth of discovery of vulnerabilities can
be attributed to the availability of LLMs.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The evidence appears to point to emerging AI models that have enabled
software suppliers and security researchers to discover and remediate
vulnerabilities that would have likely gone overlooked otherwise.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The article walks through several projects and companies, examining their
vulnerability disclosures, closing with some thoughts about how this impacts
software security:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;What’s less clear is whether these volumes will be sustained, or whether this
is a temporary surge as better AI models are pointed at different code bases
and new models continue to surface vulnerabilities.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I think one graphic, produced by Mozilla, stands out and does a good job summarizing
the situation:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;hacks.mozilla.org&#x2F;wp-content&#x2F;uploads&#x2F;2026&#x2F;05&#x2F;security-bug-fixes-1-scaled.png&quot; alt=&quot;Mozilla security bug fixes by month&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;turso-how-we-used-quint-to-find-over-10-bugs-in-sqlite-while-hardening-turso&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;turso.tech&#x2F;blog&#x2F;how-we-used-quint-to-find-over-10-bugs-in-sqlite&quot;&gt;Turso: How we used Quint to find over 10 bugs in SQLite while hardening Turso&lt;&#x2F;a&gt;
&lt;a class=&quot;zola-anchor&quot; href=&quot;#turso-how-we-used-quint-to-find-over-10-bugs-in-sqlite-while-hardening-turso&quot; aria-label=&quot;Anchor link for: turso-how-we-used-quint-to-find-over-10-bugs-in-sqlite-while-hardening-turso&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;informalsystems&#x2F;quint&quot;&gt;Quint&lt;&#x2F;a&gt; is a specification language in the TLA+ family for modelling state
machines and their transitions. From a spec you can model-check invariants and
generate traces that explore behaviour conventional tests rarely reach.&lt;&#x2F;p&gt;
&lt;p&gt;At Turso, they used this to build a specification of the SQLite API, generated
a list of traces, translated those into programs exercising both SQLite’s C API
and &lt;em&gt;Turso&lt;&#x2F;em&gt; (the company’s SQLite-compatible re-implementation in Rust). In
doing so, they were able to find and fix over 10 bugs in SQLite, in addition to
improving Turso. To quote:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Formal methods, and Quint in particular, were able to explore parts of the
specification that the Turso simulator previously could not. The general
concepts are similar, but instead of generating a random sequence of SQL
statements and enforcing system-level properties, Quint generates a list of
traces that can then be translated into any substrate we want.&lt;&#x2F;p&gt;
&lt;p&gt;In this case, we translated Quint traces into a C program that interacted
with the C API. Turso got better thanks to this endeavor. So did SQLite: at
least 10 bugs were found, reported, and fixed.&lt;&#x2F;p&gt;
&lt;p&gt;SQLite is a magnificent piece of software that has been around and thoroughly
tested for decades. And yet, bugs were lurking that previous disciplines did
not catch. Formal methods did.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;mitigations&quot;&gt;Mitigations
&lt;a class=&quot;zola-anchor&quot; href=&quot;#mitigations&quot; aria-label=&quot;Anchor link for: mitigations&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;github-staged-publishing-and-new-install-time-controls-for-npm&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.blog&#x2F;changelog&#x2F;2026-05-22-staged-publishing-and-new-install-time-controls-for-npm&#x2F;&quot;&gt;GitHub: Staged publishing and new install-time controls for npm&lt;&#x2F;a&gt;
&lt;a class=&quot;zola-anchor&quot; href=&quot;#github-staged-publishing-and-new-install-time-controls-for-npm&quot; aria-label=&quot;Anchor link for: github-staged-publishing-and-new-install-time-controls-for-npm&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Staged publishing is available for NPM. With this opt-in change, pushes of new
versions of packages (commonly originated from workflow runs) are not live
immediately. Instead, they are pushed into a publish queue and need to be
manually accepted. Also documented on the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.npmjs.com&#x2F;staged-publishing&quot;&gt;NPM docs&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dependency-cooldowns-are-unfair-we-should-use-phased-rollouts-instead&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;illegalcode.net&#x2F;rfcs&#x2F;phased_rollouts.html&quot;&gt;Dependency cooldowns are unfair; we should use phased rollouts instead&lt;&#x2F;a&gt;
&lt;a class=&quot;zola-anchor&quot; href=&quot;#dependency-cooldowns-are-unfair-we-should-use-phased-rollouts-instead&quot; aria-label=&quot;Anchor link for: dependency-cooldowns-are-unfair-we-should-use-phased-rollouts-instead&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Scott Robinson argues that instead of dependency cooldowns (where downstream
projects don’t update their dependencies to the latest versions, but the latest
versions which have been released for at least &lt;em&gt;N&lt;&#x2F;em&gt; days), &lt;em&gt;phased rollouts&lt;&#x2F;em&gt;
should be used.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I propose that instead of “everyone waits N days,” package managers should
deterministically map projects into a rollout window based on stable inputs:
a project-specific identifier, package name, version, and artifact digest.
The result is a globally distributed adoption curve rather than
timezone-based canaries.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;culture&quot;&gt;Culture
&lt;a class=&quot;zola-anchor&quot; href=&quot;#culture&quot; aria-label=&quot;Anchor link for: culture&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;open-source-resistance-keep-oss-alive-on-company-time&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ossresistance.com&quot;&gt;Open-Source Resistance: Keep OSS alive on company time&lt;&#x2F;a&gt;
&lt;a class=&quot;zola-anchor&quot; href=&quot;#open-source-resistance-keep-oss-alive-on-company-time&quot; aria-label=&quot;Anchor link for: open-source-resistance-keep-oss-alive-on-company-time&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;A “radical” proposal for employees of tech companies to help maintain
the open-source infrastructure the company depends on, on company time:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Open Source software your work relies on is maintained by others who also
work. Evenings and weekends alone cannot maintain it. Stop begging massively
profitable companies for time to fix things. Join the Open Source Resistance:
quietly, professionally and on company time, doing what needs done.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This relates to supply-chain security because this idea formalizes something
that is already the case: the dependencies (supply-chain) are &lt;em&gt;part&lt;&#x2F;em&gt; of the
product. Issues in dependencies lead to issues in company infrastructure or the
product.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;now-more-than-ever-building-reliable-software-in-the-age-of-agents&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=rUYP4C29yCw&quot;&gt;Now more than ever: building reliable software in the age of agents&lt;&#x2F;a&gt;
&lt;a class=&quot;zola-anchor&quot; href=&quot;#now-more-than-ever-building-reliable-software-in-the-age-of-agents&quot; aria-label=&quot;Anchor link for: now-more-than-ever-building-reliable-software-in-the-age-of-agents&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;A talk Ron Minsky (of Jane Street) gave at Bug Bash 2026, discussing how to
build reliable software in an age where AI agents exist. To quote from the
description:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ron stakes out their position that AI workflows should maximize human
effectiveness and understanding rather than replacing humans entirely. At
Jane Street, many traditional (aka pre-Claude) software engineering practices
have become more important rather than less. Type systems help provide fast
feedback and enforce invariants that agents can understand. Expect tests
allow developers to see system behavior and create deterministic simulations
at the library level. Code review remains crucial for maintaining code
quality and human understanding, even though agents change the dynamics.
Finally, he touches on Jane Street’s growing interest in formal verification,
and the impact of agents making previously expensive approaches, like formal
methods, more practical.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>OpenVet 0.5.0 Release</title>
        <published>2026-05-18T00:00:00+00:00</published>
        <updated>2026-05-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.openvet.org/announcements/openvet-0-5-0/"/>
        <id>https://blog.openvet.org/announcements/openvet-0-5-0/</id>
        
        <content type="html" xml:base="https://blog.openvet.org/announcements/openvet-0-5-0/">&lt;p&gt;&lt;strong&gt;OpenVet 0.5.0 is out.&lt;&#x2F;strong&gt; Highlights: an audit linter (&lt;code&gt;openvet audit check&lt;&#x2F;code&gt;),
an ad-hoc query command (&lt;code&gt;openvet query&lt;&#x2F;code&gt;), the &lt;code&gt;implies&lt;&#x2F;code&gt; operator in
requirements, and a deliberate wire-format reshape.  Most of what landed is
motivated by me doing some dogfooding. I created audits for the crates that the
openvet project itself uses. The openvet repository has about 500 dependencies,
and I audited 111 of them. On the way I learned a lot, and most of what’s in
this post is what those lessons turned into.&lt;&#x2F;p&gt;
&lt;p&gt;Ideally, when you implement OpenVet in a real-world project, you don’t have to
audit everything from scratch – you can trust audits that others have produced.
But I am the first real “user” of OpenVet, so I do have to audit everything from
scratch. And I need a dataset of audits, to test the CLI against.&lt;&#x2F;p&gt;
&lt;p&gt;I didn’t want to create all of the audits manually, especially given that the
main reason I needed them was to test stuff, and I may have to re-create them.
So, what I did was automate creating audits, by writing a “skill” that Claude
can use to audit a package. This was actually very productive, for a number of
reasons:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Writing the skill forced me to write down rules about how an audit is structured.
I had some ideas in my mind about what audits should look like — but there is
nothing like drafting it, letting Claude apply it, looking at the outcome,
and seeing that it comes out different than I expected (because I had implicit
assumptions, but I did not write them down)&lt;&#x2F;li&gt;
&lt;li&gt;Watching Claude use the CLI with my instructions and the help text revealed some
real shortcomings that the CLI had.&lt;&#x2F;li&gt;
&lt;li&gt;Having a set of audits allowed me to test the registry: to make sure that audits
render right.&lt;&#x2F;li&gt;
&lt;li&gt;It also allowed me to test the CLI, answering questions like: how fast does
check work with ~100 audits in a log? How fast is publishing? How fast does
it update and verify new commits?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;claims-vocabulary&quot;&gt;Claims vocabulary
&lt;a class=&quot;zola-anchor&quot; href=&quot;#claims-vocabulary&quot; aria-label=&quot;Anchor link for: claims-vocabulary&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;OpenVet uses the concept of a &lt;em&gt;claims vocabulary&lt;&#x2F;em&gt;. Claims are atomic “facts” about
a software package. For example, &lt;code&gt;uses-unsafe&lt;&#x2F;code&gt;. Or &lt;code&gt;uses-network&lt;&#x2F;code&gt;. I had a vocabulary
file drafted up, but I hadn’t put a lot of effort into it. The first few audits
showed me that this vocabulary was not really in the shape I wanted it to be in.&lt;&#x2F;p&gt;
&lt;p&gt;So, I ended up revising it, pointing Claude at the revised vocabulary, and it looked
much better. I came up with a pattern of claims that works well with the way we
write requirements. There are two kinds of claims:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Discovery claims&lt;&#x2F;strong&gt;: Encode a fact about something a package is, or does. These
are claims like &lt;code&gt;uses-unsafe&lt;&#x2F;code&gt; (uses memory-unsafe code), &lt;code&gt;impl-crypto&lt;&#x2F;code&gt; (implements
cryptography), &lt;code&gt;has-binaries&lt;&#x2F;code&gt; (has binaries&#x2F;executables in the package archive),
or &lt;code&gt;is-benign&lt;&#x2F;code&gt;. These are simple, can be asserted by just looking at what files
are there, what code is there. &lt;code&gt;is-benign&lt;&#x2F;code&gt; is the global one — it doesn’t slot
into the &lt;code&gt;has-*&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;uses-*&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;impl-*&lt;&#x2F;code&gt; families, but every audit asserts it like
the others.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Audit claims&lt;&#x2F;strong&gt;: These are gated on the discovery claims. For example, if a
crate implements cryptography (&lt;code&gt;impl-crypto&lt;&#x2F;code&gt;), then you may want to check
if the cryptography is implemented in a safe way for callers (&lt;code&gt;crypto-impl-safe&lt;&#x2F;code&gt;),
whether it’s implemented correctly (&lt;code&gt;crypto-impl-correct&lt;&#x2F;code&gt;), whether it is
tested (&lt;code&gt;crypto-impl-tested&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;With this vocabulary in place, the auditing process works like this:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You test and assert all discovery claims (to true or false).&lt;&#x2F;li&gt;
&lt;li&gt;You then test and assert all audit claims that are gated by the discovery claims.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The new vocabulary is now in place, and the old one is removed.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;referencing-findings-claims&quot;&gt;Referencing findings, claims
&lt;a class=&quot;zola-anchor&quot; href=&quot;#referencing-findings-claims&quot; aria-label=&quot;Anchor link for: referencing-findings-claims&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;OpenVet has a file format for audits, that has human-readable and machine-readable
pieces. For example, it has claims (boolean “facts”, machine-readable) and it has
markdown-shaped data, for human consumption. But I wanted a way to “connect” the two.&lt;&#x2F;p&gt;
&lt;p&gt;One feature that I built previously is that you can reference claims from inside the
markdown, using the &lt;code&gt;!claim-name&lt;&#x2F;code&gt; syntax. The idea behind that was that you can
link claims inside the markdown, to justify why you asserted a claim the way you did.
Similarly, I had built the &lt;code&gt;#1&lt;&#x2F;code&gt; syntax, to reference findings.&lt;&#x2F;p&gt;
&lt;p&gt;OpenVet is set up to check these: you can only reference findings that exist. If you
wrote &lt;code&gt;#34&lt;&#x2F;code&gt;, but there are only 27 findings, that is an error. Same for the claims.&lt;&#x2F;p&gt;
&lt;p&gt;Testing showed that the &lt;code&gt;#1&lt;&#x2F;code&gt; syntax for findings clashes with some things, including
GitHub pull-request references. In the registry, we render these as &lt;code&gt;FINDING-1&lt;&#x2F;code&gt;, so
the syntax was changed to &lt;code&gt;#finding-1&lt;&#x2F;code&gt;, which reduces the chance of clashes.&lt;&#x2F;p&gt;
&lt;p&gt;One issue was found with the claims: sometimes, you want to choose to not evaluate
a claim (leave it unasserted). This can happen for a number of reasons: maybe it is
not possible for you to evaluate it. Maybe the scope is too large. But you still
want to explain in the prose &lt;em&gt;why&lt;&#x2F;em&gt; you didn’t evaluate it. That did not work: OpenVet
would error if you referenced a claim that you did not assert.&lt;&#x2F;p&gt;
&lt;p&gt;So I changed that. You can now reference claims, even if you don’t assert them.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;turning-a-summary-into-a-report&quot;&gt;Turning a summary into a report
&lt;a class=&quot;zola-anchor&quot; href=&quot;#turning-a-summary-into-a-report&quot; aria-label=&quot;Anchor link for: turning-a-summary-into-a-report&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Initially, audits had a &lt;em&gt;summary&lt;&#x2F;em&gt; field as a high-level summary of the audit.
Iterating on it, I ended up standardizing on a structure for it, that begins with
a high-level explanation of the package that is audited (what it does), followed
by the methodology of the audit (how was it examined, what tools were used), followed
by a results section (explaining the outcome of the audit, explaining claims and
findings), followed by a conclusion section.&lt;&#x2F;p&gt;
&lt;p&gt;That worked, but it turns out there is a small problem: in the web interface,
what can we show as an actual summary? I considered extracting the conclusion
section from the summary, but a conclusion isn’t really a summary of an audit.&lt;&#x2F;p&gt;
&lt;p&gt;So, I changed the audit wire format. What used to be &lt;em&gt;summary&lt;&#x2F;em&gt; is now called
&lt;em&gt;report&lt;&#x2F;em&gt;, and I added an actual summary field, which is enforced to be less
than 1024 bytes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;audit-linter&quot;&gt;Audit linter
&lt;a class=&quot;zola-anchor&quot; href=&quot;#audit-linter&quot; aria-label=&quot;Anchor link for: audit-linter&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Testing showed that it is easy to forget to assert a claim. Especially the
discovery claims, ideally they should all be set to something. Claude forgot
to, and so did I.  A more structured approach was needed.&lt;&#x2F;p&gt;
&lt;p&gt;Since we have the claims vocabulary (and their relationships), and good
markdown parsing abilities (with &lt;code&gt;pulldown-cmark&lt;&#x2F;code&gt;), I thought the best way to
handle this is to build a linter. The existing audit validation code was moved
from the &lt;code&gt;openvet-proto&lt;&#x2F;code&gt; crate into a dedicated &lt;code&gt;openvet-audit&lt;&#x2F;code&gt; crate. The
vocabulary was converted into a TOML document, that the linter can validate. So
OpenVet can now enforce a number of things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;All of the discovery claims need to either be asserted, or they need to be
referenced in the markdown (a justification of why they could not be
evaluated).&lt;&#x2F;li&gt;
&lt;li&gt;Gated on the discovery claims, the applying audit claims need to either be
asserted, or referenced.&lt;&#x2F;li&gt;
&lt;li&gt;Any asserted claim needs to be referenced (explaining how it was evaluated).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Similarly, the linter also checks the report. It verifies that the markdown has
the expected headings (Subject, Methodology, Results, Conclusion).&lt;&#x2F;p&gt;
&lt;p&gt;Making the linter fast and giving it readable output took real effort. I came
up with a tree-shaped output. Here’s an example:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CDD6F4); background-color: light-dark(#EFF1F5, #1E1E2E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ openvet audit check&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;note: detected workspace audit-cargo-deranged-0.5.8-7cd812cc2bc1, performing workspace check&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;audit.pb&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├─ ✘ claims&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  ├─ ✘ &amp;#39;uses-unsfae&amp;#39; is declared but never cited&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  ├─ ✘ &amp;#39;algorithm-impl-safe&amp;#39; is asserted but &amp;#39;impl-algorithm&amp;#39; is false or absent&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  ├─ ⚠ &amp;#39;unsafe-documented&amp;#39; should be asserted because &amp;#39;uses-unsafe&amp;#39; is true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  ├─ ⚠ &amp;#39;unsafe-minimal&amp;#39; should be asserted because &amp;#39;uses-unsafe&amp;#39; is true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  ├─ ⚠ &amp;#39;unsafe-tested&amp;#39; should be asserted because &amp;#39;uses-unsafe&amp;#39; is true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  └─ ⚠ &amp;#39;uses-unsfae&amp;#39; is not in the taxonomy (typo, or a custom claim)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├─ ⚠ summary&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  └─ ⚠ summary is over the soft cap (577 bytes; soft cap 512)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├─ ✘ report&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  ├─ ⚠ unexpected section: ## Assessment (sibling-level structure should be H3+)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  ├─ ⚠ unexpected H1: # Some Other Heading (only `# Summary` is allowed at H1)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  ├─ ✘ #finding-99 (no such finding)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  └─ ⚠ &amp;#39;#1&amp;#39; inside backticks looks like a finding reference; remove the backticks to make it resolve&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├─ ✘ findings&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  ├─ ✘ findings[0].description: #99 (no such finding)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  └─ ⚠ findings[1].description: &amp;#39;#1&amp;#39; inside backticks looks like a finding reference; remove the backticks to make it resolve&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├─ ✘ annotations&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  └─ ✘ src&#x2F;unsafe_wrapper.rs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│     └─ ✘ lines 50-60 exceed file length (36)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;└─ ? signatures (not signed yet)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;5 errors, 9 warnings&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The forward-reference part of this check is wired into more than just the
command line: &lt;code&gt;openvet audit sign&lt;&#x2F;code&gt; won’t sign an audit with unresolved
references, and the registry’s publish endpoint refuses bundles whose
references don’t resolve. Same code, three call sites — what the CLI gates
pre-sign matches what the server enforces at publish time.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;querying-and-requirements&quot;&gt;Querying and requirements
&lt;a class=&quot;zola-anchor&quot; href=&quot;#querying-and-requirements&quot; aria-label=&quot;Anchor link for: querying-and-requirements&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;With 111 audits in hand, a different question came up: I now have a pretty cool
dataset (all of my dependencies audited, with claims attached), but could I
build a way to query that dataset? I wanted to answer questions like:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;How many dependencies I have use unsafe?&lt;&#x2F;li&gt;
&lt;li&gt;How many dependencies I have implement cryptography, or use cryptography?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So I built &lt;code&gt;openvet query&lt;&#x2F;code&gt;. OpenVet already has a language (the requirement
language), and all I needed was a way to evaluate a requirement expression and
pretty-print the output. That part was not so hard.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CDD6F4); background-color: light-dark(#EFF1F5, #1E1E2E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ openvet query impl-crypto&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;✓ cargo:sha1@0.11.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;✓ cargo:siphasher@1.0.3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;✓ cargo:ssh-cipher@0.2.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;✓ cargo:subtle@2.6.1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Summary: 4 match, 182 contradicted, 372 unknown, 13 skipped&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;One pattern for requirements I used a lot looks like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CDD6F4); background-color: light-dark(#EFF1F5, #1E1E2E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(not impl-crypto) or crypto-impl-safe&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This translates to: either the package does not implement cryptography, or the
cryptography must be safe. In other words: if &lt;code&gt;impl-crypto&lt;&#x2F;code&gt; is true, then
&lt;code&gt;crypto-impl-safe&lt;&#x2F;code&gt; must also be true. Because this is a common pattern, I added
syntax for it. You can now write:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CDD6F4); background-color: light-dark(#EFF1F5, #1E1E2E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;impl-crypto implies crypto-impl-safe&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This works both when querying, and when writing requirements.&lt;&#x2F;p&gt;
&lt;p&gt;I then wanted a few more things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;By default, it shows all crates that match the requirement. But I wanted a
way to show crates that don’t match it, or crates where the requirement
evaluates to &lt;em&gt;unknown&lt;&#x2F;em&gt; (because it has no audits, or the audits it has don’t
assert one of the claims in the requirement)&lt;&#x2F;li&gt;
&lt;li&gt;I also wanted a way to explain &lt;em&gt;how&lt;&#x2F;em&gt; the requirement expression was evaluated.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So I added a &lt;code&gt;--status [match|contradicted|unknown|all]&lt;&#x2F;code&gt;, and an &lt;code&gt;--explain&lt;&#x2F;code&gt;
flag. The explain output fits into a tree-like shape, similar to the audit linter.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CDD6F4); background-color: light-dark(#EFF1F5, #1E1E2E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ openvet query impl-crypto implies crypto-impl-safe --explain&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;✓ cargo:zerovec@0.11.6&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;└─ from xfbs: ✓ true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   └─ any → true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      ├─ not → true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      │  └─ impl-crypto → false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      └─ crypto-impl-safe → unknown&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;✓ cargo:zerovec-derive@0.11.3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;└─ from xfbs: ✓ true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   └─ any → true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      ├─ not → true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      │  └─ impl-crypto → false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      └─ crypto-impl-safe → unknown&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;openvet check&lt;&#x2F;code&gt; shows you the same for packages that fail your requirements, it
just breaks it down per-requirement.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;breaking-the-wire-format&quot;&gt;Breaking the wire format
&lt;a class=&quot;zola-anchor&quot; href=&quot;#breaking-the-wire-format&quot; aria-label=&quot;Anchor link for: breaking-the-wire-format&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;By now you may have noticed that 0.5.0 breaks the wire format. Several of the
changes above — splitting &lt;em&gt;summary&lt;&#x2F;em&gt; into report and summary, the &lt;code&gt;#1&lt;&#x2F;code&gt; →
&lt;code&gt;#finding-1&lt;&#x2F;code&gt; rename, the relaxation of which claim references must resolve —
are breaking. I could have implemented them in a way that preserves backwards
compatibility, but I chose not to. OpenVet does not have users yet, so I can
still “move fast and break things”. I figured: if I break the wire format, I
might as well break it right. So I snuck a number of simplifications and
improvements in there too.&lt;&#x2F;p&gt;
&lt;p&gt;A lot of RFC 3339 timestamps were replaced with a single &lt;code&gt;u64&lt;&#x2F;code&gt; UNIX timestamp.
No need to stringly-type time, saves parsing and encoding. The keys used by the
keyset tree were simplified (now are just a &lt;code&gt;TaggedHash&lt;&#x2F;code&gt;’s Display output). The
&lt;code&gt;Signature&lt;&#x2F;code&gt; type lost the embedded public key.&lt;&#x2F;p&gt;
&lt;p&gt;Adjacent to all of that I added some authoring tooling. Annotations gained a
&lt;code&gt;--remove&lt;&#x2F;code&gt; flag — previously, if you set an annotation incorrectly, you had to
recreate the audit workspace from scratch. I also added directory-level
annotations (a wire-shape change of its own); you can attach an annotation to a
whole subtree now, like a &lt;code&gt;has-tests&lt;&#x2F;code&gt; note on the &lt;code&gt;tests&#x2F;&lt;&#x2F;code&gt; directory.&lt;&#x2F;p&gt;
&lt;p&gt;The wire format isn’t perfect yet, there are still things I need to change, but
I tried to fold everything into this breaking change that I could fit.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next steps
&lt;a class=&quot;zola-anchor&quot; href=&quot;#next-steps&quot; aria-label=&quot;Anchor link for: next-steps&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;There is still a lot on the to-do list for the CLI. Some of the bigger pieces:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The TUI editor could lint as you go.&lt;&#x2F;strong&gt; It works, but you have to close it
and run &lt;code&gt;openvet audit check&lt;&#x2F;code&gt; to see what’s wrong. Folding the linter into
the TUI so it surfaces issues as you type would close the loop.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;openvet audit new --diff&lt;&#x2F;code&gt; doesn’t exist yet.&lt;&#x2F;strong&gt; Diff audits record metadata
stating what previous version and audit were consulted. The metadata for this
shipped already, but the tooling for it did not.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Encrypted SSH keys aren’t supported yet.&lt;&#x2F;strong&gt; Today OpenVet only signs with
unencrypted on-disk keys; encrypted-key support with a passphrase prompt
is a planned addition.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Only sshsig signatures are supported.&lt;&#x2F;strong&gt; Other signature formats are on
the list but unimplemented. Using SSH keys and signatures is convenient:
almost every developer has one, and they support hardware-backed tokens
(&lt;code&gt;ed25519-sk&lt;&#x2F;code&gt;). But long-term, I want to support different kinds of keys.
I am especially keen to try a WebAuthn-backed flow. The breaking changes
in the wire format support that, but I haven’t built it yet.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;There is no mirroring story yet.&lt;&#x2F;strong&gt; Self-hosting a log on static storage
works, but tooling for “mirror an existing registry-hosted log to your
own server” hasn’t been built.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I’ll keep posting here as these land.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;find-it-report-it&quot;&gt;Find it &#x2F; report it
&lt;a class=&quot;zola-anchor&quot; href=&quot;#find-it-report-it&quot; aria-label=&quot;Anchor link for: find-it-report-it&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Install:&lt;&#x2F;strong&gt; &lt;code&gt;cargo install openvet&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Source:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;openvet-org&#x2F;openvet&quot;&gt;gitlab.com&#x2F;openvet-org&#x2F;openvet&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Docs:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.openvet.org&quot;&gt;docs.openvet.org&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Issues:&lt;&#x2F;strong&gt; the GitLab tracker on the CLI repo. Issues and MRs are very
much welcome — this is a one-person project and any extra eyes help.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you’d rather reach me directly, my GitHub profile has the contact info.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>OpenVet Registry: first preview deployment</title>
        <published>2026-05-18T00:00:00+00:00</published>
        <updated>2026-05-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.openvet.org/announcements/openvet-registry-preview/"/>
        <id>https://blog.openvet.org/announcements/openvet-registry-preview/</id>
        
        <content type="html" xml:base="https://blog.openvet.org/announcements/openvet-registry-preview/">&lt;p&gt;&lt;strong&gt;The OpenVet Registry is now live at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;openvet.org&quot;&gt;openvet.org&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt; as
a preview deployment. The registry is a public, collaborative
host for cryptographically signed software dependency audits:
a place for auditors to publish their work and for projects to
subscribe to the auditors they trust.&lt;&#x2F;p&gt;
&lt;p&gt;OpenVet is a tool that tries to make it possible, affordable
and scalable for projects to adopt audited-dependencies
workflows. The status quo today is that most projects use
dependencies without reviewing what they do. As a collective,
we place implicit trust in packages published in registries.
Large corporations can afford teams dedicated to reviewing
dependencies, but most projects cannot. The OpenVet
command-line tool tries to make it easy to audit packages, and
to consume those audits. The registry tries to make it easy to
publish those, and discover logs. If you want to know more, the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.openvet.org&#x2F;concepts&#x2F;what-is-openvet&#x2F;&quot;&gt;docs have a longer answer&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;


&lt;div class=&quot;callout callout-warning&quot;&gt;
  &lt;div class=&quot;callout-label&quot;&gt;WARNING&lt;&#x2F;div&gt;
  &lt;div class=&quot;callout-body&quot;&gt;

&lt;p&gt;&lt;strong&gt;This is a preview deployment.&lt;&#x2F;strong&gt; The registry is operational
and I’m dogfooding it daily, but the data structures behind it
aren’t yet frozen. Expect that I’ll occasionally change
something and reset the registry in the process. Don’t host
audits here that you can’t afford to lose until I drop the
&lt;em&gt;preview&lt;&#x2F;em&gt; label.&lt;&#x2F;p&gt;


  &lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;h2 id=&quot;what-you-can-do-with-it-today&quot;&gt;What you can do with it today
&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-you-can-do-with-it-today&quot; aria-label=&quot;Anchor link for: what-you-can-do-with-it-today&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;I run &lt;code&gt;openvet&lt;&#x2F;code&gt; against the OpenVet repository as a continuous
dogfooding exercise. The output below is real, from a check in
mid-May 2026 — the numbers are what they are.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;setup&quot;&gt;Setup
&lt;a class=&quot;zola-anchor&quot; href=&quot;#setup&quot; aria-label=&quot;Anchor link for: setup&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;openvet init&lt;&#x2F;code&gt; drops a starter &lt;code&gt;openvet.toml&lt;&#x2F;code&gt;. Mine, after a
few edits, looks like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CDD6F4); background-color: light-dark(#EFF1F5, #1E1E2E);&quot;&gt;&lt;code data-lang=&quot;toml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #9399B2);&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;log&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;xfbs&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #9399B2);&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #94E2D5);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;https:&#x2F;&#x2F;openvet.org&#x2F;xfbs&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #9399B2);&quot;&gt;[[&lt;&#x2F;span&gt;&lt;span&gt;lockfile&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #9399B2);&quot;&gt;]]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;kind&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #94E2D5);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;cargo&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;path&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #94E2D5);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;Cargo.lock&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #9399B2);&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;requirement&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #9399B2);&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;benign&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #94E2D5);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;is-benign&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;crypto&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #94E2D5);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;uses-crypto implies crypto-safe and crypto-tested&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;unsafe&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #94E2D5);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;uses-unsafe implies unsafe-safe and unsafe-tested&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6E3A1);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I trust one log (my own, at &lt;code&gt;openvet.org&#x2F;xfbs&lt;&#x2F;code&gt;), I want to gate
the &lt;code&gt;Cargo.lock&lt;&#x2F;code&gt;, and I declare three requirements: every
dependency must be benign, must be safe-and-tested if it uses
cryptography, and must be safe-and-tested if it uses unsafe
code. The requirement syntax is boolean over claim names — see
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.openvet.org&#x2F;reference&#x2F;claims-vocabulary&#x2F;&quot;&gt;claims vocabulary&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.openvet.org&#x2F;reference&#x2F;requirement-syntax&#x2F;&quot;&gt;requirement syntax&lt;&#x2F;a&gt;
in the docs for the full set.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pin-and-verify-the-logs-you-trust&quot;&gt;Pin and verify the logs you trust
&lt;a class=&quot;zola-anchor&quot; href=&quot;#pin-and-verify-the-logs-you-trust&quot; aria-label=&quot;Anchor link for: pin-and-verify-the-logs-you-trust&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;openvet update&lt;&#x2F;code&gt; fetches each audit log you’ve subscribed to in
&lt;code&gt;openvet.toml&lt;&#x2F;code&gt;, verifies its signature chain end-to-end, and
writes the new head into &lt;code&gt;openvet.lock&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CDD6F4); background-color: light-dark(#EFF1F5, #1E1E2E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ openvet update&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;xfbs: verified 50 new commit(s) (0 keyset &#x2F; 0 gen-root); head 117574148ceb -&amp;gt; cb6e938a4ced&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;wrote openvet.lock&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The signature verification is local — the registry’s only job
here was to serve bytes. The pinned head in &lt;code&gt;openvet.lock&lt;&#x2F;code&gt; is
what guarantees the same verdict on every machine running
&lt;code&gt;openvet check&lt;&#x2F;code&gt; later.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;gate-your-project-against-the-audits&quot;&gt;Gate your project against the audits
&lt;a class=&quot;zola-anchor&quot; href=&quot;#gate-your-project-against-the-audits&quot; aria-label=&quot;Anchor link for: gate-your-project-against-the-audits&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;openvet check&lt;&#x2F;code&gt; walks your lockfiles, evaluates each requirement
against the audits in the logs you’ve pinned, and exits non-zero
on anything that doesn’t satisfy your policy:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CDD6F4); background-color: light-dark(#EFF1F5, #1E1E2E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ openvet check&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;✘ cargo:matchers@0.2.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;└─ unsafe: ✘ contradicted&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   └─ from xfbs: ✘ false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      └─ any → false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;         ├─ not → false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;         │  └─ uses-unsafe → true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;         └─ all → false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            ├─ unsafe-safe → true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            └─ unsafe-tested → false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Summary: 53 passed, 505 failed, 13 skipped&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(output trimmed)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When a requirement fails, &lt;code&gt;check&lt;&#x2F;code&gt; prints the resolution tree:
which auditor’s statement contradicted it, which claim atoms
combined under three-valued logic, and which atom resolved to
which value. The full mechanics live in
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.openvet.org&#x2F;concepts&#x2F;how-it-works&#x2F;&quot;&gt;How it works&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;505 failed&lt;&#x2F;code&gt; is honest — most crates in OpenVet’s own
dependency tree don’t have audits in my log yet. That’s the
work; closing the gap is the next stretch of dogfooding.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ask-concrete-questions-about-your-dependency-tree&quot;&gt;Ask concrete questions about your dependency tree
&lt;a class=&quot;zola-anchor&quot; href=&quot;#ask-concrete-questions-about-your-dependency-tree&quot; aria-label=&quot;Anchor link for: ask-concrete-questions-about-your-dependency-tree&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;openvet query &amp;lt;expression&amp;gt;&lt;&#x2F;code&gt; walks your lockfiles and reports
the state of a requirement expression across them. It uses the
same parser and evaluator as &lt;code&gt;[requirement]&lt;&#x2F;code&gt; in &lt;code&gt;openvet.toml&lt;&#x2F;code&gt;
— anything you can write as a requirement, you can pass to
&lt;code&gt;query&lt;&#x2F;code&gt;. The simplest case is a single claim atom: which of my
crates use unsafe code, according to my pinned audits?&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CDD6F4); background-color: light-dark(#EFF1F5, #1E1E2E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ openvet query uses-unsafe&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;warn  openvet@0.4.0 (path &#x2F; workspace dep)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;warn  openvet-audit@0.4.0 (path &#x2F; workspace dep)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;…&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;✓ cargo:bitflags@2.11.1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;✓ cargo:log@0.4.29&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;✓ cargo:matchers@0.2.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;✓ cargo:once_cell@1.21.4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;✓ cargo:tempfile@3.27.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;✓ cargo:tracing@0.1.44&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;✓ cargo:unsafe-libyaml@0.2.11&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;✓ cargo:zeroize@1.8.2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;…&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Summary: 30 match, 35 contradicted, 493 unknown, 13 skipped&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(output trimmed)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;match&lt;&#x2F;code&gt; is the count of dependencies the expression resolves to
true for. &lt;code&gt;contradicted&lt;&#x2F;code&gt; are dependencies where audits disagree
under three-valued logic. &lt;code&gt;unknown&lt;&#x2F;code&gt; is the long tail where no
pinned audit provides enough information to resolve the
expression. Compound expressions work the same way — &lt;code&gt;openvet query &quot;uses-unsafe and impl-crypto&quot;&lt;&#x2F;code&gt; would count only
dependencies where both atoms resolve to true. The summary line
on its own tells you the coverage of a given property across
your tree.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-in-this-deployment&quot;&gt;What’s in this deployment
&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-s-in-this-deployment&quot; aria-label=&quot;Anchor link for: what-s-in-this-deployment&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;The registry implements the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.openvet.org&#x2F;reference&#x2F;specification&#x2F;&quot;&gt;OpenVet protocol&lt;&#x2F;a&gt;. It’s not
a central trust anchor — the command-line tool will work with
anything that speaks the protocol. You can host your own log on
anything that can serve data over HTTP&#x2F;HTTPS, and get the same
functionality. The public deployment exists to make
participation low-friction. Signup is two clicks if you have a
GitHub account. It defaults to custodial keys, so you don’t
have to manage your own keys (unless you want to, which is
perfectly fine, and encouraged), all to make the process of
adopting it as easy as possible. The registry is free, and it
will always be. It’s not a SaaS, it’s just a (public) service.&lt;&#x2F;p&gt;
&lt;p&gt;What’s working today:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Account signup and custodial keys.&lt;&#x2F;strong&gt; New accounts get a
registry-held signing key by default. Custodial keys are
published in your keyset like any other, so consumers can verify
audits you sign with them.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Publishing audits from the CLI.&lt;&#x2F;strong&gt; Author audits locally,
sign with a self-held key, and push to your registry-hosted
log. End-to-end without going through the UI.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Package search.&lt;&#x2F;strong&gt; The registry actively synchronizes
package registry contents, so you can search for packages on
openvet.org, and find matching audits. Currently, only
synchronization with crates.io is enabled on the hosted
registry, but more will be enabled soon.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Audit log hosting for any ecosystem.&lt;&#x2F;strong&gt; Audits can be hosted
here regardless of which ecosystem the audited package comes
from.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Raw log endpoints for subscribers.&lt;&#x2F;strong&gt; The CLI subscribes
directly to a log’s HTTP endpoint and verifies the signature
chain locally; the registry’s job is to serve bytes.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;what-s-still-rough&quot;&gt;What’s still rough
&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-s-still-rough&quot; aria-label=&quot;Anchor link for: what-s-still-rough&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;In the spirit of “should I use this yet?”, a candid list:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Data structures may still change.&lt;&#x2F;strong&gt; Some breaks will be
migrations; some will be hard resets of the registry’s data.
Until I drop the &lt;em&gt;preview&lt;&#x2F;em&gt; label, anything you publish here
should be considered ephemeral.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The web UI is pre-pre-beta.&lt;&#x2F;strong&gt; The basic flows (login,
searching packages, viewing audits) work. Some things may look
strange, or be subtly broken.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Self-custodial setup doesn’t fully work yet.&lt;&#x2F;strong&gt; You can add
your own key to your log’s keyset, but there is currently no
way to remove the custodial key and give your own key
operator privileges.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Search beyond cargo is dark.&lt;&#x2F;strong&gt; RubyGems and PyPI syncers are
implemented but not yet enabled in production. npm and Go are
next. The work is queue-and-storage, not a code rewrite.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;No mirroring story yet.&lt;&#x2F;strong&gt; Self-hosting a log on static
storage works, but tooling for “mirror this registry-hosted
log to your own server” isn’t shipped.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;try-it&quot;&gt;Try it
&lt;a class=&quot;zola-anchor&quot; href=&quot;#try-it&quot; aria-label=&quot;Anchor link for: try-it&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Browsing the registry is open, no account required. Visit
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;openvet.org&quot;&gt;openvet.org&lt;&#x2F;a&gt; and search for a crate (try &lt;code&gt;serde&lt;&#x2F;code&gt;) to
see what audits exist for it, or open &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;openvet.org&#x2F;xfbs&quot;&gt;my profile&lt;&#x2F;a&gt; to see
what one auditor has published.&lt;&#x2F;p&gt;
&lt;p&gt;To participate, sign in with GitHub (two clicks). The registry
mints you a log slug and a custodial signing key. From there:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Click &lt;em&gt;Trust&lt;&#x2F;em&gt; on any profile&lt;&#x2F;strong&gt; to follow that auditor — their
audits flow into your feed.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Under &lt;em&gt;Settings → Keys&lt;&#x2F;em&gt;, add your own key.&lt;&#x2F;strong&gt; The UI gives
you a one-line &lt;code&gt;openvet login&lt;&#x2F;code&gt; command to run in your terminal,
which pairs the CLI with your log. You can create, sign and
publish audits from the CLI, and they will show up in the
registry.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;To gate your own project against the audits, the &lt;a href=&quot;https:&#x2F;&#x2F;blog.openvet.org&#x2F;announcements&#x2F;openvet-registry-preview&#x2F;#what-you-can-do-with-it-today&quot;&gt;CLI
section&lt;&#x2F;a&gt; above walks the loop: install the CLI,
declare a log to trust, run &lt;code&gt;openvet update&lt;&#x2F;code&gt; and &lt;code&gt;openvet check&lt;&#x2F;code&gt;.
The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.openvet.org&#x2F;getting-started&#x2F;quickstart&#x2F;&quot;&gt;Quickstart&lt;&#x2F;a&gt; in the docs is the end-to-end
version.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-next&quot;&gt;What’s next
&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-s-next&quot; aria-label=&quot;Anchor link for: what-s-next&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;OpenVet is still under development. I am working on the
specification, the command-line interface, and the backend in
parallel. As new bits get released, I will make announcements
on this blog, explaining what works, what doesn’t, what is
stable, and what isn’t.&lt;&#x2F;p&gt;
&lt;p&gt;One of the outstanding pieces that will be released in the
upcoming days is an initial version of the OpenVet
specification, which still needs some cleaning up. The registry
is the piece that is the most behind: audits already render
fine, but there is no web-based auditing flow implemented yet.
There are a lot of rough edges and unimplemented pieces. The
command-line tool is in better shape, but is still missing some
important features, such as signing with encrypted keys, or
supporting signatures with anything besides sshsig.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;find-it-report-it&quot;&gt;Find it &#x2F; report it
&lt;a class=&quot;zola-anchor&quot; href=&quot;#find-it-report-it&quot; aria-label=&quot;Anchor link for: find-it-report-it&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Registry source:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;openvet-org&#x2F;registry&quot;&gt;gitlab.com&#x2F;openvet-org&#x2F;registry&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;CLI source:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;openvet-org&#x2F;openvet&quot;&gt;gitlab.com&#x2F;openvet-org&#x2F;openvet&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Docs:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.openvet.org&quot;&gt;docs.openvet.org&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Issues:&lt;&#x2F;strong&gt; the GitLab tracker on whichever repo is closest to
the problem. Issues and MRs are very much welcome — this is a
one-person project and any extra eyes help.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you’d rather reach me directly, my GitHub profile has the
contact info.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Hello, OpenVet</title>
        <published>2026-05-17T00:00:00+00:00</published>
        <updated>2026-05-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.openvet.org/posts/hello-openvet/"/>
        <id>https://blog.openvet.org/posts/hello-openvet/</id>
        
        <content type="html" xml:base="https://blog.openvet.org/posts/hello-openvet/">&lt;p&gt;This is the start of the OpenVet dev log.  I plan to write about what I am
working on, what I managed to break, and from time to time post about
supply-chain security related news.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-openvet-is&quot;&gt;What OpenVet is
&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-openvet-is&quot; aria-label=&quot;Anchor link for: what-openvet-is&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;OpenVet is a project that gives you supply-chain security by requiring that
your dependencies are audited, and match requirements you set out. It has
two parts:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;openvet-org&#x2F;registry&quot;&gt;public registry&lt;&#x2F;a&gt; that hosts signed, machine-readable audits
of software dependencies. It lets you publish audits to your own
cryptographically-signed, append-only log. And it lets others see and consume
the audits you produce. It is hosted at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;openvet.org&quot;&gt;https:&#x2F;&#x2F;openvet.org&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;A &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;openvet-org&#x2F;openvet&quot;&gt;command-line tool&lt;&#x2F;a&gt; that can ingest those audit logs, enforce
that your dependencies are audited, and match the requirements you have. It
doesn’t trust the registry, it trusts the key of the publisher of the log.
The tooling is also not tied to the registry: you could just as well host
your own logs on any static site host.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Both of these components are open-source. The command-line tool is &lt;code&gt;MIT&lt;&#x2F;code&gt; and
&lt;code&gt;Apache-2.0&lt;&#x2F;code&gt; licensed, giving you freedom to adapt and integrate it. The
registry is &lt;code&gt;AGPL-3.0&lt;&#x2F;code&gt; licensed, more restrictive in what you can do with the
code, requiring modifications to be released under the same license.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why&quot;&gt;Why
&lt;a class=&quot;zola-anchor&quot; href=&quot;#why&quot; aria-label=&quot;Anchor link for: why&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;As software engineers, we don’t build everything from scratch. We build on top
of what others before us have built, which means depending on software packages
hosted on public registries — and that is a net positive.&lt;&#x2F;p&gt;
&lt;p&gt;When the internet was initially built, there was a culture of trust: there was
no encryption, little authentication. That was possible because it had few
users and a lack of valuable targets. We have a similar culture of &lt;em&gt;implicit
trust&lt;&#x2F;em&gt; on anything published in public registries. But slowly, attackers have
found out that developers’ machines are high value targets, containing API keys,
cryptocurrency wallets, and access to company infrastructure. Times are
changing.&lt;&#x2F;p&gt;
&lt;p&gt;Supply-chain attacks have moved from hypothetical to routine, accelerated in
recent years by the availability of LLMs. Some attacks like the &lt;em&gt;Shai-Hulud&lt;&#x2F;em&gt;
worm or the xz-utils have become well-known, but a major supply-chain security
company has &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.sonatype.com&#x2F;state-of-the-software-supply-chain&#x2F;introduction&quot;&gt;logged over 1.2 million malicious packages in total, with 454,600
added in 2025 alone&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The current defenses either don’t work, or don’t scale. Build and release
provenance doesn’t help when attackers hijack CI workflows that release
packages (they just prove where the release happened, not what’s in the package).
CVE scanners are reactionary: by the time the CVE lands, your API keys and
wallets have been exfiltrated, and attackers have already had access to your
infrastructure. Dependency freezes just shift the pain to whoever updates first,
and if we all implemented them, they just delay when attacks are discovered.&lt;&#x2F;p&gt;
&lt;p&gt;But malicious packages are not the only thing that OpenVet addresses: it is
also correctness. We are producing code at an ever-growing rate, with
ever-growing complexity. In my opinion, the only way to do this sustainably is
to have reviewed and well-tested primitives hosted at registries that can be
composed to build complex software.&lt;&#x2F;p&gt;
&lt;p&gt;One personal data point: my average pet project pulls in 400 dependencies
summing up to 3.5 million lines of code, as verified by &lt;code&gt;cargo vendor&lt;&#x2F;code&gt; and
&lt;code&gt;tokei&lt;&#x2F;code&gt;. You may want to check your projects, and think about: how many lines
of that have I actually reviewed? How do I know that my dependencies are
correct and safe to use?&lt;&#x2F;p&gt;
&lt;p&gt;The way out is to drop implicit trust and require external vetting (auditing)
of software dependencies. And when I say &lt;em&gt;auditing&lt;&#x2F;em&gt;, I don’t mean the multi-month
project that is auditing a cryptographic library. For most dependencies, there
are some simple questions you need to answer, like:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Does this have any build-time or install-time actions, and do these actions look safe?&lt;&#x2F;li&gt;
&lt;li&gt;Does this code make network requests, and if so, why and what is sent?&lt;&#x2F;li&gt;
&lt;li&gt;Does it read or write to the filesystem, and if so, what is read or written?&lt;&#x2F;li&gt;
&lt;li&gt;Does it read any environment variables, and if so, what does it do with them?&lt;&#x2F;li&gt;
&lt;li&gt;Does it have extensive test coverage, including randomized tests (fuzz tests, proptests)?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Any software engineer with some experience can answer that with a checklist.
Thorough audits may be required for &lt;em&gt;some&lt;&#x2F;em&gt; dependencies, but not for all.&lt;&#x2F;p&gt;
&lt;p&gt;The actually hard problems are elsewhere:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The time it takes to perform audits of all of your dependencies&lt;&#x2F;li&gt;
&lt;li&gt;The user experience for creating, publishing and discovering audits&lt;&#x2F;li&gt;
&lt;li&gt;The distribution of audits&lt;&#x2F;li&gt;
&lt;li&gt;The tooling for validating that your dependencies are audited&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;OpenVet is an approach to solving these four problems in a way that might
be able to scale beyond &lt;em&gt;niche&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-it-works-briefly&quot;&gt;How it works, briefly
&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-it-works-briefly&quot; aria-label=&quot;Anchor link for: how-it-works-briefly&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;OpenVet defines a data format for audits. They contain both machine-readable
claims (similar to what &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mozilla&#x2F;cargo-vet&quot;&gt;cargo-vet&lt;&#x2F;a&gt; does, in the future I will make a post
explaining the mental model of them), and human-readable findings, source
annotations, and a report. Audits are signed by the author, the signature is
required and verified.&lt;&#x2F;p&gt;
&lt;p&gt;OpenVet defines a way to distribute them. Audits are published in
cryptographically-signed append-only logs, so you do not need to trust the
platform, only the person holding the keys.  OpenVet has a registry for audits,
think about it like a &lt;em&gt;GitHub&lt;&#x2F;em&gt;, but it holds audits, not code.&lt;&#x2F;p&gt;
&lt;p&gt;It does not scale for us to audit every single dependency ourselves. The core
idea of OpenVet is that auditing is a collaborative process, just like
open-source development is. You publish your audits, others can trust you (and
with that, your audit-log). You can choose to trust an entity (a large company,
which has audited several dependencies, or a friend). With that, you only need
to audit dependencies that others you trust haven’t already audited, which is a
lot more manageable. It makes OpenVet’s adoption much cheaper, and thereby
scalable.&lt;&#x2F;p&gt;
&lt;p&gt;When you do need to audit software, OpenVet tries to make it easy. It has a
command-line driven workflow for auditing software that lets you create an
audit workspace. I try to build tooling that makes the process as simple as
possible. And this tooling also allows you to automate the auditing process for
low-risk dependencies, by getting an LLM to do it. I am not saying that
LLM-generated audits are the perfect solution, but in my opinion: LLM-audited
dependencies are better than unaudited dependencies, and it means you can spend
your time on higher-risk dependencies.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, OpenVet attempts to ship tooling that makes enforcing dependency
audits as painless as possible. The command-line tooling can scan all of your
lockfiles, and search through all of the audit-logs you trust for relevant
audits in parallel and with local caching. The audit log data structure is
designed for cacheability and efficient lookup.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-to-expect-here&quot;&gt;What to expect here
&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-to-expect-here&quot; aria-label=&quot;Anchor link for: what-to-expect-here&quot;&gt;#&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Don’t expect a refined product here, or a sales team. It’s just me, and this is
not a commercial endeavour. I am building this because I want it to exist, and
because it is fun to do so. I am implementing features, and breaking things
along the way, as I figure out what works and what does not.&lt;&#x2F;p&gt;
&lt;p&gt;If you think this idea has merit, feel free to stay along for the ride. I try
to post updates on this blog as I am working on OpenVet, as I implement new
features. I would ask you to like, comment, and subscribe, but I don’t have
any social media buttons on here, nor do I have a mailing list, nor a comment
feature.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to get in touch or help with this, you probably find a way to reach
me on my GitHub profile.&lt;&#x2F;p&gt;
&lt;p&gt;In the meantime, if you want the source, it’s on
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;openvet-org&#x2F;openvet&quot;&gt;GitLab&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
