feat(store,doctor): surface the hub's trust + exposure data (0.77.0)
Some checks failed
Security / Security check (push) Failing after 1s

The trust-gate dialog replaces its generic 'no per-entry status yet
(alpha)' note with the hub's classified verification statement —
pinned store key / trusted publishers (green), installs without
signature checking (amber), install would be refused (red), bridge
entry (neutral) — via one shared describeInstallVerification mapping.
Against a pre-0.23 hub the field is empty and the old honest wording
stays (pinned by test).

Information architecture: policy-off is a GLOBAL fact, so it appears
as ONE ChainInlineHelp notice above the store grid instead of a
warning pill on every card (card noise); only 'blocked' — a genuine
per-source anomaly — earns a card pill. Doctor's host services show
the hub-classified network reach per endpoint (local only / private
network / publicly reachable with a protect-it hint / reach unknown).

Verified end-to-end against the live dev hub (guide harness): the
wire field arrives, the store page shows exactly one policy notice
and quiet cards; trust-gate variants captured via the dialog
harness. Suite 123 green.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-07-21 13:14:13 +02:00
parent 87afa4dc05
commit 2f076ccf29
15 changed files with 732 additions and 13 deletions

View file

@ -937,6 +937,7 @@ class HubService {
source: e.source,
canonicalCategory: e.canonicalCategory,
canonicalCategoryLabel: e.canonicalCategoryLabel,
installVerification: e.installVerification,
),
)
.toList();
@ -1289,7 +1290,12 @@ class HubService {
services: services
.map(
(s) =>
ServiceEntry(name: s.name, endpoint: s.endpoint, tags: s.tags),
ServiceEntry(
name: s.name,
endpoint: s.endpoint,
tags: s.tags,
exposure: s.exposure,
),
)
.toList(),
update: UpdateStatus(
@ -1386,10 +1392,16 @@ class ServiceEntry {
final String endpoint;
final List<String> tags;
/// Network reach of [endpoint] as classified by the hub
/// (host part only, no DNS): "loopback" / "private" / "public" /
/// "unknown". Empty from pre-0.23 hubs.
final String exposure;
const ServiceEntry({
required this.name,
required this.endpoint,
required this.tags,
this.exposure = '',
});
}
@ -2233,6 +2245,15 @@ class StoreItem {
/// Human-readable label for [canonicalCategory] (e.g. "Data & Formats").
final String canonicalCategoryLabel;
/// How an install of this entry would be verified under the
/// hub's CURRENT policy — computed hub-side with the same
/// resolvers the install gate enforces, so this can never
/// disagree with what actually happens:
/// "pinned-key" / "trusted-publishers" / "unverified" /
/// "blocked" / "federated". Empty from pre-0.23 hubs the UI
/// keeps its generic note then.
final String installVerification;
/// True iff this entry is a Studio plugin/theme rather than a flow
/// module drives the "Modules | Studio & Themes" store segment.
bool get isStudioPlugin => canonicalCategory == 'studio-themes';
@ -2264,5 +2285,6 @@ class StoreItem {
this.source = '',
this.canonicalCategory = '',
this.canonicalCategoryLabel = '',
this.installVerification = '',
});
}