<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Hermeto]]></title><description><![CDATA[Hermeto]]></description><link>https://priyanshu-u07blog.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 27 Oct 2026 22:58:03 GMT</lastBuildDate><atom:link href="https://priyanshu-u07blog.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Hermeto: Hermetic Builds and DEB support]]></title><description><![CDATA[How Hermeto enables hermetic container builds and how a DEB backend can bring the same reproducibility to Debian/Ubuntu images.]]></description><link>https://priyanshu-u07blog.hashnode.dev/hermeto-hermetic-builds-and-deb-support</link><guid isPermaLink="true">https://priyanshu-u07blog.hashnode.dev/hermeto-hermetic-builds-and-deb-support</guid><category><![CDATA[Devops]]></category><category><![CDATA[hermetic-builds]]></category><category><![CDATA[Open Source]]></category><dc:creator><![CDATA[Priyanshu Kumar]]></dc:creator><pubDate>Sat, 28 Mar 2026 18:21:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69b9dfc7c22d3eeb8a0e1eb3/fa91ad5a-b8b6-4616-bff0-adb09a8f076a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>About Hermeto</h2>
<p>Hermeto is a CLI tool that pre-fetches our project’s dependencies so we can build our application without needing internet access. This is called a hermetic build. A build that is completely isolated from external influences.</p>
<p>Think of it like going grocery shopping before cooking. Instead of running to the store in the middle of a recipe (downloading packages during the build), you buy everything beforehand (pre-fetch), bring it home (store locally), and then cook with the kitchen door locked (a network-isolated build).</p>
<p>Hermeto is used by CI platforms like Konflux CI to make sure every container build is reproducible, secure, and auditable.</p>
<h2>Why Hermetic Builds Matter</h2>
<p>When building a container image, a Dockerfile often looks like this:</p>
<p><code>FROM python:3.12</code></p>
<p><code>RUN pip install flask requests</code></p>
<p>This downloads <code>flask</code> and <code>requests</code> from the internet during the build. That creates three serious problems:</p>
<ul>
<li><p>Not reproducible: if the package gets updated between builds, you get differen results.</p>
</li>
<li><p>Not secure: a supply chain attack could swap the real package with a harmful one.</p>
</li>
<li><p>Not auditable: you don't have a record of exactly what went into your build.</p>
</li>
</ul>
<p>Hermeto solves all three problems. It pre-downloads dependencies, verifies their checksums and generates an SBOM (Software Bill of Materials) listing every dependency used in the build.</p>
<h2>How Hermeto Works</h2>
<p>Hermeto has three main commands that work together like a pipeline.</p>
<p>Step 1: <code>fetch-deps</code>- Download Everything</p>
<p>This command reads the lockfile in your project (like <code>go.sum</code>, <code>package-lock.json</code> or <code>requirements.txt</code>), downloads every dependency listed in it, verifies checksums and saves everything to the output directory. It also generates an SBOM.</p>
<p>Step 2: <code>generate-env</code>: Set Up Environment Variables</p>
<p>Some package managers need to know where the cached dependencies are. This command generates a shell script that sets the right environment variables so that tools like <code>go</code>, <code>pip</code> or <code>npm</code> look at the local cache instead of the internet.</p>
<p>Step 3: <code>inject-files</code>: Configure For Offline Use</p>
<p>Some package managers need extra configuration files. For example, the RPM backend generates repository metadata and <code>.repo</code> files so that <code>dnf</code> can install packages from the local cache.</p>
<p>Building Offline:</p>
<p>After these three steps, you can build your container with no network access:</p>
<p>The <code>--network none</code> flag blocks all internet access. Since all dependencies are already downloaded and available locally, the build works perfectly without needing the internet.</p>
<h2>Hermeto's Architecture</h2>
<p>Package Manager Backends</p>
<p>Hermeto supports multiple package ecosystems through a backend system. Each backend knows how to read a specific lockfile format, download the right packages and generate the correct SBOM entries.</p>
<p>Currently supported backends:</p>
<table>
<thead>
<tr>
<th>Backend</th>
<th>Ecosystem</th>
<th>Lockfile</th>
</tr>
</thead>
<tbody><tr>
<td>gomod</td>
<td>Go</td>
<td><code>go.sum</code></td>
</tr>
<tr>
<td>pip</td>
<td>Python</td>
<td><code>requirements.txt</code></td>
</tr>
<tr>
<td>npm</td>
<td>JavaScript</td>
<td><code>package-lock.json</code></td>
</tr>
<tr>
<td>yarn</td>
<td>JavaScript</td>
<td><code>yarn.lock</code></td>
</tr>
<tr>
<td>cargo</td>
<td>Rust</td>
<td><code>Cargo.lock</code></td>
</tr>
<tr>
<td>bundler</td>
<td>Ruby</td>
<td><code>Gemfile.lock</code></td>
</tr>
<tr>
<td>rpm</td>
<td>Red Hat / Fedora</td>
<td><code>rpms.lock.yaml</code></td>
</tr>
<tr>
<td>generic</td>
<td>Any</td>
<td><code>artifacts.lock.yaml</code></td>
</tr>
</tbody></table>
<h2>How a Backend Works Internally</h2>
<ul>
<li><p>Every Hermeto backend follows the same pattern:</p>
</li>
<li><p>Read the lockfile (project dependency list)</p>
</li>
<li><p>Download all packages using Hermeto’s async downloader</p>
</li>
<li><p>Verify checksums and file sizes</p>
</li>
<li><p>Generate SBOM entries (PURLs) for every dependency</p>
</li>
</ul>
<p>Here is how all backends are registered in Hermeto's resolver:</p>
<pre><code class="language-python"># hermeto/core/resolver.py

_package_managers = {
    "bundler": bundler.fetch_bundler_source,
    "cargo":   cargo.fetch_cargo_source,
    "gomod":   gomod.fetch_gomod_source,
    "npm":     npm.fetch_npm_source,
    "pip":     pip.fetch_pip_source,
    "yarn":    metayarn.fetch_yarn_source,
    "generic": generic.fetch_generic_source,
    "rpm":     rpm.fetch_rpm_source,
}
</code></pre>
<p>When you run <code>hermeto fetch-deps gomod</code>, Hermeto looks up <code>"gomod"</code> in this dictionary and calls <code>gomod.fetch_gomod_source()</code>. Adding a new backend means writing the fetch function and adding it to this dictionary.</p>
<h2>The RPM Backend</h2>
<p> The RPM backend is the closest reference for the DEB backend.</p>
<p>Here is how it works:</p>
<p>Input: The RPM backend reads <code>rpms.lock.yaml</code> which lists every RPM package with its download URL, checksum and repository ID.</p>
<p>Fetch: It downloads all <code>.rpm</code> files using <code>async_download_files()</code>. Hermeto's shared async HTTP downloader. No <code>dnf</code> or <code>yum</code> is used during download, because Hermeto avoids running arbitrary code from package managers.</p>
<p>Verify: After download, it verifies every file's checksum and size against the lockfile.</p>
<p>SBOM: It queries each downloaded <code>.rpm</code> file using <code>rpm -q</code> to extract metadata (name, version, vendor) then generates PURL entries.</p>
<p>Inject-files: It runs <code>createrepo_c</code> to generate repository metadata, then creates <code>hermeto.repo</code> files so that <code>dnf</code> can find the local packages during offline builds.</p>
<h2>The DEB Backend</h2>
<p> Adding Debian/Ubuntu Support to Hermeto</p>
<p>Hermeto supports RPM packages for Red Hat-based images, but a lot of container images use Debian or Ubuntu as their base.These images use <code>.deb</code> packages and <code>apt</code> for package management.</p>
<p>Right now, Hermeto cannot pre-fetch <code>.deb</code> packages, which means Debian-based builds cannot be hermetic.</p>
<p>A DEB backend for Hermeto can follow the same pattern as the existing RPM backend: Lockfile → Fetch → Verify → SBOM → Inject</p>
<h2>Key Differences Between RPM and DEB</h2>
<table>
<thead>
<tr>
<th>Aspect</th>
<th>RPM</th>
<th>DEB</th>
</tr>
</thead>
<tbody><tr>
<td>Package format</td>
<td><code>.rpm</code></td>
<td><code>.deb</code></td>
</tr>
<tr>
<td>Package manager</td>
<td><code>dnf</code> / <code>yum</code></td>
<td><code>apt</code> / <code>dpkg</code></td>
</tr>
<tr>
<td>Lockfile</td>
<td><code>rpms.lock.yaml</code></td>
<td><code>debs.lock.yaml</code> (new)</td>
</tr>
<tr>
<td>Vendor</td>
<td>Always <code>redhat</code></td>
<td><code>debian</code> or <code>ubuntu</code></td>
</tr>
<tr>
<td>Repo metadata tool</td>
<td><code>createrepo_c</code></td>
<td><code>dpkg-scanpackages</code></td>
</tr>
<tr>
<td>Repo config format</td>
<td><code>.repo</code> (INI format)</td>
<td><code>sources.list</code> (one-liner)</td>
</tr>
<tr>
<td>PURL type</td>
<td><code>pkg:rpm</code></td>
<td><code>pkg:deb</code></td>
</tr>
</tbody></table>
<h2>The Core Challenge</h2>
<ol>
<li>No Standard Lockfile</li>
</ol>
<p>Unlike npm (<code>package-lock.json</code>) or Cargo (<code>Cargo.lock</code>), the Debian ecosystem has no lockfile format. When you run <code>apt-get install curl</code>, apt resolves dependencies, downloads packages and installs them but nothing pins the exact versions with checksums.</p>
<p>For a DEB backend, a lockfile such as <code>debs.lock.yaml</code> is needed to fill this gap. All required fields (URLs, versions, SHA256 checksums, sizes) are directly available in the Debian repository’s <code>Packages.gz</code> metadata.</p>
<p>2.The Lockfile Generator</p>
<p>Since no standard lockfile exists, a companion tool will generate <code>debs.lock.yaml</code> by taking package names as input, fetching <code>Packages.gz</code> from repositories, resolving transitive dependencies, and outputting a valid lockfile.</p>
<p>This follows the same approach as the rpm-lockfile-prototype project on GitHub for the RPM backend.</p>
<h2>SBOM: Software Bill of Materials</h2>
<p>It is a list of every component that goes into building your software, like an ingredient list on food packaging.Hermeto generates SBOMs in CycloneDX or SPDX format. Each dependency gets a Package URL (PURL) that uniquely identifies it:</p>
<h2>Hermeto's Design Principles</h2>
<p>Hermeto follows three strict rules:</p>
<ul>
<li><p>Never execute arbitrary code: Hermeto does not run <code>pip install</code>, <code>npm install</code>, or <code>apt-get install</code>. Instead, it downloads files directly via HTTP. This prevents supply chain attacks where a harmful package could run code during installation.</p>
</li>
<li><p>Everything must be defined:Dependencies must be explicitly declared in a lockfile.<br />No auto-detection and no guessing.</p>
</li>
<li><p>Everything must be reproducible: Every dependency must be pinned to an exact version. Running Hermeto twice with the same lockfile must produce the same result.</p>
</li>
</ul>
<p>These principles are what make Hermeto different from tools like <code>pip download</code> or <code>apt-get</code> download.</p>
<p> Hermeto is not a package manager. It is a dependency fetching tool that enables existing package managers to work without internet access.</p>
]]></content:encoded></item></channel></rss>