Crucible / Docs

Publish packages and releases

Use one authoring clone per registry and serialize publishers for that clone. The local clone has a release lock, but two machines can still race when they publish the same remote pointers.

#Set up an authoring clone

Create a new signed registry as shown in the quickstart. To attach a smart Git remote at creation time:

apr create acme \
  --remote ssh://git@code.example.com/acme/registry.git \
  --trust-key "$TRUST_KEY" \
  --trust-key-id initial \
  --key "$KEY"
apr push --registry acme --branch stable --set-upstream

To take over an existing registry, configure its public URL and trust anchor with apr add. When invoked as apr, the command materializes an authoring clone as well as consumer state:

apr add https://packages.example.com/acme/ \
  --name acme \
  --trust-key 'acme:Ed25519:BASE64_KEY'

Register locally held signing keys by roster id:

apr keys register initial \
  --key /secure/registry/acme-initial.key \
  --registry acme

An external secret manager can be used without writing its key to APM config:

apr keys register initial \
  --key-command 'secret-tool lookup service aos-registry key initial' \
  --registry acme

The command string is saved in local registry configuration and run through a shell. Keep secrets out of the string itself, and ensure the command writes only an OpenSSH private key to standard output.

#Publish one package

Build the package first, then publish its store path:

STORE_PATH="$(nix build .#pkg-curl --no-link --print-out-paths)"

apr publish "$STORE_PATH" \
  --registry acme \
  --description "Command-line URL transfer tool" \
  --homepage https://curl.se/ \
  --license curl \
  --maintainer registry@example.com \
  --key-id initial

apr publish writes package metadata and a realization record for every runtime-closure member. It creates a signed commit unless --no-commit is given. Use --no-commit only when deliberately grouping several changes; the final commit still needs a trusted signature.

For a grouped change, commit only the intended registry paths with apr commit. It uses the same in-process signer as the other producer commands, requires a trusted key when keys.toml has an active roster, and refuses an already-staged index so unrelated maintainer state cannot leak into the commit. apr sign is for release tags; it does not sign commits:

apr commit packages store registry.toml \
  --registry acme \
  --key-id initial \
  --message "publish the 2026.8 package set"
apr verify --registry acme

Review apr status and apr diff before committing. Name every intended path explicitly; apr commit does not retain a shell-out signing path or silently fall back to a broad git add -A.

Useful package forms include:

# Publish an OS toplevel.
apr publish /nix/store/HASH-aos-system-toplevel \
  --registry acme --sysroot --key-id initial

# Associate prebuilt image artifacts with a sysroot package.
apr publish /nix/store/HASH-aos-system-toplevel \
  --registry acme --sysroot \
  --image-payload /nix/store/HASH-aos-image-qcow2 \
  --image-disk /nix/store/HASH-aos-image-qcow2-disk \
  --image-info /nix/store/HASH-aos-image-qcow2-info \
  --image-format qcow2 --image-uki /nix/store/HASH-uki/aos.efi \
  --image-payload /nix/store/HASH-aos-image-raw \
  --image-disk /nix/store/HASH-aos-image-raw-disk \
  --image-info /nix/store/HASH-aos-image-raw-info \
  --image-format raw --image-uki /nix/store/HASH-uki/aos.efi \
  --key-id initial

Provide one payload, regular-file disk output, regular-file metadata output, format, and exact UKI for each image encoding. The disk and metadata outputs are published through the registry's Nix cache; the payload supplies layout, recovery, and update facts for validation. --previous records the prior package version when maintaining a version chain. --source-drv records source material used by apm source.

#Inspect before release

Review the authoring state and verify that the local store still contains the published closure:

apr status --registry acme
apr diff --registry acme
apr verify --registry acme
apr store verify --registry acme --deep

apr verify checks metadata and closure-graph consistency. apr validate checks that referenced artifacts are reachable from an advertised cache and is most useful after an upload:

apr validate --registry acme --jobs 32

#Run the complete release pipeline

For routine publication, prefer apr release over a hand-assembled sequence:

apr release 2026.8.0 \
  --registry acme \
  --key-id initial \
  --cache-key /secure/registry/acme-narinfo.key \
  --cache-url https://packages.example.com/acme/ \
  --upload-url s3://acme-packages/registry

The positional version is the registry snapshot version and has no v prefix. It does not need to match any package version.

When --store-path is supplied, one command publishes the package and the release:

apr release 2026.8.1 \
  --registry acme \
  --store-path "$STORE_PATH" \
  --description "Command-line URL transfer tool" \
  --license curl \
  --maintainer registry@example.com \
  --key-id initial \
  --cache-url https://packages.example.com/acme/ \
  --upload-url s3://acme-packages/registry

The package version still defaults to the version parsed from the store-path basename. Use --version when the output name does not carry its package version, or when the package version intentionally differs from the registry snapshot version. Attached image metadata must name that exact package version.

--cache-key is optional. When present, it is a Nix narinfo signing key in name:base64-secret form. It is not the OpenSSH maintainer key used by --key-id.

Preview the ordered plan without mutation:

apr release 2026.8.1 \
  --registry acme \
  --key-id initial \
  --cache-url https://packages.example.com/acme/ \
  --upload-url s3://acme-packages/registry \
  --dry-run

Use --resume after an interrupted release. It reuses immutable artifacts only when they match the current release state. Do not use it to paper over an unknown or conflicting publisher.

#Publish to a smart Git remote

apr push pushes a branch; it does not publish the static cache, channel files, or every signed tag:

apr push --registry acme --branch stable
git -C "$HOME/.local/share/apm/registries/acme" \
  push origin refs/tags/2026.8.0

A smart Git remote is useful for maintainer collaboration and branch or tag consumers. A production channel also needs the generated static origin over HTTP. The simplest production arrangement is therefore to use Git as the authoring remote and apr release --upload-url ... for the consumer surface.

#Use focused commands for repair

The release pipeline is composed from commands that remain available for inspection and recovery:

CommandUse
apr tag VERSION --key-id IDCreate a signed release tag
apr sign VERSION --key-id IDRe-sign an existing release tag
apr cache generateRegenerate or upload the static binary cache
apr origin uploadRefresh and upload the static Git origin
apr store backfillRecord closures for older package entries
apr store blessAdd an accepted realization
apr store revokeRevoke an accepted realization

Use these when the intended result is already clear. For a normal release, the guarded apr release ordering is safer and easier to audit.