@calimero-network/mero-js 22.8.0 → 23.0.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.
- package/dist/admin-api/admin-client.d.ts +38 -2
- package/dist/admin-api/admin-client.js +56 -2
- package/dist/admin-api/admin-types.d.ts +94 -0
- package/dist/device-cert/device-cert.d.ts +11 -0
- package/dist/device-cert/device-cert.js +1 -2
- package/dist/device-cert/device-scope.d.ts +58 -0
- package/dist/device-cert/device-scope.js +76 -0
- package/dist/device-cert/index.d.ts +2 -0
- package/dist/device-cert/index.js +1 -0
- package/dist/index.browser.mjs +2 -2
- package/dist/index.browser.mjs.map +4 -4
- package/dist/index.cjs +435 -34
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +8 -2
- package/dist/index.js +7 -1
- package/dist/index.mjs +435 -34
- package/dist/index.mjs.map +4 -4
- package/dist/invitation/index.d.ts +2 -0
- package/dist/invitation/index.js +2 -0
- package/dist/invitation/invitation.d.ts +106 -0
- package/dist/invitation/invitation.js +104 -0
- package/dist/namespace-op/namespace-op.d.ts +19 -1
- package/dist/namespace-op/namespace-op.js +23 -15
- package/dist/owner-op/index.d.ts +2 -0
- package/dist/owner-op/index.js +3 -0
- package/dist/owner-op/owner-op.d.ts +119 -0
- package/dist/owner-op/owner-op.js +214 -0
- package/dist/warrant/governance-op.d.ts +31 -1
- package/dist/warrant/governance-op.js +30 -1
- package/dist/warrant/index.d.ts +2 -2
- package/dist/warrant/index.js +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HttpClient, HTTPError } from '../http-client/index.js';
|
|
2
|
-
import type { HealthStatus, ReadinessStatus, AdminAuthStatus, InstallApplicationRequest, InstallApplicationResponseData, InstallDevApplicationRequest, UninstallApplicationResponseData, ListApplicationsResponseData, GetApplicationResponseData, ApplicationVersionEntry, GetApplicationAbiResponseData, GetLatestVersionResponseData, ListPackagesResponseData, ListVersionsResponseData, CreateContextRequest, CreateContextResponseData, DeleteContextResponseData, GetContextsResponseData, Context, GenerateContextIdentityResponseData, GetContextIdentitiesResponseData, JoinContextResponseData, JoinSubgroupInheritanceResponseData, ContextGroupResponseData, ContextStorageResponseData, InviteSpecializedNodeRequest, InviteSpecializedNodeResponseData, UpdateContextApplicationRequest, ResyncContextRequest, ResyncContextResponseData, ContextsWithExecutorsResponseData, UploadBlobRequest, UploadBlobResponseData, DeleteBlobResponseData, ListBlobsResponseData, GetBlobInfoResponseData, BlobReadOptions, CreateContextAliasRequest, CreateApplicationAliasRequest, CreateDeviceAliasRequest, CreateAliasResponseData, LookupAliasResponseData, DeleteAliasResponseData, ListAliasesResponseData, ListNamespacesResponseData, CreateNamespaceRequest, CreateNamespaceResponseData, DeleteNamespaceResponseData, CreateNamespaceInvitationRequest, CreateNamespaceInvitationResponseData, CreateRecursiveInvitationResponseData, JoinNamespaceRequest, JoinNamespaceResponseData, CreateGroupInNamespaceRequest, CreateGroupInNamespaceResponseData, SubgroupEntry, Namespace, NamespaceIdentity, NodeIdentity, AccountPairInitRequest, AccountPairInitResponseData, AccountPairCompleteRequest, AccountPairCompleteResponseData, AccountSignWithRootRequest, AccountSignWithRootResponseData, RelinkDeviceRequest, RelinkDeviceResponseData, RescopeDeviceRequest, RescopeDeviceResponseData, LabelDeviceRequest, LabelDeviceResponseData, AccountDeviceEntry, AccountApplicationEntry, RevokeAccountDeviceRequest, RevokeAccountDeviceResponseData, MemberDevicesEntry, ListMemberDevicesOptions, GroupInfoResponseData, CreateGroupRequest, CreateGroupResponseData, DeleteGroupResponseData, ListGroupMembersResponseData, ListGroupContextsResponseData, AddGroupMembersRequest, RemoveGroupMembersRequest, UpdateMemberRoleRequest, MemberCapabilities, SetMemberCapabilitiesRequest, SetDefaultCapabilitiesRequest, SetMemberAutoFollowRequest, SetSubgroupVisibilityRequest, SetTeeAdmissionPolicyRequest, GetTeeAdmissionPolicyResponseData, SetTeeAuthoringPolicyRequest, SetGroupMetadataRequest, SetMemberMetadataRequest, SetContextMetadataRequest, MetadataRecord, SyncGroupResponseData, UpgradeGroupRequest, UpgradeGroupResponseData, GroupUpgradeStatusResponseData, MigrationStatus, CascadeStatusEntry, RetryGroupUpgradeResponseData, ReparentGroupRequest, ReparentGroupResponseData, IssueOwnershipProofRequest, IssueNamespaceOwnershipProofRequest, IssueOwnershipProofResponseData, CreateGroupInvitationRequest, CreateGroupInvitationResponseData, CreateRecursiveGroupInvitationResponseData, JoinGroupRequest, JoinGroupResponseData, TeeInfoResponseData, TeeAttestRequest, TeeAttestResponseData, TeeVerifyQuoteRequest, TeeVerifyQuoteResponseData, PerformIntentRequest, PerformIntentResponseData, IntentRelayInfo, QueryContextRequest, QueryContextResponseData, ListMemberDevicesResponseData, SealToAccountRequest, SealedEnvelope, AdmitJoinRequest, AdmitJoinResponseData, WarrantNonceState } from './admin-types.js';
|
|
2
|
+
import type { HealthStatus, ReadinessStatus, AdminAuthStatus, InstallApplicationRequest, InstallApplicationResponseData, InstallDevApplicationRequest, UninstallApplicationResponseData, ListApplicationsResponseData, GetApplicationResponseData, ApplicationVersionEntry, GetApplicationAbiResponseData, GetLatestVersionResponseData, ListPackagesResponseData, ListVersionsResponseData, CreateContextRequest, CreateContextResponseData, DeleteContextResponseData, GetContextsResponseData, Context, GenerateContextIdentityResponseData, GetContextIdentitiesResponseData, JoinContextResponseData, JoinSubgroupInheritanceResponseData, ContextGroupResponseData, ContextStorageResponseData, InviteSpecializedNodeRequest, InviteSpecializedNodeResponseData, UpdateContextApplicationRequest, ResyncContextRequest, ResyncContextResponseData, ContextsWithExecutorsResponseData, UploadBlobRequest, UploadBlobResponseData, DeleteBlobResponseData, ListBlobsResponseData, GetBlobInfoResponseData, BlobReadOptions, CreateContextAliasRequest, CreateApplicationAliasRequest, CreateDeviceAliasRequest, CreateAliasResponseData, LookupAliasResponseData, DeleteAliasResponseData, ListAliasesResponseData, ListNamespacesResponseData, CreateNamespaceRequest, CreateNamespaceResponseData, DeleteNamespaceResponseData, CreateNamespaceInvitationRequest, CreateNamespaceInvitationResponseData, CreateRecursiveInvitationResponseData, JoinNamespaceRequest, JoinNamespaceResponseData, CreateGroupInNamespaceRequest, CreateGroupInNamespaceResponseData, SubgroupEntry, Namespace, NamespaceIdentity, NodeIdentity, AccountPairInitRequest, AccountPairInitResponseData, AccountPairCompleteRequest, AccountPairCompleteResponseData, AccountSignWithRootRequest, AccountSignWithRootResponseData, RelinkDeviceRequest, RelinkDeviceResponseData, RescopeDeviceRequest, RescopeDeviceResponseData, LabelDeviceRequest, LabelDeviceResponseData, AccountDeviceEntry, AccountApplicationEntry, RevokeAccountDeviceRequest, LinkAccountDeviceRequest, LinkAccountDeviceResponseData, RevokeAccountDeviceResponseData, MemberDevicesEntry, ListMemberDevicesOptions, GroupInfoResponseData, CreateGroupRequest, CreateGroupResponseData, DeleteGroupResponseData, ListGroupMembersResponseData, ListGroupContextsResponseData, AddGroupMembersRequest, RemoveGroupMembersRequest, UpdateMemberRoleRequest, MemberCapabilities, SetMemberCapabilitiesRequest, SetDefaultCapabilitiesRequest, SetMemberAutoFollowRequest, SetSubgroupVisibilityRequest, SetTeeAdmissionPolicyRequest, GetTeeAdmissionPolicyResponseData, SetTeeAuthoringPolicyRequest, TransferOwnershipRequest, ChangeNamespaceAdminRequest, OwnerDeleteGroupRequest, RootGuardedOpRequest, SetGroupMetadataRequest, SetMemberMetadataRequest, SetContextMetadataRequest, MetadataRecord, SyncGroupResponseData, UpgradeGroupRequest, UpgradeGroupResponseData, GroupUpgradeStatusResponseData, MigrationStatus, CascadeStatusEntry, RetryGroupUpgradeResponseData, ReparentGroupRequest, ReparentGroupResponseData, IssueOwnershipProofRequest, IssueNamespaceOwnershipProofRequest, IssueOwnershipProofResponseData, CreateGroupInvitationRequest, CreateGroupInvitationResponseData, CreateRecursiveGroupInvitationResponseData, JoinGroupRequest, JoinGroupResponseData, TeeInfoResponseData, TeeAttestRequest, TeeAttestResponseData, TeeVerifyQuoteRequest, TeeVerifyQuoteResponseData, PerformIntentRequest, PerformIntentResponseData, IntentRelayInfo, QueryContextRequest, QueryContextResponseData, ListMemberDevicesResponseData, SealToAccountRequest, SealedEnvelope, AdmitJoinRequest, AdmitJoinResponseData, WarrantNonceState } from './admin-types.js';
|
|
3
3
|
import { type RedeemOutcome } from './redeem-invitation.js';
|
|
4
4
|
/**
|
|
5
5
|
* Thrown when a node answers the warrant-nonce route with a 404.
|
|
@@ -437,6 +437,22 @@ export declare class AdminApiClient {
|
|
|
437
437
|
* that - "this device can no longer act" is the only claim it supports.
|
|
438
438
|
*/
|
|
439
439
|
revokeAccountDevice(namespaceId: string, request: RevokeAccountDeviceRequest): Promise<RevokeAccountDeviceResponseData>;
|
|
440
|
+
/**
|
|
441
|
+
* Carry the device link of an account **this node does not hold**, so the
|
|
442
|
+
* namespace binds that device's key to the account.
|
|
443
|
+
*
|
|
444
|
+
* The relay half of a nodeless account minting invitations. An account added
|
|
445
|
+
* to a group by account holds its grants but has no device bound, so peers
|
|
446
|
+
* refuse whatever its device signs as "bound to no account". The credential
|
|
447
|
+
* and scope are root-signed and self-certifying; this node adds its own
|
|
448
|
+
* member endorsement and publishes the `AccountDeviceLinked`. After that the
|
|
449
|
+
* device signs invitations itself — see `signGroupInvitation`.
|
|
450
|
+
*
|
|
451
|
+
* Refusals: `400` when a proof does not verify or the scope does not reach
|
|
452
|
+
* this namespace (re-sign it), `403` when the device was revoked here or the
|
|
453
|
+
* account is a member of nothing in the namespace (add it first).
|
|
454
|
+
*/
|
|
455
|
+
linkAccountDevice(namespaceId: string, request: LinkAccountDeviceRequest): Promise<LinkAccountDeviceResponseData>;
|
|
440
456
|
getGroupInfo(groupId: string): Promise<GroupInfoResponseData>;
|
|
441
457
|
/** Thin wrapper over {@link getGroupInfo}: returns the group's `defaultCapabilities` bitmask. */
|
|
442
458
|
getDefaultCapabilities(groupId: string): Promise<number>;
|
|
@@ -660,8 +676,28 @@ export declare class AdminApiClient {
|
|
|
660
676
|
* and admitted TEEs stay members. The node applies the same op as
|
|
661
677
|
* `setTeeAuthoringPolicy(groupId, { allowedMrtd: [] })`. `groupId` must be a
|
|
662
678
|
* namespace root.
|
|
679
|
+
*
|
|
680
|
+
* `rootProof` is the signing admin's own root proof for that empty policy
|
|
681
|
+
* (`teeAuthoringPolicyOp([])`); omit it on a node that holds that root.
|
|
682
|
+
*/
|
|
683
|
+
disableTeeAuthoringPolicy(groupId: string, options?: RootGuardedOpRequest): Promise<void>;
|
|
684
|
+
/**
|
|
685
|
+
* Hand the group to `newOwner`, who must already be one of its admins.
|
|
686
|
+
* Owner-only, and it needs the owner account's root proof; see
|
|
687
|
+
* {@link RootGuardedOpRequest}.
|
|
688
|
+
*/
|
|
689
|
+
transferOwnership(groupId: string, request: TransferOwnershipRequest): Promise<void>;
|
|
690
|
+
/**
|
|
691
|
+
* Repoint the namespace's admin pin at `newAdmin`, a member of its root.
|
|
692
|
+
* Owner-only, with the owner account's root proof; see {@link RootGuardedOpRequest}.
|
|
693
|
+
*/
|
|
694
|
+
changeNamespaceAdmin(namespaceId: string, request: ChangeNamespaceAdminRequest): Promise<void>;
|
|
695
|
+
/**
|
|
696
|
+
* Delete a group through the owner-only path: it must hold no contexts, and
|
|
697
|
+
* the op needs the owner account's root proof. {@link deleteGroup} is the
|
|
698
|
+
* admin-level cascading delete.
|
|
663
699
|
*/
|
|
664
|
-
|
|
700
|
+
ownerDeleteGroup(groupId: string, request?: OwnerDeleteGroupRequest): Promise<void>;
|
|
665
701
|
setGroupMetadata(groupId: string, request: SetGroupMetadataRequest): Promise<void>;
|
|
666
702
|
getGroupMetadata(groupId: string): Promise<MetadataRecord | null>;
|
|
667
703
|
setMemberMetadata(groupId: string, identity: string, request: SetMemberMetadataRequest): Promise<void>;
|
|
@@ -829,6 +829,24 @@ export class AdminApiClient {
|
|
|
829
829
|
async revokeAccountDevice(namespaceId, request) {
|
|
830
830
|
return unwrap(await this.httpClient.post(`/admin-api/namespaces/${namespaceId}/account/revoke`, request));
|
|
831
831
|
}
|
|
832
|
+
/**
|
|
833
|
+
* Carry the device link of an account **this node does not hold**, so the
|
|
834
|
+
* namespace binds that device's key to the account.
|
|
835
|
+
*
|
|
836
|
+
* The relay half of a nodeless account minting invitations. An account added
|
|
837
|
+
* to a group by account holds its grants but has no device bound, so peers
|
|
838
|
+
* refuse whatever its device signs as "bound to no account". The credential
|
|
839
|
+
* and scope are root-signed and self-certifying; this node adds its own
|
|
840
|
+
* member endorsement and publishes the `AccountDeviceLinked`. After that the
|
|
841
|
+
* device signs invitations itself — see `signGroupInvitation`.
|
|
842
|
+
*
|
|
843
|
+
* Refusals: `400` when a proof does not verify or the scope does not reach
|
|
844
|
+
* this namespace (re-sign it), `403` when the device was revoked here or the
|
|
845
|
+
* account is a member of nothing in the namespace (add it first).
|
|
846
|
+
*/
|
|
847
|
+
async linkAccountDevice(namespaceId, request) {
|
|
848
|
+
return unwrap(await this.httpClient.post(`/admin-api/namespaces/${namespaceId}/account/link-device`, request));
|
|
849
|
+
}
|
|
832
850
|
// ---- Group Management ----
|
|
833
851
|
async getGroupInfo(groupId) {
|
|
834
852
|
return unwrap(await this.httpClient.get(`/admin-api/groups/${groupId}`));
|
|
@@ -1174,9 +1192,45 @@ export class AdminApiClient {
|
|
|
1174
1192
|
* and admitted TEEs stay members. The node applies the same op as
|
|
1175
1193
|
* `setTeeAuthoringPolicy(groupId, { allowedMrtd: [] })`. `groupId` must be a
|
|
1176
1194
|
* namespace root.
|
|
1195
|
+
*
|
|
1196
|
+
* `rootProof` is the signing admin's own root proof for that empty policy
|
|
1197
|
+
* (`teeAuthoringPolicyOp([])`); omit it on a node that holds that root.
|
|
1198
|
+
*/
|
|
1199
|
+
async disableTeeAuthoringPolicy(groupId, options = {}) {
|
|
1200
|
+
const path = `/admin-api/groups/${groupId}/settings/tee-authoring-policy`;
|
|
1201
|
+
if (options.rootProof === undefined) {
|
|
1202
|
+
await this.httpClient.delete(path);
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
await this.httpClient.request(path, {
|
|
1206
|
+
method: 'DELETE',
|
|
1207
|
+
body: JSON.stringify({ rootProof: options.rootProof }),
|
|
1208
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1211
|
+
// ---- Root-guarded owner ops (core schema 18) ----
|
|
1212
|
+
/**
|
|
1213
|
+
* Hand the group to `newOwner`, who must already be one of its admins.
|
|
1214
|
+
* Owner-only, and it needs the owner account's root proof; see
|
|
1215
|
+
* {@link RootGuardedOpRequest}.
|
|
1216
|
+
*/
|
|
1217
|
+
async transferOwnership(groupId, request) {
|
|
1218
|
+
await this.httpClient.post(`/admin-api/groups/${groupId}/transfer-ownership`, request);
|
|
1219
|
+
}
|
|
1220
|
+
/**
|
|
1221
|
+
* Repoint the namespace's admin pin at `newAdmin`, a member of its root.
|
|
1222
|
+
* Owner-only, with the owner account's root proof; see {@link RootGuardedOpRequest}.
|
|
1223
|
+
*/
|
|
1224
|
+
async changeNamespaceAdmin(namespaceId, request) {
|
|
1225
|
+
await this.httpClient.post(`/admin-api/namespaces/${namespaceId}/admin`, request);
|
|
1226
|
+
}
|
|
1227
|
+
/**
|
|
1228
|
+
* Delete a group through the owner-only path: it must hold no contexts, and
|
|
1229
|
+
* the op needs the owner account's root proof. {@link deleteGroup} is the
|
|
1230
|
+
* admin-level cascading delete.
|
|
1177
1231
|
*/
|
|
1178
|
-
async
|
|
1179
|
-
await this.httpClient.
|
|
1232
|
+
async ownerDeleteGroup(groupId, request = {}) {
|
|
1233
|
+
await this.httpClient.post(`/admin-api/groups/${groupId}/owner-delete`, request);
|
|
1180
1234
|
}
|
|
1181
1235
|
// ---- Group / member / context metadata ----
|
|
1182
1236
|
async setGroupMetadata(groupId, request) {
|
|
@@ -1162,6 +1162,40 @@ export interface RevokeAccountDeviceResponseData {
|
|
|
1162
1162
|
*/
|
|
1163
1163
|
revokedIn: RevocationOutcome[];
|
|
1164
1164
|
}
|
|
1165
|
+
/**
|
|
1166
|
+
* Bind a device of an account the node does not hold, so what that device
|
|
1167
|
+
* signs in the namespace resolves to its account.
|
|
1168
|
+
*
|
|
1169
|
+
* For an account with **no node**: its root certifies a device offline
|
|
1170
|
+
* (`signDeviceCert`) and scopes it
|
|
1171
|
+
* (`signDeviceScope`), and a relay that is a member of the namespace carries
|
|
1172
|
+
* the link, endorsing the account with its own member key. Both proofs are
|
|
1173
|
+
* root-signed, so the relay can carry them and cannot alter them.
|
|
1174
|
+
*/
|
|
1175
|
+
export interface LinkAccountDeviceRequest {
|
|
1176
|
+
/**
|
|
1177
|
+
* The device's credential: hex, borsh `AccountProof<DeviceCert>` — what
|
|
1178
|
+
* `signDeviceCert` returns and `merod account sign-cert` prints.
|
|
1179
|
+
*/
|
|
1180
|
+
credential: string;
|
|
1181
|
+
/**
|
|
1182
|
+
* The device's scope: hex, borsh `AccountProof<DeviceScope>` — what
|
|
1183
|
+
* `signDeviceScope` returns. It must reach the namespace's application, or
|
|
1184
|
+
* name none.
|
|
1185
|
+
*/
|
|
1186
|
+
scope: string;
|
|
1187
|
+
}
|
|
1188
|
+
export interface LinkAccountDeviceResponseData {
|
|
1189
|
+
/** The account the device speaks for, 64 hex. */
|
|
1190
|
+
accountId: string;
|
|
1191
|
+
/** The device now bound, 64 hex. */
|
|
1192
|
+
deviceId: string;
|
|
1193
|
+
/**
|
|
1194
|
+
* The namespace already bound this device key, so nothing was published.
|
|
1195
|
+
* A repeat call is harmless and answers `true`.
|
|
1196
|
+
*/
|
|
1197
|
+
alreadyBound: boolean;
|
|
1198
|
+
}
|
|
1165
1199
|
/** One device of one member account, as the group's live bindings record it. */
|
|
1166
1200
|
export interface MemberDeviceEntry {
|
|
1167
1201
|
/** The device id, 64 hex - the form {@link RevokeAccountDeviceRequest.deviceId} takes. */
|
|
@@ -1303,6 +1337,19 @@ export interface GroupInfo {
|
|
|
1303
1337
|
* `null` if no metadata has ever been set for this group.
|
|
1304
1338
|
*/
|
|
1305
1339
|
metadata?: MetadataRecord | null;
|
|
1340
|
+
/** Hex hash of the group's authorization-relevant state, for convergence polling. */
|
|
1341
|
+
groupStateHash?: string;
|
|
1342
|
+
/**
|
|
1343
|
+
* The namespace this group belongs to (itself, for a namespace root). An
|
|
1344
|
+
* owner-op root proof binds it. Absent from nodes before core schema 18.
|
|
1345
|
+
*/
|
|
1346
|
+
namespaceId?: string;
|
|
1347
|
+
/**
|
|
1348
|
+
* How many root-guarded owner ops this group has applied: the `counter` a
|
|
1349
|
+
* root proof for its next one must name. Absent from nodes before core
|
|
1350
|
+
* schema 18.
|
|
1351
|
+
*/
|
|
1352
|
+
ownerOpCounter?: number;
|
|
1306
1353
|
}
|
|
1307
1354
|
export type GroupInfoResponseData = GroupInfo;
|
|
1308
1355
|
/**
|
|
@@ -1479,6 +1526,12 @@ export interface MeasurementTeeAdmissionPolicyRequest {
|
|
|
1479
1526
|
acceptMock: boolean;
|
|
1480
1527
|
/** Absent means `replica`. */
|
|
1481
1528
|
mode?: TeeAdmissionMode;
|
|
1529
|
+
/**
|
|
1530
|
+
* Hex borsh of the signing admin's own root proof for this policy op
|
|
1531
|
+
* ({@link signOwnerOpProof} with the matching policy encoder). Omit it on a
|
|
1532
|
+
* node that holds that admin's account root, which signs one itself.
|
|
1533
|
+
*/
|
|
1534
|
+
rootProof?: string;
|
|
1482
1535
|
}
|
|
1483
1536
|
/** A policy that admits by signed release. The node refuses measurement lists beside it. */
|
|
1484
1537
|
export interface SignedReleaseTeeAdmissionPolicyRequest {
|
|
@@ -1487,6 +1540,12 @@ export interface SignedReleaseTeeAdmissionPolicyRequest {
|
|
|
1487
1540
|
acceptMock: boolean;
|
|
1488
1541
|
/** Absent means `replica`. */
|
|
1489
1542
|
mode?: TeeAdmissionMode;
|
|
1543
|
+
/**
|
|
1544
|
+
* Hex borsh of the signing admin's own root proof for this policy op
|
|
1545
|
+
* ({@link signOwnerOpProof} with the matching policy encoder). Omit it on a
|
|
1546
|
+
* node that holds that admin's account root, which signs one itself.
|
|
1547
|
+
*/
|
|
1548
|
+
rootProof?: string;
|
|
1490
1549
|
}
|
|
1491
1550
|
export type SetTeeAdmissionPolicyRequest = MeasurementTeeAdmissionPolicyRequest | SignedReleaseTeeAdmissionPolicyRequest;
|
|
1492
1551
|
export type SetTeeAdmissionPolicyResponseData = Record<string, never>;
|
|
@@ -1517,7 +1576,42 @@ export interface GetTeeAdmissionPolicyResponseData {
|
|
|
1517
1576
|
*/
|
|
1518
1577
|
export interface SetTeeAuthoringPolicyRequest {
|
|
1519
1578
|
allowedMrtd: string[];
|
|
1579
|
+
/**
|
|
1580
|
+
* Hex borsh of the signing admin's own root proof for this policy op
|
|
1581
|
+
* ({@link signOwnerOpProof} with the matching policy encoder). Omit it on a
|
|
1582
|
+
* node that holds that admin's account root, which signs one itself.
|
|
1583
|
+
*/
|
|
1584
|
+
rootProof?: string;
|
|
1520
1585
|
}
|
|
1586
|
+
/**
|
|
1587
|
+
* The root proof an owner-level op carries (core schema 18).
|
|
1588
|
+
*
|
|
1589
|
+
* A device key alone cannot transfer a group, repoint a namespace's admin,
|
|
1590
|
+
* delete a group through the owner-only path, or set a TEE policy: each needs a
|
|
1591
|
+
* proof signed by the account's ROOT key. On a node that holds that root (as
|
|
1592
|
+
* `merod init` provisions) omit `rootProof` and the node signs it. Otherwise sign
|
|
1593
|
+
* one offline with {@link signOwnerOpProof}, over the group's current
|
|
1594
|
+
* `ownerOpCounter` and `namespaceId` from {@link GroupInfo}. Each proof applies
|
|
1595
|
+
* once. Without either, the node answers `403`.
|
|
1596
|
+
*/
|
|
1597
|
+
export interface RootGuardedOpRequest {
|
|
1598
|
+
/** Hex borsh of an `AccountProof<OwnerOpAuthorization>`. */
|
|
1599
|
+
rootProof?: string;
|
|
1600
|
+
}
|
|
1601
|
+
/** Hand a group to `newOwner`, an account (64 hex) that is already one of its admins. */
|
|
1602
|
+
export interface TransferOwnershipRequest extends RootGuardedOpRequest {
|
|
1603
|
+
newOwner: string;
|
|
1604
|
+
}
|
|
1605
|
+
/** Repoint a namespace's admin pin at `newAdmin`, an account that is a member of its root. */
|
|
1606
|
+
export interface ChangeNamespaceAdminRequest extends RootGuardedOpRequest {
|
|
1607
|
+
newAdmin: string;
|
|
1608
|
+
}
|
|
1609
|
+
/**
|
|
1610
|
+
* The owner-only deletion of a group that holds no contexts. The admin-level
|
|
1611
|
+
* cascading delete is {@link AdminApiClient.deleteGroup}.
|
|
1612
|
+
*/
|
|
1613
|
+
export type OwnerDeleteGroupRequest = RootGuardedOpRequest;
|
|
1614
|
+
export type RootGuardedOpResponseData = Record<string, never>;
|
|
1521
1615
|
export type SetTeeAuthoringPolicyResponseData = Record<string, never>;
|
|
1522
1616
|
/**
|
|
1523
1617
|
* Generic metadata record attached to a group, group member, or
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import { type Signer } from '../signer/signer.js';
|
|
2
|
+
/**
|
|
3
|
+
* Core's `ACCOUNT_GENESIS_VERSION`. It is part of the `AccountId` preimage, so
|
|
4
|
+
* this value decides which account a root key names — a mismatch does not fail
|
|
5
|
+
* as a version error at the edge, it silently derives a different account and
|
|
6
|
+
* core refuses the credential as unverifiable.
|
|
7
|
+
*
|
|
8
|
+
* `2` since the genesis dropped its per-namespace nonce. This was `1`, which
|
|
9
|
+
* produced credentials current core rejects outright; the shape was already v2,
|
|
10
|
+
* only the tag was stale.
|
|
11
|
+
*/
|
|
12
|
+
export declare const ACCOUNT_GENESIS_VERSION = 2;
|
|
2
13
|
/** What a root certifies about one device. */
|
|
3
14
|
export interface DeviceCertInput {
|
|
4
15
|
/**
|
|
@@ -20,7 +20,6 @@ import { resolveSigner } from '../signer/signer.js';
|
|
|
20
20
|
const CERT_DOMAIN = new TextEncoder().encode('calimero.device.cert.v1');
|
|
21
21
|
const ACCOUNT_ID_DOMAIN = new TextEncoder().encode('calimero.account.genesis.v1');
|
|
22
22
|
const DEVICE_ID_DOMAIN = new TextEncoder().encode('calimero.device.id.v1');
|
|
23
|
-
/** `AccountGenesis::version`, which the credential's borsh encoding leads with. */
|
|
24
23
|
/**
|
|
25
24
|
* Core's `ACCOUNT_GENESIS_VERSION`. It is part of the `AccountId` preimage, so
|
|
26
25
|
* this value decides which account a root key names — a mismatch does not fail
|
|
@@ -31,7 +30,7 @@ const DEVICE_ID_DOMAIN = new TextEncoder().encode('calimero.device.id.v1');
|
|
|
31
30
|
* produced credentials current core rejects outright; the shape was already v2,
|
|
32
31
|
* only the tag was stale.
|
|
33
32
|
*/
|
|
34
|
-
const ACCOUNT_GENESIS_VERSION = 2;
|
|
33
|
+
export const ACCOUNT_GENESIS_VERSION = 2;
|
|
35
34
|
/** Byte length of an `AccountProof<DeviceCert>` with an empty handoff chain. */
|
|
36
35
|
const ACCOUNT_PROOF_BYTES = 237;
|
|
37
36
|
/**
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type Signer } from '../signer/signer.js';
|
|
2
|
+
/** What a root scopes one device to. */
|
|
3
|
+
export interface DeviceScopeInput {
|
|
4
|
+
/**
|
|
5
|
+
* The account root's signing secret, 64 hex. Pass this or {@link signer}.
|
|
6
|
+
*/
|
|
7
|
+
rootSecret?: string;
|
|
8
|
+
/**
|
|
9
|
+
* The account root as a {@link Signer} — for a root that cannot be exported.
|
|
10
|
+
* Its `publicKey` is the genesis key the proof carries, and the account is
|
|
11
|
+
* derived from it.
|
|
12
|
+
*/
|
|
13
|
+
signer?: Signer;
|
|
14
|
+
/** The device being scoped, 64 hex — the one its certificate names. */
|
|
15
|
+
device: string;
|
|
16
|
+
/**
|
|
17
|
+
* Application ids the device may speak for, 64 hex each. **Empty means all of
|
|
18
|
+
* them**, which is also what the default is.
|
|
19
|
+
*
|
|
20
|
+
* A namespace binds the device only when its application is listed here (or
|
|
21
|
+
* the list is empty), so a scope naming the wrong application yields a relay
|
|
22
|
+
* refusal of `400`, not a binding.
|
|
23
|
+
*/
|
|
24
|
+
applications?: readonly string[];
|
|
25
|
+
/**
|
|
26
|
+
* Orders scopes for this device: only a higher one supersedes. 0 for a
|
|
27
|
+
* device's first scope.
|
|
28
|
+
*/
|
|
29
|
+
scopeEpoch?: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The 32 bytes a root signs to scope a device: `domain_hash` over account,
|
|
33
|
+
* device, `scope_epoch` (u32 LE), `key_epoch` (u32 LE), then each application id
|
|
34
|
+
* as its own length-prefixed part.
|
|
35
|
+
*/
|
|
36
|
+
export declare function deviceScopePayload(input: {
|
|
37
|
+
account: string;
|
|
38
|
+
device: string;
|
|
39
|
+
applications: readonly string[];
|
|
40
|
+
scopeEpoch: number;
|
|
41
|
+
keyEpoch: number;
|
|
42
|
+
}): Promise<Uint8Array>;
|
|
43
|
+
/**
|
|
44
|
+
* Scope a device, returning the hex `AccountProof<DeviceScope>` a relay's
|
|
45
|
+
* `linkAccountDevice` takes beside the credential.
|
|
46
|
+
*
|
|
47
|
+
* ```
|
|
48
|
+
* AccountProof { genesis: AccountGenesis, chain: Vec<RootKeyHandoff>, statement: DeviceScope }
|
|
49
|
+
* AccountGenesis { version: u8, root_sign_pk: [u8; 32] }
|
|
50
|
+
* chain → u32-LE count (0 here)
|
|
51
|
+
* DeviceScope { account, device: [u8; 32] x2, applications: Vec<[u8; 32]>,
|
|
52
|
+
* scope_epoch: u32, key_epoch: u32, signature: [u8; 64] }
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* `chain` is empty and `key_epoch` 0 for the reason they are in
|
|
56
|
+
* {@link signDeviceCert}: a root that was never handed off signs directly.
|
|
57
|
+
*/
|
|
58
|
+
export declare function signDeviceScope(input: DeviceScopeInput): Promise<string>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scoping a device offline: which applications a device of an account may speak
|
|
3
|
+
* for, signed by the account root.
|
|
4
|
+
*
|
|
5
|
+
* The certificate from {@link signDeviceCert} says *this key is a device of the
|
|
6
|
+
* account*. It does not say where: that is this statement, and a namespace binds
|
|
7
|
+
* a device only when both arrive together. A node that pairs a device signs the
|
|
8
|
+
* scope for you; an account with **no node** has to sign it itself, next to the
|
|
9
|
+
* certificate, and hand both to a relay's `linkAccountDevice`.
|
|
10
|
+
*
|
|
11
|
+
* Derived from core:
|
|
12
|
+
*
|
|
13
|
+
* - `DeviceScope` and `DeviceScope::signing_payload` — `crates/account/src/scope.rs`
|
|
14
|
+
* - `DEVICE_SCOPE_SIGN_DOMAIN` — `crates/account/src/domain.rs`
|
|
15
|
+
* - `AccountProof` / `AccountGenesis` borsh layout — `crates/account/src/{signed,account}.rs`
|
|
16
|
+
*
|
|
17
|
+
* The unit tests pin vectors produced by core's own `DeviceScope::sign`.
|
|
18
|
+
*/
|
|
19
|
+
import { concat, domainHash, fromHex, hex, u32le } from '../crypto/internal.js';
|
|
20
|
+
import { resolveSigner } from '../signer/signer.js';
|
|
21
|
+
import { accountForRootPublicKey, ACCOUNT_GENESIS_VERSION } from './device-cert.js';
|
|
22
|
+
const SCOPE_DOMAIN = new TextEncoder().encode('calimero.device.scope.v1');
|
|
23
|
+
/**
|
|
24
|
+
* The 32 bytes a root signs to scope a device: `domain_hash` over account,
|
|
25
|
+
* device, `scope_epoch` (u32 LE), `key_epoch` (u32 LE), then each application id
|
|
26
|
+
* as its own length-prefixed part.
|
|
27
|
+
*/
|
|
28
|
+
export async function deviceScopePayload(input) {
|
|
29
|
+
return domainHash(SCOPE_DOMAIN, [
|
|
30
|
+
fromHex(input.account, 'account', 32),
|
|
31
|
+
fromHex(input.device, 'device', 32),
|
|
32
|
+
u32le(input.scopeEpoch),
|
|
33
|
+
u32le(input.keyEpoch),
|
|
34
|
+
...input.applications.map((a, i) => fromHex(a, `applications[${i}]`, 32)),
|
|
35
|
+
]);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Scope a device, returning the hex `AccountProof<DeviceScope>` a relay's
|
|
39
|
+
* `linkAccountDevice` takes beside the credential.
|
|
40
|
+
*
|
|
41
|
+
* ```
|
|
42
|
+
* AccountProof { genesis: AccountGenesis, chain: Vec<RootKeyHandoff>, statement: DeviceScope }
|
|
43
|
+
* AccountGenesis { version: u8, root_sign_pk: [u8; 32] }
|
|
44
|
+
* chain → u32-LE count (0 here)
|
|
45
|
+
* DeviceScope { account, device: [u8; 32] x2, applications: Vec<[u8; 32]>,
|
|
46
|
+
* scope_epoch: u32, key_epoch: u32, signature: [u8; 64] }
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* `chain` is empty and `key_epoch` 0 for the reason they are in
|
|
50
|
+
* {@link signDeviceCert}: a root that was never handed off signs directly.
|
|
51
|
+
*/
|
|
52
|
+
export async function signDeviceScope(input) {
|
|
53
|
+
const signer = await resolveSigner(input.rootSecret, input.signer, 'rootSecret');
|
|
54
|
+
const rootPublicKey = fromHex(signer.publicKey, 'signer.publicKey', 32);
|
|
55
|
+
const account = await accountForRootPublicKey(hex(rootPublicKey));
|
|
56
|
+
const applications = input.applications ?? [];
|
|
57
|
+
const scopeEpoch = input.scopeEpoch ?? 0;
|
|
58
|
+
// `u32le` would wrap a negative or oversized value into a different epoch
|
|
59
|
+
// rather than refuse it, and the scope would then order itself wrongly.
|
|
60
|
+
if (!Number.isInteger(scopeEpoch) || scopeEpoch < 0 || scopeEpoch > 4294967295) {
|
|
61
|
+
throw new Error(`scopeEpoch must be a u32, got ${scopeEpoch}`);
|
|
62
|
+
}
|
|
63
|
+
const keyEpoch = 0;
|
|
64
|
+
const payload = await deviceScopePayload({
|
|
65
|
+
account,
|
|
66
|
+
device: input.device,
|
|
67
|
+
applications,
|
|
68
|
+
scopeEpoch,
|
|
69
|
+
keyEpoch,
|
|
70
|
+
});
|
|
71
|
+
const signature = await signer.sign(payload);
|
|
72
|
+
if (signature.length !== 64) {
|
|
73
|
+
throw new Error(`signer returned ${signature.length} bytes, expected a 64-byte Ed25519 signature`);
|
|
74
|
+
}
|
|
75
|
+
return hex(concat(new Uint8Array([ACCOUNT_GENESIS_VERSION]), rootPublicKey, u32le(0), fromHex(account, 'account', 32), fromHex(input.device, 'device', 32), u32le(applications.length), ...applications.map((a, i) => fromHex(a, `applications[${i}]`, 32)), u32le(scopeEpoch), u32le(keyEpoch), signature));
|
|
76
|
+
}
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export { signDeviceCert, mintDeviceId, deviceCertPayload, accountForRoot, accountForRootPublicKey, accountProofBytes, parseDeviceCredential, verifyDeviceCredential, } from './device-cert.js';
|
|
2
2
|
export type { DeviceCertInput, DeviceCredential } from './device-cert.js';
|
|
3
|
+
export { signDeviceScope, deviceScopePayload } from './device-scope.js';
|
|
4
|
+
export type { DeviceScopeInput } from './device-scope.js';
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
// Offline device certification — the account-root half of the keyholder story.
|
|
2
2
|
export { signDeviceCert, mintDeviceId, deviceCertPayload, accountForRoot, accountForRootPublicKey, accountProofBytes, parseDeviceCredential, verifyDeviceCredential, } from './device-cert.js';
|
|
3
|
+
export { signDeviceScope, deviceScopePayload } from './device-scope.js';
|