sow dist
A Dist is a named set of packages in exactly one format (rpm or deb) inside one Repository. It is
what a client points at. A Repository can hold RPM and DEB Dists side by side; they share one pool/
but render into completely separate dists/ subtrees.
Synopsis
sow dist ls [-C DIR] [-r NAME] [--json]
sow dist new NAME --format rpm|deb [-C DIR] [-r NAME] [-T DUR | -N] [--json]
sow dist show NAME [-C DIR] [-r NAME] [--json]
sow dist rm NAME [-f|--force] [-C DIR] [-r NAME] [-T DUR | -N] [--json]
Naming
Dist names follow the same rule as Repository names: [a-z0-9][a-z0-9._-]*, excluding ., ..,
.sow, pool and dists.
To SOW the name is an opaque string. el9, trixie, el9-beta, customer-acme, 2026-07-31 are
all just names — beta channels, per-customer views and snapshots are naming conventions you impose,
not features SOW models.
sow dist ls
Read-only flat listing of the selected Repository’s Dists.
sow dist ls -r pigsty
NAME FORMAT ARCHITECTURES DESIRED BUILT GENERATION DIRTY DIRTY_REASONS
el9 rpm x86_64,aarch64 0 0 1 false []
trixie deb x86_64,aarch64 0 0 2 false []
DESIRED and BUILT are membership counts. When they diverge, DIRTY_REASONS says why:
sow dist ls -r demo
NAME FORMAT ARCHITECTURES DESIRED BUILT GENERATION DIRTY DIRTY_REASONS
el9 rpm x86_64,aarch64 2 1 4 true ["Desired and Built membership sets differ"]
| Flag | Description | Default |
|---|---|---|
-C, --workdir DIR | Workspace discovery start directory | current directory |
-r, --repo NAME | Select a repository | selection rules |
--json | Emit the versioned JSON envelope | false |
Architectures print as canonical families. The JSON output carries both spellings, which is how you
confirm that a DEB Dist renders binary-amd64 and binary-arm64:
"architectures":[{"family":"x86_64","ecosystem_arch":"amd64"},{"family":"aarch64","ecosystem_arch":"arm64"}]
sow dist new
Creates an ordinary, still-editable Dist. The only business argument is --format.
sow dist new el9 --format rpm -r pigsty
created el9: format=rpm architectures=x86_64,aarch64 members=0/0 generation=1 dirty=false
sow dist new trixie --format deb -r pigsty
created trixie: format=deb architectures=x86_64,aarch64 members=0/0 generation=2 dirty=false
| Flag | Description | Default |
|---|---|---|
--format FORMAT | Required; rpm or deb | — |
-C, --workdir DIR | Workspace discovery start directory | current directory |
-r, --repo NAME | Select a repository | selection rules |
-T, --timeout DUR | Maximum lock wait; 0 waits indefinitely | 0 |
-N, --no-wait | Fail immediately when the lock is held | false |
--json | Emit the versioned JSON envelope | false |
--format is mandatory and closed:
sow dist new x -r alpha
usage error: dist new requires --format rpm|deb
sow dist new x --format zip -r alpha
usage error: --format must be rpm or deb
There is no --arch. Architectures are inherited from the Workspace permit list; an advanced user
narrows them per Dist by editing sow.yml. Policy (limit, exclude) is likewise configured in
sow.yml, never re-modelled on the command line.
Re-running dist new with the same name and the same format converges and reports the current state.
A name collision with a different format is rejected:
sow dist new el9 --format deb -r alpha
operation rejected: managed: operation rejected: dist "el9" already exists with format rpm
The three-way transaction
dist new is committed across three places at once: the sow.yml entry, the Repository database,
and the on-disk tree. It goes through the SQLite Operation Journal (the Repository database already
exists at this point, unlike repo new) and produces a new Built Generation with empty indexes.
That means a fresh Dist is immediately consumable. An RPM Dist gets an empty repodata/ under every
architecture view; a DEB Dist gets empty Packages, Packages.gz, the by-hash/SHA256/ entries and
a signed-if-configured Release.
sow dist show
Read-only detail for one Dist.
sow dist show trixielim -r pgsql
dist trixielim:
format: deb
architectures: x86_64,aarch64
desired_members: 3
built_members: 3
generation: 6
status: clean
dirty: false
dirty_reasons: []
| Flag | Description | Default |
|---|---|---|
-C, --workdir DIR | Workspace discovery start directory | current directory |
-r, --repo NAME | Select a repository | selection rules |
--json | Emit the versioned JSON envelope | false |
The JSON form additionally exposes effective_config_sha256, the digest of the resolved Dist
configuration. That digest is what makes a Dist dirty when you change limit, exclude or a signing
key — the config identity changed, so the Built Generation no longer matches Desired.
sow dist show el9 -r pgsql --json
{"schema":"sow.cli/v1","command":"dist show","ok":true,"repository":"pgsql","operation":null,"result":{"name":"el9","format":"rpm","architectures":[{"family":"x86_64","ecosystem_arch":"x86_64"},{"family":"aarch64","ecosystem_arch":"aarch64"}],"desired_members":0,"built_members":0,"generation":1,"dirty":false,"status":"clean","effective_config_sha256":"a0b3ae2f943bc4fce951aaadda0fc8fb146ccf7944b0193a0dcc2b86ddc7ce7e","config":{"format":"rpm","architectures":["x86_64","aarch64"],"limit":1,"exclude":[{"kind":["debuginfo","debugsource"]}]}},"errors":[]}
sow dist show nope -r demo
operation rejected: managed: operation rejected: dist "nope" does not exist
sow dist rm
Removes a Dist’s Membership and derived indexes.
sow dist rm el9 -r pgsql
operation rejected: managed: operation rejected: dist "el9" is not empty; use --force
sow dist rm el9 -r pgsql -f
removed dist el9 from pgsql
| Flag | Description | Default |
|---|---|---|
-f, --force | Remove membership and indexes but retain pool packages | false |
-C, --workdir DIR | Workspace discovery start directory | current directory |
-r, --repo NAME | Select a repository | selection rules |
-T, --timeout DUR | Maximum lock wait; 0 waits indefinitely | 0 |
-N, --no-wait | Fail immediately when the lock is held | false |
--json | Emit the versioned JSON envelope | false |
Pool bytes always survive
Removing a Dist never deletes a package from pool/. The whole Dist directory is moved into the
recovery area and removed atomically; the pool is untouched:
sow dist rm el9 -r pgsql -f
removed dist el9 from pgsql
find pgsql -type f
pgsql/pool/e/epel-release/epel-release-7-5.noarch.rpm
There is no garbage collection. Orphaned pool objects stay until you remove the whole Repository.
A Repository’s protected: true blocks Repository deletion only; normal Dist maintenance on a
protected Repository continues to work.
Examples
Give one Repository an RPM and a DEB face:
sow dist new el9 --format rpm -r pgsql
sow dist new trixie --format deb -r pgsql
Add a beta channel with its own retention policy — create it, then set the policy in sow.yml and
converge:
sow dist new el9-beta --format rpm -r pgsql
$EDITOR sow.yml # el9-beta: { limit: 0 }
sow config check
sow build -r pgsql -d el9-beta
Which Dists are behind their Desired state:
sow dist ls -r pgsql --json | jq -r '.result.dists[] | select(.dirty) | .name'
Exit codes
| Code | Trigger |
|---|---|
0 | Listed, created, shown or removed; or dist new converged an existing Dist |
1 | Runtime I/O or renderer error creating the empty indexes |
2 | Usage error — missing or invalid --format, Workspace not found, ambiguous Repository |
4 | Repository lock held and --no-wait given or --timeout expired |
5 | Integrity or recovery error in the Operation Journal |
6 | Invalid name, unknown Dist, format conflict with an existing name, non-empty without -f |
See also
- sow repo — the layer above
- Pool & Architecture Views — how one Dist becomes several client-visible views
- Membership Policy — configuring
limitandexclude - sow build — converging after a policy edit
- Repository Layout — the RPM and DEB
dists/structures