Skip to content

20260827 - Tolerate a node with no location set - #17

Merged
Purple10101 merged 1 commit into
mainfrom
20260827-tolerate-unset-location
Aug 27, 2026
Merged

Purple10101 merged 1 commit into
mainfrom
20260827-tolerate-unset-location

Conversation

@Purple10101

Copy link
Copy Markdown
Collaborator

Phase 1 of stopping nodes from telling the server a location nobody chose. (86cba5qt4)

Important

Must be released before retina-node's null default (offworldlabs/retina-node#TBD). Today's _require raises on a null geometry, so shipping the new default first would make every unsited node report a malformed config and never register — worse than the bug being fixed.

Why

retina-node/config/default.yml ships a complete, plausible geometry (Greenwich Observatory / Crystal Palace) and config-merger writes it on first boot. Nothing downstream can tell an unconfigured node from a configured one, so nodes have registered claiming to sit in south-east London and the server's data is poisoned with positions nobody chose.

The fix is for the node to ship no location until an owner picks a tower. This side has to tolerate that before the default changes.

What

  • _optional reads the seven geometry keys. An unset geometry is the ordinary state of a node whose owner has not reached the tower step, not a malformed config. A wrongly typed coordinate still raises: optional means "may be absent", not "may be anything".
  • is_located says whether the node knows where it is. All-or-nothing, because the bistatic solution needs the whole geometry and a missing value becomes NaN downstream rather than an error, so a partial set would look like a working node that silently associates nothing. tx_name is excluded: a name is a label, not a position.
  • An unsited node holds registration rather than asserting a position nobody chose, and says so through the status document's detail.
  • Zero is a real coordinate throughout. Testing for truthiness would report a node on the equator as unsited.

Two deliberate choices worth a reviewer's eye

detail, not a NodeState. Being unsited does not change what the node is doing; everything else about it is normal. A state would have to sit somewhere in the precedence chain and would misreport a node that is otherwise healthy.

The hold is in __main__.py, not wire/. wire/ answers "can this payload be built"; the service layer answers "should we send". Those coincide today but diverge in Phase 2, where the payload becomes buildable with nulls and we will want to send it. Phase 2's change here is then a five-line deletion. Note this is asymmetric with consent, which is enforced in wire/registration.py — deliberately, because a missing consent record should never be sendable, whereas an unsited node will become sendable.

Phase 2, once the spec carries a nullable geometry

An unsited node should register with explicit nulls rather than holding, so the fleet can count nodes nobody has configured and prompt their owners. That needs the spec revision (in progress with the server author) and the server accepting an unsited node rather than refusing. Marked as interim in the code comment.

Testing

6 new collect tests, 3 new service tests. tools/check.sh --tracked passes all five gates against a clean copy of tracked files.

Verified end to end on owl-ded9 with the full stack running unsited (patched into the running containers, node restored afterwards, config checksums confirmed identical):

  • telemetry reported the exact detail message, registered: false, and made zero registration attempts
  • restoring the location cleared the detail and registration was attempted within 40s, no restart
  • blah2 (C++) runs normally with a null location — it reads only capture, network, process, save

Not verified: this ran as patched files in a running container, not from a built image via Mender.

🤖 Generated with Claude Code

retina-node is about to ship the receiver and transmitter geometry null
until an owner picks a tower, rather than defaulting to a plausible site
that nothing downstream can distinguish from a real choice.

This side has to tolerate that first. `_require` raises on a null, so
shipping the new default before this lands would make every unsited node
report a malformed config and never register - worse than the bug being
fixed.

`_optional` reads the seven geometry keys, `is_located` says whether the
node knows where it is, and an unsited node holds registration rather than
asserting a position nobody chose. A wrongly typed coordinate still raises:
optional means "may be absent", not "may be anything".

is_located is all-or-nothing. The bistatic solution needs the whole
geometry, and a missing value becomes NaN downstream rather than an error,
so a partial set would look like a working node that silently associates
nothing. tx_name is excluded because a name is a label, not a position.
Zero is a real coordinate throughout.

Reported through the status document's `detail` rather than as a NodeState,
because being unsited does not change what the node is doing - everything
else about it is normal.

Holding is an interim. Once the spec carries a nullable geometry, an
unsited node should register with explicit nulls so the fleet can count
nodes nobody has configured.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Purple10101
Purple10101 merged commit bc9656e into main Aug 27, 2026
2 checks passed
@Purple10101
Purple10101 deleted the 20260827-tolerate-unset-location branch September 6, 2026 15:51
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.

1 participant