Skip to content

docs(ns8): :latest is amd64-only, not unusable - #150

Merged
shockalotti merged 1 commit into
mainfrom
docs/ns8-latest-arch-wording
Oct 16, 2026
Merged

shockalotti merged 1 commit into
mainfrom
docs/ns8-latest-arch-wording

Conversation

@shockalotti

Copy link
Copy Markdown
Contributor

Corrects a claim in packaging/ns8/README.md that had become wrong in both directions.

What it said

Use :edge, not :latest. This module has not had a release tag yet, so :latest still points at the first build... That build fails at add-module.

Because :latest only moves on a release tag, and no module release has been cut yet, :latest is currently unusable.

What is true

A release has been cut (v0.10.1). So :latest resolves. Verified against the ghcr API:

tag digest mediaType arches
latest sha256:01ceca48... oci.image.manifest.v1+json linux/amd64 only
0.10.1 sha256:01ceca48... same same
0.10 sha256:01ceca48... same same
edge sha256:087ccfdb... oci.image.index.v1+json amd64 + arm64

All three public version tags are the same amd64-only manifest. It installs and works on amd64 and fails on arm64.

Leaving "unusable" in place would have been wrong twice over: it sends an amd64 admin hunting for a fault they do not have, and it understates the risk to an arm64 admin, who gets a hard failure at add-module rather than anything resembling "not usable yet". The new wording names the architecture and the actual symptom.

Also in this PR

Why it happened, which the old text got wrong. That release predates the multi-arch publishing work, so each architecture was pushed straight onto the public tags instead of being assembled into a manifest list. v0.10.1 does not contain push-manifest.sh; v0.11.0 is the first tag that does.

An honest arm64 label, since we agreed to ship it that way. The image builds for both architectures and the manifest lists both, and the arm64 app binary has been executed and works: keyvault generated a DEK, goose applied all 70 migrations, the server listened on 3000, / returned 302, /admin/ and /healthz returned 200, and there were zero error/fatal/panic lines. But that was under CPU emulation. Nothing has run on a physical arm64 machine or on an arm64 NS8 node, and emulation cannot demonstrate native kernel behaviour or performance. The README now says so plainly rather than implying the emulated result is a hardware result.

The :edge recommendation stays, and the pinned-version-tag advice stays, both still gated on a real multi-arch release landing. Noted that #149 now asserts multi-arch on every publish, so this specific breakage cannot recur silently.

No em dashes, per house style.

The README said :latest was "currently unusable" because no module release had
been cut. That stopped being true and then became misleading in the other
direction: a release HAS been cut, so :latest resolves, installs and works on
an amd64 node. Saying it is unusable would send an amd64 admin hunting for a
problem they do not have, while saying nothing would send an arm64 admin into
a hard failure at add-module.

The accurate statement: :latest, :0.10 and :0.10.1 all point at the same
amd64-only manifest. Works on amd64, fails on arm64. :edge is multi-arch and
correct on both. The :edge recommendation stays until a real multi-arch
release lands.

Also explains why, which the old wording got wrong: that release predates the
multi-arch publishing work, so each architecture was pushed straight onto the
public tags rather than assembled into a manifest list.

And labels arm64 support honestly. The image builds for both architectures and
the manifest lists both, and the arm64 app binary has been executed and works
(keyvault, all migrations, HTTP served). But that was under CPU emulation, so
arm64 is NOT verified on real hardware. Nothing has run on a physical arm64
machine or an arm64 NS8 node, and emulation cannot show native kernel
behaviour or performance. Stating that here is the point.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Important

The correction itself checks out against the registry, but the new claim that CI now guarantees multi-arch is not true yet: that gate is failing on every publish before it inspects anything, and it runs after the push either way.

Reviewed changes

  • Registry-verified tag facts replaces the ":latest is unusable" claim with the measured state. Confirmed independently: :latest, :0.10 and :0.10.1 all resolve to sha256:01ceca48…, a single oci.image.manifest.v1+json whose config declares linux/amd64 and carries org.nethserver.images = ghcr.io/calnode/calnode:0.10.1, so it does install on amd64. :edge is an oci.image.index.v1+json with amd64 + arm64, each entry's config declaring its own arch and pointing at a multi-arch app image. Both halves of the new wording are correct.
  • Root cause named for the amd64-only tag (the 0.10.1 release predates push-manifest.sh, so each arch was pushed straight onto the public tags), which is the piece the old text got wrong.
  • CI guarantee claims the new post-publish assert-multiarc.py gate means this breakage cannot recur silently. It cannot recur silently, but not for the reason given, and not because it prevents shipping. See the inline comment on line 52.
  • arm64 honesty states plainly that arm64 has only been exercised under emulation and not on hardware. Good addition, and the honest framing is the right call.

ℹ️ v0.11.0 is a GitHub release with no module image

This PR is about not misleading admins about which tags exist, and the same gap is still open one release up: v0.11.0 is a published GitHub release and a git tag, but its ns8-module.yml run failed at the manifest assembly step, so :0.11.0 and :0.11 both 404 in GHCR. "The one release so far" is true of module images in the registry but not of what a reader sees in the Releases list, and the next module publish will most likely re-cut v0.11.0 rather than introduce a new number. Worth a sentence either way, since the install callout now actively steers admins toward pinned version tags.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using space-bunny-free (free) | 𝕏

Comment thread packaging/ns8/README.md
> When the next module release lands, `:latest` will be multi-arch too, and a
> pinned version tag (`ghcr.io/calnode/ns8-calnode:0.11.1`, say) becomes the
> better thing to install. CI now asserts that every published tag really is a
> multi-arch index before a release is allowed to pass, so this specific

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This promise does not hold yet: the gate is failing on every publish before it can inspect anything. ns8-module.yml:257 passes MODULE_REPO: ghcr.io/Calnode/ns8-calnode, assert-multiarc.py:66 builds the GHCR token scope straight from that string, and the redundant ghcr.io/ prefix makes the token endpoint answer 400 NAME_INVALID. Main run 38836790601 (the #149 merge) dies here with cannot fetch ghcr.io/Calnode/ns8-calnode:edge: HTTP 400, so nothing is being asserted about :edge or any version tag.

Either fix the workflow first (push-manifest.sh:36 already normalises with ${repobase,,}; the assert path has no equivalent) or soften this sentence and its twin in Development. Same applies to "fails the release rather than shipping" on line 169: the assert runs after push-manifest.sh has already pushed, so it can turn a run red but cannot keep a bad tag out of the public registry.

Technical details
# The multi-arch release gate never actually runs

## Affected sites
- `.github/workflows/ns8-module.yml:257` — `MODULE_REPO: ghcr.io/${{ github.repository_owner }}/ns8-calnode` expands to `ghcr.io/Calnode/ns8-calnode`
- `packaging/ns8/assert-multiarc.py:66` — `scope=repository:{repo}:pull` takes `repo` exactly as passed in, host prefix and mixed case included
- `packaging/ns8/README.md:51` and `packaging/ns8/README.md:166` — both new sentences rest on this gate holding

## Evidence
- main run `38836790601` (push of `212f779`), step "Assert the published tags are multi-arch", `IMAGETAGS: edge`:
  `FAIL cannot fetch ghcr.io/Calnode/ns8-calnode: HTTP 400` after 4 retries
- reproduced directly:
  `https://ghcr.io/token?scope=repository:ghcr.io/Calnode/ns8-calnode:pull` → `400 {"code":"NAME_INVALID"}`;
  the same scope with the host prefix stripped, or lowercased, → `200` with a token
- `push-manifest.sh:36` already does `repobase="${repobase,,}"`, and the build step's `REPOBASE` works because `manifest add` tolerates the mixed case; only the assert's token scope is case and prefix sensitive

## Required outcome
- the gate resolves the published tags and inspects them, so a green `ns8-module.yml` run means the registry really is multi-arch
- the README states the guarantee only once it holds, in both the Install callout and the Development section

## Open question for the human
- sequencing: the assert reads the registry after `buildah manifest push --all`, so even once the casing is fixed it cannot stop a bad tag from shipping, only make the publish red. The guard that actually keeps a single-arch tag out is the arch-count refusal in `push-manifest.sh:89-95`. If "fails the release rather than shipping" is to be literally true, either reword it (the run goes red, the tag is already public) or move the assertion ahead of the push.

Comment thread packaging/ns8/README.md
cut yet, `:latest` is currently unusable (see Install). Use `:edge` until the
first module release lands.
Because `:latest` only moves on a release tag, and the one release so far was
cut before the multi-arch work landed, `:latest` is currently amd64-only. Use

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This contradicts line 162, three lines below: "Every published module tag is a multi-arch manifest (amd64 + arm64)". As written, an arm64 admin reading Development is told every tag is fine, installs :latest, and hits the exact failure this PR exists to prevent. Scope the claim to what is true, multi-arch from the next publish onward with 0.10.1 named as the amd64-only holdover.

Technical details
# "Every published module tag is multi-arch" contradicts the paragraph above it

## Affected sites
- `packaging/ns8/README.md:158-160` — "the one release so far was cut before the multi-arch work landed, `:latest` is currently amd64-only"
- `packaging/ns8/README.md:162` — "Every published module tag is a **multi-arch** manifest (amd64 + arm64)"

Both sentences are in the same section, two lines apart, and cannot both be true: `latest`, `0.10` and `0.10.1` all resolve to the amd64-only manifest `sha256:01ceca48…`.

## Required outcome
- the Development section carries one scoped claim: tags published from the next module release onward are multi-arch, and the 0.10.1 release is the known exception
- the `:latest` advice in the same section stays consistent with it

@shockalotti
shockalotti merged commit 3a4ea7c into main Oct 16, 2026
7 checks passed
@shockalotti
shockalotti deleted the docs/ns8-latest-arch-wording branch October 16, 2026 01:59
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 16, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant