Skip to content

feat(release): publish a Registry Discovery image - #797

Merged
jeremi merged 5 commits into
mainfrom
worktree-discovery-release-image
Aug 20, 2026
Merged

feat(release): publish a Registry Discovery image#797
jeremi merged 5 commits into
mainfrom
worktree-discovery-release-image

Conversation

@jeremi

@jeremi jeremi commented Aug 20, 2026

Copy link
Copy Markdown
Member

What

Registry Discovery had no released artifact. This adds
release/docker/Dockerfile.discovery and carries both a
ghcr.io/registrystack/discovery image and a discovery-<tag>-linux-amd64
binary through the release pipeline, gated at v0.24.0 so every released and
staged version keeps its exact recorded inventory (v0.23.0 / beta-34 is already
staged and is untouched).

Why the Dockerfile differs from the other runtimes

Discovery serves one immutable index and writes nothing, so unlike relay,
evidence, and mint it pre-owns no managed state or audit directory and needs no
chown. It also reads no environment variable: the runtime file is bound by
CMD, not ENV.

release/scripts/check-debian13-images.py now models that. The environment
key of an HTTP-probed contract became optional, and to keep that from being a
silent weakening, a service that declares no environment must carry no ENV in
its runtime stage, so a second undeclared configuration source cannot appear
later. A test injects ENV SMUGGLED_CONFIG=... and expects the failure.

Security review notes (release provenance)

  • The image roster stays version-derived from _candidate_image_names, so the
    candidate manifest, per-image SPDX, Syft and Grype reports, advisory
    subjects, and the promotion binding all pick discovery up together at
    v0.24.0 and never retroactively for an already-published tag.
  • Candidate cleanup gains discovery-candidate in the exact allowlist and
    discovery in the public denylist, so its private candidate versions expire
    on the same eight-day schedule and its public package can never be deleted.
  • The weekly repeatability proof now rebuilds and byte-compares the discovery
    binary alongside the others.

Known follow-up: the advisory baseline

release/security/discovery-advisory-baseline.json is deliberately not
included. A v4 baseline binds a reviewed runtime block (image digest, ordered
layer IDs, exact OCI process config, definition digest), an exception set,
owner, and expiry to a real candidate image, which does not exist yet. An empty
exception list would not clear the pinned Debian 13 runtime's High/Critical
findings.

The candidate fails closed on the missing file, which is the correct behavior.
release/OPERATIONS.md now states how to author the baseline from the first
discovery candidate run. This needs a human security sign-off before the
first v0.24.0 candidate.

Verification

  • release/scripts/smoke-release-image-oci-labels.sh against real Docker
    29.4.0 / buildx v0.33.0: builds Dockerfile.discovery twice with different
    source mtimes and compares the OCI layouts blob-for-blob, so the image is
    reproducible and carries the required source, revision, and version labels.
  • 388 release-tooling tests (1 skipped), check-debian13-images.py,
    check-gates-inventory.py (151 gates), check-release-source-model.sh,
    registry-release validate on the staged beta-34 manifest.
  • shellcheck clean on the changed scripts;
    cargo build --locked -p registry-discovery --bin discovery.
  • Docs site: full npm run check equivalent green (check:source 18
    sub-checks, build, check:built:current including 18439 link targets).

jeremi added 2 commits August 20, 2026 22:26
Registry Discovery had no released artifact. Add
release/docker/Dockerfile.discovery and carry the discovery image and a
discovery-<tag>-linux-amd64 binary through the release pipeline, gated at
v0.24.0 so every released and staged version keeps its exact recorded
inventory.

Discovery serves one immutable index and writes nothing, so unlike the other
runtime images it pre-owns no managed state or audit directory. It also reads
no environment variable, so the runtime file is bound by the command rather
than by ENV. check-debian13-images.py now models that: the environment key of
an HTTP-probed contract is optional, and a service that declares none must
carry no ENV in its runtime stage, so a second undeclared configuration source
cannot appear later.

Security review notes (release provenance):

- The image roster stays version-derived from _candidate_image_names, so the
  candidate manifest, per-image SPDX, Syft and Grype reports, advisory
  subjects, and the promotion binding all pick discovery up together at
  v0.24.0 and never retroactively for an already-published tag.
- Candidate cleanup gains discovery-candidate in the exact allowlist and
  discovery in the public denylist, so its private candidate versions expire
  on the same eight-day schedule and its public package can never be deleted.
- The weekly repeatability proof now rebuilds and byte-compares the discovery
  binary alongside the others.
- release/security/discovery-advisory-baseline.json is deliberately not
  included: a v4 baseline binds a reviewed runtime block, exception set, owner,
  and expiry to a real candidate image, which does not exist yet. The candidate
  fails closed on the missing file, and OPERATIONS.md now states how to author
  it from the first discovery candidate run.

Verified: the OCI label smoke builds Dockerfile.discovery twice under the
pinned BuildKit and compares the layouts, so the image is reproducible and
carries the required source, revision, and version labels; 388 release-tooling
tests, check-debian13-images.py, check-gates-inventory.py,
check-release-source-model.sh, and shellcheck on the changed scripts all pass.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
The release artifacts row lists the container images the release workflow
publishes, so it goes stale the moment Registry Discovery ships one. Name
`ghcr.io/registrystack/discovery` there with the version it starts at, so the
row keeps describing the roster a reader can actually pull.

The row states a covered interface rather than a reviewed claim about the rest
of the page, so `last_reviewed` stays where the last human review left it.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
@jeremi
jeremi force-pushed the worktree-discovery-release-image branch from 4944a08 to 89cf002 Compare August 20, 2026 20:26

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4944a08bbd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread release/scripts/release_candidate.py
Comment thread release/scripts/build-release-binaries.sh Outdated
Comment thread release/scripts/cleanup-release-candidates.py Outdated
Comment thread .github/workflows/nightly-security.yml
Comment thread docs/site/src/content/docs/reference/api-stability.mdx
jeremi added 3 commits August 20, 2026 22:47
The `discovery` binary declared no version, so `discovery --version` exited 2
with "unexpected argument". The release candidate asserts every image reports
`<name> <version>`, so no Discovery image could ever pass that gate.

Take the version from `registry-platform-buildinfo::DISPLAY_VERSION`, as every
other Registry Stack executable does, so a build without `REGISTRY_RELEASE_TAG`
reports `<version>-dev` and only a release build reports the bare version.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
The binary recipe runs for every supported version, including a candidate
rebuilt for a version whose recorded inventory predates Discovery. Staging
`discovery-<tag>-linux-amd64` unconditionally would hand seal-candidate a
payload the recorded inventory does not name, so a rebuild of an earlier
release would fail.

Gate the build and the staged asset lists on the same 0.24.0 boundary the
release inventory already uses, and keep the pre-0.24 asset ordering so those
candidates still produce byte-identical SHA256SUMS.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
The daily cleanup lists exactly the names in `CANDIDATE_PACKAGES` and fails
closed on a package it cannot list, a deliberate and tested behaviour. Naming
`discovery-candidate` before v0.24.0 publishes it would abort every scheduled
run on a 404, and it sorts first, so no other candidate would be cleaned.

Withdraw the name until the package exists and record the enrolment step in
release operations beside the advisory-baseline prerequisite. The public
`discovery` name stays on the denylist, so cleanup can never reach a released
image.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0200227ed2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread release/scripts/cleanup-release-candidates.py
@jeremi
jeremi merged commit 41b5058 into main Aug 20, 2026
44 checks passed
@jeremi
jeremi deleted the worktree-discovery-release-image branch August 20, 2026 20:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant