@calimero-network/mero-js 17.0.0 → 18.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { HttpClient } from '../http-client/index.js';
2
- import type { HealthStatus, AdminAuthStatus, InstallApplicationRequest, InstallApplicationResponseData, InstallDevApplicationRequest, UninstallApplicationResponseData, ListApplicationsResponseData, GetApplicationResponseData, ApplicationVersionEntry, GetApplicationAbiResponseData, GetLatestVersionResponseData, ListPackagesResponseData, ListVersionsResponseData, CreateContextRequest, CreateContextResponseData, DeleteContextRequest, DeleteContextResponseData, GetContextsResponseData, Context, GenerateContextIdentityResponseData, GetContextIdentitiesResponseData, JoinContextResponseData, JoinSubgroupInheritanceResponseData, ContextGroupResponseData, ContextStorageResponseData, InviteSpecializedNodeRequest, InviteSpecializedNodeResponseData, UpdateContextApplicationRequest, ResyncContextRequest, ResyncContextResponseData, ContextsWithExecutorsResponseData, UploadBlobRequest, UploadBlobResponseData, DeleteBlobResponseData, ListBlobsResponseData, GetBlobInfoResponseData, CreateContextAliasRequest, CreateApplicationAliasRequest, CreateAliasResponseData, LookupAliasResponseData, DeleteAliasResponseData, ListAliasesResponseData, ListNamespacesResponseData, CreateNamespaceRequest, CreateNamespaceResponseData, DeleteNamespaceRequest, DeleteNamespaceResponseData, CreateNamespaceInvitationRequest, CreateNamespaceInvitationResponseData, CreateRecursiveInvitationResponseData, JoinNamespaceRequest, JoinNamespaceResponseData, CreateGroupInNamespaceRequest, CreateGroupInNamespaceResponseData, SubgroupEntry, Namespace, NamespaceIdentity, NodeIdentity, GroupInfoResponseData, DeleteGroupRequest, DeleteGroupResponseData, ListGroupMembersResponseData, ListGroupContextsResponseData, AddGroupMembersRequest, RemoveGroupMembersRequest, UpdateMemberRoleRequest, MemberCapabilities, SetMemberCapabilitiesRequest, SetDefaultCapabilitiesRequest, SetSubgroupVisibilityRequest, SetTeeAdmissionPolicyRequest, GetTeeAdmissionPolicyResponseData, SetGroupMetadataRequest, SetMemberMetadataRequest, SetContextMetadataRequest, MetadataRecord, SyncGroupRequest, SyncGroupResponseData, UpgradeGroupRequest, UpgradeGroupResponseData, GroupUpgradeStatusResponseData, MigrationStatus, CascadeStatusEntry, RetryGroupUpgradeRequest, RetryGroupUpgradeResponseData, ReparentGroupRequest, ReparentGroupResponseData, DetachContextFromGroupRequest, CreateGroupInvitationRequest, CreateGroupInvitationResponseData, CreateRecursiveGroupInvitationResponseData, JoinGroupRequest, JoinGroupResponseData, TeeInfoResponseData, TeeAttestRequest, TeeAttestResponseData, TeeVerifyQuoteRequest, TeeVerifyQuoteResponseData, PerformIntentRequest, PerformIntentResponseData, AdmitJoinRequest, AdmitJoinResponseData } from './admin-types.js';
2
+ import type { HealthStatus, AdminAuthStatus, InstallApplicationRequest, InstallApplicationResponseData, InstallDevApplicationRequest, UninstallApplicationResponseData, ListApplicationsResponseData, GetApplicationResponseData, ApplicationVersionEntry, GetApplicationAbiResponseData, GetLatestVersionResponseData, ListPackagesResponseData, ListVersionsResponseData, CreateContextRequest, CreateContextResponseData, DeleteContextRequest, DeleteContextResponseData, GetContextsResponseData, Context, GenerateContextIdentityResponseData, GetContextIdentitiesResponseData, JoinContextResponseData, JoinSubgroupInheritanceResponseData, ContextGroupResponseData, ContextStorageResponseData, InviteSpecializedNodeRequest, InviteSpecializedNodeResponseData, UpdateContextApplicationRequest, ResyncContextRequest, ResyncContextResponseData, ContextsWithExecutorsResponseData, UploadBlobRequest, UploadBlobResponseData, DeleteBlobResponseData, ListBlobsResponseData, GetBlobInfoResponseData, CreateContextAliasRequest, CreateApplicationAliasRequest, CreateAliasResponseData, LookupAliasResponseData, DeleteAliasResponseData, ListAliasesResponseData, ListNamespacesResponseData, CreateNamespaceRequest, CreateNamespaceResponseData, DeleteNamespaceRequest, DeleteNamespaceResponseData, CreateNamespaceInvitationRequest, CreateNamespaceInvitationResponseData, CreateRecursiveInvitationResponseData, JoinNamespaceRequest, JoinNamespaceResponseData, CreateGroupInNamespaceRequest, CreateGroupInNamespaceResponseData, SubgroupEntry, Namespace, NamespaceIdentity, NodeIdentity, AccountPairInitRequest, AccountPairInitResponseData, AccountPairCompleteRequest, AccountPairCompleteResponseData, RelinkDeviceRequest, RelinkDeviceResponseData, AccountDeviceEntry, AccountApplicationEntry, RevokeAccountDeviceRequest, RevokeAccountDeviceResponseData, GroupInfoResponseData, DeleteGroupRequest, DeleteGroupResponseData, ListGroupMembersResponseData, ListGroupContextsResponseData, AddGroupMembersRequest, RemoveGroupMembersRequest, UpdateMemberRoleRequest, MemberCapabilities, SetMemberCapabilitiesRequest, SetDefaultCapabilitiesRequest, SetSubgroupVisibilityRequest, SetTeeAdmissionPolicyRequest, GetTeeAdmissionPolicyResponseData, SetGroupMetadataRequest, SetMemberMetadataRequest, SetContextMetadataRequest, MetadataRecord, SyncGroupRequest, SyncGroupResponseData, UpgradeGroupRequest, UpgradeGroupResponseData, GroupUpgradeStatusResponseData, MigrationStatus, CascadeStatusEntry, RetryGroupUpgradeRequest, RetryGroupUpgradeResponseData, ReparentGroupRequest, ReparentGroupResponseData, DetachContextFromGroupRequest, CreateGroupInvitationRequest, CreateGroupInvitationResponseData, CreateRecursiveGroupInvitationResponseData, JoinGroupRequest, JoinGroupResponseData, TeeInfoResponseData, TeeAttestRequest, TeeAttestResponseData, TeeVerifyQuoteRequest, TeeVerifyQuoteResponseData, PerformIntentRequest, PerformIntentResponseData, AdmitJoinRequest, AdmitJoinResponseData } from './admin-types.js';
3
3
  /**
4
4
  * Compare two version strings by SemVer 2.0 precedence, ascending: negative if
5
5
  * `a < b`, positive if `a > b`, `0` if equal. Build metadata (`+...`) is stripped
@@ -16,16 +16,14 @@ export declare class AdminApiClient {
16
16
  constructor(httpClient: HttpClient);
17
17
  healthCheck(): Promise<HealthStatus>;
18
18
  isAuthed(): Promise<AdminAuthStatus>;
19
- installApplication(request: InstallApplicationRequest): Promise<InstallApplicationResponseData>;
20
19
  /**
21
- * Resolve a `package@version` to its registry artifact URL and install it.
22
- * Node install is URL-based (no node-side package+version resolution), so this
23
- * fetches the bundle manifest from the registry, derives the `.mpk` artifact
24
- * URL, then calls {@link installApplication}. `registryUrl` is the registry
25
- * origin. This is the discrete "download" step an Updates flow pairs with a
26
- * subsequent `upgradeGroup`.
20
+ * Install a published application by its `package@version` coordinates. The
21
+ * node resolves and fetches them from its own configured `[registry]`, so no
22
+ * URL is passed and none is accepted. A 502 means the node's source has
23
+ * nothing published at those coordinates. This is the discrete "download"
24
+ * step an Updates flow pairs with a subsequent `upgradeGroup`.
27
25
  */
28
- installFromRegistry(registryUrl: string, packageName: string, version: string): Promise<InstallApplicationResponseData>;
26
+ installApplication(request: InstallApplicationRequest): Promise<InstallApplicationResponseData>;
29
27
  /**
30
28
  * List a package's published versions from the registry, newest-first by
31
29
  * semver. Reads the registry's V2 bundle listing
@@ -180,6 +178,97 @@ export declare class AdminApiClient {
180
178
  joinNamespace(namespaceId: string, request: JoinNamespaceRequest): Promise<JoinNamespaceResponseData>;
181
179
  createGroupInNamespace(namespaceId: string, request?: CreateGroupInNamespaceRequest): Promise<CreateGroupInNamespaceResponseData>;
182
180
  listNamespaceGroups(namespaceId: string): Promise<SubgroupEntry[]>;
181
+ /**
182
+ * Mint this node a device for an account that already lives somewhere else -
183
+ * the first half of pairing, run on the joining node.
184
+ *
185
+ * Publishes nothing and needs no key: it produces the device id, the two
186
+ * public keys and the signature that the account holder's
187
+ * {@link completeAccountPairing} certifies, plus a code to read out to them
188
+ * over a channel the payload does not travel on. Until that second half runs,
189
+ * the device this mints is inert.
190
+ *
191
+ * Takes a set of namespaces rather than one, and takes it from the caller
192
+ * because this node cannot discover it: a node that is a member of nothing can
193
+ * neither read the account's namespace set off a DAG nor derive it. One device
194
+ * covers however many are named - the certificate is over the account, not a
195
+ * scope - so there is one id and one code to hand over regardless.
196
+ */
197
+ initAccountPairing(request: AccountPairInitRequest): Promise<AccountPairInitResponseData>;
198
+ /**
199
+ * Certify a device another node minted, link it, and deliver its scope keys -
200
+ * the second half of pairing, run on the node that holds the account root.
201
+ *
202
+ * Every field but `applications` is copied verbatim out of that node's
203
+ * {@link initAccountPairing} answer. Only this side can complete it: the
204
+ * certificate is signed by the account root, which never leaves the node that
205
+ * holds it.
206
+ *
207
+ * The scope is named in applications, not namespaces, because that is the
208
+ * question a person can answer - a namespace is an implementation unit they
209
+ * never chose. Leave it out to grant every application.
210
+ */
211
+ completeAccountPairing(request: AccountPairCompleteRequest): Promise<AccountPairCompleteResponseData>;
212
+ /**
213
+ * Re-run the pairing fan-out for a device this account already certified,
214
+ * against the namespaces this node takes part in now.
215
+ *
216
+ * Pairing is a snapshot, and this is how it is repeated. A namespace created
217
+ * or joined afterwards holds no binding for a device paired before it, and the
218
+ * symptom is silence - the device simply never sees that namespace. Nothing
219
+ * but the id is needed to repair it: the certificate is already held here,
220
+ * root-signed and naming no namespace, so a fresh endorsement and a key wrap
221
+ * are all a later namespace is missing. The device does not have to be online.
222
+ *
223
+ * Passing `applications` widens the device's stored scope as well as
224
+ * repairing. Passing none - or an empty list - repairs against the scope
225
+ * already stored, which is deliberately NOT the "every application" that an
226
+ * empty list means on {@link completeAccountPairing}: overloading it here
227
+ * would make the accidental request the widest one.
228
+ */
229
+ relinkAccountDevice(deviceId: string, request?: RelinkDeviceRequest): Promise<RelinkDeviceResponseData>;
230
+ /**
231
+ * Every device of this account, as this node sees them - its own included.
232
+ *
233
+ * Joined from the node-local certificate cache and the live bindings of every
234
+ * namespace this node takes part in, so it is this node's view rather than a
235
+ * global one: a device certified by another holder appears with an empty
236
+ * `applications`, because the certificate that would name its scope is not
237
+ * cached here.
238
+ *
239
+ * The wire wraps this in `devices` rather than the usual `data`; the array is
240
+ * what a caller wants, so that wrapper is not passed on.
241
+ */
242
+ listAccountDevices(): Promise<AccountDeviceEntry[]>;
243
+ /**
244
+ * Every application this account speaks in, with the namespaces targeting each.
245
+ *
246
+ * What a device-scoping UI lists: {@link completeAccountPairing} and
247
+ * {@link relinkAccountDevice} both take applications, and this is where the
248
+ * ids they take come from. Derived from the namespaces this node takes part
249
+ * in, so an application installed with no namespace yet is absent - it has
250
+ * nothing to scope a device to until one exists.
251
+ *
252
+ * Wrapped in `applications` on the wire rather than `data`, unwrapped here for
253
+ * the same reason {@link listAccountDevices} unwraps `devices`.
254
+ */
255
+ listAccountApplications(): Promise<AccountApplicationEntry[]>;
256
+ /**
257
+ * Withdraw a device from this account, terminally.
258
+ *
259
+ * Two authorities reach the same endpoint. An admin may revoke any device and
260
+ * rotates the scope key as it goes. The account holder may revoke its own with
261
+ * a root-signed `proof`, which does not rotate - so read
262
+ * {@link RevokeAccountDeviceResponseData.revokedIn} rather than assuming the
263
+ * device lost its access: a namespace reporting `keyRotated: false` has
264
+ * stopped the device writing but leaves it able to read until an admin
265
+ * rotates.
266
+ *
267
+ * `namespaceId` names where to publish from, not the extent of the revocation:
268
+ * a device belongs to the account, so every namespace holding a binding for it
269
+ * is withdrawn from and reported back.
270
+ */
271
+ revokeAccountDevice(namespaceId: string, request: RevokeAccountDeviceRequest): Promise<RevokeAccountDeviceResponseData>;
183
272
  getGroupInfo(groupId: string): Promise<GroupInfoResponseData>;
184
273
  /** Thin wrapper over {@link getGroupInfo}: returns the group's `defaultCapabilities` bitmask. */
185
274
  getDefaultCapabilities(groupId: string): Promise<number>;
@@ -107,34 +107,15 @@ export class AdminApiClient {
107
107
  return this.httpClient.get('/admin-api/is-authed');
108
108
  }
109
109
  // ---- Application Management ----
110
- async installApplication(request) {
111
- return unwrap(await this.httpClient.post('/admin-api/install-application', request));
112
- }
113
110
  /**
114
- * Resolve a `package@version` to its registry artifact URL and install it.
115
- * Node install is URL-based (no node-side package+version resolution), so this
116
- * fetches the bundle manifest from the registry, derives the `.mpk` artifact
117
- * URL, then calls {@link installApplication}. `registryUrl` is the registry
118
- * origin. This is the discrete "download" step an Updates flow pairs with a
119
- * subsequent `upgradeGroup`.
111
+ * Install a published application by its `package@version` coordinates. The
112
+ * node resolves and fetches them from its own configured `[registry]`, so no
113
+ * URL is passed and none is accepted. A 502 means the node's source has
114
+ * nothing published at those coordinates. This is the discrete "download"
115
+ * step an Updates flow pairs with a subsequent `upgradeGroup`.
120
116
  */
121
- async installFromRegistry(registryUrl, packageName, version) {
122
- const base = new URL(registryUrl).origin;
123
- const manifestUrl = new URL(`/api/v2/bundles/${encodeURIComponent(packageName)}/${encodeURIComponent(version)}`, base).toString();
124
- const resp = await fetchRegistry(manifestUrl, 'manifest', `${packageName}@${version}`);
125
- const bundle = (await resp.json());
126
- // Encode the path segments — the package/version come from a (best-effort
127
- // trusted) registry response, so guard against odd characters breaking or
128
- // traversing the artifact path. For normal ids/semvers this is a no-op.
129
- const pkg = encodeURIComponent(bundle.package);
130
- const ver = encodeURIComponent(bundle.appVersion);
131
- const artifactUrl = `${base}/artifacts/${pkg}/${ver}/${pkg}-${ver}.mpk`;
132
- return this.installApplication({
133
- url: artifactUrl,
134
- package: bundle.package,
135
- version: bundle.appVersion,
136
- metadata: [],
137
- });
117
+ async installApplication(request) {
118
+ return unwrap(await this.httpClient.post('/admin-api/install-application', request));
138
119
  }
139
120
  /**
140
121
  * List a package's published versions from the registry, newest-first by
@@ -505,6 +486,112 @@ export class AdminApiClient {
505
486
  async listNamespaceGroups(namespaceId) {
506
487
  return unwrap(await this.httpClient.get(`/admin-api/namespaces/${namespaceId}/groups`));
507
488
  }
489
+ // ---- Account Devices & Pairing ----
490
+ /**
491
+ * Mint this node a device for an account that already lives somewhere else -
492
+ * the first half of pairing, run on the joining node.
493
+ *
494
+ * Publishes nothing and needs no key: it produces the device id, the two
495
+ * public keys and the signature that the account holder's
496
+ * {@link completeAccountPairing} certifies, plus a code to read out to them
497
+ * over a channel the payload does not travel on. Until that second half runs,
498
+ * the device this mints is inert.
499
+ *
500
+ * Takes a set of namespaces rather than one, and takes it from the caller
501
+ * because this node cannot discover it: a node that is a member of nothing can
502
+ * neither read the account's namespace set off a DAG nor derive it. One device
503
+ * covers however many are named - the certificate is over the account, not a
504
+ * scope - so there is one id and one code to hand over regardless.
505
+ */
506
+ async initAccountPairing(request) {
507
+ return unwrap(await this.httpClient.post('/admin-api/account/pair-init', request));
508
+ }
509
+ /**
510
+ * Certify a device another node minted, link it, and deliver its scope keys -
511
+ * the second half of pairing, run on the node that holds the account root.
512
+ *
513
+ * Every field but `applications` is copied verbatim out of that node's
514
+ * {@link initAccountPairing} answer. Only this side can complete it: the
515
+ * certificate is signed by the account root, which never leaves the node that
516
+ * holds it.
517
+ *
518
+ * The scope is named in applications, not namespaces, because that is the
519
+ * question a person can answer - a namespace is an implementation unit they
520
+ * never chose. Leave it out to grant every application.
521
+ */
522
+ async completeAccountPairing(request) {
523
+ return unwrap(await this.httpClient.post('/admin-api/account/pair-complete', request));
524
+ }
525
+ /**
526
+ * Re-run the pairing fan-out for a device this account already certified,
527
+ * against the namespaces this node takes part in now.
528
+ *
529
+ * Pairing is a snapshot, and this is how it is repeated. A namespace created
530
+ * or joined afterwards holds no binding for a device paired before it, and the
531
+ * symptom is silence - the device simply never sees that namespace. Nothing
532
+ * but the id is needed to repair it: the certificate is already held here,
533
+ * root-signed and naming no namespace, so a fresh endorsement and a key wrap
534
+ * are all a later namespace is missing. The device does not have to be online.
535
+ *
536
+ * Passing `applications` widens the device's stored scope as well as
537
+ * repairing. Passing none - or an empty list - repairs against the scope
538
+ * already stored, which is deliberately NOT the "every application" that an
539
+ * empty list means on {@link completeAccountPairing}: overloading it here
540
+ * would make the accidental request the widest one.
541
+ */
542
+ async relinkAccountDevice(deviceId, request) {
543
+ return unwrap(await this.httpClient.post(`/admin-api/account/devices/${deviceId}/relink`, request ?? {}));
544
+ }
545
+ /**
546
+ * Every device of this account, as this node sees them - its own included.
547
+ *
548
+ * Joined from the node-local certificate cache and the live bindings of every
549
+ * namespace this node takes part in, so it is this node's view rather than a
550
+ * global one: a device certified by another holder appears with an empty
551
+ * `applications`, because the certificate that would name its scope is not
552
+ * cached here.
553
+ *
554
+ * The wire wraps this in `devices` rather than the usual `data`; the array is
555
+ * what a caller wants, so that wrapper is not passed on.
556
+ */
557
+ async listAccountDevices() {
558
+ const response = await this.httpClient.get('/admin-api/account/devices');
559
+ return response.devices;
560
+ }
561
+ /**
562
+ * Every application this account speaks in, with the namespaces targeting each.
563
+ *
564
+ * What a device-scoping UI lists: {@link completeAccountPairing} and
565
+ * {@link relinkAccountDevice} both take applications, and this is where the
566
+ * ids they take come from. Derived from the namespaces this node takes part
567
+ * in, so an application installed with no namespace yet is absent - it has
568
+ * nothing to scope a device to until one exists.
569
+ *
570
+ * Wrapped in `applications` on the wire rather than `data`, unwrapped here for
571
+ * the same reason {@link listAccountDevices} unwraps `devices`.
572
+ */
573
+ async listAccountApplications() {
574
+ const response = await this.httpClient.get('/admin-api/account/applications');
575
+ return response.applications;
576
+ }
577
+ /**
578
+ * Withdraw a device from this account, terminally.
579
+ *
580
+ * Two authorities reach the same endpoint. An admin may revoke any device and
581
+ * rotates the scope key as it goes. The account holder may revoke its own with
582
+ * a root-signed `proof`, which does not rotate - so read
583
+ * {@link RevokeAccountDeviceResponseData.revokedIn} rather than assuming the
584
+ * device lost its access: a namespace reporting `keyRotated: false` has
585
+ * stopped the device writing but leaves it able to read until an admin
586
+ * rotates.
587
+ *
588
+ * `namespaceId` names where to publish from, not the extent of the revocation:
589
+ * a device belongs to the account, so every namespace holding a binding for it
590
+ * is withdrawn from and reported back.
591
+ */
592
+ async revokeAccountDevice(namespaceId, request) {
593
+ return unwrap(await this.httpClient.post(`/admin-api/namespaces/${namespaceId}/account/revoke`, request));
594
+ }
508
595
  // ---- Group Management ----
509
596
  async getGroupInfo(groupId) {
510
597
  return unwrap(await this.httpClient.get(`/admin-api/groups/${groupId}`));