Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 51 additions & 15 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ jobs:
echo "manifest=${manifest}"
} >> "${GITHUB_OUTPUT}"

- name: Verify lock-bearing release version
run: |
release/scripts/registry-release verify-registryctl-image-lock-release-version \
--version "${{ steps.release.outputs.version }}"

- name: Validate release manifest
run: release/scripts/registry-release validate "${{ steps.release.outputs.manifest }}"

Expand Down Expand Up @@ -191,6 +196,12 @@ jobs:
(cd dist/bin && sha256sum -- * > SHA256SUMS)
(cd dist/image-bin && sha256sum -- * > SHA256SUMS)

- name: Verify built registryctl binary version
run: |
release/scripts/registry-release verify-registryctl-binary-version \
"dist/bin/registryctl-${{ needs.verify.outputs.tag }}-linux-amd64" \
--version "${{ needs.verify.outputs.version }}"

- name: Upload binary artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
Expand Down Expand Up @@ -445,13 +456,43 @@ jobs:
name: registry-stack-registryctl-linux-arm64
path: dist/bin

- name: Refresh binary checksums
- name: Verify registryctl binary version
run: |
chmod 0755 "dist/bin/registryctl-${{ needs.verify.outputs.tag }}-linux-amd64"
release/scripts/registry-release verify-registryctl-binary-version \
"dist/bin/registryctl-${{ needs.verify.outputs.tag }}-linux-amd64" \
--version "${{ needs.verify.outputs.version }}"

- name: Download image evidence
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: registry-stack-image-evidence
path: dist/image-evidence

- name: Render registryctl release image lock
shell: bash
run: |
set -euo pipefail
mapfile -d '' -t relay_digests < <(find dist/image-evidence -type f -name registry-relay.digest -print0)
mapfile -d '' -t notary_digests < <(find dist/image-evidence -type f -name registry-notary.digest -print0)
if [[ "${#relay_digests[@]}" -ne 1 || "${#notary_digests[@]}" -ne 1 ]]; then
echo "Expected exactly one Relay and one Notary digest evidence file" >&2
exit 1
fi
release/scripts/registry-release render-registryctl-image-lock \
"${{ needs.verify.outputs.manifest }}" \
--relay-digest "${relay_digests[0]}" \
--notary-digest "${notary_digests[0]}" \
--tag-target "${{ needs.verify.outputs.tag_target }}" \
--output "dist/bin/registryctl-${{ needs.verify.outputs.tag }}-image-lock.json"

- name: Refresh release file checksums
shell: bash
run: |
set -euo pipefail
# The hermetic builder's SHA256SUMS covers only the linux/amd64 binaries.
# Regenerate over the full dist/bin set so the macOS/arm64 binaries are
# checksummed for the release capsule, SHA256SUMS asset, and install.sh.
# Regenerate over the full dist/bin set so cross-platform binaries and
# the registryctl image lock are covered by the capsule and installer.
(cd dist/bin && rm -f SHA256SUMS && sha256sum -- * > SHA256SUMS)

- name: Install Syft
Expand All @@ -469,28 +510,22 @@ jobs:
tar -xzf /tmp/registry-stack-tools/syft.tar.gz -C "${HOME}/.local/bin" syft
echo "${HOME}/.local/bin" >> "${GITHUB_PATH}"

- name: Generate binary SBOMs
- name: Generate release file SBOMs
shell: bash
run: |
set -euo pipefail
mkdir -p dist/binary-sbom
while IFS= read -r -d '' binary; do
asset="$(basename "${binary}")"
while IFS= read -r -d '' release_file; do
asset="$(basename "${release_file}")"
sbom="dist/binary-sbom/${asset}.spdx.json"
syft "file:${binary}" -o "spdx-json=${sbom}"
digest="$(sha256sum "${binary}" | awk '{print $1}')"
syft "file:${release_file}" -o "spdx-json=${sbom}"
digest="$(sha256sum "${release_file}" | awk '{print $1}')"
release/scripts/registry-release bind-spdx-file-subject \
"${sbom}" \
--file-name "${asset}" \
--sha256 "${digest}"
done < <(find dist/bin -maxdepth 1 -type f ! -name SHA256SUMS -print0 | sort -z)

- name: Download image evidence
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: registry-stack-image-evidence
path: dist/image-evidence

- name: Render release capsule
shell: bash
run: |
Expand All @@ -509,7 +544,8 @@ jobs:
--workflow-run-id "${{ github.run_id }}" \
--default-branch-protection unverified \
--repo . \
--default-branch origin/main
--default-branch origin/main \
--require-registryctl-image-lock

- name: Generate provenance subjects
id: provenance-subjects
Expand Down
15 changes: 15 additions & 0 deletions crates/registryctl/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,28 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [Unreleased]

### Added

- Release distributions now include a strict, versioned registryctl image lock
that binds the release source and exact Relay and Notary image digests. Project
generation fails before mutation when the matching lock is missing or invalid.

### Changed

- The installer checksum-verifies and installs the registryctl binary and image
lock together. Existing generated projects still start from their stored image
pins without consulting the lock.

### Fixed

- Generated local Relay and Notary configs now declare `deployment.profile: local`,
preserving fail-closed profile validation while allowing new projects to start.
- Aggregated doctor JSON now preserves each product report's optional
`audit_shipping` section and validates it against
`registryctl.validation.report.v1`.
- Generated project image pins now come from tag-built release evidence instead
of digests compiled into registryctl, closing the stale-pin failure tracked in
GH#278 without using mutable image tags or live registry lookup.

## [0.8.4] - 2026-07-04

Expand Down
52 changes: 37 additions & 15 deletions crates/registryctl/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ Install a pinned release without cloning this repo:
curl -fsSL https://raw.githubusercontent.com/registrystack/registry-stack/refs/tags/v0.8.4/crates/registryctl/install.sh | bash
```

The quick installer verifies the downloaded binary against `SHA256SUMS` only.
It does not verify cosign signatures or SLSA provenance; use
The quick installer verifies downloaded release assets against `SHA256SUMS`
only. It installs the binary for releases before `v0.9.0`; beginning with
`v0.9.0`, it installs the binary and matching image lock beside each other. It
does not verify cosign signatures or SLSA provenance; use
[`release/VERIFY.md`](../../release/VERIFY.md) for release authenticity checks.

Then create and start your first secured spreadsheet API:
Expand Down Expand Up @@ -52,18 +54,39 @@ The installer defaults to `v0.8.4`. To install a different pinned release, set
`REGISTRYCTL_VERSION`:

```sh
curl -fsSL https://raw.githubusercontent.com/registrystack/registry-stack/refs/tags/v0.8.4/crates/registryctl/install.sh | REGISTRYCTL_VERSION=vX.Y.Z bash
curl -fsSL https://raw.githubusercontent.com/registrystack/registry-stack/refs/tags/vX.Y.Z/crates/registryctl/install.sh | REGISTRYCTL_VERSION=vX.Y.Z bash
```

The installer still fetches the script from `v0.8.4`; `REGISTRYCTL_VERSION`
selects the release asset to install.
Fetch the installer from the same pinned tag selected by
`REGISTRYCTL_VERSION`. An older installer does not know the asset contract of a
newer release.

Prebuilt binaries are published for the `v0.8.4` stack release on Linux x86_64,
Linux arm64, and macOS arm64. On other platforms, install from source with
`cargo install --git https://github.com/registrystack/registry-stack --tag v0.8.4 registryctl --locked`.
Intel macOS has no prebuilt binary for `v0.8.4`, so the installer stops after
printing that Cargo command. It does not run the source build automatically.

## Release image lock (`v0.9.0` and later)

`registryctl init` and `registryctl add` read
`registryctl-vX.Y.Z-image-lock.json` beside the running binary before writing
project files. The strict lock binds the CLI release, source commit, tag target,
`linux/amd64` platform, and exact Relay and Notary image digests. Registryctl
does not discover images from mutable tags or a live registry.

For `v0.9.0` and later, if you move or build the binary separately, place the
checksum-verified image lock from the same release beside it. An operator or
source test can set `REGISTRYCTL_IMAGE_LOCK` to an explicit verified lock path.
Registryctl never searches the current working directory for a lock, and
rejects a missing, mismatched, oversized, symlinked, or structurally invalid
file.

Existing projects do not need the lock for `start`, `stop`, `status`, or other
runtime commands. They keep using the immutable image references already stored
in `registryctl.yaml` and `compose.yaml`. A later `init` or `add` is a generation
operation and requires the lock for that registryctl version.

## Update checks

`registryctl` checks GitHub releases at most once per day for normal
Expand Down Expand Up @@ -133,16 +156,15 @@ shared crates have fresh release tags.

## End-to-end smoke

The generated project uses the digest-pinned Registry Relay image recorded in the registryctl
templates, not a floating image tag. With Docker Compose available, run:
The generated project uses the digest-pinned Registry Relay image recorded in
the matching registryctl release image lock, not a floating image tag. The
source tutorial gate builds registryctl and the product images from the same
checkout, places a strict test lock beside the binary, rebinds the generated
project to those local images, and executes both reader tutorials. With Docker
and the docs dependencies available, run:

```sh
tmpdir="$(mktemp -d)"
cargo run -- init relay "$tmpdir/my-first-api" --sample benefits
cd "$tmpdir/my-first-api"
registryctl doctor --profile local --format json
registryctl start
registryctl status
registryctl smoke
registryctl stop
cd docs/site
npm ci
npm run check:tutorial:registryctl
```
Loading
Loading