Skip to content

sow repo

List, create, inspect and remove Repositories — the lock, transaction and Generation boundary.

A Repository owns one pool/, one dists/, one SQLite database and one private state directory. It is the boundary of locking, transaction recovery, Generation numbering and Changesets — nothing is deduplicated across Repositories and no cross-Repository commit is atomic. sow repo manages that boundary.

Synopsis

sow repo ls [-C DIR] [--json]
sow repo new NAME [-C DIR] [-T DUR | -N] [--json]
sow repo show [NAME] [-C DIR] [-r NAME] [--json]
sow repo migrate [NAME] [--abort] [-j N] [-C DIR] [-r NAME] [-T DUR | -N] [--json]
sow repo rm NAME [-f|--force] [-C DIR] [-T DUR | -N] [--json]

Naming

A Repository name must match [a-z0-9][a-z0-9._-]* and may not be ., .., .sow, pool, dists, or collide with a Workspace reserved file.

sow repo new .sow
operation rejected: managed: operation rejected: name ".sow" must match [a-z0-9][a-z0-9._-]*

You cannot choose the path. A Repository always lives at <workspace>/<NAME>/.

sow repo ls

Read-only listing of every Repository in the Workspace.

sow repo ls
NAME	PROTECTED	DISTS	GENERATION	STATUS	PACKAGES	MEMBERSHIPS
infra	true	1	1	clean	0	0
pgsql	false	2	2	clean	0	0
Flag Description Default
-C, --workdir DIR Workspace discovery start directory current directory
--json Emit the versioned JSON envelope false

STATUS is one of clean, dirty, recovering or error. See Transactions & Recovery for what each one implies for clients.

sow repo new

Atomically updates sow.yml, then creates <workspace>/<NAME>/{pool,dists}, the SQLite database and the private state directory. A new Repository is Generation 0 and clean.

sow repo new pigsty
created pigsty: path=/srv/repo/pigsty protected=false dists=0 generation=0 status=clean packages=0 memberships=0
Flag Description Default
-C, --workdir DIR Workspace discovery start directory current directory
-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

repo new takes the Workspace lock, not a Repository lock — the Repository database does not exist yet. It does not accept -r; the positional argument already names the target.

Running it again on an existing Repository is a converging no-op that reports the current state, so it is safe in a provisioning script.

sow repo show

Read-only detail for one Repository. With NAME omitted, the usual Repository selection rules apply.

sow repo show pigsty
repository pigsty:
  path: /srv/repo/pigsty
  protected: false
  dists: 2
  generation: 6
  desired_revision: 6
  status: clean
  packages: 5
  memberships: 8
  config: {"protected":false,"signing":{"rpm":{"packages":{"mode":"never"}}},"dists":{"el9":{"format":"rpm","architectures":["x86_64","aarch64"],"limit":1,"exclude":[{"kind":["debuginfo","debugsource"]}]},"trixie":{"format":"deb","architectures":["x86_64","aarch64"],"limit":0,"exclude":null}}}
  dirty_reasons: []
  recent_operation: id=4142220455201181493 kind=add state=done error_class= created_at=2026-08-04T04:09:24.995538Z updated_at=2026-08-04T04:09:25.332772Z
Flag Description Default
-C, --workdir DIR Workspace discovery start directory current directory
-r, --repo NAME Select the repository when NAME is omitted selection rules
--json Emit the versioned JSON envelope false

If you give both NAME and -r, they must agree; disagreement fails before any state is read:

sow repo show demo -r empty
operation rejected: repo show NAME "demo" and --repo "empty" select different repositories

sow repo migrate

This is a specialized maintenance command, not part of a fresh 0.5 Managed workflow. New 0.5 Repositories use schema v13 and the current single-payload layout. Upgrading an existing v0.3 or v0.4 Workspace requires explicit migration: stop all Workspace writers, take a backup, and run the command once for every configured Repository before ordinary reads or writes.

cp -a /srv/sow /srv/sow.backup-before-0.5.0   # GNU cp; elsewhere use rsync -aH or tar
sow repo migrate pigsty -C /srv/sow
sow repo migrate pgsql -C /srv/sow

The backup must preserve hard links (DEB by-hash entries are hard links); macOS cp -a does not. The output reports the schema change, for example migrated repository pigsty: single-payload-v1 -> single-payload-v1 schema=12->13 …; JSON has schema_from and schema_to, and a repeated migration reports schema=13->13. Until a Repository is migrated, its write commands exit 5 with repository schema v12 predates this binary (v13); back up the workspace, then run `sow repo migrate NAME`.

Schema v13 adds indexes for candidate pool-path ownership lookups, avoiding full publication-history scans on each add. Index creation costs time and private database space once during migration. Run sow build and sow check afterwards to refresh affected RPM authentication and APT metadata contracts. A v0.3 database also receives the v11/v12 repairs: it recomputes Repository status from every Dist, repairs publication and Generation signer projections without guessing a missing historical signer, removes stale abandoned-object evidence, and backfills revision 1 of the append-only publication-target binding ledger. An unrecorded v0.3 historical signer remains explicitly unverified; it cannot reach the current head or become a retained trust assertion.

The completed schema transition is one-way. Do not reopen the database with an older SOW binary, and do not edit PRAGMA user_version. --abort applies only to a diagnosed pre-commit layout-maintenance attempt; it does not undo a completed schema migration. Outside an upgrade or an explicit SOW diagnostic, do not run migration speculatively.

Starting a new conversion from a pre-release C2 layout is unsupported. Preserve its files and rebuild with a supported release. An already recorded layout transition may still complete its bound plan, but the result is not a supported 0.5 Repository: the next write may still build one Generation, after which the Repository reports status=error. Rebuild it from the original packages.

Flag Description Default
-j, --jobs N Parallel verification/render workers logical CPUs
--abort Abandon a maintenance attempt before its commit decision false
-C, --workdir DIR Workspace discovery start directory current directory
-r, --repo NAME Select the repository when NAME is omitted 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

sow repo rm

Removes a Repository: its sow.yml entry, database, pool/, dists/ and private state. It never follows symlinks and never steps outside the fixed Repository path.

Without -f, only an empty Repository — no Dists, no Memberships, no Package Objects — can be removed:

sow repo rm infra
removed repository infra
sow repo rm pgsql
operation rejected: managed: operation rejected: repository "pgsql" is not empty; use --force
sow repo rm pgsql -f
removed repository pgsql
Flag Description Default
-f, --force Remove a non-empty unprotected repository false
-C, --workdir DIR Workspace discovery start directory current directory
-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

What -f actually downgrades

-f only relaxes the emptiness precondition. It does not bypass path safety, symlink refusal, or the protected gate.

protected

protected: true in sow.yml blocks Repository deletion outright, -f included:

sow repo rm alpha -f
operation rejected: managed: operation rejected: repository "alpha" is protected

To remove a protected Repository you must edit sow.yml, pass sow config check, and try again. There is no --yes and no temporary override.

protected scopes to Repository deletion only. Package-level work on a protected Repository is unaffected — add, rm, build, and even dist rm, all continue to work:

sow dist rm el9 -r alpha -f
removed dist el9 from alpha

Examples

Create the Repositories for a two-tier layout:

sow repo new infra
sow repo new pgsql

Fail fast in a cron job rather than queue behind another writer:

sow repo new nightly -N || echo "another writer holds the workspace lock"

Audit every Repository in one line each:

sow repo ls --json | jq -r '.result.repositories[] | "\(.name)\t\(.status)\tgen=\(.generation)"'

Exit codes

Code Trigger
0 Listed, created, shown, migrated, abandoned a pre-commit transition, or removed; or repo new converged an existing Repository
1 Runtime I/O error creating or removing the tree
2 Usage error, Workspace not found, or an ambiguous Repository selection
4 Workspace lock held and --no-wait given or --timeout expired
5 Integrity or recovery error in the Workspace journal
6 Invalid name, unknown Repository, non-empty without -f, protected, or NAME conflicting with -r

See also