From 0eafaa763acb84da69a502dc5d8c1d207395c4d6 Mon Sep 17 00:00:00 2001 From: flemming-it Date: Tue, 21 Jul 2026 12:56:35 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20trust/exposure=20fields=20=E2=80=94=20i?= =?UTF-8?q?nstallVerification,=20keyPinned,=20exposure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regenerated stubs for the additive proto: per-entry StoreEntry.installVerification, StoreSource.keyPinned, the ListStoresResponse policy snapshot (requireSignatures / trustedPublisherCount) and DeclaredService.exposure. New listStoresFull() exposes the response-level snapshot; listStores() stays as the plain source list. Signed-off-by: flemming-it --- CHANGELOG.md | 8 ++ lib/src/generated/chain/v1/hub.pb.dart | 96 ++++++++++++++++++++++ lib/src/generated/chain/v1/hub.pbjson.dart | 34 +++++++- lib/src/hub_client.dart | 9 ++ 4 files changed, 143 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aec10ec..8bbf372 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ for the authoritative per-release detail. ## Unreleased +- Regenerated stubs for the trust/exposure proto additions: + `StoreEntry.installVerification` (pinned-key / trusted-publishers / + unverified / blocked / federated), `StoreSource.keyPinned`, + `ListStoresResponse.requireSignatures` / `trustedPublisherCount`, + `DeclaredService.exposure` (loopback/private/public/unknown). +- New `listStoresFull()` returning the full response incl. the + policy snapshot; `listStores()` unchanged. + - `InvocationList.detachedEnabled`: whether the operator enabled detached invocations — monitor UIs can distinguish "feature off" from "on, but no runs yet". Regenerated stubs. diff --git a/lib/src/generated/chain/v1/hub.pb.dart b/lib/src/generated/chain/v1/hub.pb.dart index 373eff9..fde471a 100644 --- a/lib/src/generated/chain/v1/hub.pb.dart +++ b/lib/src/generated/chain/v1/hub.pb.dart @@ -4962,12 +4962,14 @@ class DeclaredService extends $pb.GeneratedMessage { $core.String? endpoint, $core.String? healthPath, $core.Iterable<$core.String>? tags, + $core.String? exposure, }) { final result = create(); if (name != null) result.name = name; if (endpoint != null) result.endpoint = endpoint; if (healthPath != null) result.healthPath = healthPath; if (tags != null) result.tags.addAll(tags); + if (exposure != null) result.exposure = exposure; return result; } @@ -4988,6 +4990,7 @@ class DeclaredService extends $pb.GeneratedMessage { ..aOS(2, _omitFieldNames ? '' : 'endpoint') ..aOS(3, _omitFieldNames ? '' : 'healthPath') ..pPS(4, _omitFieldNames ? '' : 'tags') + ..aOS(5, _omitFieldNames ? '' : 'exposure') ..hasRequiredFields = false; @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') @@ -5039,6 +5042,23 @@ class DeclaredService extends $pb.GeneratedMessage { @$pb.TagNumber(4) $pb.PbList<$core.String> get tags => $_getList(3); + + /// Network reach of `endpoint`, classified from its host part + /// (no DNS lookups — named hosts other than localhost report + /// "unknown"): + /// "loopback" — 127.0.0.0/8, ::1, localhost + /// "private" — RFC1918 / ULA / link-local ranges + /// "public" — any other literal IP + /// "unknown" — unparseable, or a hostname we cannot judge + /// Empty from pre-0.23 hubs. + @$pb.TagNumber(5) + $core.String get exposure => $_getSZ(4); + @$pb.TagNumber(5) + set exposure($core.String value) => $_setString(4, value); + @$pb.TagNumber(5) + $core.bool hasExposure() => $_has(4); + @$pb.TagNumber(5) + void clearExposure() => $_clearField(5); } class StoreEntry extends $pb.GeneratedMessage { @@ -5067,6 +5087,7 @@ class StoreEntry extends $pb.GeneratedMessage { $core.String? source, $core.String? canonicalCategory, $core.String? canonicalCategoryLabel, + $core.String? installVerification, }) { final result = create(); if (name != null) result.name = name; @@ -5096,6 +5117,8 @@ class StoreEntry extends $pb.GeneratedMessage { if (canonicalCategory != null) result.canonicalCategory = canonicalCategory; if (canonicalCategoryLabel != null) result.canonicalCategoryLabel = canonicalCategoryLabel; + if (installVerification != null) + result.installVerification = installVerification; return result; } @@ -5136,6 +5159,7 @@ class StoreEntry extends $pb.GeneratedMessage { ..aOS(22, _omitFieldNames ? '' : 'source') ..aOS(23, _omitFieldNames ? '' : 'canonicalCategory') ..aOS(24, _omitFieldNames ? '' : 'canonicalCategoryLabel') + ..aOS(25, _omitFieldNames ? '' : 'installVerification') ..hasRequiredFields = false; @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') @@ -5396,6 +5420,33 @@ class StoreEntry extends $pb.GeneratedMessage { $core.bool hasCanonicalCategoryLabel() => $_has(23); @$pb.TagNumber(24) void clearCanonicalCategoryLabel() => $_clearField(24); + + /// How an install of this entry would be verified, given its + /// origin store and the operator's CURRENT security policy. The + /// hub computes this with the same resolution the install path + /// enforces (pinned store key -> trusted publishers -> policy), + /// so the display can never disagree with the gate: + /// "pinned-key" — verified against the origin store's + /// pinned cosign key (or the built-in + /// official key for bundled/official) + /// "trusted-publishers" — verified against the applicable + /// trusted-publisher list + /// "unverified" — require_signatures is off; installs + /// are NOT signature-checked + /// "blocked" — policy requires signatures but no key + /// material applies to this source; an + /// install would be refused + /// "federated" — bridge entry (MCP/n8n/…); no bundle + /// signature applies + /// Empty from pre-0.23 hubs — clients keep their generic note. + @$pb.TagNumber(25) + $core.String get installVerification => $_getSZ(24); + @$pb.TagNumber(25) + set installVerification($core.String value) => $_setString(24, value); + @$pb.TagNumber(25) + $core.bool hasInstallVerification() => $_has(24); + @$pb.TagNumber(25) + void clearInstallVerification() => $_clearField(25); } class ChannelStatusResponse extends $pb.GeneratedMessage { @@ -8963,12 +9014,14 @@ class StoreSource extends $pb.GeneratedMessage { $core.String? url, $core.bool? removable, $core.int? moduleCount, + $core.bool? keyPinned, }) { final result = create(); if (name != null) result.name = name; if (url != null) result.url = url; if (removable != null) result.removable = removable; if (moduleCount != null) result.moduleCount = moduleCount; + if (keyPinned != null) result.keyPinned = keyPinned; return result; } @@ -8989,6 +9042,7 @@ class StoreSource extends $pb.GeneratedMessage { ..aOS(2, _omitFieldNames ? '' : 'url') ..aOB(3, _omitFieldNames ? '' : 'removable') ..aI(4, _omitFieldNames ? '' : 'moduleCount') + ..aOB(5, _omitFieldNames ? '' : 'keyPinned') ..hasRequiredFields = false; @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') @@ -9049,14 +9103,32 @@ class StoreSource extends $pb.GeneratedMessage { $core.bool hasModuleCount() => $_has(3); @$pb.TagNumber(4) void clearModuleCount() => $_clearField(4); + + /// True when this store pins a cosign publisher key + /// (`trust.public_key_pem`, or the built-in official key for the + /// bundled seed) — installs from it are verified against that key + /// whenever signature checking is on. + @$pb.TagNumber(5) + $core.bool get keyPinned => $_getBF(4); + @$pb.TagNumber(5) + set keyPinned($core.bool value) => $_setBool(4, value); + @$pb.TagNumber(5) + $core.bool hasKeyPinned() => $_has(4); + @$pb.TagNumber(5) + void clearKeyPinned() => $_clearField(5); } class ListStoresResponse extends $pb.GeneratedMessage { factory ListStoresResponse({ $core.Iterable? stores, + $core.bool? requireSignatures, + $core.int? trustedPublisherCount, }) { final result = create(); if (stores != null) result.stores.addAll(stores); + if (requireSignatures != null) result.requireSignatures = requireSignatures; + if (trustedPublisherCount != null) + result.trustedPublisherCount = trustedPublisherCount; return result; } @@ -9075,6 +9147,8 @@ class ListStoresResponse extends $pb.GeneratedMessage { createEmptyInstance: create) ..pPM(1, _omitFieldNames ? '' : 'stores', subBuilder: StoreSource.create) + ..aOB(2, _omitFieldNames ? '' : 'requireSignatures') + ..aI(3, _omitFieldNames ? '' : 'trustedPublisherCount') ..hasRequiredFields = false; @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') @@ -9098,6 +9172,28 @@ class ListStoresResponse extends $pb.GeneratedMessage { @$pb.TagNumber(1) $pb.PbList get stores => $_getList(0); + + /// Operator policy snapshot so store UIs can say honestly whether + /// installs are signature-checked at all. + @$pb.TagNumber(2) + $core.bool get requireSignatures => $_getBF(1); + @$pb.TagNumber(2) + set requireSignatures($core.bool value) => $_setBool(1, value); + @$pb.TagNumber(2) + $core.bool hasRequireSignatures() => $_has(1); + @$pb.TagNumber(2) + void clearRequireSignatures() => $_clearField(2); + + /// Number of globally trusted publisher identities + /// (`security.trusted_publishers`). + @$pb.TagNumber(3) + $core.int get trustedPublisherCount => $_getIZ(2); + @$pb.TagNumber(3) + set trustedPublisherCount($core.int value) => $_setSignedInt32(2, value); + @$pb.TagNumber(3) + $core.bool hasTrustedPublisherCount() => $_has(2); + @$pb.TagNumber(3) + void clearTrustedPublisherCount() => $_clearField(3); } class AddStoreRequest extends $pb.GeneratedMessage { diff --git a/lib/src/generated/chain/v1/hub.pbjson.dart b/lib/src/generated/chain/v1/hub.pbjson.dart index 3fc8cd5..b95b0de 100644 --- a/lib/src/generated/chain/v1/hub.pbjson.dart +++ b/lib/src/generated/chain/v1/hub.pbjson.dart @@ -1311,6 +1311,7 @@ const DeclaredService$json = { {'1': 'endpoint', '3': 2, '4': 1, '5': 9, '10': 'endpoint'}, {'1': 'health_path', '3': 3, '4': 1, '5': 9, '10': 'healthPath'}, {'1': 'tags', '3': 4, '4': 3, '5': 9, '10': 'tags'}, + {'1': 'exposure', '3': 5, '4': 1, '5': 9, '10': 'exposure'}, ], }; @@ -1318,7 +1319,7 @@ const DeclaredService$json = { final $typed_data.Uint8List declaredServiceDescriptor = $convert.base64Decode( 'Cg9EZWNsYXJlZFNlcnZpY2USEgoEbmFtZRgBIAEoCVIEbmFtZRIaCghlbmRwb2ludBgCIAEoCV' 'IIZW5kcG9pbnQSHwoLaGVhbHRoX3BhdGgYAyABKAlSCmhlYWx0aFBhdGgSEgoEdGFncxgEIAMo' - 'CVIEdGFncw=='); + 'CVIEdGFncxIaCghleHBvc3VyZRgFIAEoCVIIZXhwb3N1cmU='); @$core.Deprecated('Use storeEntryDescriptor instead') const StoreEntry$json = { @@ -1372,6 +1373,13 @@ const StoreEntry$json = { '5': 9, '10': 'canonicalCategoryLabel' }, + { + '1': 'install_verification', + '3': 25, + '4': 1, + '5': 9, + '10': 'installVerification' + }, ], }; @@ -1391,7 +1399,8 @@ final $typed_data.Uint8List storeEntryDescriptor = $convert.base64Decode( 'oIcHJvdmlkZXIYFCABKAlSCHByb3ZpZGVyEh8KC3NvdXJjZV9raW5kGBUgASgJUgpzb3VyY2VL' 'aW5kEhYKBnNvdXJjZRgWIAEoCVIGc291cmNlEi0KEmNhbm9uaWNhbF9jYXRlZ29yeRgXIAEoCV' 'IRY2Fub25pY2FsQ2F0ZWdvcnkSOAoYY2Fub25pY2FsX2NhdGVnb3J5X2xhYmVsGBggASgJUhZj' - 'YW5vbmljYWxDYXRlZ29yeUxhYmVs'); + 'YW5vbmljYWxDYXRlZ29yeUxhYmVsEjEKFGluc3RhbGxfdmVyaWZpY2F0aW9uGBkgASgJUhNpbn' + 'N0YWxsVmVyaWZpY2F0aW9u'); @$core.Deprecated('Use channelStatusResponseDescriptor instead') const ChannelStatusResponse$json = { @@ -2179,6 +2188,7 @@ const StoreSource$json = { {'1': 'url', '3': 2, '4': 1, '5': 9, '10': 'url'}, {'1': 'removable', '3': 3, '4': 1, '5': 8, '10': 'removable'}, {'1': 'module_count', '3': 4, '4': 1, '5': 5, '10': 'moduleCount'}, + {'1': 'key_pinned', '3': 5, '4': 1, '5': 8, '10': 'keyPinned'}, ], }; @@ -2186,7 +2196,7 @@ const StoreSource$json = { final $typed_data.Uint8List storeSourceDescriptor = $convert.base64Decode( 'CgtTdG9yZVNvdXJjZRISCgRuYW1lGAEgASgJUgRuYW1lEhAKA3VybBgCIAEoCVIDdXJsEhwKCX' 'JlbW92YWJsZRgDIAEoCFIJcmVtb3ZhYmxlEiEKDG1vZHVsZV9jb3VudBgEIAEoBVILbW9kdWxl' - 'Q291bnQ='); + 'Q291bnQSHQoKa2V5X3Bpbm5lZBgFIAEoCFIJa2V5UGlubmVk'); @$core.Deprecated('Use listStoresResponseDescriptor instead') const ListStoresResponse$json = { @@ -2200,13 +2210,29 @@ const ListStoresResponse$json = { '6': '.chain.v1.StoreSource', '10': 'stores' }, + { + '1': 'require_signatures', + '3': 2, + '4': 1, + '5': 8, + '10': 'requireSignatures' + }, + { + '1': 'trusted_publisher_count', + '3': 3, + '4': 1, + '5': 5, + '10': 'trustedPublisherCount' + }, ], }; /// Descriptor for `ListStoresResponse`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List listStoresResponseDescriptor = $convert.base64Decode( 'ChJMaXN0U3RvcmVzUmVzcG9uc2USLQoGc3RvcmVzGAEgAygLMhUuY2hhaW4udjEuU3RvcmVTb3' - 'VyY2VSBnN0b3Jlcw=='); + 'VyY2VSBnN0b3JlcxItChJyZXF1aXJlX3NpZ25hdHVyZXMYAiABKAhSEXJlcXVpcmVTaWduYXR1' + 'cmVzEjYKF3RydXN0ZWRfcHVibGlzaGVyX2NvdW50GAMgASgFUhV0cnVzdGVkUHVibGlzaGVyQ2' + '91bnQ='); @$core.Deprecated('Use addStoreRequestDescriptor instead') const AddStoreRequest$json = { diff --git a/lib/src/hub_client.dart b/lib/src/hub_client.dart index 822333c..311a133 100644 --- a/lib/src/hub_client.dart +++ b/lib/src/hub_client.dart @@ -75,6 +75,7 @@ typedef CheckUpdateResponse = pb.CheckUpdateResponse; typedef ChannelStatusResponse = pb.ChannelStatusResponse; typedef ChannelEntry = pb.ChannelEntry; typedef StoreSource = pb.StoreSource; +typedef ListStoresResponse = pb.ListStoresResponse; typedef AddStoreResponse = pb.AddStoreResponse; typedef RemoveStoreResponse = pb.RemoveStoreResponse; typedef SystemAiStatusResponse = pb.SystemAiStatusResponse; @@ -764,6 +765,14 @@ class HubClient { return r.stores; } + /// Full store listing including the operator policy snapshot + /// (`requireSignatures`, `trustedPublisherCount`) the hub reports + /// alongside the sources since 0.23 — store UIs use it to state + /// honestly whether installs are signature-checked at all. + /// [listStores] stays as the plain source list. + Future listStoresFull() => + _admin.listStores(pb.ListStoresRequest()); + /// Register a new module store. The hub fetches + validates its index, /// persists it to config, and merges it live so its modules are /// immediately searchable + installable.