@calimero-network/mero-js 26.10.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 -12767
  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 -12767
  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 -686
  62. package/dist/admin-api/admin-client.js +0 -1225
  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 -2041
  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,188 +0,0 @@
1
- /**
2
- * Mint a warrant: the author's consent for one relay to run one intent, once.
3
- *
4
- * This exists because the case delegated authorship is *for* is an account that
5
- * holds no node — a browser tab, a Node service, a bot. Every one of those is a
6
- * JS runtime, and until now the only thing that could mint a warrant was `merod`,
7
- * so "a member with no node" in practice meant "a member with a node it is not
8
- * allowed to use for anything else".
9
- *
10
- * **No dependencies, and that is not incidental.** The primitives are all built
11
- * in: `crypto.subtle.digest('SHA-256')` for the hash, `crypto.subtle` Ed25519
12
- * for the signature, and `DataView` for the integers.
13
- *
14
- * Warrant v2 (core#3933) gave up the fixed-width encoding that used to make this
15
- * pure concatenation: `method` travels in the clear as a string and each cited
16
- * head list is a vector, so three borsh `u32` counts now sit inside the bytes.
17
- * Two widths to keep straight, and they are not the same: the counts **inside
18
- * the encoding** are `u32`, while the counts the **signing preimage** hashes are
19
- * `u64`. Same numbers, different serialization — reusing one for the other
20
- * produces a warrant that is well-formed and verifies nowhere.
21
- *
22
- * **The byte contract is pinned in core**, at
23
- * `crates/account/src/tests/warrant_wire_fixture.rs`. The domain strings below
24
- * are `pub(crate)` there, so nothing in that repository forces anyone to notice
25
- * this file depends on them — which is exactly why the fixture lives on that side
26
- * and this module's test asserts the same vectors. A drift would otherwise leave
27
- * this producing well-formed warrants whose signatures verify nowhere, surfacing
28
- * at a relay as a 403: an authorization refusal, nowhere near the cause.
29
- */
30
- import { type Signer } from '../signer/signer.js';
31
- /** Ed25519 PKCS#8 prefix, so a raw 32-byte seed can be imported by WebCrypto. */
32
- /** What a warrant authorises. */
33
- export interface WarrantInput {
34
- /**
35
- * The context, hex — the same string every other method here takes.
36
- *
37
- * Hex like everything else on this interface. It was base58 while the node
38
- * spelled `ContextId` and `PublicKey` that way and `AccountId` and `DeviceId`
39
- * in hex; that split is gone, so this module no longer needs a rule per field
40
- * and no longer needs a base58 decoder to enforce one.
41
- */
42
- context: string;
43
- /** The author's account, hex — whose consent this is. */
44
- authorAccount: string;
45
- /** The relay's account, hex: the subject its authorship grant is held by. */
46
- executor: string;
47
- /**
48
- * The one signing key of `executor` that may spend this warrant, hex: the
49
- * relay's `executorKey` from discovery. Any other key of the account is refused.
50
- */
51
- executorKey: string;
52
- /**
53
- * The blob id of the release the context's group names, hex (32 bytes): the
54
- * relay's `releaseBytecodeId` from discovery.
55
- *
56
- * Pins the code, so a relay cannot wait for an upgrade that widens what
57
- * `method` does and then spend a warrant signed against the narrower one. The
58
- * relay refuses a warrant pinning any release other than the one it runs.
59
- */
60
- releaseBytecodeId: string;
61
- /**
62
- * That release's semver, discovery's `releaseVersion`: signed, never compared,
63
- * at most 256 bytes of UTF-8. Defaults to empty, as `merod account warrant` does.
64
- */
65
- releaseVersion?: string;
66
- /**
67
- * The method the relay may run.
68
- *
69
- * Carried in the clear as of v2, so a peer can select a per-method write-set
70
- * without reversing a hash against the app's ABI. The *arguments* are still
71
- * committed to rather than carried — see {@link intentHash}.
72
- */
73
- method: string;
74
- /** Its arguments, as the JSON the guest will receive. */
75
- argsJson: unknown;
76
- /**
77
- * Monotonic per author **device**.
78
- *
79
- * Per device rather than per account because two devices of one account are
80
- * independent replicas: they cannot coordinate on a counter, so an
81
- * account-scoped sequence would have them refusing each other's warrants.
82
- */
83
- nonce: number | bigint;
84
- /**
85
- * Account-log heads this author saw when signing, each hex (32 bytes).
86
- *
87
- * Empty by default, and empty is the honest answer for a client that tracks
88
- * no log — a fabricated view is worse than none. At most
89
- * {@link MAX_CITED_HEADS}.
90
- */
91
- accountHeads?: string[];
92
- /**
93
- * Governance heads this author's view descended from, each hex (32 bytes).
94
- *
95
- * **Where these come from decides whether they mean anything.** A floor the
96
- * relay supplied is a floor the relay chose: a relay that withholds a
97
- * governance op can hand you a stale view and collect a warrant citing it.
98
- * Take the value from the account's own devices or a co-signing peer outside
99
- * the relay's operator. As it stands this detects an honest relay's staleness
100
- * and does not constrain a dishonest one.
101
- */
102
- governanceFloor?: string[];
103
- /** Unix seconds after which the relay must refuse it. */
104
- notAfter: number | bigint;
105
- /**
106
- * The author device's ed25519 signing secret, hex (32 bytes).
107
- *
108
- * Never sent anywhere. It signs locally and only the signature travels, which
109
- * is the whole reason a warrant can be minted by something holding no node.
110
- *
111
- * Mutually exclusive with {@link WarrantInput.signer}: exactly one is
112
- * required. Prefer `signer` in a browser, since a secret held as a string is
113
- * readable by anything on the origin.
114
- */
115
- deviceSecret?: string;
116
- /**
117
- * The author device's signer, in place of {@link WarrantInput.deviceSecret}:
118
- * for a key that cannot be exported to hex (a non-extractable WebCrypto key,
119
- * a passkey, a hardware or remote signer).
120
- */
121
- signer?: Signer;
122
- }
123
- /**
124
- * The commitment a warrant carries in place of the intent itself.
125
- *
126
- * Its own domain, distinct from the signing one, so a value computed for one
127
- * purpose cannot be presented for the other.
128
- */
129
- export declare function intentHash(method: string, argsJson: unknown): Promise<Uint8Array>;
130
- /**
131
- * Sign a warrant and return it hex-encoded, ready for `performIntent`.
132
- *
133
- * Returns the encoding rather than an object because the signature covers
134
- * exactly these bytes: a caller that rebuilt the fields from JSON would have a
135
- * second spelling able to disagree with what was signed.
136
- */
137
- export declare function signWarrant(input: WarrantInput): Promise<string>;
138
- /**
139
- * Decode a cited-head list, refusing one longer than a node will accept.
140
- * Shared with `creation-warrant.ts`; internal, not exported from the package root.
141
- */
142
- /**
143
- * A signer's output, refused unless it is a 64-byte Ed25519 signature.
144
- *
145
- * The signature is spliced into the encoding at a fixed width, so a signer that
146
- * returned anything else would yield a warrant that parses as garbage at a
147
- * relay, far from the signer at fault. Internal.
148
- */
149
- export declare function checkedSignature(signature: Uint8Array): Uint8Array;
150
- /** A release version as UTF-8, refused past what a node accepts. Shared with `RelayClient`; internal. */
151
- export declare function releaseVersionBytes(version: string): Uint8Array;
152
- export declare function citedHeads(heads: string[] | undefined, label: string): Uint8Array[];
153
- /** A warrant's fields, as hex, decoded from the wire encoding. */
154
- export interface WarrantFields {
155
- context: string;
156
- authorAccount: string;
157
- /** The author device key `signWarrant` derived from the secret. */
158
- deviceKey: string;
159
- executor: string;
160
- executorKey: string;
161
- releaseBytecodeId: string;
162
- /** UTF-8, as hex like `method`. */
163
- releaseVersion: string;
164
- method: string;
165
- intentHash: string;
166
- accountHeads: string[];
167
- governanceFloor: string[];
168
- /** u64 little-endian, as it sits on the wire. */
169
- nonce: string;
170
- /** u64 little-endian, as it sits on the wire. */
171
- notAfter: string;
172
- signature: string;
173
- }
174
- /**
175
- * Decode a hex warrant back into its fields.
176
- *
177
- * Exists because v2's offsets are no longer constants: `method` is a string and
178
- * the head lists are vectors, so everything after `releaseBytecodeId` moves with
179
- * the release version's and the method's lengths. Anything reading a warrant by
180
- * fixed offset - a test, a client inspecting one it was handed - silently reads
181
- * the wrong field instead of failing, which is how the three call sites that did
182
- * so passed while comparing the method's length prefix to a nonce.
183
- *
184
- * One copy of the layout, beside the encoder that produces it, so the two cannot
185
- * drift. Internal: not exported from the package root, because a client that
186
- * needs a warrant's fields has the input it signed.
187
- */
188
- export declare function parseWarrant(warrant: string): WarrantFields;
@@ -1,202 +0,0 @@
1
- /**
2
- * Mint a warrant: the author's consent for one relay to run one intent, once.
3
- *
4
- * This exists because the case delegated authorship is *for* is an account that
5
- * holds no node — a browser tab, a Node service, a bot. Every one of those is a
6
- * JS runtime, and until now the only thing that could mint a warrant was `merod`,
7
- * so "a member with no node" in practice meant "a member with a node it is not
8
- * allowed to use for anything else".
9
- *
10
- * **No dependencies, and that is not incidental.** The primitives are all built
11
- * in: `crypto.subtle.digest('SHA-256')` for the hash, `crypto.subtle` Ed25519
12
- * for the signature, and `DataView` for the integers.
13
- *
14
- * Warrant v2 (core#3933) gave up the fixed-width encoding that used to make this
15
- * pure concatenation: `method` travels in the clear as a string and each cited
16
- * head list is a vector, so three borsh `u32` counts now sit inside the bytes.
17
- * Two widths to keep straight, and they are not the same: the counts **inside
18
- * the encoding** are `u32`, while the counts the **signing preimage** hashes are
19
- * `u64`. Same numbers, different serialization — reusing one for the other
20
- * produces a warrant that is well-formed and verifies nowhere.
21
- *
22
- * **The byte contract is pinned in core**, at
23
- * `crates/account/src/tests/warrant_wire_fixture.rs`. The domain strings below
24
- * are `pub(crate)` there, so nothing in that repository forces anyone to notice
25
- * this file depends on them — which is exactly why the fixture lives on that side
26
- * and this module's test asserts the same vectors. A drift would otherwise leave
27
- * this producing well-formed warrants whose signatures verify nowhere, surfacing
28
- * at a relay as a 403: an authorization refusal, nowhere near the cause.
29
- */
30
- import { concat, domainHash, fromHex, hex, u32le, u64le } from '../crypto/internal.js';
31
- import { resolveSigner } from '../signer/signer.js';
32
- const SIGN_DOMAIN = new TextEncoder().encode('calimero.warrant.v2');
33
- const INTENT_DOMAIN = new TextEncoder().encode('calimero.warrant.intent.v1');
34
- /**
35
- * core's `MAX_WARRANT_CITED_HEADS`. A node refuses more than this before doing
36
- * any Ed25519 work, so minting past it produces a warrant nothing will spend.
37
- */
38
- const MAX_CITED_HEADS = 64;
39
- /** core's `MAX_WARRANT_RELEASE_VERSION_LEN`, in UTF-8 bytes; a node refuses a longer one before any signature work. */
40
- const MAX_RELEASE_VERSION_LEN = 256;
41
- /**
42
- * The commitment a warrant carries in place of the intent itself.
43
- *
44
- * Its own domain, distinct from the signing one, so a value computed for one
45
- * purpose cannot be presented for the other.
46
- */
47
- export async function intentHash(method, argsJson) {
48
- const args = new TextEncoder().encode(JSON.stringify(argsJson));
49
- return domainHash(INTENT_DOMAIN, [new TextEncoder().encode(method), args]);
50
- }
51
- /**
52
- * Sign a warrant and return it hex-encoded, ready for `performIntent`.
53
- *
54
- * Returns the encoding rather than an object because the signature covers
55
- * exactly these bytes: a caller that rebuilt the fields from JSON would have a
56
- * second spelling able to disagree with what was signed.
57
- */
58
- export async function signWarrant(input) {
59
- const context = fromHex(input.context, 'context', 32);
60
- const authorAccount = fromHex(input.authorAccount, 'authorAccount', 32);
61
- const executor = fromHex(input.executor, 'executor', 32);
62
- const executorKey = fromHex(input.executorKey, 'executorKey', 32);
63
- const releaseBytecodeId = fromHex(input.releaseBytecodeId, 'releaseBytecodeId', 32);
64
- const releaseVersion = releaseVersionBytes(input.releaseVersion ?? '');
65
- const accountHeads = citedHeads(input.accountHeads, 'accountHeads');
66
- const governanceFloor = citedHeads(input.governanceFloor, 'governanceFloor');
67
- const signer = await resolveSigner(input.deviceSecret, input.signer, 'deviceSecret');
68
- const publicKey = fromHex(signer.publicKey, 'signer.publicKey', 32);
69
- const method = new TextEncoder().encode(input.method);
70
- const commitment = await intentHash(input.method, input.argsJson);
71
- const nonce = u64le(input.nonce);
72
- const notAfter = u64le(input.notAfter);
73
- // Each head list is preceded by its own count. `domainHash` length-prefixes
74
- // every part it is given, so the heads are individually unambiguous — but the
75
- // two lists are adjacent, so without the counts `accountHeads = [a, b],
76
- // governanceFloor = []` hashes identically to `[a], [b]`, and a relay could
77
- // relabel which plane a head was cited from. u64 HERE; u32 in the encoding.
78
- const preimage = await domainHash(SIGN_DOMAIN, [
79
- context,
80
- authorAccount,
81
- publicKey,
82
- executor,
83
- executorKey,
84
- releaseBytecodeId,
85
- releaseVersion,
86
- method,
87
- commitment,
88
- u64le(accountHeads.length),
89
- ...accountHeads,
90
- u64le(governanceFloor.length),
91
- ...governanceFloor,
92
- nonce,
93
- notAfter,
94
- ]);
95
- const signature = checkedSignature(await signer.sign(preimage));
96
- // Borsh: a `String` is a u32 length then its UTF-8 bytes; a `Vec<[u8; 32]>` is
97
- // a u32 count then the elements. u32 HERE; u64 in the preimage above.
98
- return hex(concat(context, authorAccount, publicKey, executor, executorKey, releaseBytecodeId, u32le(releaseVersion.length), releaseVersion, u32le(method.length), method, commitment, u32le(accountHeads.length), ...accountHeads, u32le(governanceFloor.length), ...governanceFloor, nonce, notAfter, signature));
99
- }
100
- /**
101
- * Decode a cited-head list, refusing one longer than a node will accept.
102
- * Shared with `creation-warrant.ts`; internal, not exported from the package root.
103
- */
104
- /**
105
- * A signer's output, refused unless it is a 64-byte Ed25519 signature.
106
- *
107
- * The signature is spliced into the encoding at a fixed width, so a signer that
108
- * returned anything else would yield a warrant that parses as garbage at a
109
- * relay, far from the signer at fault. Internal.
110
- */
111
- export function checkedSignature(signature) {
112
- if (!(signature instanceof Uint8Array) || signature.length !== 64) {
113
- const got = signature instanceof Uint8Array ? `${signature.length} bytes` : typeof signature;
114
- throw new Error(`signer returned ${got}, expected a 64-byte Ed25519 signature`);
115
- }
116
- return signature;
117
- }
118
- /** A release version as UTF-8, refused past what a node accepts. Shared with `RelayClient`; internal. */
119
- export function releaseVersionBytes(version) {
120
- const bytes = new TextEncoder().encode(version);
121
- checkReleaseVersionLength(bytes.length);
122
- return bytes;
123
- }
124
- function checkReleaseVersionLength(len) {
125
- if (len > MAX_RELEASE_VERSION_LEN) {
126
- throw new Error(`releaseVersion is ${len} bytes, over the ${MAX_RELEASE_VERSION_LEN} a node accepts`);
127
- }
128
- }
129
- export function citedHeads(heads, label) {
130
- const list = heads ?? [];
131
- if (list.length > MAX_CITED_HEADS) {
132
- throw new Error(`${label} cites ${list.length} heads, over the ${MAX_CITED_HEADS} a node accepts`);
133
- }
134
- return list.map((head, i) => fromHex(head, `${label}[${i}]`, 32));
135
- }
136
- /**
137
- * Decode a hex warrant back into its fields.
138
- *
139
- * Exists because v2's offsets are no longer constants: `method` is a string and
140
- * the head lists are vectors, so everything after `releaseBytecodeId` moves with
141
- * the release version's and the method's lengths. Anything reading a warrant by
142
- * fixed offset - a test, a client inspecting one it was handed - silently reads
143
- * the wrong field instead of failing, which is how the three call sites that did
144
- * so passed while comparing the method's length prefix to a nonce.
145
- *
146
- * One copy of the layout, beside the encoder that produces it, so the two cannot
147
- * drift. Internal: not exported from the package root, because a client that
148
- * needs a warrant's fields has the input it signed.
149
- */
150
- export function parseWarrant(warrant) {
151
- const at = (start, bytes) => warrant.slice(start * 2, (start + bytes) * 2);
152
- // A borsh u32 is little-endian, so the hex pairs read back to front.
153
- const u32At = (start) => Number.parseInt((at(start, 4).match(/../g) ?? []).reverse().join(''), 16);
154
- let o = 0;
155
- const take = (bytes) => {
156
- const field = at(o, bytes);
157
- o += bytes;
158
- return field;
159
- };
160
- const context = take(32);
161
- const authorAccount = take(32);
162
- const deviceKey = take(32);
163
- const executor = take(32);
164
- const executorKey = take(32);
165
- const releaseBytecodeId = take(32);
166
- const releaseVersionLen = u32At(o);
167
- o += 4;
168
- checkReleaseVersionLength(releaseVersionLen);
169
- const releaseVersion = take(releaseVersionLen);
170
- const methodLen = u32At(o);
171
- o += 4;
172
- const method = take(methodLen);
173
- const intentHashField = take(32);
174
- const accountCount = u32At(o);
175
- o += 4;
176
- const accountHeads = Array.from({ length: accountCount }, () => take(32));
177
- const governanceCount = u32At(o);
178
- o += 4;
179
- const governanceFloor = Array.from({ length: governanceCount }, () => take(32));
180
- const nonce = take(8);
181
- const notAfter = take(8);
182
- const signature = take(64);
183
- if (o * 2 !== warrant.length) {
184
- throw new Error(`warrant is ${warrant.length / 2} bytes but its fields account for ${o}`);
185
- }
186
- return {
187
- context,
188
- authorAccount,
189
- deviceKey,
190
- executor,
191
- executorKey,
192
- releaseBytecodeId,
193
- releaseVersion,
194
- method,
195
- intentHash: intentHashField,
196
- accountHeads,
197
- governanceFloor,
198
- nonce,
199
- notAfter,
200
- signature,
201
- };
202
- }