Skip to content

fix(recovery): tell unreachable relays from empty ones, validate relay answers - #24

Open
dmnyc wants to merge 3 commits into
DocNR:mainfrom
dmnyc:fix/lazarus-relay-outcomes
Open

dmnyc wants to merge 3 commits into
DocNR:mainfrom
dmnyc:fix/lazarus-relay-outcomes

Conversation

@dmnyc

@dmnyc dmnyc commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Follow-up to #22, bringing data recovery up to the Lazarus spec's 0.6.2-draft (https://github.com/dmnyc/lazarus/blob/main/SPEC.md).

  • Relay outcomes. Every relay request now ends as answered, failed, or timed out, and only an answered relay counts as having nothing. Each relay is asked directly: the shared subscription reports a relay that couldn't connect as finished, so a dead relay read as an empty answer, and a dead write relay could confirm the current version. Versions a relay sent before failing stay in the list. A scan no relay answered shows as an error instead of "no versions found", and nothing is recommended until one of your write relays has answered, since the newest version found may not be current.
  • Authentication. A scan authenticates only to relays in your own relay list, so the archival and default relays never learn that the account itself is asking.
  • Relay list. When the app has no relay list for the account, a direct lookup tells a missing list (the default relays stand in, and the page says so) from one that couldn't be fetched.
  • Results show how many relays answered, with each relay's outcome on request.
  • Pre-sign check. Only a newer version counts as a change, including one from the same second with a lower id (the one relays keep), and without an answer from a write relay the restore stops instead of assuming nothing changed.
  • Scanned events count only with a valid signature, the scanned author, and the requested kind.
  • Items are counted once each, a tag without a value isn't one, a profile's items are its fields (so a profile wiped to {} reads as empty), and relay URLs compare normalized.
  • Private items. The review says which version's private items couldn't be decrypted. When it's your current version's, the restore replaces items nobody counted, so it takes the same separate confirmation as a restore that removes items, now a checkbox.
  • Encryption key lists (kind 10044) count the n tags NIP-4e puts keys in; the registry counted p tags, so every key list scanned as empty. The review states what both versions mean, in the direction of the restore, and asks you to confirm the change.
  • Publish succeeds when one write relay accepts the restore. The shared publish waited for a third of the relays, so a restore that had gone out could be reported as failed.
  • Profile changes cover every field and tag a restore would replace, not a fixed list.

…y answers

Every relay request now ends as answered, failed or timed out, and only an answered relay counts as having nothing. Versions a relay sent before failing stay candidates, a scan no relay answered is an error rather than an empty result, and nothing is recommended until one of the user's write relays has answered. When the app has no relay list for the user, a direct lookup tells a missing list (the defaults stand in, and the page says so) from one that couldn't be fetched (current stays unconfirmed). The results show how many relays answered, with each relay's outcome on request.

A scanned event counts only with a valid signature, the scanned author and the requested kind. The re-read before a restore goes through checkLazarusCurrent: only a newer version counts as a change, and without an answer from a write relay the restore stops. A restore succeeds when one write relay accepts it. Profile changes cover every field and tag a restore would replace.

Spec: https://github.com/dmnyc/lazarus/blob/main/SPEC.md (0.6.0-draft)
The kind 10044 registry entry counted p tags, but NIP-4e lists encryption pubkeys in n tags, so every key list scanned as empty.

Spec: https://github.com/dmnyc/lazarus/blob/main/SPEC.md (0.6.1-draft)
The scan asked relays through the client's shared subscription, which reports a relay that couldn't connect as finished. A dead relay counted as answered with nothing, so a refused write relay could confirm current and let the re-read before a restore pass. Each relay is now asked directly: only EOSE is an answer, a refused connection or a CLOSED is a failure, and a relay's own EOSE timeout can't stand in for one. Scans authenticate only to relays in the user's own list.

Items are counted once each, a tag without a value isn't one, a profile's items are its fields, and relay URLs compare normalized. Versions from the same second order as NIP-01 keeps them, including in the re-read. The review says which side's private items couldn't be decrypted; uncounted ones on current, like a shrinking restore, need a separate confirmation, now a checkbox. Kind 10044 states what both versions mean in the restore's direction and asks for intent.

Spec: https://github.com/dmnyc/lazarus/blob/main/SPEC.md (0.6.2-draft)
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