Capability Overview

What SOW covers across both execution paths, both package formats, signing, and platforms — and how it compares to createrepo_c and reprepro.

SOW is a self-contained software repository manager: a single static Go binary (CGO_ENABLED=0) that creates and maintains APT (DEB) and YUM (RPM) repositories on Linux and macOS. It does not call createrepo_c, dpkg-scanpackages, reprepro, or modifyrepo_c, 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.

The current release is sow 0.2.0-dev.

Two execution paths

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.

Plain modeManaged mode
Entry commandsow createsow init / repo / dist / add / build
Inputone directory of .rpm / .deb filespackages added by path into a workspace
Layout producedflat — packages and index in the same directoryDebian-style pool/ plus dists/ publication views
Persistent statenone (a transient journal during the operation only)sow.yml plus one SQLite database per repository
Configurationnone — no config file is readsow.yml, strictly validated
Multi-architectureall architectures land in one flat indexone rendered view per architecture per Dist
Historynonemonotonic generations and an operation ledger
Comparable tocreaterepo_c + dpkg-scanpackagesreprepro

Plain mode is the right choice when you have a directory of packages and want an index over it. Managed mode is the right choice when the same repository is going to be updated repeatedly over months, by policy, with an audit trail.

Neither path knows anything about remote endpoints. A finished repository directory is just files — you serve it with any web server or copy it with rsync. See Serve Repositories.

Format coverage

CapabilityRPM / YUMDEB / APT
Index files generatedrepodata/ with primary, filelists, other and repomd.xmlPackages, Packages.gz, Release
ChecksumsSHA-256, checksum-named metadata filesSHA-256 only (no MD5Sum/SHA1)
Package facts read fromRPM header (never the filename)control in the .deb archive
Coordinate (identity)NEVRAname=version:arch
Architecture views (Managed)x86_64, aarch64binary-amd64, binary-arm64
Architecture-neutral packagesnoarchall
by-hash index fetchnot applicableyes, Acquire-By-Hash: yes
Metadata signaturerepodata/repomd.xml.ascInRelease and Release.gpg
Package signatureembedded OpenPGP signature, fill / always modesnot applicable

A single command handles both formats at once. In Plain mode, a directory containing both .rpm and .deb files produces repodata/ and Packages in one operation. In Managed mode, one Repository can own an RPM Dist and a DEB Dist that share the same pool/.

Signing coverage

There are two independent trust chains, configured separately. See Signing Model for the full picture.

ChainPlain modeManaged modeVerified by the client with
Repository metadatanot availablesigning.rpm.metadata.key, signing.deb.metadata.keydnf repo_gpgcheck=1, apt Signed-By
RPM package bodiescreate -S KEY [--overwrite]signing.rpm.packages.mode: fill | alwaysdnf gpgcheck=1

Metadata keys given as file:// or env:// are used by an in-process Go signer, so no external tool is needed. RPM package signing and agent:// key references call the rpm and gpg binaries in your environment.

Platform coverage

The binary builds for darwin and linux on amd64 and arm64. There is no runtime dependency on a package manager, a database server, or a Python stack.

The only external programs SOW will ever invoke are rpm (for RPM package signing) and gpg (for agent:// key references). A repository that uses no signing, or uses file:///env:// metadata keys only, needs nothing installed beyond the sow binary itself.

One hard requirement applies to Managed mode: a repository’s pool/ and its dists/ views must live on the same POSIX filesystem, because views are hardlink projections. Crossing a device boundary is a hard failure, never a silent copy. See Pool & Architecture Views.

Client compatibility

Every combination below was exercised against a real client:

ClientVersionResult
AlmaLinux 8 / 9 / 10 dnfdnf4makecache and install with repo_gpgcheck=1 and gpgcheck=1
CentOS 7 yum3.4.3makecache and correct multi-version NEVRA listing
Debian 13 apt3.0.3update with InRelease verification and by-hash fetch, then install
Debian 12 apt2.6.1same, plus flat repositories via [trusted=yes]
dnf reposyncEL9complete mirror following the pool/ layout

The full matrix, including the by-hash requirement of APT ≥ 1.2, is in Compatibility.

Compared with createrepo_c and reprepro

These are the tools SOW is measured against. The comparison below reflects side-by-side runs over the same package sets, not documentation claims.

DimensionSOWcreaterepo_creprepro
RPM metadataprimary/filelists/other, semantically equivalentbaseline
sqlite repodatanot generated (explicit non-goal)generated by default
DEB Packages fieldsequivalent, SHA-256 onlybaseline, emits MD5Sum + SHA1 + SHA256
by-hashsupported (Acquire-By-Hash: yes)not supported
Pool layoutpool/<prefix>/<source>/ (no component level)pool/main/<prefix>/<source>/
Per-architecture Release stubsnot generated (apt does not need them)generated
PlatformsLinux and macOS, single binaryLinux in practiceLinux only
Transactions and crash recoveryjournal with roll-forward/rollbacknonedatabase is fragile
Auditoperation ledger with JSONL exportnonelimited logging

Two details are worth spelling out, because they surprise people migrating:

Against createrepo_c on 9 test packages and 87 real production packages, every field in primary, filelists, and other matched semantically — name, arch, EVR, checksum, sizes, provides, requires flags, files, changelog, header range. The single divergence: when an RPM header lists /bin/sh twice in both a pre and a non-pre context, SOW keeps one entry and createrepo_c keeps both.

Against dpkg-scanpackages, the Packages fields match, except that SOW emits only SHA256 — modern APT clients need nothing else — and omits absent fields such as Section entirely rather than writing an empty value.

If you are moving an existing repository over, read Migrate from createrepo_c / reprepro first; taking over a directory in place leaves the old tool’s files on disk for you to remove.

Performance anchors

Measured on macOS arm64, cold:

OperationScaleWall time
sow create9 RPMs0.31 s
sow create87 RPMs, 2.9 GB (full SHA-256)10.7 s
sow add + automatic build9 RPMs, 31 MB~1.3 s
sow check (all eight layers)16-package workspace0.12 s

Commands that parse, hash, render, or verify accept -j/--jobs N, defaulting to the logical CPU count. Parallelism never changes the output: final serialization runs in a fixed order, so the same input always produces the same bytes.

Deliberate non-goals

These are not missing features waiting to be built. They are excluded by design, and no empty command or hidden flag pretends otherwise:

  • modulemd generation, injection, or passthrough
  • sqlite repodata and zchunk
  • SRPM / DSC source indexes
  • remote publishing, CDN, object storage, or endpoint configuration of any kind
  • multi-writer and multi-host operation
  • garbage collection, cross-repository deduplication
  • a serving daemon or a web UI
  • building packages

SOW manages repositories on local disk and hands you a directory. What transports that directory is your choice.

Next

Last modified: 2026-08-08: init commit (fe725aa)