@calimero-network/mero-js 18.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
@@ -178,6 +178,97 @@ export declare class AdminApiClient {
178
178
  joinNamespace(namespaceId: string, request: JoinNamespaceRequest): Promise<JoinNamespaceResponseData>;
179
179
  createGroupInNamespace(namespaceId: string, request?: CreateGroupInNamespaceRequest): Promise<CreateGroupInNamespaceResponseData>;
180
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>;
181
272
  getGroupInfo(groupId: string): Promise<GroupInfoResponseData>;
182
273
  /** Thin wrapper over {@link getGroupInfo}: returns the group's `defaultCapabilities` bitmask. */
183
274
  getDefaultCapabilities(groupId: string): Promise<number>;
@@ -486,6 +486,112 @@ export class AdminApiClient {
486
486
  async listNamespaceGroups(namespaceId) {
487
487
  return unwrap(await this.httpClient.get(`/admin-api/namespaces/${namespaceId}/groups`));
488
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
+ }
489
595
  // ---- Group Management ----
490
596
  async getGroupInfo(groupId) {
491
597
  return unwrap(await this.httpClient.get(`/admin-api/groups/${groupId}`));
@@ -441,6 +441,18 @@ export interface NodeIdentity {
441
441
  * claim something the node the caller chose may not report.
442
442
  */
443
443
  accountRootPublicKey?: string;
444
+ /**
445
+ * Whether this node holds the root key of the account it speaks for. `false`
446
+ * means it runs on a delegate device key, so it cannot certify another device
447
+ * into the account — which is what a pairing invite needs to know before
448
+ * offering the option.
449
+ *
450
+ * Optional for the same reason as `accountRootPublicKey`: a node at or below
451
+ * `0.11.0-rc.30` does not send it. `undefined` is therefore "this node is too
452
+ * old to say", which is not the same answer as `false`, and a caller gating an
453
+ * invite on it should not collapse the two.
454
+ */
455
+ holdsAccountRoot?: boolean;
444
456
  }
445
457
  /**
446
458
  * @deprecated Use {@link AdminApiClient.getNodeIdentity} instead. Nothing here
@@ -530,6 +542,330 @@ export interface SubgroupEntry {
530
542
  groupId: string;
531
543
  name?: string;
532
544
  }
545
+ /**
546
+ * Adopt an account that already lives on another device, and mint this node a
547
+ * device of its own for it.
548
+ *
549
+ * The first half of pairing, run on the joining node. It publishes nothing and
550
+ * needs no key: it produces the values the account holder's
551
+ * {@link AdminApiClient.completeAccountPairing} certifies, plus a confirmation
552
+ * code to read out to them.
553
+ *
554
+ * Account-level, and that is the whole point of it: both credentials pairing
555
+ * mints were always account-wide - the certificate is root-signed and the
556
+ * endorsement node-level, so neither ever named a namespace - while the older
557
+ * per-namespace route still made the caller name one, and the device then
558
+ * listened on that one topic while its holder's link fanned out across all of
559
+ * them.
560
+ */
561
+ export interface AccountPairInitRequest {
562
+ /**
563
+ * The epoch-0 root **public** key of the account to join, 64 hex characters -
564
+ * exactly what {@link NodeIdentity.accountRootPublicKey} reports on the node
565
+ * that already holds the account.
566
+ *
567
+ * Named for the half it carries. An ed25519 private and public key are both 32
568
+ * bytes and both hex, so nothing but the name tells them apart; the private
569
+ * root never crosses this boundary at all.
570
+ */
571
+ accountRootPublicKey: string;
572
+ /**
573
+ * Ids of the namespaces to enroll into, 64 hex characters each. At least one:
574
+ * a device certified into nothing listens on no topic.
575
+ *
576
+ * The caller has to supply these, because the joining node cannot discover
577
+ * them - it is a member of nothing and holds no scope key, so it can neither
578
+ * read the account's namespace set off a DAG nor derive it. The device that
579
+ * already holds the account is the only party that knows it.
580
+ *
581
+ * This decides what the new device *listens* on, and it does not have to agree
582
+ * with the applications the holder scopes `pair-complete` to: a binding
583
+ * published where the device is not listening is picked up whenever it does
584
+ * subscribe, and a subscription the holder never reaches costs nothing.
585
+ */
586
+ namespaces: string[];
587
+ }
588
+ /**
589
+ * What the joining device minted, for the account holder to certify.
590
+ *
591
+ * These are round-trip tokens: a caller copies them verbatim into
592
+ * {@link AdminApiClient.completeAccountPairing}. The signing key is the NEW
593
+ * device's, so it appears in no member listing yet and there is nothing here to
594
+ * compare it against.
595
+ *
596
+ * One device is minted however many namespaces were named, so there is one of
597
+ * each of these to hand over, not one per namespace.
598
+ */
599
+ export interface AccountPairInitResponseData {
600
+ /** The account this device will speak for once linked, 64 hex characters. */
601
+ accountId: string;
602
+ /** The device that was minted, 64 hex characters. */
603
+ deviceId: string;
604
+ /** X25519 agreement key a scope key must be wrapped under to reach this device, hex. */
605
+ kemPublicKey: string;
606
+ /**
607
+ * The Ed25519 key this device signs its ops with, hex.
608
+ *
609
+ * The account holder cannot derive this - it is minted here - and the
610
+ * certificate names it, so it has to travel with the other two.
611
+ */
612
+ signPublicKey: string;
613
+ /**
614
+ * Ed25519 signature over the account, the device id and both keys above, hex.
615
+ *
616
+ * Carried so the three values arrive as a statement by the device that minted
617
+ * them rather than as assertions by whoever relayed them; `pair-complete`
618
+ * refuses without it.
619
+ */
620
+ statement: string;
621
+ /**
622
+ * The value to read out to the account holder, who compares it against what
623
+ * their `pair-complete` reports.
624
+ *
625
+ * The part a substituting attacker cannot fake: it can re-sign its own
626
+ * statement, but it would have to make its own keys derive the code the other
627
+ * side is already reading.
628
+ */
629
+ confirmationCode: string;
630
+ }
631
+ /**
632
+ * Certify a device another node minted, link it, and deliver its scope keys.
633
+ *
634
+ * The second half of pairing, run on the node that holds the account root - the
635
+ * only party that can sign the certificate that makes the device real. Every
636
+ * field but `applications` is copied verbatim from that other node's
637
+ * {@link AccountPairInitResponseData}.
638
+ */
639
+ export interface AccountPairCompleteRequest {
640
+ /** The `deviceId` the other node minted, 64 hex characters. */
641
+ deviceId: string;
642
+ /** That device's X25519 agreement key, 64 hex characters. */
643
+ kemPublicKey: string;
644
+ /** That device's Ed25519 signing key, 64 hex characters. */
645
+ signPublicKey: string;
646
+ /**
647
+ * The signature from that node's pair-init, 128 hex characters (64 bytes).
648
+ *
649
+ * Not optional: without it the three values above are only claims by whoever
650
+ * sent them, and certifying those would make attacker-supplied keys a trusted
651
+ * device of this account.
652
+ */
653
+ statement: string;
654
+ /**
655
+ * The code the account holder read off the pairing device, e.g.
656
+ * `7BC0-DAAC-CCB4-84A4`. Grouping and case are ignored.
657
+ *
658
+ * Required so the comparison cannot be skipped: this side re-derives the code
659
+ * for the key material that actually arrived and refuses a mismatch. Its value
660
+ * rests on the code reaching the holder independently of this payload - sent
661
+ * beside the keys, it proves nothing. One code covers the whole pairing,
662
+ * because one device was minted for it.
663
+ */
664
+ confirmationCode: string;
665
+ /**
666
+ * Which applications this device may speak for, 64 hex each. Omitted or empty means
667
+ * every one of them.
668
+ *
669
+ * Scoped by application rather than by namespace because a person can answer
670
+ * "which apps may this device use" and cannot answer "which namespaces" - a
671
+ * namespace is an implementation unit they never named. The node resolves
672
+ * these to namespaces through the same lookup
673
+ * {@link AdminApiClient.listNamespacesForApplication} reads.
674
+ */
675
+ applications?: string[];
676
+ }
677
+ /** What pairing established. */
678
+ export interface AccountPairCompleteResponseData {
679
+ /** The account the device now speaks for, 64 hex characters. */
680
+ accountId: string;
681
+ /** The device that was linked, 64 hex characters. */
682
+ deviceId: string;
683
+ /**
684
+ * Whether the current scope key was wrapped and published for the device.
685
+ *
686
+ * `false` does not mean pairing failed - the link is what confers authority,
687
+ * and the device's own sync pull re-requests the key. It does mean the device
688
+ * cannot read until that lands.
689
+ */
690
+ keyDelivered: boolean;
691
+ /**
692
+ * The code for the key material this certified - the same value the request
693
+ * carried, echoed so the operator can see what the certificate names.
694
+ */
695
+ confirmationCode: string;
696
+ /**
697
+ * The `AccountProof<DeviceCert>` this pairing minted, hex-encoded borsh.
698
+ *
699
+ * The certified device needs this to present itself as a device of the
700
+ * account, and cannot read it off the DAG: doing so means being a member of a
701
+ * group the account speaks in, which a thin client never is. Not a secret - a
702
+ * certificate is public and proves nothing without the device key it names.
703
+ */
704
+ credential: string;
705
+ }
706
+ /**
707
+ * Repair or widen the reach of a device this account already certified.
708
+ *
709
+ * Pairing is a snapshot: it bound the device wherever this node took part at the
710
+ * time, so a namespace created or joined afterwards holds no binding for it and
711
+ * the paired device silently never sees that namespace. This re-runs the fan-out
712
+ * against the namespaces this node takes part in now. The device need not be
713
+ * online, and nothing but its id is required - the certificate is already held
714
+ * here, root-signed and naming no namespace, so a fresh endorsement and a key
715
+ * wrap are all a later namespace is missing.
716
+ */
717
+ export interface RelinkDeviceRequest {
718
+ /**
719
+ * Applications to add to the device's stored scope, 64 hex each.
720
+ *
721
+ * **Omitted or empty changes nothing** and repairs against the scope already
722
+ * stored, which is the request an operator makes to heal drift. Note that this
723
+ * is the one place an empty list does not mean "all": widening a narrow scope
724
+ * to every application is deliberately not expressible here, because
725
+ * overloading the empty list would make the accidental request the widest one.
726
+ */
727
+ applications?: string[];
728
+ }
729
+ /** One namespace the relink published the device's link into. */
730
+ export interface RelinkOutcomeEntry {
731
+ /** The namespace id, 64 hex characters. */
732
+ namespaceId: string;
733
+ /**
734
+ * Whether the scope key was wrapped and published for the device here.
735
+ *
736
+ * `false` means the link landed and the delivery did not; the link is what
737
+ * confers authority, and the device's own sync pull re-requests the key.
738
+ */
739
+ keyDelivered: boolean;
740
+ }
741
+ /** One namespace the relink published nothing into, and why. */
742
+ export interface RelinkSkipEntry {
743
+ /** The namespace id, 64 hex characters. */
744
+ namespaceId: string;
745
+ /**
746
+ * Why nothing was published there. Currently one of `outOfScope`,
747
+ * `alreadyBound`, `noScopeKey`, `revoked`, `ownDevice`, `failed`; left as a
748
+ * string because a node may report a reason newer than this SDK.
749
+ */
750
+ reason: string;
751
+ }
752
+ /** What the relink repaired, and what it left alone. */
753
+ export interface RelinkDeviceResponseData {
754
+ /** The account the device speaks for, 64 hex characters. */
755
+ accountId: string;
756
+ /** The device that was repaired, 64 hex characters. */
757
+ deviceId: string;
758
+ /**
759
+ * The device's scope after the request, 64 hex each. Empty means every
760
+ * application, which is what a pairing that named none asked for.
761
+ */
762
+ applications: string[];
763
+ /**
764
+ * Namespaces the link was published into by this call. Reported per namespace
765
+ * because publication is per-DAG, so which namespaces a device actually
766
+ * reached is a state the caller has to be able to see.
767
+ */
768
+ linkedIn: RelinkOutcomeEntry[];
769
+ /** Namespaces nothing was published into. */
770
+ skipped: RelinkSkipEntry[];
771
+ }
772
+ /**
773
+ * One device of this account, joined from the node-local certificate cache and
774
+ * the live bindings of every namespace this node takes part in.
775
+ */
776
+ export interface AccountDeviceEntry {
777
+ /** This device's id, 64 hex characters. */
778
+ deviceId: string;
779
+ /** The key this device signs its ops with, 64 hex. */
780
+ signingKey: string;
781
+ /** Set only on the device this node itself presents. */
782
+ isSelf: boolean;
783
+ revoked: boolean;
784
+ /**
785
+ * Applications this device may speak for, 64 hex each. **Empty means every
786
+ * application**, the same convention the stored certificate uses - and the
787
+ * opposite of what an empty {@link RelinkDeviceRequest.applications} asks for.
788
+ * Empty also for a device this node holds no cached certificate for: one bound
789
+ * before the cache existed, or certified by another holder.
790
+ */
791
+ applications: string[];
792
+ /**
793
+ * Ids of the namespaces currently holding a live binding for this device, 64
794
+ * hex characters each. Empty for a certified device not yet bound anywhere.
795
+ */
796
+ namespaces: string[];
797
+ }
798
+ /**
799
+ * One application this account speaks in, derived from the namespaces this node
800
+ * takes part in that target it.
801
+ *
802
+ * An application installed with no namespace yet is absent here: it has no
803
+ * cross-device meaning until a namespace exists, so there is nothing to scope a
804
+ * device to.
805
+ */
806
+ export interface AccountApplicationEntry {
807
+ /** The application id, 64 hex. */
808
+ applicationId: string;
809
+ /** Ids of the namespaces targeting it, 64 hex characters each. */
810
+ namespaces: string[];
811
+ }
812
+ /** Withdraw a device from an account, terminally. */
813
+ export interface RevokeAccountDeviceRequest {
814
+ /** The device to withdraw, 64 hex. */
815
+ deviceId: string;
816
+ /**
817
+ * A hex-encoded, borsh-serialized signed revocation minted elsewhere -
818
+ * the output of `merod account revoke-proof`.
819
+ *
820
+ * The lost-device path: the account root that owns the device is not on this
821
+ * node, so the proof is signed offline and this node only publishes it. Omit
822
+ * it and the node mints its own proof if it owns the account, and otherwise
823
+ * revokes as an admin.
824
+ *
825
+ * Not a credential and not a secret - it authorises this one revocation of
826
+ * this one device, and only alongside a stored binding naming the same
827
+ * account.
828
+ */
829
+ proof?: string;
830
+ }
831
+ /** What a revocation withdrew in one namespace. */
832
+ export interface RevocationOutcome {
833
+ /** The namespace, 64 hex. */
834
+ namespaceId: string;
835
+ /**
836
+ * Whether the scope key rotated in the same op, in THIS namespace.
837
+ *
838
+ * `false` means the device stopped writing there at once but still holds the
839
+ * key it had, so it can keep READING until an admin rotates. Only an admin
840
+ * may rotate and an account holder revoking their own device usually is not
841
+ * one, so this is commonly owed rather than exceptional.
842
+ */
843
+ keyRotated: boolean;
844
+ }
845
+ export interface RevokeAccountDeviceResponseData {
846
+ /** The account the device spoke for, 64 hex. */
847
+ accountId: string;
848
+ /** The device that was withdrawn, 64 hex. */
849
+ deviceId: string;
850
+ /**
851
+ * Whether the scope key rotated in the namespace named in the request.
852
+ *
853
+ * {@link revokedIn} is the full picture; prefer it. This reports only the one
854
+ * namespace the caller asked about and is retained because core keeps it for
855
+ * a released `calimero-client-py` wheel that requires the field.
856
+ */
857
+ keyRotated: boolean;
858
+ /**
859
+ * Every namespace the revocation was published into.
860
+ *
861
+ * A device belongs to an account rather than to a scope, so revoking it
862
+ * withdraws it from every namespace holding a binding for it - not only the
863
+ * one named in the request. Per namespace because publication is per-DAG: a
864
+ * namespace absent here did not receive the op, and a partially propagated
865
+ * revocation is a state the caller has to be able to see.
866
+ */
867
+ revokedIn: RevocationOutcome[];
868
+ }
533
869
  export interface CreateGroupRequest {
534
870
  applicationId: string;
535
871
  groupId?: string;