fix: drive mesh convergence from the replication feed - #271
Open
LiranCohen wants to merge 4 commits into
Open
Conversation
Add feedsync.json ({version, linkKey, cursor, fingerprint, updatedAt}) to
the profile state dir with the same atomic-write + alignOwnerToDir rules
as network.json, so a root daemon leaves it readable by the non-root CLI.
Checkpoints from an incompatible schema version are discarded on load,
and clearing is wired for network leave.
Export ProgressToken.Valid so restorers can discard unusable persisted
cursors instead of failing the subscribe.
Refs #267
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ti-entropy
Nodes that can speak the Messages interface — the anchor owner (tenant
bypass) and wallet delegates holding active protocol-root Messages.Read
grants — now run ONE 'feed' subscription stream (MessagesSubscribe,
filters [{protocol: mesh}], plural permissionGrantIds, persisted resume
cursor) instead of the topology+delivery Records streams. The server's
core-protocol shadow filters carry delivery and permission records on
the same stream, so it subsumes both. Events remain invalidation-only
wake hints; role-only nodes keep the existing two-stream behavior.
- RefreshCoordinator: constructor-configurable stream set ([feed] vs
[topology, delivery]); operations on unknown streams are ignored;
health/mode semantics unchanged. New DegradedReason status field.
- SubscriptionWatcher: feed mode with cursor persistence (debounced on
events, immediate on EOSE, cleared on 410 ProgressGap) through a
feedSyncRecorder that owns the checkpoint; the auth-terminal sentinel
(grants revoked/expired mid-stream) drops the stream to uncovered
polling fallback and surfaces an operator-facing reason.
- DWNControl: periodic-only refresh batches probe the remote feed
fingerprint (cids-only MessagesQuery via control.FeedFingerprint) and
skip the full 8-phase LoadState when it matches the fingerprint
recorded immediately before the last successful load; a full rebuild
is still forced at least every 30 minutes. Event-driven, startup,
manual, and endpoint reasons always rebuild.
- Engine.Config: feed grant IDs + epoch tuples and FeedSyncLoad/Save
hooks (the engine never learns file paths); the link key
(tenant^endpoint^projectionId^authorizationEpoch) gates cursor resume
so grant churn discards stale checkpoints.
Refs #267
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tatus
DelegateSession now resolves the active protocol-root Messages.Read
grants for the mesh protocol into canonical grant IDs plus the
{id, dateGranted, dateExpires} epoch tuples; sessions without feed
coverage stay on the Records streams. cmd/meshd threads them (and the
feedsync.json load/save hooks) into engine.Config like the existing
grant plumbing, and clears the feed checkpoint on network leave.
daemon.SnapshotStatus carries the coordinator's DegradedReason so
'meshd status' can tell operators when wallet session grants expired
mid-stream and a reconnect is needed.
Refs #267
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MessagesSubscribeDeliveryFailed is the server's generic non-DwnError
delivery fault, unrelated to grants, but it was wrapped in
ErrSubscriptionAuthorizationRevoked alongside the authorization code.
That made handleFeedLifecycle drop the feed stream to uncovered polling
and surface the wallet-grant degraded reason ('run meshd auth connect')
— categorically wrong on anchor-owner nodes, which have no wallet
session, and misleading for delegates. Only
MessagesSubscribeDeliveryAuthorizationFailed now wraps the sentinel;
DeliveryFailed takes the plain terminal path (coverage retained,
live=false, fallback cadence), matching the enbox reference agent,
which pauses only on auth codes and repairs/retries otherwise.
Refs #267
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Fixes #267
What changed
The daemon's control plane now converges via the replication feed wherever it can, replacing blind full reloads with fingerprint-verified freshness:
Messages.Readgrants — which meshd has requested at connect since forever but never invoked): the two Records WebSocket streams collapse into oneMessagesSubscribestream filtered to the mesh protocol. The server's core-protocol shadow filters mean this single stream also carries$encryption/deliverycontrol records and mesh-tagged permission/encryption records. Events remain invalidation-only wake hints — never checkpoint evidence.MessagesQuery, limit 1); if unchanged since the last successful load, the full 8-phaseLoadStateis skipped. Event/startup/manual/endpoint reasons always load, and a full load is forced at least every 30 minutes regardless.feedsync.jsonin the profile state dir, atomic write + owner alignment): resume cursor + fingerprint bound to a link key (tenant^endpoint^projectionId^authorizationEpoch). Grant churn or expiry changes the epoch → cursor discarded; 410 ProgressGap clears it; restart resumes instead of cold full-refresh.Messages.Readgrants means no Messages interface (tenant bypass is owner-only), so they keep the existing topology+delivery Records streams and polling exactly as today. The RefreshCoordinator's stream set is now constructor-configurable to support both shapes; subscribe-before-repair and sequence-fenced repair semantics are preserved.Verification
go build ./...— cleango vet ./...— cleango test ./... -count=1 -race— all packages pass, no data racesDWN_ENDPOINT.🤖 Generated with Claude Code