@calimero-network/mero-js 26.11.0 → 27.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.
Files changed (220) hide show
  1. package/README.md +73 -288
  2. package/dist/cloud-v2/attestation.d.ts +91 -0
  3. package/dist/cloud-v2/attestation.js +135 -0
  4. package/dist/cloud-v2/capabilities.d.ts +30 -0
  5. package/dist/cloud-v2/capabilities.js +31 -0
  6. package/dist/cloud-v2/client.d.ts +426 -0
  7. package/dist/cloud-v2/client.js +646 -0
  8. package/dist/cloud-v2/errors.d.ts +98 -0
  9. package/dist/cloud-v2/errors.js +165 -0
  10. package/dist/cloud-v2/index.d.ts +10 -0
  11. package/dist/cloud-v2/index.js +5 -0
  12. package/dist/cloud-v2/passkey.d.ts +27 -0
  13. package/dist/cloud-v2/passkey.js +73 -0
  14. package/dist/crypto/internal.d.ts +3 -37
  15. package/dist/crypto/internal.js +3 -72
  16. package/dist/index.browser.mjs +4 -2
  17. package/dist/index.browser.mjs.map +4 -4
  18. package/dist/index.cjs +1750 -12772
  19. package/dist/index.cjs.map +4 -4
  20. package/dist/index.d.ts +3 -56
  21. package/dist/index.js +14 -75
  22. package/dist/index.mjs +1750 -12772
  23. package/dist/index.mjs.map +4 -4
  24. package/dist/sealed/index.d.ts +4 -4
  25. package/dist/sealed/index.js +2 -2
  26. package/dist/sealed/release.d.ts +21 -76
  27. package/dist/sealed/release.js +24 -108
  28. package/dist/sealed/sealed.d.ts +8 -56
  29. package/dist/sealed/sealed.js +11 -89
  30. package/dist/sealed/verify.d.ts +7 -13
  31. package/dist/sealed/verify.js +3 -7
  32. package/package.json +8 -13
  33. package/dist/account/account-admin.d.ts +0 -80
  34. package/dist/account/account-admin.js +0 -346
  35. package/dist/account/account.d.ts +0 -196
  36. package/dist/account/account.js +0 -269
  37. package/dist/account/application-id.d.ts +0 -50
  38. package/dist/account/application-id.js +0 -83
  39. package/dist/account/bootstrap-from-invitation.d.ts +0 -110
  40. package/dist/account/bootstrap-from-invitation.js +0 -80
  41. package/dist/account/create-context.d.ts +0 -103
  42. package/dist/account/create-context.js +0 -267
  43. package/dist/account/govern.d.ts +0 -24
  44. package/dist/account/govern.js +0 -50
  45. package/dist/account/index.d.ts +0 -26
  46. package/dist/account/index.js +0 -15
  47. package/dist/account/join-as-account.d.ts +0 -23
  48. package/dist/account/join-as-account.js +0 -34
  49. package/dist/account/join-with-node.d.ts +0 -115
  50. package/dist/account/join-with-node.js +0 -161
  51. package/dist/account/node-admin.d.ts +0 -20
  52. package/dist/account/node-admin.js +0 -33
  53. package/dist/account/relay-from-invitation.d.ts +0 -137
  54. package/dist/account/relay-from-invitation.js +0 -180
  55. package/dist/account/relay-login-error.d.ts +0 -32
  56. package/dist/account/relay-login-error.js +0 -34
  57. package/dist/account/session.d.ts +0 -266
  58. package/dist/account/session.js +0 -915
  59. package/dist/account/wordlist.d.ts +0 -16
  60. package/dist/account/wordlist.js +0 -273
  61. package/dist/admin-api/admin-client.d.ts +0 -688
  62. package/dist/admin-api/admin-client.js +0 -1230
  63. package/dist/admin-api/admin-factory.d.ts +0 -14
  64. package/dist/admin-api/admin-factory.js +0 -22
  65. package/dist/admin-api/admin-types.d.ts +0 -2046
  66. package/dist/admin-api/admin-types.js +0 -12
  67. package/dist/admin-api/index.d.ts +0 -4
  68. package/dist/admin-api/index.js +0 -5
  69. package/dist/admin-api/redeem-invitation.d.ts +0 -101
  70. package/dist/admin-api/redeem-invitation.js +0 -200
  71. package/dist/auth/index.d.ts +0 -34
  72. package/dist/auth/index.js +0 -67
  73. package/dist/auth-api/auth-client.d.ts +0 -31
  74. package/dist/auth-api/auth-client.js +0 -117
  75. package/dist/auth-api/auth-factory.d.ts +0 -7
  76. package/dist/auth-api/auth-factory.js +0 -41
  77. package/dist/auth-api/auth-types.d.ts +0 -130
  78. package/dist/auth-api/auth-types.js +0 -2
  79. package/dist/auth-api/index.d.ts +0 -3
  80. package/dist/auth-api/index.js +0 -4
  81. package/dist/capabilities.d.ts +0 -84
  82. package/dist/capabilities.js +0 -117
  83. package/dist/cloud/account-ownership.d.ts +0 -63
  84. package/dist/cloud/account-ownership.js +0 -87
  85. package/dist/cloud/cloud-client.d.ts +0 -949
  86. package/dist/cloud/cloud-client.js +0 -1068
  87. package/dist/cloud/connect.d.ts +0 -213
  88. package/dist/cloud/connect.js +0 -260
  89. package/dist/cloud/enrol-redirect.d.ts +0 -181
  90. package/dist/cloud/enrol-redirect.js +0 -244
  91. package/dist/cloud/index.d.ts +0 -12
  92. package/dist/cloud/index.js +0 -10
  93. package/dist/cloud/link-redirect.d.ts +0 -95
  94. package/dist/cloud/link-redirect.js +0 -121
  95. package/dist/cloud/link-state.d.ts +0 -36
  96. package/dist/cloud/link-state.js +0 -104
  97. package/dist/cloud/routing-proof.d.ts +0 -113
  98. package/dist/cloud/routing-proof.js +0 -96
  99. package/dist/device-cert/device-cert.d.ts +0 -189
  100. package/dist/device-cert/device-cert.js +0 -239
  101. package/dist/device-cert/device-scope.d.ts +0 -58
  102. package/dist/device-cert/device-scope.js +0 -76
  103. package/dist/device-cert/index.d.ts +0 -4
  104. package/dist/device-cert/index.js +0 -3
  105. package/dist/ephemeral/codec.d.ts +0 -4
  106. package/dist/ephemeral/codec.js +0 -12
  107. package/dist/ephemeral/index.d.ts +0 -57
  108. package/dist/ephemeral/index.js +0 -101
  109. package/dist/ephemeral/types.d.ts +0 -27
  110. package/dist/ephemeral/types.js +0 -1
  111. package/dist/errors/classify.d.ts +0 -65
  112. package/dist/errors/classify.js +0 -117
  113. package/dist/errors/index.d.ts +0 -2
  114. package/dist/errors/index.js +0 -1
  115. package/dist/events/group.d.ts +0 -95
  116. package/dist/events/group.js +0 -10
  117. package/dist/events/index.d.ts +0 -5
  118. package/dist/events/index.js +0 -2
  119. package/dist/events/sse.d.ts +0 -127
  120. package/dist/events/sse.js +0 -469
  121. package/dist/events/ws.d.ts +0 -57
  122. package/dist/events/ws.js +0 -212
  123. package/dist/http-client/api-response.d.ts +0 -15
  124. package/dist/http-client/api-response.js +0 -1
  125. package/dist/http-client/http-factory.d.ts +0 -71
  126. package/dist/http-client/http-factory.js +0 -68
  127. package/dist/http-client/http-types.d.ts +0 -87
  128. package/dist/http-client/http-types.js +0 -1
  129. package/dist/http-client/index.d.ts +0 -7
  130. package/dist/http-client/index.js +0 -11
  131. package/dist/http-client/path-segment.d.ts +0 -1
  132. package/dist/http-client/path-segment.js +0 -8
  133. package/dist/http-client/retry.d.ts +0 -5
  134. package/dist/http-client/retry.js +0 -82
  135. package/dist/http-client/signal-utils.d.ts +0 -2
  136. package/dist/http-client/signal-utils.js +0 -41
  137. package/dist/http-client/web-client.d.ts +0 -80
  138. package/dist/http-client/web-client.js +0 -557
  139. package/dist/invitation/index.d.ts +0 -2
  140. package/dist/invitation/index.js +0 -2
  141. package/dist/invitation/invitation.d.ts +0 -106
  142. package/dist/invitation/invitation.js +0 -104
  143. package/dist/login/index.d.ts +0 -4
  144. package/dist/login/index.js +0 -3
  145. package/dist/login/login.d.ts +0 -111
  146. package/dist/login/login.js +0 -110
  147. package/dist/login/session.d.ts +0 -124
  148. package/dist/login/session.js +0 -141
  149. package/dist/mero-js.d.ts +0 -231
  150. package/dist/mero-js.js +0 -469
  151. package/dist/namespace-op/index.d.ts +0 -2
  152. package/dist/namespace-op/index.js +0 -3
  153. package/dist/namespace-op/namespace-op.d.ts +0 -150
  154. package/dist/namespace-op/namespace-op.js +0 -191
  155. package/dist/nodeDiscovery.d.ts +0 -47
  156. package/dist/nodeDiscovery.js +0 -101
  157. package/dist/owner-op/index.d.ts +0 -2
  158. package/dist/owner-op/index.js +0 -3
  159. package/dist/owner-op/owner-op.d.ts +0 -119
  160. package/dist/owner-op/owner-op.js +0 -214
  161. package/dist/presence/relay-presence.d.ts +0 -56
  162. package/dist/presence/relay-presence.js +0 -107
  163. package/dist/presence/statement.d.ts +0 -12
  164. package/dist/presence/statement.js +0 -17
  165. package/dist/relay/index.d.ts +0 -4
  166. package/dist/relay/index.js +0 -2
  167. package/dist/relay/nonce-source.d.ts +0 -72
  168. package/dist/relay/nonce-source.js +0 -111
  169. package/dist/relay/relay-client.d.ts +0 -472
  170. package/dist/relay/relay-client.js +0 -518
  171. package/dist/relay-attestation/attest-node-key.d.ts +0 -66
  172. package/dist/relay-attestation/attest-node-key.js +0 -110
  173. package/dist/relay-attestation/index.d.ts +0 -1
  174. package/dist/relay-attestation/index.js +0 -1
  175. package/dist/request/caller-proof.d.ts +0 -43
  176. package/dist/request/caller-proof.js +0 -36
  177. package/dist/request/index.d.ts +0 -3
  178. package/dist/request/index.js +0 -3
  179. package/dist/request/proof-signer.d.ts +0 -49
  180. package/dist/request/proof-signer.js +0 -59
  181. package/dist/request/request-sig.d.ts +0 -99
  182. package/dist/request/request-sig.js +0 -95
  183. package/dist/request-proof/authorizer.d.ts +0 -41
  184. package/dist/request-proof/authorizer.js +0 -60
  185. package/dist/request-proof/index.d.ts +0 -4
  186. package/dist/request-proof/index.js +0 -2
  187. package/dist/request-proof/request-proof.d.ts +0 -53
  188. package/dist/request-proof/request-proof.js +0 -95
  189. package/dist/rpc/index.d.ts +0 -64
  190. package/dist/rpc/index.js +0 -78
  191. package/dist/signer/index.d.ts +0 -2
  192. package/dist/signer/index.js +0 -3
  193. package/dist/signer/signer.d.ts +0 -92
  194. package/dist/signer/signer.js +0 -147
  195. package/dist/token-store/index.d.ts +0 -27
  196. package/dist/token-store/index.js +0 -62
  197. package/dist/transport/index.d.ts +0 -6
  198. package/dist/transport/index.js +0 -3
  199. package/dist/transport/mero-client.d.ts +0 -294
  200. package/dist/transport/mero-client.js +0 -386
  201. package/dist/transport/relay-observer.d.ts +0 -152
  202. package/dist/transport/relay-observer.js +0 -173
  203. package/dist/transport/relay-transport.d.ts +0 -73
  204. package/dist/transport/relay-transport.js +0 -94
  205. package/dist/transport/types.d.ts +0 -100
  206. package/dist/transport/types.js +0 -32
  207. package/dist/types/api-response.d.ts +0 -15
  208. package/dist/types/api-response.js +0 -1
  209. package/dist/types/index.d.ts +0 -1
  210. package/dist/types/index.js +0 -1
  211. package/dist/warrant/creation-warrant.d.ts +0 -163
  212. package/dist/warrant/creation-warrant.js +0 -194
  213. package/dist/warrant/governance-op.d.ts +0 -202
  214. package/dist/warrant/governance-op.js +0 -355
  215. package/dist/warrant/governance-warrant.d.ts +0 -124
  216. package/dist/warrant/governance-warrant.js +0 -164
  217. package/dist/warrant/index.d.ts +0 -8
  218. package/dist/warrant/index.js +0 -5
  219. package/dist/warrant/warrant.d.ts +0 -188
  220. package/dist/warrant/warrant.js +0 -202
@@ -1,355 +0,0 @@
1
- /**
2
- * Encode the governance ops a member may have a relay publish for them.
3
- *
4
- * A governance warrant commits to an op's **borsh bytes**, not to a description
5
- * of it, so the client has to produce exactly the bytes the node decodes. These
6
- * encoders cover the delegable ops (core's `GroupOp::delegable_form` and
7
- * `RootOp::delegable_form`) that a keyholder app is likely to need. Every
8
- * discriminant is a variant's position in core's enum, which is how borsh
9
- * encodes a Rust enum; they are pinned against core's own vectors in the test.
10
- *
11
- * **Delegable form.** A removal, a leave and a cascade delete carry fields only
12
- * the publisher can compute (post-state hashes, the enumerated subtree). The
13
- * member signs them cleared, and these encoders only ever produce the cleared
14
- * form, which is the one a relay accepts. The relay fills them in afterwards.
15
- *
16
- * Sources in core, for the next person to check a discriminant:
17
- * `crates/governance-types/src/lib.rs` (`GroupOp`, `RootOp`),
18
- * `crates/primitives/src/context.rs` (`GroupMemberRole`),
19
- * `crates/account/src/namespace_id.rs` (`founded_namespace_id`), and the vectors in
20
- * `crates/governance-types/src/tests.rs` (`delegated_governance_op_vectors_are_stable`).
21
- */
22
- import { CAPABILITIES, hasCap } from '../capabilities.js';
23
- import { concat, domainHash, fromHex, fromHexUnsized, hex, u32le } from '../crypto/internal.js';
24
- /**
25
- * `GroupMemberRole` discriminants. The two TEE roles (3 and 4) are minted only
26
- * by attestation admission and never by a member's op, so they are not offered.
27
- */
28
- const ROLE = {
29
- Admin: 0,
30
- Member: 1,
31
- ReadOnly: 2,
32
- };
33
- /** `GroupOp` discriminants, by position in core's enum. */
34
- const GROUP_OP = {
35
- MemberAdded: 1,
36
- MemberRemoved: 2,
37
- MemberLeft: 3,
38
- MemberRoleSet: 4,
39
- MemberCapabilitySet: 5,
40
- DefaultCapabilitiesSet: 6,
41
- TargetApplicationSet: 7,
42
- ContextDetached: 9,
43
- SubgroupVisibilitySet: 10,
44
- GroupMetadataSet: 11,
45
- MemberMetadataSet: 12,
46
- ContextMetadataSet: 13,
47
- ContextCapabilityGranted: 16,
48
- ContextCapabilityRevoked: 17,
49
- };
50
- /** `RootOp` discriminants, by position in core's enum. */
51
- const ROOT_OP = {
52
- GroupCreated: 0,
53
- GroupReparented: 1,
54
- GroupDeleted: 2,
55
- MemberJoinedOpen: 7,
56
- NamespaceCreatedV2: 9,
57
- };
58
- /** core's `NAMESPACE_ID_DOMAIN`, which a founded namespace id is hashed under. */
59
- const NAMESPACE_ID_DOMAIN = new TextEncoder().encode('calimero.namespace.id.v1');
60
- /** A post-state hash left for the relay to compute: 32 zero bytes. */
61
- const CLEARED_HASH = new Uint8Array(32);
62
- /** An empty borsh `Vec`: a zero `u32` count. */
63
- const EMPTY_VEC = u32le(0);
64
- function role(value) {
65
- const byte = ROLE[value];
66
- if (byte === undefined) {
67
- throw new Error(`role must be Admin, Member or ReadOnly, got ${String(value)}`);
68
- }
69
- return new Uint8Array([byte]);
70
- }
71
- function tag(byte) {
72
- return new Uint8Array([byte]);
73
- }
74
- /**
75
- * Add `member` (an account, hex) to the group with `role`.
76
- *
77
- * Applied as the author: adding a `Member` needs `MANAGE_MEMBERS` or admin,
78
- * adding an `Admin` needs admin.
79
- */
80
- export function memberAddedOp(member, memberRole) {
81
- return {
82
- kind: 'group',
83
- bytes: concat(tag(GROUP_OP.MemberAdded), fromHex(member, 'member', 32), role(memberRole)),
84
- };
85
- }
86
- /**
87
- * Remove `member` (an account, hex) from the group, in delegable form: both
88
- * post-state claims cleared, for the relay to compute from its own view.
89
- */
90
- export function memberRemovedOp(member) {
91
- return {
92
- kind: 'group',
93
- bytes: concat(tag(GROUP_OP.MemberRemoved), fromHex(member, 'member', 32), CLEARED_HASH, EMPTY_VEC),
94
- };
95
- }
96
- /**
97
- * The author leaves the group, in delegable form. The node applies it only when
98
- * `member` is the author's own account.
99
- */
100
- export function memberLeftOp(member) {
101
- return {
102
- kind: 'group',
103
- bytes: concat(tag(GROUP_OP.MemberLeft), fromHex(member, 'member', 32), CLEARED_HASH, EMPTY_VEC),
104
- };
105
- }
106
- /** Set `member`'s role (an account, hex). */
107
- export function memberRoleSetOp(member, memberRole) {
108
- return {
109
- kind: 'group',
110
- bytes: concat(tag(GROUP_OP.MemberRoleSet), fromHex(member, 'member', 32), role(memberRole)),
111
- };
112
- }
113
- /** Domain of core's `created_subgroup_id`. */
114
- const SUBGROUP_ID_DOMAIN = new TextEncoder().encode('calimero.subgroup.id.v1');
115
- /**
116
- * The id `admin` derives for a subgroup under `parentId` with birth visibility
117
- * `restricted` and `salt` (all hex), as core's `created_subgroup_id` does:
118
- * `domain_hash("calimero.subgroup.id.v1", [admin, parentId, [restricted], salt])`.
119
- *
120
- * A subgroup's id is bound to its create, so nobody but its creator can name
121
- * it: a node refuses a `GroupCreated` whose fields do not reproduce its id.
122
- * That is what stops a second member racing a concurrent create for an id it
123
- * saw gossiped.
124
- */
125
- export async function createdSubgroupId(admin, parentId, restricted, salt) {
126
- return hex(await domainHash(SUBGROUP_ID_DOMAIN, [
127
- fromHex(admin, 'admin', 32),
128
- fromHex(parentId, 'parentId', 32),
129
- new Uint8Array([restricted ? 1 : 0]),
130
- fromHex(salt, 'salt', 32),
131
- ]));
132
- }
133
- /**
134
- * Set the group's default capability mask (`MemberCapabilities` bits, a `u32`),
135
- * the capabilities every member holds without a per-member grant. Needs admin.
136
- *
137
- * The relay refuses any change to `CAN_AUTHOR_ON_BEHALF` (bit 9) through this
138
- * path, and no namespace starts with it in its default mask, so a mask that
139
- * sets it is refused here rather than burning a nonce on a certain 403.
140
- */
141
- /** A borsh `String`: a u32 byte length, then the UTF-8 bytes. */
142
- function borshString(value, what) {
143
- if (value.length === 0)
144
- throw new Error(`${what} must not be empty`);
145
- const bytes = new TextEncoder().encode(value);
146
- return concat(u32le(bytes.length), bytes);
147
- }
148
- /**
149
- * Choose the application a group runs (`GroupOp::TargetApplicationSet`), in
150
- * its delegable form: `bytecode_id` is left for the relay to fill (32 zero
151
- * bytes), resolved from `package@version` in the registry. The member signs
152
- * the application id, package and version, so the relay can pick no other
153
- * build; the application id binds package and signer, and every node checks
154
- * the bundle's signature.
155
- *
156
- * Through a relay this is accepted only as a group's FIRST application, on a
157
- * group that targets none yet (a namespace an account founded). Moving an
158
- * existing target is an upgrade, which never goes through a relay.
159
- */
160
- export function targetApplicationSetOp(input) {
161
- return {
162
- kind: 'group',
163
- bytes: concat(tag(GROUP_OP.TargetApplicationSet), CLEARED_HASH, fromHex(input.applicationId, 'applicationId', 32), borshString(input.package, 'package'), borshString(input.version, 'version')),
164
- };
165
- }
166
- export function defaultCapabilitiesSetOp(capabilities) {
167
- if (!Number.isInteger(capabilities) || capabilities < 0 || capabilities > 4294967295) {
168
- throw new Error(`capabilities must be a u32 bit mask, got ${String(capabilities)}`);
169
- }
170
- // Never changed through a relay.
171
- if (hasCap(capabilities, CAPABILITIES.CAN_AUTHOR_ON_BEHALF)) {
172
- throw new Error('capabilities may not include CAN_AUTHOR_ON_BEHALF (512): a relay never carries a change to it');
173
- }
174
- return {
175
- kind: 'group',
176
- bytes: concat(tag(GROUP_OP.DefaultCapabilitiesSet), u32le(capabilities)),
177
- };
178
- }
179
- /**
180
- * Create a subgroup under `parentId`, a root op published on the namespace.
181
- *
182
- * Needs `CAN_CREATE_SUBGROUP` or admin on the parent; the author, not the
183
- * relay, becomes the new subgroup's admin.
184
- */
185
- export function groupCreatedOp(input) {
186
- return {
187
- kind: 'root',
188
- bytes: concat(tag(ROOT_OP.GroupCreated), fromHex(input.groupId, 'groupId', 32), fromHex(input.parentId, 'parentId', 32), tag(input.restricted ? 1 : 0), fromHex(input.admin, 'admin', 32), fromHex(input.salt, 'salt', 32)),
189
- };
190
- }
191
- /**
192
- * Create a subgroup under `parentId`: draws a fresh salt (unless one is given),
193
- * derives the id with {@link createdSubgroupId}, and encodes the op.
194
- */
195
- export async function subgroupCreation(input) {
196
- const salt = input.salt ?? hex(crypto.getRandomValues(new Uint8Array(32)));
197
- const groupId = await createdSubgroupId(input.admin, input.parentId, input.restricted, salt);
198
- return { groupId, salt, op: groupCreatedOp({ ...input, groupId, salt }) };
199
- }
200
- /** Move `childGroupId` under `newParentId`, a root op. */
201
- export function groupReparentedOp(childGroupId, newParentId) {
202
- return {
203
- kind: 'root',
204
- bytes: concat(tag(ROOT_OP.GroupReparented), fromHex(childGroupId, 'childGroupId', 32), fromHex(newParentId, 'newParentId', 32)),
205
- };
206
- }
207
- /**
208
- * Delete `rootGroupId` and its subtree, in delegable form: both cascade lists
209
- * empty, for the relay to enumerate.
210
- */
211
- export function groupDeletedOp(rootGroupId) {
212
- return {
213
- kind: 'root',
214
- bytes: concat(tag(ROOT_OP.GroupDeleted), fromHex(rootGroupId, 'rootGroupId', 32), EMPTY_VEC, EMPTY_VEC),
215
- };
216
- }
217
- /**
218
- * The id of the namespace `founder` founds with `salt`: core's
219
- * `founded_namespace_id`, `domain_hash("calimero.namespace.id.v1", [founder, salt])`.
220
- *
221
- * Anyone holding the pair can recompute it, and only the founder's account
222
- * hashes to it, which is why a relay founding a namespace for a member cannot
223
- * choose the id: the member computes it and signs it.
224
- */
225
- export async function foundedNamespaceId(founder, salt) {
226
- return hex(await domainHash(NAMESPACE_ID_DOMAIN, [
227
- fromHex(founder, 'founder', 32),
228
- fromHex(salt, 'salt', 32),
229
- ]));
230
- }
231
- /**
232
- * Found a namespace: core's `RootOp::NamespaceCreatedV2` genesis, a root op
233
- * published on the new namespace's own log.
234
- *
235
- * Its delegable form is the op itself, nothing cleared, so the author signs the
236
- * exact genesis. Borsh is `[9] || founder || credential || salt`: the credential
237
- * is a nested struct, not a length-prefixed byte string, so it is written
238
- * inline. The warrant's scope must be {@link foundedNamespaceId}`(founder, salt)`.
239
- */
240
- export function namespaceCreatedOp(input) {
241
- const credential = fromHexUnsized(input.credential, 'credential');
242
- if (credential.length === 0) {
243
- throw new Error('credential must be the founder\'s AccountProof<DeviceCert>, got nothing');
244
- }
245
- return {
246
- kind: 'root',
247
- bytes: concat(tag(ROOT_OP.NamespaceCreatedV2), fromHex(input.founder, 'founder', 32), credential, fromHex(input.salt, 'salt', 32)),
248
- };
249
- }
250
- // ---------------------------------------------------------------------------
251
- // The rest of the delegable set. Layouts are pinned against core's
252
- // `delegable_governance_op_vectors_for_non_rust_encoders_are_stable`
253
- // (crates/governance-types/src/tests.rs, core#4272).
254
- /** A borsh `String`: a u32 byte length, then the UTF-8 bytes. */
255
- function str(value) {
256
- const bytes = new TextEncoder().encode(value);
257
- return concat(u32le(bytes.length), bytes);
258
- }
259
- /** A borsh `Option<String>`: 0, or 1 then the string. */
260
- function optionalStr(value) {
261
- return value === undefined ? tag(0) : concat(tag(1), str(value));
262
- }
263
- /**
264
- * A borsh `BTreeMap<String, String>`: a u32 count, then each entry in the
265
- * map's own order, which is by key BYTES (not JS string order).
266
- */
267
- function stringMap(data = {}) {
268
- const enc = new TextEncoder();
269
- const entries = Object.entries(data)
270
- .map(([k, v]) => [enc.encode(k), v, k])
271
- .sort(([a], [b]) => {
272
- for (let i = 0; i < Math.min(a.length, b.length); i++)
273
- if (a[i] !== b[i])
274
- return a[i] - b[i];
275
- return a.length - b.length;
276
- });
277
- return concat(u32le(entries.length), ...entries.flatMap(([, v, k]) => [str(k), str(v)]));
278
- }
279
- /** Validate a u32 capability mask a relay may carry: never bit 9. */
280
- function relayableMask(capabilities, what) {
281
- if (!Number.isInteger(capabilities) || capabilities < 0 || capabilities > 4294967295) {
282
- throw new Error(`${what} must be a u32 bit mask, got ${String(capabilities)}`);
283
- }
284
- if (hasCap(capabilities, CAPABILITIES.CAN_AUTHOR_ON_BEHALF)) {
285
- throw new Error(`${what} may not include CAN_AUTHOR_ON_BEHALF (512): a relay never carries a change to it`);
286
- }
287
- return capabilities;
288
- }
289
- /** Set `member`'s capabilities in the group (`GroupOp::MemberCapabilitySet`). Bit 9 is refused. */
290
- export function memberCapabilitySetOp(member, capabilities) {
291
- return {
292
- kind: 'group',
293
- bytes: concat(tag(GROUP_OP.MemberCapabilitySet), fromHex(member, 'member', 32), u32le(relayableMask(capabilities, 'capabilities'))),
294
- };
295
- }
296
- /** Detach a context from the group (`GroupOp::ContextDetached`). */
297
- export function contextDetachedOp(contextId) {
298
- return { kind: 'group', bytes: concat(tag(GROUP_OP.ContextDetached), fromHex(contextId, 'contextId', 32)) };
299
- }
300
- /** Make a subgroup open (members of the parent may join it themselves) or restricted. */
301
- export function subgroupVisibilitySetOp(mode) {
302
- if (mode !== 'open' && mode !== 'restricted')
303
- throw new Error(`mode must be open or restricted, got ${String(mode)}`);
304
- return { kind: 'group', bytes: concat(tag(GROUP_OP.SubgroupVisibilitySet), tag(mode === 'open' ? 0 : 1)) };
305
- }
306
- /** Name the group, with optional key/value data (`GroupOp::GroupMetadataSet`). */
307
- export function groupMetadataSetOp(input = {}) {
308
- return { kind: 'group', bytes: concat(tag(GROUP_OP.GroupMetadataSet), optionalStr(input.name), stringMap(input.data)) };
309
- }
310
- /** Name a member in the group (`GroupOp::MemberMetadataSet`). */
311
- export function memberMetadataSetOp(member, input = {}) {
312
- return {
313
- kind: 'group',
314
- bytes: concat(tag(GROUP_OP.MemberMetadataSet), fromHex(member, 'member', 32), optionalStr(input.name), stringMap(input.data)),
315
- };
316
- }
317
- /** Name a context in the group (`GroupOp::ContextMetadataSet`). */
318
- export function contextMetadataSetOp(contextId, input = {}) {
319
- return {
320
- kind: 'group',
321
- bytes: concat(tag(GROUP_OP.ContextMetadataSet), fromHex(contextId, 'contextId', 32), optionalStr(input.name), stringMap(input.data)),
322
- };
323
- }
324
- function contextCapability(capability) {
325
- if (!Number.isInteger(capability) || capability < 1 || capability > 0xff) {
326
- throw new Error(`capability must be a non-zero u8, got ${String(capability)}`);
327
- }
328
- return tag(capability);
329
- }
330
- /** Grant `member` a per-context capability (`GroupOp::ContextCapabilityGranted`). */
331
- export function contextCapabilityGrantedOp(contextId, member, capability) {
332
- return {
333
- kind: 'group',
334
- bytes: concat(tag(GROUP_OP.ContextCapabilityGranted), fromHex(contextId, 'contextId', 32), fromHex(member, 'member', 32), contextCapability(capability)),
335
- };
336
- }
337
- /** Revoke a per-context capability from `member` (`GroupOp::ContextCapabilityRevoked`). */
338
- export function contextCapabilityRevokedOp(contextId, member, capability) {
339
- return {
340
- kind: 'group',
341
- bytes: concat(tag(GROUP_OP.ContextCapabilityRevoked), fromHex(contextId, 'contextId', 32), fromHex(member, 'member', 32), contextCapability(capability)),
342
- };
343
- }
344
- /**
345
- * Join an open subgroup yourself (`RootOp::MemberJoinedOpen`), a root op posted
346
- * to the namespace. `member` must be the author; `credential` is the author's
347
- * own `AccountProof<DeviceCert>`, hex, carried as-is. The author's
348
- * `CAN_JOIN_OPEN_SUBGROUPS` and the open inheritance path are checked as usual.
349
- */
350
- export function memberJoinedOpenOp(input) {
351
- return {
352
- kind: 'root',
353
- bytes: concat(tag(ROOT_OP.MemberJoinedOpen), fromHex(input.member, 'member', 32), fromHex(input.groupId, 'groupId', 32), fromHexUnsized(input.credential, 'credential')),
354
- };
355
- }
@@ -1,124 +0,0 @@
1
- /**
2
- * Mint a governance warrant: the author's consent for one relay to publish one
3
- * governance op in the author's name, once.
4
- *
5
- * The intent warrant lets an account with no node write to a context and the
6
- * creation warrant lets it create one. This covers the governance around them:
7
- * adding the other person to a DM, creating a channel's subgroup, changing a
8
- * role. The relay wraps the op in `OnBehalf` and publishes it; every peer applies
9
- * the inner op **as the author**, so the author's own authority decides
10
- * (`MANAGE_MEMBERS` to add a member, `CAN_CREATE_SUBGROUP` to create a subgroup,
11
- * and so on) and the relay needs only standing to act for members.
12
- *
13
- * **It commits to the op's bytes.** The warrant carries
14
- * `H(kind || op bytes)` over the op's delegable form (see `governance-op.ts`), so
15
- * what the op does, to whom and where is signed byte for byte and the relay
16
- * chooses nothing about it. `kind` is inside the commitment, so a group op can
17
- * never be presented as a root op whose bytes happen to coincide.
18
- *
19
- * **`scope` is where the op is published**: the group itself for a group op,
20
- * the namespace for a root op.
21
- *
22
- * Same primitives and the same two-widths trap as `warrant.ts`: counts inside
23
- * the encoding are borsh `u32`, counts in the signing preimage are `u64`.
24
- *
25
- * **The byte contract is pinned in core**, at
26
- * `crates/account/src/tests/governance_wire_fixture.rs`, and this module's test
27
- * asserts the same vectors. A drift would surface at a relay as a 400.
28
- */
29
- import { type Signer } from '../signer/signer.js';
30
- import type { GovernanceOp, GovernanceOpKind } from './governance-op.js';
31
- /** What a governance warrant authorises. */
32
- export interface GovernanceWarrantInput {
33
- /**
34
- * Where the op is published, hex (32 bytes): the group for a group op, the
35
- * namespace for a root op. The relay refuses a warrant presented anywhere else.
36
- */
37
- scope: string;
38
- /** The op, from one of the encoders in `governance-op.ts`. Its kind is signed over. */
39
- op: GovernanceOp;
40
- /** The author's account, hex: whose authority the op is applied under. */
41
- authorAccount: string;
42
- /** The relay's account, hex: the subject its authorship grant is held by. */
43
- executor: string;
44
- /** The one signing key of `executor` that may publish it, hex: discovery's `executorKey`. */
45
- executorKey: string;
46
- /**
47
- * Monotonic per author **device**, spent in a per-group ledger. The same
48
- * nonce source `RelayClient` uses for every other warrant is correct.
49
- */
50
- nonce: number | bigint;
51
- /** Unix seconds after which the relay must refuse it. */
52
- notAfter: number | bigint;
53
- /** Account-log heads the author saw, each hex (32 bytes). At most 64. */
54
- accountHeads?: string[];
55
- /** Governance heads the author's view descended from, each hex (32 bytes). At most 64. */
56
- governanceFloor?: string[];
57
- /**
58
- * The author device's ed25519 signing secret, hex (32 bytes). Never sent.
59
- *
60
- * Mutually exclusive with {@link GovernanceWarrantInput.signer}: exactly one is required.
61
- * Prefer `signer` in a browser, since a secret held as a string is readable
62
- * by anything on the origin.
63
- */
64
- deviceSecret?: string;
65
- /**
66
- * The author device's signer, in place of {@link GovernanceWarrantInput.deviceSecret}: for a
67
- * key that cannot be exported to hex (a non-extractable WebCrypto key, a
68
- * passkey, a hardware or remote signer). Its `publicKey` becomes the
69
- * warrant's `author_device_key`, and it signs the 32-byte preimage as is.
70
- */
71
- signer?: Signer;
72
- }
73
- /** A governance warrant's fields, decoded from the wire encoding. */
74
- export interface GovernanceWarrantFields {
75
- scope: string;
76
- kind: GovernanceOpKind;
77
- authorAccount: string;
78
- /** The author device key: the signer's public key. */
79
- deviceKey: string;
80
- executor: string;
81
- executorKey: string;
82
- opHash: string;
83
- accountHeads: string[];
84
- governanceFloor: string[];
85
- nonce: bigint;
86
- notAfter: bigint;
87
- signature: string;
88
- }
89
- /**
90
- * The commitment a governance warrant carries in place of the op:
91
- * `H(kind || op bytes)` under its own domain.
92
- */
93
- export declare function governanceOpHash(op: GovernanceOp): Promise<Uint8Array>;
94
- /** The already-decoded fields the signature covers. Internal. */
95
- export interface GovernancePreimageParts {
96
- scope: Uint8Array;
97
- kind: GovernanceOpKind;
98
- authorAccount: Uint8Array;
99
- deviceKey: Uint8Array;
100
- executor: Uint8Array;
101
- executorKey: Uint8Array;
102
- opHash: Uint8Array;
103
- accountHeads: Uint8Array[];
104
- governanceFloor: Uint8Array[];
105
- nonce: number | bigint;
106
- notAfter: number | bigint;
107
- }
108
- /**
109
- * The bytes the device key signs. Exported for the conformance test only, not
110
- * from the package root.
111
- */
112
- export declare function governanceWarrantPreimage(p: GovernancePreimageParts): Promise<Uint8Array>;
113
- /**
114
- * Sign a governance warrant and return it hex-encoded (borsh
115
- * `GovernanceWarrant`), ready for the relay alongside the op's own bytes.
116
- */
117
- export declare function signGovernanceWarrant(input: GovernanceWarrantInput): Promise<string>;
118
- /**
119
- * Decode a hex governance warrant back into its fields.
120
- *
121
- * Refuses truncated or trailing bytes and an unknown kind. Does not verify the
122
- * signature.
123
- */
124
- export declare function parseGovernanceWarrant(warrant: string): GovernanceWarrantFields;
@@ -1,164 +0,0 @@
1
- /**
2
- * Mint a governance warrant: the author's consent for one relay to publish one
3
- * governance op in the author's name, once.
4
- *
5
- * The intent warrant lets an account with no node write to a context and the
6
- * creation warrant lets it create one. This covers the governance around them:
7
- * adding the other person to a DM, creating a channel's subgroup, changing a
8
- * role. The relay wraps the op in `OnBehalf` and publishes it; every peer applies
9
- * the inner op **as the author**, so the author's own authority decides
10
- * (`MANAGE_MEMBERS` to add a member, `CAN_CREATE_SUBGROUP` to create a subgroup,
11
- * and so on) and the relay needs only standing to act for members.
12
- *
13
- * **It commits to the op's bytes.** The warrant carries
14
- * `H(kind || op bytes)` over the op's delegable form (see `governance-op.ts`), so
15
- * what the op does, to whom and where is signed byte for byte and the relay
16
- * chooses nothing about it. `kind` is inside the commitment, so a group op can
17
- * never be presented as a root op whose bytes happen to coincide.
18
- *
19
- * **`scope` is where the op is published**: the group itself for a group op,
20
- * the namespace for a root op.
21
- *
22
- * Same primitives and the same two-widths trap as `warrant.ts`: counts inside
23
- * the encoding are borsh `u32`, counts in the signing preimage are `u64`.
24
- *
25
- * **The byte contract is pinned in core**, at
26
- * `crates/account/src/tests/governance_wire_fixture.rs`, and this module's test
27
- * asserts the same vectors. A drift would surface at a relay as a 400.
28
- */
29
- import { concat, domainHash, fromHex, hex, u32le, u64le } from '../crypto/internal.js';
30
- import { resolveSigner } from '../signer/signer.js';
31
- import { checkedSignature, citedHeads } from './warrant.js';
32
- const SIGN_DOMAIN = new TextEncoder().encode('calimero.governance-warrant.v1');
33
- const OP_DOMAIN = new TextEncoder().encode('calimero.governance-warrant.op.v1');
34
- /** core's `GovernanceOpKind` discriminants. */
35
- const KIND_BYTE = { group: 0, root: 1 };
36
- /**
37
- * The commitment a governance warrant carries in place of the op:
38
- * `H(kind || op bytes)` under its own domain.
39
- */
40
- export async function governanceOpHash(op) {
41
- return domainHash(OP_DOMAIN, [new Uint8Array([kindByte(op.kind)]), op.bytes]);
42
- }
43
- /**
44
- * The bytes the device key signs. Exported for the conformance test only, not
45
- * from the package root.
46
- */
47
- export async function governanceWarrantPreimage(p) {
48
- // u64 counts HERE; u32 on the wire. The counts keep the two adjacent head
49
- // lists from being relabelled into each other.
50
- return domainHash(SIGN_DOMAIN, [
51
- p.scope,
52
- new Uint8Array([kindByte(p.kind)]),
53
- p.authorAccount,
54
- p.deviceKey,
55
- p.executor,
56
- p.executorKey,
57
- p.opHash,
58
- u64le(p.accountHeads.length),
59
- ...p.accountHeads,
60
- u64le(p.governanceFloor.length),
61
- ...p.governanceFloor,
62
- u64le(p.nonce),
63
- u64le(p.notAfter),
64
- ]);
65
- }
66
- /**
67
- * Sign a governance warrant and return it hex-encoded (borsh
68
- * `GovernanceWarrant`), ready for the relay alongside the op's own bytes.
69
- */
70
- export async function signGovernanceWarrant(input) {
71
- const scope = fromHex(input.scope, 'scope', 32);
72
- const authorAccount = fromHex(input.authorAccount, 'authorAccount', 32);
73
- const executor = fromHex(input.executor, 'executor', 32);
74
- const executorKey = fromHex(input.executorKey, 'executorKey', 32);
75
- const accountHeads = citedHeads(input.accountHeads, 'accountHeads');
76
- const governanceFloor = citedHeads(input.governanceFloor, 'governanceFloor');
77
- const kind = kindByte(input.op.kind);
78
- const signer = await resolveSigner(input.deviceSecret, input.signer, 'deviceSecret');
79
- const deviceKey = fromHex(signer.publicKey, 'signer.publicKey', 32);
80
- const opHash = await governanceOpHash(input.op);
81
- const preimage = await governanceWarrantPreimage({
82
- scope,
83
- kind: input.op.kind,
84
- authorAccount,
85
- deviceKey,
86
- executor,
87
- executorKey,
88
- opHash,
89
- accountHeads,
90
- governanceFloor,
91
- nonce: input.nonce,
92
- notAfter: input.notAfter,
93
- });
94
- const signature = checkedSignature(await signer.sign(preimage));
95
- return hex(concat(scope, new Uint8Array([kind]), authorAccount, deviceKey, executor, executorKey, opHash, u32le(accountHeads.length), ...accountHeads, u32le(governanceFloor.length), ...governanceFloor, u64le(input.nonce), u64le(input.notAfter), signature));
96
- }
97
- /**
98
- * Decode a hex governance warrant back into its fields.
99
- *
100
- * Refuses truncated or trailing bytes and an unknown kind. Does not verify the
101
- * signature.
102
- */
103
- export function parseGovernanceWarrant(warrant) {
104
- const clean = warrant.trim();
105
- if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length % 2 !== 0) {
106
- throw new Error('governance warrant must be an even number of hex characters');
107
- }
108
- const bytes = new Uint8Array(clean.length / 2);
109
- for (let i = 0; i < bytes.length; i += 1) {
110
- bytes[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);
111
- }
112
- const view = new DataView(bytes.buffer);
113
- let o = 0;
114
- const take = (n, what) => {
115
- if (o + n > bytes.length) {
116
- throw new Error(`governance warrant ends inside ${what} (${bytes.length} bytes)`);
117
- }
118
- const out = bytes.subarray(o, o + n);
119
- o += n;
120
- return out;
121
- };
122
- const u32 = (what) => view.getUint32(take(4, what).byteOffset, true);
123
- const u64 = (what) => view.getBigUint64(take(8, what).byteOffset, true);
124
- const heads = (what) => {
125
- const count = u32(what);
126
- if (count > 64) {
127
- throw new Error(`${what} cites ${count} heads, over the 64 a node accepts`);
128
- }
129
- return Array.from({ length: count }, () => hex(take(32, what)));
130
- };
131
- const kind = () => {
132
- const byte = take(1, 'kind')[0];
133
- if (byte === 0)
134
- return 'group';
135
- if (byte === 1)
136
- return 'root';
137
- throw new Error(`governance warrant has kind ${byte}, expected 0 (group) or 1 (root)`);
138
- };
139
- const fields = {
140
- scope: hex(take(32, 'scope')),
141
- kind: kind(),
142
- authorAccount: hex(take(32, 'authorAccount')),
143
- deviceKey: hex(take(32, 'deviceKey')),
144
- executor: hex(take(32, 'executor')),
145
- executorKey: hex(take(32, 'executorKey')),
146
- opHash: hex(take(32, 'opHash')),
147
- accountHeads: heads('accountHeads'),
148
- governanceFloor: heads('governanceFloor'),
149
- nonce: u64('nonce'),
150
- notAfter: u64('notAfter'),
151
- signature: hex(take(64, 'signature')),
152
- };
153
- if (o !== bytes.length) {
154
- throw new Error(`governance warrant is ${bytes.length} bytes but its fields account for ${o}`);
155
- }
156
- return fields;
157
- }
158
- function kindByte(kind) {
159
- const byte = KIND_BYTE[kind];
160
- if (byte === undefined) {
161
- throw new Error(`op kind must be 'group' or 'root', got ${String(kind)}`);
162
- }
163
- return byte;
164
- }
@@ -1,8 +0,0 @@
1
- export { signWarrant, intentHash } from './warrant.js';
2
- export type { WarrantInput } from './warrant.js';
3
- export { signCreationWarrant, parseCreationWarrant, creationInitHash, } from './creation-warrant.js';
4
- export type { CreationWarrantInput, CreationWarrantFields, SignedCreationWarrant, } from './creation-warrant.js';
5
- export { signGovernanceWarrant, parseGovernanceWarrant, governanceOpHash, } from './governance-warrant.js';
6
- export type { GovernanceWarrantInput, GovernanceWarrantFields, } from './governance-warrant.js';
7
- export { memberAddedOp, memberRemovedOp, memberLeftOp, memberRoleSetOp, groupCreatedOp, createdSubgroupId, subgroupCreation, groupReparentedOp, groupDeletedOp, memberCapabilitySetOp, contextDetachedOp, subgroupVisibilitySetOp, groupMetadataSetOp, memberMetadataSetOp, contextMetadataSetOp, contextCapabilityGrantedOp, contextCapabilityRevokedOp, memberJoinedOpenOp, namespaceCreatedOp, defaultCapabilitiesSetOp, targetApplicationSetOp, foundedNamespaceId, } from './governance-op.js';
8
- export type { GovernanceOp, GovernanceOpKind, GovernanceMemberRole, GroupCreatedInput, SubgroupCreation, NamespaceCreatedInput, } from './governance-op.js';
@@ -1,5 +0,0 @@
1
- // Warrant signing — the author's half of delegated authorship
2
- export { signWarrant, intentHash } from './warrant.js';
3
- export { signCreationWarrant, parseCreationWarrant, creationInitHash, } from './creation-warrant.js';
4
- export { signGovernanceWarrant, parseGovernanceWarrant, governanceOpHash, } from './governance-warrant.js';
5
- export { memberAddedOp, memberRemovedOp, memberLeftOp, memberRoleSetOp, groupCreatedOp, createdSubgroupId, subgroupCreation, groupReparentedOp, groupDeletedOp, memberCapabilitySetOp, contextDetachedOp, subgroupVisibilitySetOp, groupMetadataSetOp, memberMetadataSetOp, contextMetadataSetOp, contextCapabilityGrantedOp, contextCapabilityRevokedOp, memberJoinedOpenOp, namespaceCreatedOp, defaultCapabilitiesSetOp, targetApplicationSetOp, foundedNamespaceId, } from './governance-op.js';