github getnora-io/nora v1.3.1

4 hours ago

[1.3.1] - 2026-09-09

Changed

  • npm rebuilds a packument concurrently (#956 follow-up)regenerate_packument walked versions/ and dist-tags/ one key at a time. On an object store each key is a round-trip, and #956 put that walk on a read path, so the first request after a packument went missing paid one round-trip per version: 505 reads and 0.77 s for a 500-version package against a loopback S3 store, linear from there. It now reads them through the same buffered fan-out the RPM and Deb index rebuilds use, keeping the lenient behaviour — an unreadable or unparsable version is skipped rather than failing the rebuild.
  • Every storage round-trip is counted (#969 follow-up)stat, pin, list and list_with_meta reached the backend without touching nora_storage_operations_total, which is why a handler issuing one stat() per file (tens of thousands of HEAD requests on a single PyPI index response) moved no metric and could only be found by reading code. All four now increment it; on stat/pin an absent object or an unpinned one is status="miss", so ordinary misses do not inflate error-rate alerting. Counters only — no behavioural change.
  • Changelog entries live in changelog.d/ (#971 follow-up)## [Unreleased] in CHANGELOG.md is no longer edited by hand. Each change adds changelog.d/<number>.<category>.md, and scripts/changelog-fragments.sh assembles them (--check, --render, --apply, --release <version>). Three pull requests merged into 1.3.1 each added a bullet to the same few lines and each conflicted with the previous one; separate files cannot. A .gitattributes union merge was the lighter alternative and was rejected: it duplicates the ### Fixed heading when two branches both introduce it, and it is unclear whether the merge performed on the forge honours the driver at all, which is where the conflicts actually surfaced.

Fixed

  • PyPI simple JSON no longer stats every file (#969) — the PEP 700 fields added in 1.3.0 made GET /simple/{name}/ with a JSON Accept header issue one storage stat() per file in the index, serially. On an object store that is a HEAD round-trip each, so the response time scaled with the number of files the index lists and uv — which always negotiates the JSON index — hit its timeout; the HTML index pip uses was never on that path. Each file’s size now comes from the listing the handler already performs, and the cached dates.json is read once per response instead of twice. Measured on S3 against a 40 000-file proxied index: uv lock went from 129.8 s and 59 764 storage HEAD requests, failing after three retries, to 0.5 s and 2. A second regression on the same path is fixed with it: an upstream that answers the PEP 691 JSON without upload-time left nothing cached, so every request re-downloaded the whole upstream index; it is now recorded once, while a failed fetch still caches nothing so a transient outage stays retryable.
  • lock-audit no longer depends on gawk (#971 follow-up) — Checks 1 and 3 used gawk's three-argument match(), which mawk rejects. mawk is the default awk on Debian, Ubuntu and the CI images, and an aborted awk prints nothing — so on any machine without gawk the audit reported no findings and looked clean. The awk programs are now POSIX, and scripts/test-lock-audit.sh runs the audit under every implementation it finds, asserting they agree and that each run still produced the known finding, so "both silent because both aborted" cannot pass.
  • lock-audit no longer reports a guard that cannot drop early (#971) — Check 2 scanned forward from the end of a guarded block to the next column-0 } and flagged the first storage write it met, with no notion of branch exclusivity. A publish_lock taken under if !is_tarball was therefore reported against a write under if is_tarball, which no request can reach on the same path. The scan now skips a write whose enclosing if chain contains the textual negation of a condition enclosing the guard. Where the reasoning is beyond textual analysis, a // LOCK-SAFE: <reason> comment inside the block of the write exempts it, with the reason recorded next to the code; a bare marker with no reason silences nothing, and the function-level markers the repo already carries are deliberately not honoured, because a function-wide exemption would hide a genuine finding elsewhere in the same handler. scripts/test-lock-audit.sh pins all four directions.
  • npm no longer caches a proxied packument over a locally owned key (#975)npm/{name}/metadata.json is the assembled form of keys this registry owns, but the proxy branch cached the upstream document there unconditionally. On the metadata path that branch is reached with local versions present only by falling through the rebuild — its write failed, or the read-back missed — and the cached upstream document then dropped every locally published version from the packument. It did not self-heal, because the read-path rebuild triggers on the key being absent and it now existed with the wrong content, so npm install pkg@<locally-published-version> failed until the next publish. The write also landed unserialized against the publish_lock released a few lines above, so a concurrent publish could be overwritten. The upstream bytes are still served for that request; only the write is skipped, and the next request retries the rebuild.

Install

# x86_64
curl -LO https://github.com/getnora-io/nora/releases/download/v1.3.1/nora-linux-amd64
chmod +x nora-linux-amd64
sudo mv nora-linux-amd64 /usr/local/bin/nora

# ARM64 (Apple Silicon, Graviton, Ampere)
curl -LO https://github.com/getnora-io/nora/releases/download/v1.3.1/nora-linux-arm64
chmod +x nora-linux-arm64
sudo mv nora-linux-arm64 /usr/local/bin/nora

Docker

docker pull getnora/nora:1.3.1
Variant Image Platforms
Alpine (default) getnora/nora:1.3.1 amd64, arm64
RED OS getnora/nora:1.3.1-redos amd64
Astra Linux SE getnora/nora:1.3.1-astra amd64
GHCR ghcr.io/getnora-io/nora:1.3.1 amd64, arm64

DEB / RPM

# Debian / Ubuntu / Astra Linux (amd64)
curl -LO https://github.com/getnora-io/nora/releases/download/v1.3.1/nora-amd64.deb
sudo dpkg -i nora-amd64.deb

# RHEL / Fedora / RED OS (amd64)
curl -LO https://github.com/getnora-io/nora/releases/download/v1.3.1/nora-amd64.rpm
sudo rpm -i nora-amd64.rpm

Changelog

See CHANGELOG.md

Don't miss a new nora release

NewReleases is sending notifications on new releases.