Skip to content

Active observers vanish as nodes after retention.nodeDays without an advert; observer link dead-ends on 'Node not found' #199

Closed

Description

@dborup

Summary

An observer that is online can vanish as a node.

  • The node row is moved to inactive_nodes after retention.nodeDays without an advert, even though the same device is actively uploading as an observer.
  • The Observers page still lists it as online. Its "View node detail" link (public/observer-detail.js around line 84) then opens #/nodes/<pubkey>, which shows "Node not found" (public/nodes.js around line 1136).
  • The node is also missing from the map with every status filter.

Evidence

Production, read-only, 2026-10-04, reported by users:

  • Observer: a repeater observer in one region. Its observers.last_seen is current (2026-10-04T04:23Z), and it was first seen 2026-09-11.
  • nodes: no row for its pubkey. /api/nodes/<pubkey> returns 404.
  • inactive_nodes: has the row, with last_seen 2026-09-24T15:35Z.
  • Adverts: its last advert in transmissions is from 2026-09-23T06:33Z.
  • Config: retention.nodeDays is 7. MoveStaleNodes (cmd/ingestor/db.go around line 1900, called from main.go at start-up and daily) moves every node whose last_seen < now - nodeDays. last_seen is updated only by adverts and direct upserts. Observer activity does not count.

This is not a regression. The behaviour predates v0.2.0.

Why adverts can be missing. A node never receives its own transmissions, so its adverts must be heard by another observer. The firmware's flood.advert.interval can be set from 3 to 168 hours, with a default of 12 hours for repeaters. At 168 hours, a node with nodeDays: 7 flickers in and out of nodes. A badly placed node may also simply not reach any other observer for a week.

Proposed changes

A. Ingestor: do not retire active observers. In MoveStaleNodes, exclude nodes whose pubkey matches an observer seen within nodeDays. Observer IDs are upper-case pubkeys and node keys are lower-case, so the comparison must normalise case.

  • When the observer goes quiet, the node is retired as before.
  • This needs a test: an active observer whose node has an old last_seen stays in nodes, and an inactive one is moved.
  • last_seen must not be faked. The node page should still show when the last advert was heard.

B. UI: do not dead-end on "Node not found".

  • Option 1: when the node is missing but present in inactive_nodes, the node page says so, e.g. "No advert heard since ; this device is inactive", and shows the inactive row's name and role.
  • Option 2: at minimum, the observer-detail page does not offer "View node detail" for a pubkey that has no nodes row, or it explains why.

cmd/server/identity_visibility.go already reads inactive_nodes, so there is a read-only precedent in the server.

Acceptance criteria

  • An online observer whose last advert is older than nodeDays stays visible as a node, on the map and on the node page (test in cmd/ingestor).
  • A node that is in neither nodes nor the active observer set is retired as before (test).
  • From the Observers page, a device without a nodes row never dead-ends on a bare "Node not found". Either the page explains the state or the link is hidden (unit or E2E test).
  • No new per-item API calls, and the server stays read-only.

Out of scope

  • Changing retention.nodeDays on any instance; that is an operational decision.
  • The map status filter. It defaults to "All" and only persists the user's own last choice in localStorage (meshcore-map-status-filter).

Activity

  1. added a commit that references this issue on Oct 4, 2026
  2. dborup commented on Oct 4, 2026

    @dborup
    OwnerAuthor

    Fixed by #203 (merged as caee53f8):

    • MoveStaleNodes no longer retires nodes whose pubkey matches an observer seen within nodeDays. The comparison ignores case.
    • The node page explains an inactive device instead of a bare "Node not found".

    Nodes already in inactive_nodes return with their next advert. Review follow-ups (P3) are tracked in #208.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions