The package feed and its health

The store's catalog is a live feed, not a stored list. Every configured package source — a git repo of package manifests, or another MeshWeaver instance's registry — is polled every 5 minutes, and the union of what they answer is what /Store shows and what @Store/data/StorePackage returns.

The degradation is deliberate

A poll pass can fail: GitHub can time out, a registry can answer 503. When that happens the feed keeps the last good snapshot for that source. That is correct — a store must not empty itself because one source blinked, and a source that recovers is picked up on the next pass with no intervention.

…but it used to be invisible, and that was the defect

Before #1180 the feed could not tell three different things apart, because all three produced the same "nothing to report" marker:

What actually happened What the feed recorded
The head has not moved — nothing new to fetch nothing to report
The pass blew up with a transient error nothing to report
The pass blew up because the credential is rejected nothing to report

So on memex.meshweaver.cloud every git poll answered Octokit.AuthorizationException: Bad credentials, for the entire readable window, while @Store/data/StorePackage kept returning 132 packages and the store looked completely healthy. The catalog was frozen at whenever the credential last worked; a package published after that simply did not appear, and the only trace anywhere was a warn line every five minutes.

What the feed reports now

Each source carries a StoreFeedStatus row — published as a second virtual collection beside the packages, from the same poll passes (never a second poll loop):

Field Meaning
source / kind / target which source this row is about (git or registry)
consecutiveFailures how many passes have failed in a row — zero after any success
lastSuccess UTC instant of the last pass that ANSWERED (an unmoved head counts)
lastFailure, error when it last failed, and a short bounded description
fault None · Credential · Missing · Transient
degraded whether this is a reported condition
verdict one sentence: what is wrong, how stale the catalog is, and whether retrying can help

The signal is PERSISTENCE, never one failure

A source becomes degraded after 3 consecutive failed passes (15 minutes). One failed poll is normal, and serving the last good snapshot is the right answer to it — a signal that fires on one bad minute is a signal people learn to ignore. The fault kind changes the wording and the log level; it never changes the threshold.

A permanent fault SAYS retrying will not help

Bad credentials, a 401, a non-rate-limited 403 and a 404 are permanent: every further pass fails identically, and the verdict says so in those words. Telling somebody to "retry shortly" there sends them round a loop that cannot close.

🚨 A rate-limited 403 is transient and is checked first. GitHub answers 403 both for "this identity may not read that" and for "you have asked too often" — and reading the second as a dead credential would send an operator to replace a credential that is perfectly fine, which is the one false positive that destroys trust in a signal like this.

Frozen is not the same as never loaded

If a pod restarted after the fault began, the source has no last-good list at all: its part of the catalog is missing, not stale. The verdict distinguishes the two, because "you are seeing an old list" and "you are seeing none of this source's packages" call for different reactions.

Where to read it

Which sources are live — the supply-chain rule

StoreManifestSource.Resolve decides the EFFECTIVE source list from three inputs, and it is a statement about the deployment's supply chain rather than about the Store node's content:

  1. the node's declared packageSources, blanks dropped;
  2. plus a registry source for every registry the deployment configures (PluginCatalog:Registries / RegistryUrl) that the node does not already name;
  3. plus a git source for every remote git repository the deployment configures (PluginCatalog:Sources:N:RepoPath + :Ref — what a Deployment record's pluginRepos[] entry with isRegistrySource: true renders) that the node does not already name, however the URL is spelled (case, a trailing slash, .git); the node's own entry wins when both exist;
  4. minus every git source — declared or configured alike — when the installation has no GitHub App identity and another supply (a registry or a local mount): the fetch would go out anonymous against a private repo and can only 404.

Configuration, not node content, is what survives: an unclaimed Store node is re-imported from the shipped Store/index.json on the next Store update, so an edit there is undone. Rules 2 and 3 are what let a consumer instance work with no per-instance edit — and rule 3 is how a client-private node repo, mounted on ONE instance through that instance's record, gets its Store card there without ever being listed on the public registry (whose registration seed grants every source it configures). The feed logs the effective list once per change: [StoreManifestSource] N package source(s): … (git identity, configured registries, configured git sources, local mount).

What this does NOT do

It does not fix a broken credential, and it must not try to. Rotating the GitHub App installation token an instance polls with is an operator action on a running system.

Reconnecting…
The connection to the server was interrupted. Trying to restore it…
Trying again…
The connection could not be restored. Reloading the page…
The server was updated. Reloading the page to pick up the latest version.