Files
ngit-grasp/docs/explanation/grasp-03-proactive-sync-plus.md
T
DanConwayDev e84be42446 feat(sync): cover roots with missing relay lists
Some accepted root authors do not publish a discoverable NIP-65 kind 10002, leaving their conversations outside Sync+ even after a successful user-index lookup. Use a bounded operator-configured fallback relay set only after such a successful empty lookup.

Feed fallback roots into the existing GRASP-02 target overlay, so historic, live, rotating, subscription-budget, and recovery behavior remain unified. A later accepted relay list removes the author from desired fallback coverage while shared live subscriptions drain naturally.

Keep eligibility restricted to accepted root authors and preserve single-flight identity discovery. This does not expand to response authors, add another scheduler, or infer fallback need from failed queries.

Validated with the end-to-end missing-list fallback and later-list replacement scenario, discovery/config unit tests, Nix module parsing, rustfmt, clippy with warnings denied, and diff checks.
2026-08-12 17:44:00 +00:00

86 lines
5.2 KiB
Markdown

# GRASP-03 proactive sync plus
GRASP-03 extends repository-declared GRASP-02 coverage to the Nostr outbox
model. An accepted issue, patch or pull request may have replies in the root
author's inbox even when those events are absent from repository relays.
The overlay is enabled by default and can be disabled with
`NGIT_SYNC_PLUS_ENABLED=false`. Because it extends the proactive GRASP-02
manager, it is effective only while that manager is running. NIP-11 advertises
`GRASP-03` only when the overlay is enabled.
## Minimal approach
The implementation adds a narrow discovery control-plane, not a second sync
engine: derive accepted root IDs and authors locally; ask a small existing
index set for each author's latest profile kind `0` and NIP-65 kind `10002`;
retain those replaceable events locally; treat `read` and unmarked relays as
inboxes; and merge inbox-to-root mappings into ordinary per-relay sync targets.
GRASP-02 then owns all transport. Its current root tiers, historic and
live/rotating coverage, grouping, subscription ledger, rate-limit recovery,
pagination and per-relay connection lifecycle apply unchanged.
Authors without an accepted stored relay list are queried through the configured
user-index set plus the bootstrap relay. Once a list is retained, discovery
follows its `write` and unmarked outboxes instead, allowing newer replacements
to converge without making arbitrary repository relays identity sources.
Discovery remains best effort and bounded to this operator-visible source graph.
If a successful user-index query returns no accepted kind `10002`, accepted
roots by that author temporarily use the operator-configured Sync+ fallback
relay set as inboxes. This is the same root-only overlay consumed by ordinary
GRASP-02 historic, live and rotating coverage; it creates no separate
subscription scheduler. An accepted relay list removes the author from desired
fallback coverage and installs its declared inboxes. Existing shared live
subscriptions still drain naturally, as with any other relay-list replacement.
The self-subscriber builds a compact root-candidate inventory during its
existing startup load and maintains it incrementally as roots arrive. StateOnly
candidates remain inert; promotion of their repository to Full makes them
eligible without rescanning retained events. Eligible identity authors are the
owners and declared maintainers of accepted Full announcements plus accepted
root authors—not every author retained by the relay. A once-per-minute
reconciliation derives sources from this index. Remote queries contain at most 100 authors and
only one discovery batch may be in flight globally. Admission samples immediate
transient capacity; if historic work wins the small race before the permit is
acquired, that single batch may wait but discovery can never build a waiter
queue. Its SDK-owned REQ draws from the same pacer and subscription ledger as
historic work. Discovery sources use the managed connection safety and
reconnection machinery, but are a distinct control-plane role: connecting to a
user index or outbox does not start ordinary announcement, repository, or
descendant sync against it. At most one new discovery source is dialled per
maintenance pass, and an exclusively discovery connection retires after its
currently due author batches drain. A relay that independently becomes a
repository source is promoted to the ordinary lifecycle without opening a
duplicate connection. Authors returned by a successful query refresh after 24 hours;
missing authors and failed queries retry after five minutes. Configured user
index relays discover authors with no accepted local relay list, requesting the
profile alongside it. Accepted NIP-65
write/unmarked outboxes are then followed additively for newer replacements;
new outboxes discovered by a replacement join the same bounded round until no
unvisited source remains. Identity events
pass through the ordinary write policy, persistence and broadcast path. Only a
stored, accepted kind `10002` can change inbox ownership. On startup, retained
relay lists for eligible authors rebuild inbox ownership from the local
database before any network refresh, so serving established coverage does not
depend on an external index remaining available. Remote discovery then refreshes
that retained state on the normal cadence.
Inbox replacement/removal changes desired ownership immediately. Additions are
derived promptly. Removals prevent future rotating and historic work, but do
not eagerly CLOSE live descendants or abort shared in-flight batches: existing
coverage drains on its natural EOSE/CLOSED/disconnect or an ordinary later
consolidation rebuild. This avoids interrupting unrelated roots and subscription
churn merely because one author replaced a relay list. Root deletion follows
the existing GRASP-02 root-index lifecycle and is reconstructed from retained
accepted events on restart; this change does not add a second deletion graph.
## Deliberately excluded
- the old recursive `SyncScope` graph and response-author fan-out;
- maintainer mailbox expansion unrelated to an accepted root;
- identity storage for authors other than accepted roots;
- per-user fallback configuration knobs; and
- a separate GRASP-03 subscription scheduler.