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.4 Managed workflow. A Repository created by SOW 0.4 already has the current single-payload layout and schema.

After upgrading an existing v0.3 Workspace, however, migration is mandatory: 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.4.0
sow repo migrate pigsty -C /srv/sow
sow repo migrate pgsql -C /srv/sow

The 0.4 transition installs schema v11 and v12. 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 SOW 0.3, 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.

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