Skip to content

fix(myuplink): make the signed artifact agree that myuplink is read-only - #83

Merged
frahlg merged 5 commits into
mainfrom
claude/hopeful-fermi-390u3f
Aug 16, 2026
Merged

fix(myuplink): make the signed artifact agree that myuplink is read-only#83
frahlg merged 5 commits into
mainfrom
claude/hopeful-fermi-390u3f

Conversation

@frahlg

@frahlg frahlg commented Aug 16, 2026

Copy link
Copy Markdown
Member

Carries #65 (@HuggeK) verbatim — same five commits, same authorship, same
tree. The only difference is a Signed-off-by trailer on the merge commit that
GitHub's "Update branch" button created without one, which failed the required
dco check on #65. That branch lives on a fork this session cannot push to, so
the fix is carried here instead. Credit for the change is HuggeK's; please close
#65 in favour of this.

Summary

myuplink declares read_only = true and auth_post_path = "/oauth/token" in
its DRIVER table. The signed channel infers control from the mere presence of
a driver_command entrypoint, so a driver whose entrypoint refuses every
command was still published control_enabled: true, read_only: false, with a
write permission. It now publishes read-only.

The read-only guard would otherwise deny host.http_post, which this driver
needs to exchange its refresh token before it can read anything. The generated
guard confines that POST to the declared path rather than trusting it, so the
flag enforces "this POST is authentication" instead of asserting it. A path
rather than a URL, because base_url is config-overridable.

The catalog-convention checks that lit up when this driver lost its
baselines/ftw byte-identity exemption are already handled on main: the
connectivity-aware test split stops holding cloud drivers to the local
config.host / config.port / http:// rules, and the driver contract accepts
host.emit_metric for metrics-only drivers.

Driver evidence

  • Driver id: myuplink
  • Device make/model: MyUplink cloud API (NIBE, Bosch, Atlantic, Daikin, ...)
  • Firmware tested: not applicable — vendor cloud REST API, no device firmware
  • Protocol/register source: MyUplink Cloud REST API v2, upstream_docs in the manifest
  • Hardware test performed: none, and none is claimed — support status stays
    not_assessed for both targets. This is a read-only labelling and artifact
    correction, not a hardware claim.
  • Known limits: the driver remains observe-only; driver_command still refuses
    every command it is handed.

Safety

  • The driver starts read-only, or this is a separately reviewed control change.
  • Vendor signs are converted at the driver boundary.
  • Cached telemetry becomes stale instead of being re-emitted as fresh.
  • No credentials, serial numbers, private addresses or site data are included.

The one write-capable permission this driver retains (http.post) is scoped by
the generated guard to the declared auth path.
test_signing_in_is_declared_or_it_does_not_happen fails if any driver other
than myuplink loses the blanket denial, and asserts every other read-only
driver has neither auth_post_path nor http.post.

Package or promotion evidence

  • Public source SHA: 6074b70eb2bbe49481fcc59551d65474cb8129ae5370e9c1844c23b08a5f6fd6
  • Package ID and new version: myuplink 1.1.1 → 1.2.0 (the Lua bytes moved;
    the DRIVER block's stale 1.0.0 version line is also corrected to match the
    manifest)
  • Target matrix: ftw-core, blixt-l1 — both not_assessed, unchanged
  • Material and candidate artifact SHA-256: as above; signed channel accepts this tree confirms the bump covers the changed bytes
  • HIL evidence or required: not applicable — no control path
  • Control enabled: false
  • Control changes include default-mode, lease-expiry and HIL evidence. — not
    applicable; this removes a control claim rather than adding one.

Checks

  • Commits include Signed-off-by. — all five verified
  • make check — 3616 passed, 1333 skipped, on this exact tree

Channel note

A push to main publishes to the beta driver channel only
(.github/workflows/ftw-drivers-release.yml); promotion to drivers-stable
stays a manual workflow_dispatch. Sites on the stable driver channel are
unaffected, which is what makes this safe to land while ftw 1.16.x (carrying
RuntimePolicy.AuthPostPath) is still beta-only.


Generated by Claude Code

claude and others added 5 commits August 4, 2026 09:24
Not ready to merge. Two blockers found while building it, both external
to this diff -- recorded here so the work is not lost.

What this does: myuplink declares read_only = true and
auth_post_path = "/oauth/token" in its DRIVER table. The channel then
publishes it read_only with control_enabled false, and the generated
guard permits host.http_post only for a URL whose path ends in the
declared path, refusing it everywhere else. So the flag enforces "this
POST is authentication" rather than asserting it. A path rather than a
URL because base_url is config-overridable.

Blocker 1 -- FTW rejects it. go/internal/driverrepo/sourceful.go,
validateSourcefulReadOnlyContract, allows a read-only package only
http.get, modbus.read, mqtt.subscribe, serial.read and returns
"read-only package requests write-capable permission" for anything else.
Publishing this without an FTW change first makes every host refuse
myuplink at install.

Blocker 2 -- editing the Lua forfeits the FTW-baseline exemption.
drivers/tests/conftest.py exempts a driver byte-identical to
baselines/ftw and says so explicitly: it "stops being exempt the moment
someone edits it". Adding two metadata fields costs the exemption and
lights up 7 catalog-convention checks this driver was never written to
meet -- among them "should use 'http://' scheme" and "reference
config.port" for an HTTPS cloud API, and "should call host.emit()" when
emitting only metrics is the driver's stated design.

The catalog half is already shippable and separate: see the branch
fix/myuplink-read-only-catalog (#64), which is green.

Signed-off-by: HuggeK <[email protected]>
Co-authored-by: HuggeK <[email protected]>
Conflicts: CHANGELOG (kept both sides' Unreleased entries), SUPPORT_STATUS
rows (myuplink 1.2.0 from this branch, nibe_local 1.1.2 from main), and
test_driver_contract.py where main's test_calls_emit_in_poll supersedes this
branch's simpler emit-or-metric rule with the DER-gated version.

Co-authored-by: HuggeK <[email protected]>

Signed-off-by: Hugo Karlsson <[email protected]>
@frahlg
frahlg merged commit 45da847 into main Aug 16, 2026
6 checks passed
@frahlg
frahlg deleted the claude/hopeful-fermi-390u3f branch August 16, 2026 07:29
frahlg added a commit that referenced this pull request Aug 16, 2026
…#84)

Branch protection requires a pull request to be up to date with main before it
merges, and the merge commit GitHub's "Update branch" button writes carries no
Signed-off-by trailer. The check counted it, so bringing a stale pull request up
to date was the act that made it unmergeable.

There was no way out from inside the rule. Fixing it means force-pushing a
trailer onto a commit the contributor did not write, and for a pull request from
a fork this repository cannot push to, nobody who can merge can also do that. It
deadlocked #65 -- relanded as #83 to carry a one-line trailer -- and a0d5d73 on
that same branch before it.

A sign-off certifies authored content. A merge commit from the update button
carries none, so git rev-list gains --no-merges and the check asks for the
trailer on the commits that do, the same line the upstream DCO app draws. A
conflict resolved inside a merge commit becomes content the check no longer
sees; the reviewable content still arrives on the authored commits either side.

The failure message now also names the two commands that fix a genuine miss.

Verified against the range that failed on #65 (4fb8608..01da267): it fails under
the old rule and passes under the new one, while an authored commit with no
trailer still fails.

Signed-off-by: Fredrik Ahlgren <[email protected]>
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.

3 participants