Repository navigation
Read both node_id formats, and refuse to register what the spec cannot carry - #20
Closed
Purple10101 wants to merge 1 commit into
Closed
Purple10101 wants to merge 1 commit into
Purple10101 wants to merge 1 commit into
Conversation
… spec cannot carry Two changes that pull in opposite directions on purpose. collect/identity.py now accepts retg + 15 hex alongside ret + 8 hex. A node validates only its own id, so a node on either format is judged correctly by the same rule and a legacy node is unaffected by the new format existing elsewhere. That is what lets the fleet migrate one node at a time instead of at once. wire/registration.py gates in the other direction. The ingest spec still pins ret + 8 hex, and wire/models.py is generated from it, so a migrated node would otherwise build a payload the generated model rejects with a validation error nobody can act on. Instead it is held back deliberately, with a reason that names the spec rather than the board. The gate reads its pattern out of the generated model rather than repeating it. wire/models.py is derived from the contract, so regenerating it against a spec that carries the current format lifts the gate by itself, with no code change here. A second copy of the pattern would be a second place to remember, and therefore the one that gets forgotten. Only ids that are genuinely ours are intercepted. "Unknown", the retina-node config placeholder and anything truncated are not nodes waiting on a spec, and saying so would send whoever read it hunting for a migration that never happened. Those keep falling through to the generated model, whose validation error is the honest answer. NodeState.NODE_ID_UNSUPPORTED sits directly below NO_IDENTITY and above the agreement and wizard gates. Those two are cleared by the owner doing something; this one is not, and telling someone to finish a wizard that cannot unblock them is worse than telling them nothing. It reaches the server in no form, because saying so needs a token. Consequence worth stating plainly: from the moment this ships, any node built from a current image is telemetry-invisible until the spec moves. Not only migrated nodes. The node keeps running, keeps its identity, and says why. retina-gui needs no change to display it: telemetry_status.py passes state and detail through verbatim and node_id_unsupported is not in TRANSIENT_STATES. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
Closing: this work is not being taken forward. The branch is deleted, but the commits stay reachable from this closed PR if it is ever revived. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part 2 of 5, with owl-os#59, node-infra, retina-gui and retina-node all on
20260921-node-id-schema-v2. This is the one worth real scrutiny — the other four are tolerance widenings that provably change nothing today. This one changes runtime behaviour.Two changes pulling opposite ways, deliberately
collect/identity.pywidens.retg+ 15 hex is accepted alongsideret+ 8 hex. A node validates only its own id, so either format is judged correctly by the same rule, and a legacy node is unaffected by the new format existing elsewhere. That is what lets the fleet migrate one node at a time rather than at once.wire/registration.pygates. The ingest spec still pins^ret[0-9a-f]{8}$, andwire/models.pyis generated from it, so a migrated node would otherwise build a payload the generated model rejects with a Pydantic validation error nobody can act on. Instead it is held back on purpose, with a message that names the spec rather than the board.The gate opens by itself
spec_node_id_pattern()reads the pattern out of the generated model rather than repeating it.wire/models.pyis derived fromdocs/node-ingest-v1.yml, which is the contract, so regenerating it against a spec that carries the current format lifts the gate with no code change here. A second copy of the pattern would be a second place to remember, and therefore the one that gets forgotten.Only real ids are intercepted
"Unknown", the retina-node config placeholder and anything truncated are not nodes waiting on a spec. Claiming they were would send whoever read it hunting for a migration that never happened. They keep falling through to the generated model, whose validation error is the honest answer. Two existing tests caught this in an earlier draft of the guard and are the reason it is narrowed.NodeState.NODE_ID_UNSUPPORTEDSits directly below
NO_IDENTITYand above the agreement and wizard gates. Those two are cleared by the owner doing something; this one is not, and telling someone to finish a wizard that cannot unblock them is worse than telling them nothing. It reaches the server in no form, because saying so needs a token.An operator sees, verbatim:
The consequence to be explicit about
From the moment this ships, any node built from a current image is telemetry-invisible until the spec moves. Not only migrated nodes. A fresh board enrols in Mender, gets a Cloudflare tunnel and reports inventory, and never registers.
That is the intended trade: a deliberate, self-explaining hold instead of a confusing failure. It is worth agreeing to explicitly rather than discovering.
retina-gui needs no change to show it
Checked:
telemetry_status.pypassesstateanddetailthrough verbatim,node_id_unsupportedis not inTRANSIENT_STATES, and no template does state-specific rendering.Verified
Full suite green,
ruff checkandruff format --checkclean. New tests cover the union pattern, the gate reading the generated model, the refusal naming the spec, the subclass so existingIncompletePayloadhandlers still catch it, the precedence of the new state, and that garbage still reaches Pydantic.Vendors
tests/node-id-test-vectors.jsonfrom owl-os with a test asserting this repo's pattern is the canonical one, so a drifted copy fails rather than looking authoritative.Not verified
The container has never run on hardware. The gate has not been observed on a real node. Closes naturally in Wave 1 of the rollout, when the image ships via retina-node's
TELEMETRY_Vpin.🤖 Generated with Claude Code