My Zero-Dependency Tools Had No Clothes: What The Update Framework Taught Me
I run a scanner that hunts malware in user-submitted Linux packages. Same week, I installed my own build tooling like this:
curl -sLO https://example.com/install && sudo ./install
Read those two sentences again. One of them is a security control. The other is the vulnerability it exists to catch.
The Blind Spot in My Own Doctrine
Zero-dependency is my design religion. My scanners run on a single Babashka binary plus ripgrep — no package managers, no transitive libraries, no dependency tree for an attacker to poison. I thought that made the whole pipeline trustworthy.
It made the runtime trustworthy. It did nothing for the delivery.
A tool with no dependencies still reaches your machine through a
channel. Mine reached it through a URL I typed once into a README. If
that URL is compromised — server takeover, DNS hijack, a typo’d domain,
a maintainer’s stolen session cookie — then curl | bash
executes the attacker’s code as root, and my
zero-dependency purity counts for exactly nothing.
Enter The Update Framework
The Update Framework (TUF) is a CNCF-graduated specification for one narrow question: how do you distribute software when you must assume the update channel itself will be attacked?
Not a library. Not a product to bolt on. A trust design — and its answer is structural:
| Role | Signs | Survives |
|---|---|---|
| root | Who is allowed to sign (quorum of keys) | Compromise of any single signing key |
| targets | Which files exist, with hashes | Attacker swapping payload binaries |
| snapshot | Which target versions are current | Rollback to old vulnerable releases |
| timestamp | Proof of freshness, short-lived | Freeze attacks — stale metadata expires fast |
The insight is not cryptography. Signatures alone were already everywhere. The insight is role separation with thresholds: no single compromised key can push malicious updates, because pushing requires multiple roles to agree, and each role has different exposure and lifetime. Trust survives the worst day.
Post-Hoc Detection vs Pre-Hoc Prevention
Here is the part that stung.
My AUR scanner operates after upload: package appears, static analysis runs, malware gets flagged. Useful — and fundamentally reactive. The AUR’s real weakness is structural: user submissions carry no enforced integrity layer, so every scanner downstream is triage.
TUF operates before: the compromise either fails signature thresholds or expires against a fresh timestamp. Detection becomes a backstop instead of a load-bearing wall.
That distinction reframed my whole portfolio. Every artifact I had shipped shared the same gap:
| Artifact | Runtime hygiene | Distribution hygiene |
|---|---|---|
| Compliance scanners | ✅ zero deps, tested | ❌ unsigned URL |
| CI security gates | ✅ fixtures both directions | ❌ unattested pushes |
| Job-intelligence pipeline | ✅ deterministic, deduped | ❌ local-only |
Consistent philosophy, consistently missing its top layer.
The Doctrine Upgrade
Full TUF implementation is overkill for solo-maintained tooling — running a root-of-trust ceremony for a Bash scanner is compliance theater. The transferable lesson is the threat model, and it upgrades my doctrine from three words to a sentence:
Zero-dependency keeps third-party code out of the runtime. Zero-trust distribution keeps attackers out of the delivery path. A tool needs both or it has neither.
Concretely, going forward:
- Every release ships with signed checksums — Sigstore keyless signing through GitHub Actions makes this ~15 lines of YAML, no key management for a solo maintainer
- Every install instruction includes a verification
step, even one line of
sha256sum - Every repo assumes its own update channel will be attacked eventually, and plans for recovery, not just prevention
The Meta-Lesson
The scanner taught me to audit strangers’ packages. TUF taught me to audit the one package nobody thinks to suspect — my own installer.
Your supply chain includes the rope you pull things up with.
Takeaway: audit your installer before you praise your dependencies.