Serve and Publish Repositories
SOW writes static files; it is not an HTTP server. This guide keeps the writable Workspace separate from the path Nginx serves.
Public and private paths
| Mode | Public unit | Never serve |
|---|---|---|
| Plain | the directory passed to sow create |
transient .sow-plain-stage-* output; no durable journal |
| Managed | one Repository’s complete pool/ + dists/ tree |
Workspace sow.yml, .sow/, SQLite, locks, journals, staging |
For the workspace in First Workspace, the source Repository is
/srv/sow/local. Do not make /srv/sow the document root.
1. Gate the source Generation
Continue only when check returns 0. status is useful for diagnosis, but check is
the full read-only delivery proof.
2. Configure a filesystem target
Create the endpoint directory first. It must be a real, canonical directory, not a symlink; SOW refuses to create a missing endpoint for you.
The second command gives the current operator write access; use the account that will run
sow publish if publication runs under a dedicated service user.
Add a target to /srv/sow/sow.yml:
The three booleans are mandatory safety acknowledgements. The endpoint and prefix combine
to /srv/repo-public/local; SOW creates and owns the prefix below the pre-existing endpoint.
Validate and publish:
Publication copies immutable payloads and metadata before mutable protocol pointers, verifies the result, and records a target checkpoint. Repeating the command for an unchanged Generation is an idempotent no-op.
Do not let another tool write into the same target prefix. The target contract is single-writer and exclusive.
3. Serve the target with Nginx
Reload Nginx after validating its configuration. The client URLs are:
If metadata or packages are signed, publish the corresponding public key separately and
configure gpgkey/Signed-By; private keys never belong under the document root.
4. Verify the served entry points
Then run the actual package manager from a client host. HTTP reachability is not package client verification; both checks matter.
The complete Repository prefix must have one access policy. RPM metadata may resolve a
package through ../../../pool/..., and APT Filename fields point to pool/... directly.
Protecting dists/ while accidentally exposing or blocking pool/ breaks the repository.
Manual and air-gapped delivery
If sow publish cannot reach the destination:
- run
sow checkon the source; - copy the complete Repository into a new, non-live staging or release directory;
- verify transport checksums against
sow changes 0or an archive manifest; - atomically switch an operator-owned parent reference to the new directory;
- keep the previous release until clients and caches have moved past it.
Do not run an unordered rsync --delete directly against a live Repository root. That
does not preserve SOW’s pointer ordering, target checkpoint, cache grace, or recovery state.
sow changes describes Generation differences; it is not an authorization to mutate a
live target without those controls.
R2 targets
provider: r2 uses an S3-compatible storage transport and report-only target GC. The
transport integration covers list, HEAD, GET, and conditional PUT against a pinned MinIO
fixture. Validate credentials, bucket policy, public endpoint, cache behavior, replay, and
recovery on a nonproduction prefix before enabling a production target. See
Platforms & Integrations.