Skip to content

fix(nibe_local): version the DER correction so the channel can publish it - #55

Merged
HuggeK merged 1 commit into
srcfl:mainfrom
HuggeK:fix/nibe-local-channel-version
Jul 31, 2026
Merged

fix(nibe_local): version the DER correction so the channel can publish it#55
HuggeK merged 1 commit into
srcfl:mainfrom
HuggeK:fix/nibe-local-channel-version

Conversation

@HuggeK

@HuggeK HuggeK commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

The driver release failed on main with

error: nibe_local: changed artifact needs a higher version than 1.1.0

Nothing shipped, which is the correct outcome.

#53 corrected nibe_local's manifest from ders: [meter] to ders: [heatpump] and read that as catalog-only metadata, so it left the version at 1.1.0. It is not catalog-only. _load_channel copies ders into the artifact's capabilities, and the channel signs source and metadata as one file — so the correction moved the published bytes under a version that has already shipped. The channel refused, and it was right to: that immutability is what lets an operator name the bytes a site is running.

So nibe_local takes a patch, 1.1.0 → 1.1.1.

Why a patch and not a minor

No register, field or emitted metric changed — only the DER the catalog states. This follows the precedent in #32, where drivers that gained a control path took a minor because a host could now do something new, and the rest took a patch for a bytes-only move.

The Lua source is untouched and its sha256 with it, and bump_driver leaves the DRIVER table alone because the file is byte-identical to its FTW baseline — so the provenance guarantee #29 established is not spent on this.

Verified

Built the channel entries locally against the currently published beta manifest and compared every artifact digest:

total drivers in tree: 80  published: 80
changed bytes vs published (1):   ('nibe_local', '1.1.0', '1.1.1')
BLOCKED - needs higher version (0):

Before the bump that same check reported nibe_local blocked, reproducing the release failure exactly. nibe_local is the only driver that differs from what is published — the release stops at the first offender, so this confirms there is no second one waiting behind it.

Other checks run
  • validate_manifest.py — 80 manifests, 0 errors
  • generate_index.py, generate_devices.py, generate_support_status.py — regenerated and idempotent; sync_manifests.py --id nibe_local reports the manifest matches its source
  • generate_history.py --check — driver history intact
  • host_api_check.py --check, check_public_boundary.py — pass
  • pytest drivers/tests tests — 2947 passed. Four modules needing the package extra (cryptography) were skipped, and 10 tests fail identically on the unmodified base commit: they need the lua55 interpreter the Makefile builds, plus one Windows path-handling issue in test_blueprint.py. Nothing here is affected by this change; CI runs all of them.

The trap that caused it

CONTRIBUTING.md said to raise the version whenever the Lua source changes, which is exactly what #53 followed. It now also names the manifest fields that reach the signed artifact — ders, protocol, and a tested_devices manufacturer and model_family — and warns that this check runs on main after the merge rather than in the pull request, because only the release job can read the channel.

Possible follow-up, deliberately not in this PR

The gap is structural: a PR that edits one of those manifest fields merges green and breaks the release afterwards. A pre-merge job could download the published manifest.json and run the same comparison — it needs only the public key, which is already in the workflow env and not a secret. That is a new check rather than a fix to this failure, so it is left out here.

The CHANGELOG.md entry from #53 is amended in the same commit: it claimed the version and the published bytes were untouched, and half of that is no longer true.

…h it

The driver release failed on main with

    error: nibe_local: changed artifact needs a higher version than 1.1.0

srcfl#53 corrected the manifest from ders: [meter] to ders: [heatpump] and read
that as catalog-only metadata, so it left the version at 1.1.0. It is not
catalog-only. _load_channel copies ders into the artifact's capabilities and
the channel signs source and metadata as one file, so the correction moved
the published bytes under a version that has already shipped. The channel
refused, and it was right to: that immutability is what lets an operator name
the bytes a site is running.

So nibe_local takes a patch. Patch rather than minor because no register,
field or emitted metric changed -- only the DER the catalog states. The Lua
source is untouched and its sha256 with it, and bump_driver leaves the DRIVER
table alone because the file is byte-identical to its FTW baseline, so the
provenance guarantee srcfl#29 established is spent on nothing.

Verified against the currently published beta manifest: of 80 drivers exactly
one differs from what is published, and after the bump nothing is blocked.

CONTRIBUTING said to raise the version whenever the Lua source changes, which
is what srcfl#53 followed. It now also names the manifest fields that reach the
signed artifact -- ders, protocol, and a tested_devices manufacturer and
model_family -- and warns that this check runs on main after the merge rather
than in the pull request, because only the release can read the channel.

Signed-off-by: Hugo Karlsson <48095810+HuggeK@users.noreply.github.com>
Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
@HuggeK
HuggeK marked this pull request as ready for review July 31, 2026 09:12
@HuggeK
HuggeK merged commit 7b8a18a into srcfl:main Jul 31, 2026
5 checks passed
HuggeK added a commit that referenced this pull request Jul 31, 2026
…o unlock it

protocol names a transport and answers neither question an owner asks first.
nibe_local and myuplink are both protocol: http and both read a NIBE heat
pump, and they are opposites: one reads the pump over the LAN, the other
reads NIBE's cloud. The catalog could not tell them apart.

Two fields, orthogonal to protocol rather than folded into it. Splitting
protocol into http_local/http_online was the obvious move and the wrong one:
protocol feeds PROTOCOL_PERMISSIONS, so new values change what a package
build grants, and it does not generalise -- MQTT can be a cloud broker too.

  connectivity: local | cloud    where the driver talks while running
  setup: [...]                   what a human must obtain first, and from whom

Backfilled across all 80 drivers from what the sources do. 77 local, 3 cloud:
myuplink, easee_cloud and tibber are the only drivers that hardcode a vendor
endpoint. Every other HTTP driver builds its base URL from config, and the
Modbus, MQTT and serial drivers name a vendor only as homepage.

setup is the second axis, and a driver can be local and still locked until
somebody unlocks the interface: nibe_local is enabled in the pump's own
installer menu -- no app, no account -- while sonnen and CTEK are enabled in
the device's web UI, sma_pv needs an installer, and eight drivers need a
bridge in between. 17 have a recorded gate; 63 do not, and absent means
unrecorded. The page renders that as "Not recorded", never "Nothing to set
up", and only setup: [none] claims a device needs nothing but an address.

tests/test_connectivity.py stops connectivity being decorative. A cloud
endpoint must be hardcoded -- there is nowhere else for it to come from --
while a LAN address comes from config, so the test reads that asymmetry in
both directions and separates exactly the three from the other 77.

Neither field reaches the signed artifact: _load_channel copies an explicit
allowlist and these are not in it. So no driver version moves, unlike the
ders correction in #53 that #55 had to version.

Signed-off-by: Hugo Karlsson <48095810+HuggeK@users.noreply.github.com>
Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
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.

2 participants