<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Cargo on PHP Boy Scout</title><link>https://blog-570662.gitlab.io/tags/cargo/</link><description>Recent content in Cargo on PHP Boy Scout</description><generator>Hugo -- gohugo.io</generator><language>en-gb</language><copyright>Matt Cockayne</copyright><lastBuildDate>Thu, 30 Apr 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://blog-570662.gitlab.io/tags/cargo/index.xml" rel="self" type="application/rss+xml"/><item><title>Two kinds of feature flag</title><link>https://blog-570662.gitlab.io/two-kinds-of-feature-flag/</link><pubDate>Thu, 30 Apr 2026 00:00:00 +0000</pubDate><guid>https://blog-570662.gitlab.io/two-kinds-of-feature-flag/</guid><description>&lt;img src="https://blog-570662.gitlab.io/two-kinds-of-feature-flag/cover-two-kinds-of-feature-flag.png" alt="Featured image of post Two kinds of feature flag" /&gt;&lt;p&gt;go-tool-base has feature flags: switches that decide which built-in commands are live in a given run. rust-tool-base has those too. But it also has a second, completely separate kind of flag, and the difference between them is one of those distinctions that&amp;rsquo;s obvious the moment you see it and dangerously easy to conflate before you do. One decides what a command &lt;em&gt;does&lt;/em&gt;. The other decides whether a chunk of code is &lt;em&gt;in the binary at all&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="a-workspace-of-crates"&gt;A workspace of crates
&lt;/h2&gt;&lt;p&gt;Before the flags, the shape that makes them possible. go-tool-base is one Go module with packages under &lt;code&gt;pkg/&lt;/code&gt;. rust-tool-base is a Cargo &lt;em&gt;workspace&lt;/em&gt; of seventeen crates: &lt;code&gt;rtb-app&lt;/code&gt;, &lt;code&gt;rtb-config&lt;/code&gt;, &lt;code&gt;rtb-cli&lt;/code&gt;, &lt;code&gt;rtb-vcs&lt;/code&gt;, &lt;code&gt;rtb-ai&lt;/code&gt;, &lt;code&gt;rtb-mcp&lt;/code&gt;, &lt;code&gt;rtb-docs&lt;/code&gt;, &lt;code&gt;rtb-telemetry&lt;/code&gt;, and so on, with an umbrella crate called &lt;code&gt;rtb&lt;/code&gt; that re-exports the public surface.&lt;/p&gt;
&lt;p&gt;That isn&amp;rsquo;t tidiness for its own sake. Each subsystem being a separately compilable crate is what gives you a unit you can include or exclude &lt;em&gt;wholesale&lt;/em&gt;. Hold onto that, because it&amp;rsquo;s the hinge for everything below.&lt;/p&gt;
&lt;h2 id="the-flag-go-tool-base-already-has"&gt;The flag go-tool-base already has
&lt;/h2&gt;&lt;p&gt;go-tool-base has feature flags, and I&amp;rsquo;d describe them as runtime flags. A tool built on it can enable or disable built-in commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetFeatures&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Disable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;InitCmd&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Enable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AiCmd&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At startup the framework resolves that set and decides which commands are reachable for this run. The &lt;code&gt;init&lt;/code&gt; command might be present in the binary but switched off; the &lt;code&gt;ai&lt;/code&gt; command might be switched on. It&amp;rsquo;s about the &lt;em&gt;user-facing surface&lt;/em&gt;: which commands exist for someone typing &lt;code&gt;--help&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;rust-tool-base keeps this idea. A command carries a &lt;code&gt;CommandSpec&lt;/code&gt; with an optional &lt;code&gt;feature&lt;/code&gt; field, and the runtime decides whether a feature-gated command is reachable. Same purpose: shape the surface per invocation.&lt;/p&gt;
&lt;p&gt;If that were the whole story, there&amp;rsquo;d be nothing to write. The reason there&amp;rsquo;s a post is the &lt;em&gt;other&lt;/em&gt; kind of flag, which Rust makes available and Go really doesn&amp;rsquo;t.&lt;/p&gt;
&lt;h2 id="the-flag-rust-adds"&gt;The flag Rust adds
&lt;/h2&gt;&lt;p&gt;Cargo features are a compile-time mechanism. The &lt;code&gt;rtb&lt;/code&gt; umbrella crate declares them like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;features&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cli&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;update&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;docs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;mcp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;credentials&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;tui&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;cli&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;dep:rtb-cli&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;update&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;dep:rtb-update&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;ai&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;dep:rtb-ai&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;rtb-docs?/ai&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;vcs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;dep:rtb-vcs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;telemetry&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;dep:rtb-telemetry&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;full&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cli&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;update&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;docs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;mcp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ai&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;credentials&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;tui&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;telemetry&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;vcs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Each subsystem is an &lt;em&gt;optional&lt;/em&gt; crate dependency, and a feature switches it on. This is a different kind of switch entirely, and the difference is the whole point.&lt;/p&gt;
&lt;p&gt;A runtime flag decides what a command does &lt;em&gt;while the program runs&lt;/em&gt;. The code is in the binary either way; the flag just gates it.&lt;/p&gt;
&lt;p&gt;A Cargo feature decides what&amp;rsquo;s &lt;em&gt;in the binary in the first place&lt;/em&gt;. Build a tool without the &lt;code&gt;vcs&lt;/code&gt; feature and &lt;code&gt;rtb-vcs&lt;/code&gt; is not compiled. Its dependencies are not compiled. &lt;code&gt;gix&lt;/code&gt;, the pure-Rust Git implementation &lt;code&gt;rtb-vcs&lt;/code&gt; pulls in, roughly two and a half megabytes of it, is not compiled and not linked. It isn&amp;rsquo;t switched off in the binary. It was never in the binary. The compiler never even saw it.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s something a runtime flag cannot do, because by the time anything runs, the binary already exists with everything in it.&lt;/p&gt;
&lt;h2 id="two-axes-kept-separate"&gt;Two axes, kept separate
&lt;/h2&gt;&lt;p&gt;So rust-tool-base has two flag systems answering two genuinely different questions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cargo features&lt;/strong&gt; answer: &lt;em&gt;what is this binary made of?&lt;/em&gt; They&amp;rsquo;re decided when you build the tool, in &lt;code&gt;Cargo.toml&lt;/code&gt;. They control compilation, binary size, dependency surface, and compile time. A tool that never touches Git builds without &lt;code&gt;vcs&lt;/code&gt; and is smaller, faster to compile, and has a smaller dependency tree to audit. A tool that wants everything turns on &lt;code&gt;full&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Runtime feature flags&lt;/strong&gt; answer: &lt;em&gt;what can the user do in this run?&lt;/em&gt; They&amp;rsquo;re decided as the program starts. They control which commands appear, which paths are reachable.&lt;/p&gt;
&lt;p&gt;These could have been mashed into one mechanism, and it would have been a mistake. The app-context design notes are blunt about it: feature gating doesn&amp;rsquo;t belong on the per-command context object, because a feature-gated command &amp;ldquo;either exists or doesn&amp;rsquo;t&amp;rdquo; rather than changing its behaviour mid-run. Compile-time composition is one decision, made by the person building the tool. Runtime gating is another, made per invocation. Conflating them would mean you couldn&amp;rsquo;t reason cleanly about either.&lt;/p&gt;
&lt;h2 id="the-go-version-of-this-had-to-be-hand-built"&gt;The Go version of this had to be hand-built
&lt;/h2&gt;&lt;p&gt;This isn&amp;rsquo;t a thing Go simply lacks. I &lt;a class="link" href="https://blog-570662.gitlab.io/the-blank-import-that-keeps-a-dependency-out-of-your-binary/" &gt;wrote a whole post&lt;/a&gt; about how go-tool-base keeps its optional keychain dependency out of binaries that don&amp;rsquo;t want it, using a blank import and the linker&amp;rsquo;s dead-code elimination. It works. But it was a piece of deliberate engineering for &lt;em&gt;one&lt;/em&gt; dependency, and getting it right took care.&lt;/p&gt;
&lt;p&gt;Cargo features make that same outcome a first-class, declarative thing, and not for one dependency but for every subsystem the framework has. You don&amp;rsquo;t engineer the exclusion. You name a feature and leave it off. The crate, and its whole subtree, stays out. Rust&amp;rsquo;s build system was designed for exactly this, and rust-tool-base leans on it across the entire workspace rather than hand-rolling it once.&lt;/p&gt;
&lt;h2 id="where-this-leaves-us"&gt;Where this leaves us
&lt;/h2&gt;&lt;p&gt;go-tool-base has runtime feature flags: they decide, per invocation, which built-in commands are reachable. rust-tool-base keeps that, and adds a second kind that Rust makes available.&lt;/p&gt;
&lt;p&gt;Cargo features decide what the binary is &lt;em&gt;compiled from&lt;/em&gt;. Each of the framework&amp;rsquo;s seventeen crates is an optional dependency, and a feature switched off means that crate and its entire dependency subtree are never compiled or linked. A runtime flag gates what code &lt;em&gt;does&lt;/em&gt;; a Cargo feature gates whether code &lt;em&gt;is there at all&lt;/em&gt;. Two axes, two questions, deliberately kept as separate systems.&lt;/p&gt;</description></item><item><title>Waivers with an expiry date</title><link>https://blog-570662.gitlab.io/waivers-with-an-expiry-date/</link><pubDate>Sun, 26 Apr 2026 00:00:00 +0000</pubDate><guid>https://blog-570662.gitlab.io/waivers-with-an-expiry-date/</guid><description>&lt;img src="https://blog-570662.gitlab.io/waivers-with-an-expiry-date/cover-waivers-with-an-expiry-date.png" alt="Featured image of post Waivers with an expiry date" /&gt;&lt;p&gt;A vulnerability scanner gives you a yes or a no. Is there a known advisory on a path you actually use? Yes, or no. That&amp;rsquo;s genuinely useful, and you should run one. But it&amp;rsquo;s a snapshot, taken on the day you ask, and supply-chain risk in a framework is a bigger and more ongoing thing than a single yes-or-no can capture.&lt;/p&gt;
&lt;p&gt;So rust-tool-base treats its whole dependency tree as something to have a &lt;em&gt;policy&lt;/em&gt; about, not something to scan and forget.&lt;/p&gt;
&lt;h2 id="a-scanner-answers-one-question"&gt;A scanner answers one question
&lt;/h2&gt;&lt;p&gt;When I &lt;a class="link" href="https://blog-570662.gitlab.io/every-finding-was-the-same-shape/" &gt;had go-tool-base security-audited&lt;/a&gt;, part of the routine was running a vulnerability scanner over the dependencies. Go has a good one. It looks at your dependency graph, cross-references known advisories, and tells you whether any of them reach code you actually call.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s useful and you should do it. But notice the shape of what it gives back: essentially a yes or a no. Either there&amp;rsquo;s a known vulnerability on a reachable path or there isn&amp;rsquo;t. It answers one question, on the day you ask it.&lt;/p&gt;
&lt;p&gt;Supply-chain risk in a framework is broader than that one question, because a framework drags its entire dependency tree into every tool built on it. rust-tool-base treats the whole tree as something to have a &lt;em&gt;policy&lt;/em&gt; about, and the tool for that is &lt;code&gt;cargo-deny&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="a-gate-not-a-scan"&gt;A gate, not a scan
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;cargo-deny&lt;/code&gt; reads a &lt;code&gt;deny.toml&lt;/code&gt; and checks the dependency graph against four kinds of rule.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Licences.&lt;/strong&gt; There&amp;rsquo;s an allowlist: MIT, Apache-2.0, the BSD variants, ISC, a handful of others. Every transitive crate&amp;rsquo;s licence has to be on it. A dependency that pulls in something copyleft, or something with no licence at all, fails the build. You find out the first time it enters the tree, not during a release scramble when someone finally reads the legal implications.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Advisories.&lt;/strong&gt; It checks the RustSec advisory database, and yanked crates are set to &lt;code&gt;deny&lt;/code&gt;, so a dependency that&amp;rsquo;s been pulled from the registry stops CI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bans.&lt;/strong&gt; Wildcard version requirements (&lt;code&gt;version = &amp;quot;*&amp;quot;&lt;/code&gt;) are denied outright, because a dependency that floats to whatever&amp;rsquo;s newest is a supply-chain hole by construction. Duplicate versions of the same crate get surfaced too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sources.&lt;/strong&gt; Crates may only come from the official registry. An unknown registry or a stray git dependency is denied. Nothing sneaks in from a URL.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s a gate. It encodes, as rules in a file, what the project will and won&amp;rsquo;t accept into its dependency tree, and it enforces them on every build instead of once an audit.&lt;/p&gt;
&lt;h2 id="the-honest-part-is-the-waiver-list"&gt;The honest part is the waiver list
&lt;/h2&gt;&lt;p&gt;Here&amp;rsquo;s the thing every real project runs into. Sooner or later there&amp;rsquo;s an advisory you genuinely can&amp;rsquo;t fix this week. It&amp;rsquo;s against a crate three levels down your tree. The fix needs an upstream release that hasn&amp;rsquo;t happened. The crate is scheduled to be reworked two milestones from now anyway. The gate is going to fail, and the work to satisfy it honestly isn&amp;rsquo;t available to you yet.&lt;/p&gt;
&lt;p&gt;The lazy response is a blanket ignore: silence the advisory, move on, forget. Now your gate has a hole in it that nobody remembers opening.&lt;/p&gt;
&lt;p&gt;rust-tool-base&amp;rsquo;s &lt;code&gt;deny.toml&lt;/code&gt; does something better. Every waiver in the &lt;code&gt;ignore&lt;/code&gt; list is a documented record. Each one carries a comment that names the crate, traces the &lt;em&gt;exact dependency path&lt;/em&gt; that reaches it, gives the reason, and names the condition that lifts it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;ignore&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;# `instant` - reached via async-openai -&amp;gt; backoff -&amp;gt; rtb-ai (v0.3).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;RUSTSEC-2024-0384&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;# `paste` - reached via ratatui -&amp;gt; rtb-docs (v0.2) / rtb-tui (v0.4).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;RUSTSEC-2024-0436&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The file states the policy out loud: &amp;ldquo;Every waiver points at a deferred stub crate that will be reworked before its ship milestone. Lift each waiver when the owning crate lands its v0.1.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Some waivers go further and carry a structured reason field, so the &lt;em&gt;why&lt;/em&gt; travels with the entry rather than living only in a comment above it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;RUSTSEC-2025-0140&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;reason&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;gix-date via gix is a stub dependency; rtb-vcs v0.5 will upgrade&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Read that list and you don&amp;rsquo;t see a project that quietly stopped caring about seven advisories. You see seven advisories the project knows about, can trace, and has tied to a specific milestone. The waiver has an expiry condition. When &lt;code&gt;rtb-vcs&lt;/code&gt; reaches v0.5, that &lt;code&gt;gix&lt;/code&gt; entry is meant to come out, and the comment is the reminder that it should.&lt;/p&gt;
&lt;h2 id="why-this-is-the-bit-to-copy"&gt;Why this is the bit to copy
&lt;/h2&gt;&lt;p&gt;A gate that can&amp;rsquo;t be relaxed is a gate people route around. They&amp;rsquo;ll find the broadest possible ignore and use it, because the alternative is being blocked on someone else&amp;rsquo;s release. The pressure to do that is real, and it&amp;rsquo;s not unreasonable.&lt;/p&gt;
&lt;p&gt;So the design that actually holds up isn&amp;rsquo;t a stricter gate. It&amp;rsquo;s a gate with an honest, structured escape hatch: you &lt;em&gt;can&lt;/em&gt; waive an advisory, but a waiver costs you a documented record with a dependency path and an expiry condition. That price is small enough that nobody routes around it, and high enough that waivers don&amp;rsquo;t accumulate silently. The &lt;code&gt;ignore&lt;/code&gt; list stays readable, and every line in it is something you could defend out loud.&lt;/p&gt;
&lt;p&gt;Supply-chain hygiene framed this way isn&amp;rsquo;t an audit you survive once a year. It&amp;rsquo;s bookkeeping: a ledger of what you accepted, why, and when each exception is due to close. Which, now I write it down, is just the &lt;a class="link" href="https://blog-570662.gitlab.io/introducing-go-tool-base/" &gt;Boy Scout rule&lt;/a&gt; again, pointed at a dependency tree. Leave it tidier than you found it, and write down the bits you couldn&amp;rsquo;t tidy yet.&lt;/p&gt;
&lt;h2 id="where-this-leaves-us"&gt;Where this leaves us
&lt;/h2&gt;&lt;p&gt;A vulnerability scanner answers one question on one day. &lt;code&gt;cargo-deny&lt;/code&gt; is a standing policy gate: licences against an allowlist, advisories and yanked crates denied, wildcard versions banned, sources restricted to the official registry, enforced on every build.&lt;/p&gt;
&lt;p&gt;The part of rust-tool-base&amp;rsquo;s setup worth copying is the waiver list. Every advisory that can&amp;rsquo;t be fixed yet is recorded with its crate, its dependency path, its reason and the milestone that removes it. A waiver is a dated note, not a shrug, and that&amp;rsquo;s what keeps the gate honest enough that nobody actually wants to bypass it.&lt;/p&gt;</description></item></channel></rss>