Repository navigation
docs(ns8): :latest is amd64-only, not unusable - #150
Conversation
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.
There was a problem hiding this comment.
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 "
:latestis unusable" claim with the measured state. Confirmed independently::latest,:0.10and:0.10.1all resolve tosha256:01ceca48…, a singleoci.image.manifest.v1+jsonwhose config declareslinux/amd64and carriesorg.nethserver.images = ghcr.io/calnode/calnode:0.10.1, so it does install on amd64.:edgeis anoci.image.index.v1+jsonwith 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.pygate 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.
space-bunny-free (free) | 𝕏
| > 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 |
There was a problem hiding this comment.
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.| 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 |
There was a problem hiding this comment.
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
Corrects a claim in
packaging/ns8/README.mdthat had become wrong in both directions.What it said
What is true
A release has been cut (
v0.10.1). So:latestresolves. Verified against the ghcr API:latestsha256:01ceca48...oci.image.manifest.v1+jsonlinux/amd64only0.10.1sha256:01ceca48...0.10sha256:01ceca48...edgesha256:087ccfdb...oci.image.index.v1+jsonAll 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-modulerather 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.1does not containpush-manifest.sh;v0.11.0is 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/healthzreturned 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
:edgerecommendation 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.