<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Features on SOW</title><link>https://sow.pgsty.com/docs/feature/</link><description>Recent content in Features on SOW</description><generator>Hugo</generator><language>en</language><atom:link href="https://sow.pgsty.com/docs/feature/index.xml" rel="self" type="application/rss+xml"/><item><title>Capability Overview</title><link>https://sow.pgsty.com/docs/feature/overview/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sow.pgsty.com/docs/feature/overview/</guid><description>&lt;p&gt;SOW is a self-contained software repository manager: a single static Go binary (&lt;code&gt;CGO_ENABLED=0&lt;/code&gt;) that creates and maintains APT (DEB) and YUM (RPM) repositories on Linux and macOS. It does not call &lt;code&gt;createrepo_c&lt;/code&gt;, &lt;code&gt;dpkg-scanpackages&lt;/code&gt;, &lt;code&gt;reprepro&lt;/code&gt;, or &lt;code&gt;modifyrepo_c&lt;/code&gt;, and it does not run a daemon. This page is the map of what it covers; the rest of this section explains how each piece works.&lt;/p&gt;
&lt;p&gt;The current release is &lt;code&gt;sow 0.2.0-dev&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="two-execution-paths"&gt;Two execution paths&lt;/h2&gt;
&lt;p&gt;SOW gives you two ways to build a repository, and they are deliberately isolated from each other. Nothing is shared between them except the low-level package parsers, renderers, version comparators, locks, and safe file primitives.&lt;/p&gt;</description></item><item><title>Plain Flat Repositories</title><link>https://sow.pgsty.com/docs/feature/plain/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sow.pgsty.com/docs/feature/plain/</guid><description>&lt;p&gt;&lt;code&gt;sow create&lt;/code&gt; takes a directory that already contains &lt;code&gt;.rpm&lt;/code&gt; and &lt;code&gt;.deb&lt;/code&gt; files and writes an index over them, in place. That is the whole job. This page explains what it reads, what it writes, what it promises never to touch, and how it survives being killed halfway through.&lt;/p&gt;
&lt;h2 id="the-invariants"&gt;The invariants&lt;/h2&gt;
&lt;p&gt;Three rules hold for every &lt;code&gt;sow create&lt;/code&gt; run, and everything else follows from them:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;SOW only replaces index paths it owns.&lt;/strong&gt; Your packages, your &lt;code&gt;README&lt;/code&gt;, your leftover files from another tool — untouched. The one exception is &lt;code&gt;--pigsty&lt;/code&gt;, which is an explicit request to delete specific packages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Same input, same bytes.&lt;/strong&gt; Timestamps and compression parameters are fixed and sort order is stable, so re-running over an unchanged directory produces a byte-identical index and reports &lt;code&gt;noop=true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The commit is all-or-nothing per format pointer.&lt;/strong&gt; Metadata is fully generated and validated in a staging area on the same filesystem, and only then swapped in with atomic renames. If anything fails before the swap, the previous index keeps serving.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Plain mode has no workspace, no configuration file, and no database. It does not read &lt;code&gt;sow.yml&lt;/code&gt; even if one exists in a parent directory, and it never performs workspace discovery.&lt;/p&gt;</description></item><item><title>Managed Workspaces</title><link>https://sow.pgsty.com/docs/feature/managed/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sow.pgsty.com/docs/feature/managed/</guid><description>&lt;p&gt;Managed mode is what you use when the same repository will be updated for months: packages arrive in batches, policy decides what stays, and you need to prove afterwards what changed and when. This page explains the three-tier model, the layout it produces, and how commands figure out which repository and which Dist you meant.&lt;/p&gt;
&lt;h2 id="the-three-tiers"&gt;The three tiers&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Workspace discovery and configuration boundary
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── Repository ownership boundary: pool, dists, SQLite, lock, generations
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └── Dist a named membership set in exactly one format
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └── Architecture View a rendered projection — not a membership
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Each tier has one job, and the boundaries are strict:&lt;/p&gt;</description></item><item><title>Pool &amp; Architecture Views</title><link>https://sow.pgsty.com/docs/feature/views/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sow.pgsty.com/docs/feature/views/</guid><description>&lt;p&gt;If you list a Managed repository you will find the same package file at three different paths, and &lt;code&gt;du&lt;/code&gt; will insist it only occupies space once. That is not an illusion and it is not a symlink. This page explains the projection model — why it exists, what it guarantees, and the one constraint it puts on your filesystem.&lt;/p&gt;
&lt;h2 id="the-invariant"&gt;The invariant&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;The root pool owns the bytes. Everything under &lt;code&gt;dists/&lt;/code&gt; is a projection that can be deleted and rebuilt without touching an owned object.&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Membership Policy</title><link>https://sow.pgsty.com/docs/feature/policy/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sow.pgsty.com/docs/feature/policy/</guid><description>&lt;p&gt;Policy is the answer to &amp;ldquo;I dumped a build directory into this Dist and I do not want the debuginfo packages, and I only want the latest version of each package.&amp;rdquo; Two rules do that work, they run in a fixed order, and they run over the whole candidate set — not just the packages you happened to add this time.&lt;/p&gt;
&lt;h2 id="the-two-rules-and-their-order"&gt;The two rules and their order&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;candidate set → exclude → limit → Desired Membership
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;exclude&lt;/code&gt; drops packages that match a rule. &lt;code&gt;limit&lt;/code&gt; then caps how many versions survive per package name and architecture. The order is fixed and never configurable, because the reverse order would let an excluded package consume a version slot on its way out.&lt;/p&gt;</description></item><item><title>Signing Model</title><link>https://sow.pgsty.com/docs/feature/signing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sow.pgsty.com/docs/feature/signing/</guid><description>&lt;p&gt;There are two different questions a client can ask about a repository, and SOW answers them with two separate mechanisms. Confusing them is the most common source of &amp;ldquo;I signed it but &lt;code&gt;dnf&lt;/code&gt; still complains&amp;rdquo;, so this page starts by pulling them apart.&lt;/p&gt;
&lt;h2 id="two-independent-trust-chains"&gt;Two independent trust chains&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;/th&gt;
					&lt;th&gt;Metadata signing&lt;/th&gt;
					&lt;th&gt;RPM package signing&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Question answered&lt;/td&gt;
					&lt;td&gt;&amp;ldquo;Is this index really from you, and unmodified?&amp;rdquo;&lt;/td&gt;
					&lt;td&gt;&amp;ldquo;Is this &lt;code&gt;.rpm&lt;/code&gt; file really from you?&amp;rdquo;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Configured by&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;signing.rpm.metadata&lt;/code&gt;, &lt;code&gt;signing.deb.metadata&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;signing.rpm.packages&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Produces&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;repodata/repomd.xml.asc&lt;/code&gt;, &lt;code&gt;InRelease&lt;/code&gt;, &lt;code&gt;Release.gpg&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;an OpenPGP signature embedded in the package&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Changes package bytes&lt;/td&gt;
					&lt;td&gt;no&lt;/td&gt;
					&lt;td&gt;yes&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Client setting&lt;/td&gt;
					&lt;td&gt;dnf &lt;code&gt;repo_gpgcheck=1&lt;/code&gt;, apt &lt;code&gt;Signed-By&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;dnf &lt;code&gt;gpgcheck=1&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Available in Plain mode&lt;/td&gt;
					&lt;td&gt;no&lt;/td&gt;
					&lt;td&gt;yes, via &lt;code&gt;create -S KEY&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;They are configured separately and can be used separately. Metadata signing alone is usually the right starting point: it authenticates the whole index in one place and requires no change to the packages you received from upstream.&lt;/p&gt;</description></item><item><title>Transactions &amp; Recovery</title><link>https://sow.pgsty.com/docs/feature/transactions/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sow.pgsty.com/docs/feature/transactions/</guid><description>&lt;p&gt;Repository tools fail in one of two embarrassing ways: they leave an index that points at packages that no longer exist, or they corrupt their own database and require a rebuild from scratch. SOW is built to make both impossible, and this page explains the machinery that does it.&lt;/p&gt;
&lt;h2 id="the-invariant"&gt;The invariant&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;A client following a protocol pointer always reads a complete old view or a complete new view. There is no third option, including after a power loss.&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Observability &amp; Audit</title><link>https://sow.pgsty.com/docs/feature/audit/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sow.pgsty.com/docs/feature/audit/</guid><description>&lt;p&gt;Four read-only commands answer four different questions, and each one refuses to do the others&amp;rsquo; job. That separation is deliberate: a cheap status check that occasionally hashes the whole repository is useless in a loop, and a full verification that silently repairs things is useless as evidence.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Command&lt;/th&gt;
					&lt;th&gt;Question&lt;/th&gt;
					&lt;th&gt;Cost&lt;/th&gt;
					&lt;th&gt;Writes&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;status&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;What state is this repository in right now?&lt;/td&gt;
					&lt;td&gt;cheap, no hashing&lt;/td&gt;
					&lt;td&gt;never&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;check&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Can I prove this tree is correct and shippable?&lt;/td&gt;
					&lt;td&gt;full verification&lt;/td&gt;
					&lt;td&gt;never&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;changes&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Which files do I need to copy to bring a mirror up to date?&lt;/td&gt;
					&lt;td&gt;reads the generation manifest&lt;/td&gt;
					&lt;td&gt;never&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;log&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;What happened, when, and to which packages?&lt;/td&gt;
					&lt;td&gt;reads the ledger&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;prune&lt;/code&gt; only&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="status--cheap-state"&gt;&lt;code&gt;status&lt;/code&gt; — cheap state&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sow status
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;repository=pigsty status=clean ready_to_copy=true revision=4 generation=4 dirty_dists= pending=0/0 locked=false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;status&lt;/code&gt; never hashes the repository, never recovers an operation, and never builds. It reports the repository state, the Desired revision, the Built generation, which Dists are dirty, how many pending payload objects exist and how many bytes they occupy, whether a lock is held, and the most recent operation.&lt;/p&gt;</description></item></channel></rss>