@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,53 +0,0 @@
1
- import type { Signer } from '../signer/index.js';
2
- /** The header a signed request carries. */
3
- export declare const REQUEST_PROOF_HEADER = "X-Calimero-Proof";
4
- export interface RequestProofInput {
5
- /**
6
- * The HTTP method, exactly as it will be sent.
7
- *
8
- * Not folded. `HEAD` and `GET` are the same *permission* and the node's
9
- * permission table treats them so, but they are different *requests*: folding
10
- * them here would let a proof minted for one be presented as the other.
11
- */
12
- method: string;
13
- /**
14
- * The path and query, exactly as on the request line, with no re-encoding or
15
- * reordering. Use `signedPath` to take it from a URL.
16
- */
17
- path: string;
18
- /**
19
- * The body, exactly as it will be sent. Omit when there is none.
20
- *
21
- * There is no "absent body" case: an omitted body commits to the hash of no
22
- * bytes, so a signer and a verifier cannot disagree about which encoding a
23
- * bodyless request used.
24
- */
25
- body?: string | Uint8Array;
26
- /** The `AccountProof<DeviceCert>` from `signDeviceCert`, hex. */
27
- credential: string;
28
- /** The certified device's ed25519 secret, hex. Or pass `signer`. */
29
- deviceSecret?: string;
30
- /** A signer, for a key that cannot be exported to hex. */
31
- signer?: Signer;
32
- /** Seconds the signature stays valid. Defaults to 300. */
33
- validForSeconds?: number;
34
- /** Override the clock, for tests. Unix seconds. */
35
- now?: number;
36
- }
37
- /**
38
- * Sign one request and return the `X-Calimero-Proof` value, hex.
39
- *
40
- * The encoding is borsh, and it is assembled rather than re-derived: the
41
- * account proof is the **first** field of `CallerProof`, so the credential hex
42
- * a caller already holds is exactly its own prefix. Concatenating avoids a
43
- * second implementation of a structure the node deserializes — the class of bug
44
- * that surfaces far away, as a node refusing a credential nothing can explain.
45
- *
46
- * CallerProof = <account_proof bytes> || 0x00 (no session) || RequestSig
47
- * RequestSig = u32le(len method) || method
48
- * || u32le(len path) || path (with its query)
49
- * || body_hash[32]
50
- * || u64le(issued_at) || u64le(expires_at)
51
- * || signature[64]
52
- */
53
- export declare function signRequestProof(input: RequestProofInput): Promise<string>;
@@ -1,95 +0,0 @@
1
- /**
2
- * Signing one request, so a caller's identity travels with it.
3
- *
4
- * This is the credential a keyholder presents when it has no session: the device
5
- * key signs the request itself, and the certificate its account signed says whose
6
- * key that is. The node verifies both with no store read and no prior
7
- * relationship — `crates/server/src/proof_auth.rs` — which is what lets a hosted
8
- * relay serve a caller it has never seen and holds no auth provider for.
9
- *
10
- * ## Why this exists alongside `login()`
11
- *
12
- * A session is a second link in the same chain, not an alternative to it. It
13
- * earns its place where the device key sits behind a boundary worth crossing
14
- * once — a hardware key, an extension — so a per-call signature becomes a
15
- * per-session one. A CLI, a server job, or a browser holding its device secret
16
- * in memory gains nothing from it, and `login()` additionally needs the node's
17
- * signing key learned out of band. This path needs neither.
18
- *
19
- * Measured against a node built from core `aecba573b`: a two-link proof is
20
- * served `200` on `/admin-api/namespaces`, `/contexts`, `/contexts/{id}`,
21
- * `/groups/{id}`, `/groups/{id}/contexts`, `/contexts/{id}/identities` and
22
- * `/storage`, and the listings are **filtered** — an account in one of a node's
23
- * two namespaces is shown one. `POST /admin-api/namespaces` is refused `403`, so
24
- * this authenticates a caller without granting operator actions.
25
- */
26
- import { concat, domainHash, fromHex, hex, importSigningKey, u32le, u64le, } from '../crypto/internal.js';
27
- import { requestBodyHash, SIGN_DOMAIN } from '../request/request-sig.js';
28
- const utf8 = (text) => new TextEncoder().encode(text);
29
- /** The header a signed request carries. */
30
- export const REQUEST_PROOF_HEADER = 'X-Calimero-Proof';
31
- /** Seconds a signature stays honourable. Short, because it bounds replay. */
32
- const DEFAULT_VALID_FOR = 300;
33
- /**
34
- * Sign one request and return the `X-Calimero-Proof` value, hex.
35
- *
36
- * The encoding is borsh, and it is assembled rather than re-derived: the
37
- * account proof is the **first** field of `CallerProof`, so the credential hex
38
- * a caller already holds is exactly its own prefix. Concatenating avoids a
39
- * second implementation of a structure the node deserializes — the class of bug
40
- * that surfaces far away, as a node refusing a credential nothing can explain.
41
- *
42
- * CallerProof = <account_proof bytes> || 0x00 (no session) || RequestSig
43
- * RequestSig = u32le(len method) || method
44
- * || u32le(len path) || path (with its query)
45
- * || body_hash[32]
46
- * || u64le(issued_at) || u64le(expires_at)
47
- * || signature[64]
48
- */
49
- export async function signRequestProof(input) {
50
- const { method, path, credential } = input;
51
- if (!method)
52
- throw new Error('signRequestProof needs the method the request will use');
53
- if (!path.startsWith('/')) {
54
- throw new Error(`signRequestProof needs a path, got ${JSON.stringify(path)}`);
55
- }
56
- const bodyHash = await requestBodyHash(input.body);
57
- const issuedAt = Math.floor(input.now ?? Date.now() / 1000);
58
- const expiresAt = issuedAt + (input.validForSeconds ?? DEFAULT_VALID_FOR);
59
- const payload = await domainHash(SIGN_DOMAIN, [
60
- utf8(method),
61
- utf8(path),
62
- bodyHash,
63
- u64le(issuedAt),
64
- u64le(expiresAt),
65
- ]);
66
- const signature = await signPayload(payload, input);
67
- if (signature.length !== 64) {
68
- throw new Error(`the signer returned ${signature.length} bytes, not a 64-byte signature`);
69
- }
70
- const requestSig = concat(u32le(utf8(method).length), utf8(method), u32le(utf8(path).length), utf8(path), bodyHash, u64le(issuedAt), u64le(expiresAt), signature);
71
- // The credential IS the account-proof prefix. Length is not asserted: it varies
72
- // with the handoff chain, and a wrong one fails as a refused proof rather than
73
- // silently as something else.
74
- const accountProof = hexToBytes(credential, 'credential');
75
- // 0x00: borsh's `None`. The device key signed the request, so there is no
76
- // session link — a different encoding, not a shorter one.
77
- return hex(concat(accountProof, new Uint8Array([0]), requestSig));
78
- }
79
- async function signPayload(payload, input) {
80
- if (input.signer)
81
- return input.signer.sign(payload);
82
- if (!input.deviceSecret) {
83
- throw new Error('signRequestProof needs either `deviceSecret` or `signer`');
84
- }
85
- const key = await importSigningKey(input.deviceSecret);
86
- return new Uint8Array(await crypto.subtle.sign({ name: 'Ed25519' }, key, payload));
87
- }
88
- /** Hex of unknown length — `fromHex` requires a fixed byte count. */
89
- function hexToBytes(value, label) {
90
- const clean = value.trim();
91
- if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length % 2 !== 0) {
92
- throw new Error(`${label} is not an even-length hex string`);
93
- }
94
- return fromHex(clean, label, clean.length / 2);
95
- }
@@ -1,64 +0,0 @@
1
- import type { HttpClient } from '../http-client/index.js';
2
- /** Result of the owner-driven `migrate_my_entries` convert (counts are u32). */
3
- export interface MigrateMyEntriesSummary {
4
- converted: number;
5
- remaining: number;
6
- }
7
- export interface ExecuteParams {
8
- contextId: string;
9
- method: string;
10
- argsJson?: Record<string, unknown>;
11
- /** @deprecated No longer used by the server. Ignored if provided. */
12
- executorPublicKey?: string;
13
- }
14
- export declare class RpcError extends Error {
15
- code: number;
16
- type?: string;
17
- data?: unknown;
18
- constructor(code: number, message: string, data?: unknown, type?: string);
19
- }
20
- /**
21
- * POST a JSON-RPC call and unwrap `result`, converting an `error` payload into
22
- * an `RpcError`. The node returns `{ type, data }` (not always `{ code,
23
- * message }`), so this is the one place that normalization lives — every
24
- * JSON-RPC caller in this SDK shares it rather than re-deriving a bare
25
- * `Error(message)` and dropping the typed detail.
26
- */
27
- export declare function jsonRpcCall<T>(httpClient: HttpClient, method: string, params: unknown): Promise<T>;
28
- export declare class RpcClient {
29
- private httpClient;
30
- /**
31
- * Which write path this is. Present so `RpcClient` satisfies
32
- * `ExecuteTransport` as-is — the transport abstraction adopted *this* class's
33
- * `execute` shape as the canonical one, so the node side is the interface
34
- * rather than an adapter onto it.
35
- */
36
- readonly kind: "node";
37
- /** A node serves `/sse` and `/ws` to the credential it issued. */
38
- readonly canSubscribe = true;
39
- constructor(opts: {
40
- httpClient: HttpClient;
41
- });
42
- execute<T = unknown>(params: ExecuteParams): Promise<T>;
43
- /**
44
- * The same call, in the shape the transport abstraction shares with the
45
- * relay.
46
- *
47
- * `rootHash` is left absent rather than synthesized: JSON-RPC `execute` does
48
- * not report the context's scope root, and inventing a value — or a `''` —
49
- * would make "the node does not tell us" indistinguishable from an answer.
50
- */
51
- executeWithMetadata<T = unknown>(params: ExecuteParams): Promise<{
52
- returns: T;
53
- transport: 'node';
54
- }>;
55
- /**
56
- * One-tap owner-driven convert: re-signs the caller's identity-gated entries
57
- * to the current schema. The export converts all of the caller's
58
- * below-target entries in a single sweep, so this issues one call and returns
59
- * the resulting summary — it does not loop.
60
- */
61
- migrateMyEntries(contextId: string): Promise<MigrateMyEntriesSummary>;
62
- /** Read-only count of the caller's entries still below the target schema. */
63
- countMyPending(contextId: string): Promise<number>;
64
- }
package/dist/rpc/index.js DELETED
@@ -1,78 +0,0 @@
1
- export class RpcError extends Error {
2
- constructor(code, message, data, type) {
3
- super(message);
4
- this.name = 'RpcError';
5
- this.code = code;
6
- this.data = data;
7
- this.type = type;
8
- }
9
- }
10
- /**
11
- * POST a JSON-RPC call and unwrap `result`, converting an `error` payload into
12
- * an `RpcError`. The node returns `{ type, data }` (not always `{ code,
13
- * message }`), so this is the one place that normalization lives — every
14
- * JSON-RPC caller in this SDK shares it rather than re-deriving a bare
15
- * `Error(message)` and dropping the typed detail.
16
- */
17
- export async function jsonRpcCall(httpClient, method, params) {
18
- const response = await httpClient.post('/jsonrpc', {
19
- jsonrpc: '2.0',
20
- id: 1,
21
- method,
22
- params,
23
- });
24
- if (response.error) {
25
- const err = response.error;
26
- throw new RpcError(err.code ?? -1, err.message ?? err.type ?? 'RPC error', err.data, err.type);
27
- }
28
- return response.result;
29
- }
30
- export class RpcClient {
31
- constructor(opts) {
32
- /**
33
- * Which write path this is. Present so `RpcClient` satisfies
34
- * `ExecuteTransport` as-is — the transport abstraction adopted *this* class's
35
- * `execute` shape as the canonical one, so the node side is the interface
36
- * rather than an adapter onto it.
37
- */
38
- this.kind = 'node';
39
- /** A node serves `/sse` and `/ws` to the credential it issued. */
40
- this.canSubscribe = true;
41
- this.httpClient = opts.httpClient;
42
- }
43
- async execute(params) {
44
- const result = await jsonRpcCall(this.httpClient, 'execute', {
45
- contextId: params.contextId,
46
- method: params.method,
47
- argsJson: params.argsJson ?? {},
48
- });
49
- if (result && 'output' in result) {
50
- return result.output;
51
- }
52
- return result;
53
- }
54
- /**
55
- * The same call, in the shape the transport abstraction shares with the
56
- * relay.
57
- *
58
- * `rootHash` is left absent rather than synthesized: JSON-RPC `execute` does
59
- * not report the context's scope root, and inventing a value — or a `''` —
60
- * would make "the node does not tell us" indistinguishable from an answer.
61
- */
62
- async executeWithMetadata(params) {
63
- return { returns: await this.execute(params), transport: 'node' };
64
- }
65
- /**
66
- * One-tap owner-driven convert: re-signs the caller's identity-gated entries
67
- * to the current schema. The export converts all of the caller's
68
- * below-target entries in a single sweep, so this issues one call and returns
69
- * the resulting summary — it does not loop.
70
- */
71
- async migrateMyEntries(contextId) {
72
- return this.execute({ contextId, method: 'migrate_my_entries' });
73
- }
74
- /** Read-only count of the caller's entries still below the target schema. */
75
- async countMyPending(contextId) {
76
- return this.execute({ contextId, method: 'count_my_pending' });
77
- }
78
- }
@@ -1,2 +0,0 @@
1
- export { signerFromSecret, signerFromCryptoKey } from './signer.js';
2
- export type { Signer } from './signer.js';
@@ -1,3 +0,0 @@
1
- // Who signs — a key's capability, separated from its material, so a
2
- // non-extractable `CryptoKey` can be used wherever a hex secret could.
3
- export { signerFromSecret, signerFromCryptoKey } from './signer.js';
@@ -1,92 +0,0 @@
1
- /**
2
- * Who signs, separated from what is signed.
3
- *
4
- * Every signing entry point in this package used to take 32 hex bytes. That is
5
- * fine for a CLI or a server holding a key file, and it is the one thing a
6
- * browser must not do: a secret that exists as a string can be read by anything
7
- * running on the origin, and a stolen account key is the account — permanently,
8
- * because unlike a device key there is nothing above it to revoke it with.
9
- *
10
- * WebCrypto can hold an Ed25519 key that **signs but cannot be exported**
11
- * (`generateKey(..., extractable: false, ...)`). Such a key has no hex
12
- * representation to pass, so it could not be used with this package at all —
13
- * which pushed consumers into reimplementing core's wire formats around their
14
- * own key. That is the failure `crates/account/src/tests/sdk_credential_fixture.rs`
15
- * exists to document: a second implementation of a byte contract agreed with
16
- * itself for a full release while core rejected every credential it produced.
17
- *
18
- * So the interface is the key's *capability*, not its material:
19
- *
20
- * ```ts
21
- * const signer = await signerFromSecret(deviceSecret); // as before
22
- * const signer = await signerFromCryptoKey(privateKey, pubKey); // unexportable
23
- * await signWarrant({ ...terms, signer });
24
- * ```
25
- *
26
- * `deviceSecret` and `rootSecret` keep working everywhere they did. They are
27
- * now one way of producing a `Signer` rather than the only way, and callers
28
- * that pass a secret get a signer built for them.
29
- */
30
- /**
31
- * Something that can produce Ed25519 signatures and name the key they verify
32
- * against.
33
- *
34
- * Deliberately the smallest surface that works: a signature and the public key
35
- * it belongs to. Anything larger would start describing *how* the key is held,
36
- * which is the detail this exists to hide — a hex seed, a non-extractable
37
- * `CryptoKey`, a hardware token behind an async prompt, and a remote signing
38
- * service all satisfy it.
39
- */
40
- export interface Signer {
41
- /**
42
- * The public half, 64 hex characters.
43
- *
44
- * Carried rather than derived on demand because a non-extractable private key
45
- * cannot produce it: the public half has to be captured when the key is
46
- * generated. A signer that could not name its key would force every caller to
47
- * pass the public key alongside it, and the two could then disagree.
48
- */
49
- readonly publicKey: string;
50
- /**
51
- * Sign `payload` — the exact bytes, with no hashing or framing added.
52
- *
53
- * Every caller in this package passes a 32-byte domain-separated digest it
54
- * has already computed, and an implementation that hashed again would produce
55
- * signatures that verify nowhere.
56
- */
57
- sign(payload: Uint8Array): Promise<Uint8Array>;
58
- }
59
- /**
60
- * A signer over a raw 32-byte Ed25519 seed, hex.
61
- *
62
- * The public half is derived from the seed rather than accepted alongside it,
63
- * for the reason core does the same: a caller able to name a key it does not
64
- * hold could produce a statement it cannot sign, and the field would stop
65
- * meaning "who authorised this".
66
- *
67
- * @param secret the seed, 64 hex characters
68
- * @param label what to call it in an error — `deviceSecret`, `rootSecret`
69
- */
70
- export declare function signerFromSecret(secret: string, label?: string): Promise<Signer>;
71
- /**
72
- * A signer over a `CryptoKey` this process may use and cannot read.
73
- *
74
- * The point of the whole interface. Generate the key with
75
- * `generateKey({ name: 'Ed25519' }, false, ['sign', 'verify'])`, keep the pair's
76
- * private half (structured-clone it into IndexedDB if it must survive a reload),
77
- * and pass it here with its public half.
78
- *
79
- * @param privateKey the signing key — extractable or not, it is never exported
80
- * @param publicKey its public half: 64 hex, or the pair's `publicKey` (which
81
- * WebCrypto leaves exportable even when the private half is not)
82
- */
83
- export declare function signerFromCryptoKey(privateKey: CryptoKey, publicKey: string | CryptoKey): Promise<Signer>;
84
- /**
85
- * Resolve the signer an input describes, accepting either form.
86
- *
87
- * Internal. Every signing entry point takes an optional secret *and* an
88
- * optional signer, and exactly one must be present — passing both is refused
89
- * rather than silently preferring one, because the two could name different
90
- * keys and the caller would have no way to tell which signed.
91
- */
92
- export declare function resolveSigner(secret: string | undefined, signer: Signer | undefined, label: string): Promise<Signer>;
@@ -1,147 +0,0 @@
1
- /**
2
- * Who signs, separated from what is signed.
3
- *
4
- * Every signing entry point in this package used to take 32 hex bytes. That is
5
- * fine for a CLI or a server holding a key file, and it is the one thing a
6
- * browser must not do: a secret that exists as a string can be read by anything
7
- * running on the origin, and a stolen account key is the account — permanently,
8
- * because unlike a device key there is nothing above it to revoke it with.
9
- *
10
- * WebCrypto can hold an Ed25519 key that **signs but cannot be exported**
11
- * (`generateKey(..., extractable: false, ...)`). Such a key has no hex
12
- * representation to pass, so it could not be used with this package at all —
13
- * which pushed consumers into reimplementing core's wire formats around their
14
- * own key. That is the failure `crates/account/src/tests/sdk_credential_fixture.rs`
15
- * exists to document: a second implementation of a byte contract agreed with
16
- * itself for a full release while core rejected every credential it produced.
17
- *
18
- * So the interface is the key's *capability*, not its material:
19
- *
20
- * ```ts
21
- * const signer = await signerFromSecret(deviceSecret); // as before
22
- * const signer = await signerFromCryptoKey(privateKey, pubKey); // unexportable
23
- * await signWarrant({ ...terms, signer });
24
- * ```
25
- *
26
- * `deviceSecret` and `rootSecret` keep working everywhere they did. They are
27
- * now one way of producing a `Signer` rather than the only way, and callers
28
- * that pass a secret get a signer built for them.
29
- */
30
- import { concat, fromHex, hex } from '../crypto/internal.js';
31
- /** Ed25519 PKCS#8 prefix, so a raw 32-byte seed can be imported by WebCrypto. */
32
- const PKCS8_ED25519_PREFIX = new Uint8Array([
33
- 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04,
34
- 0x22, 0x04, 0x20,
35
- ]);
36
- /** Turn WebCrypto's bare `NotSupportedError` into something actionable. */
37
- function unsupported(cause) {
38
- // The original is folded into the message rather than passed as `cause`:
39
- // this package targets ES2020, where `Error` has no `cause` option.
40
- const detail = cause instanceof Error ? `: ${cause.message}` : '';
41
- return new Error('this runtime has no WebCrypto Ed25519, so nothing can be signed here. ' +
42
- 'Node 18.4+, Safari 17+, Firefox 129+ and Chrome 137+ have it' +
43
- detail);
44
- }
45
- /**
46
- * A signer over a raw 32-byte Ed25519 seed, hex.
47
- *
48
- * The public half is derived from the seed rather than accepted alongside it,
49
- * for the reason core does the same: a caller able to name a key it does not
50
- * hold could produce a statement it cannot sign, and the field would stop
51
- * meaning "who authorised this".
52
- *
53
- * @param secret the seed, 64 hex characters
54
- * @param label what to call it in an error — `deviceSecret`, `rootSecret`
55
- */
56
- export async function signerFromSecret(secret, label = 'secret') {
57
- const seed = fromHex(secret, label, 32);
58
- const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
59
- let privateKey;
60
- let publicKey;
61
- try {
62
- // Imported twice on purpose: `extractable: true` is needed to read the
63
- // public half out of a JWK, and the signing key is then re-imported
64
- // unexportable so a `Signer` built from a secret is no more powerful than
65
- // one built from a `CryptoKey`.
66
- const exportable = await crypto.subtle.importKey('pkcs8', pkcs8, { name: 'Ed25519' }, true, ['sign']);
67
- const jwk = (await crypto.subtle.exportKey('jwk', exportable));
68
- publicKey = hex(Uint8Array.from(atob(jwk.x.replace(/-/g, '+').replace(/_/g, '/')), (c) => c.charCodeAt(0)));
69
- privateKey = await crypto.subtle.importKey('pkcs8', pkcs8, { name: 'Ed25519' }, false, ['sign']);
70
- }
71
- catch (cause) {
72
- throw unsupported(cause);
73
- }
74
- return fromKeyAndPublic(privateKey, publicKey);
75
- }
76
- /**
77
- * A signer over a `CryptoKey` this process may use and cannot read.
78
- *
79
- * The point of the whole interface. Generate the key with
80
- * `generateKey({ name: 'Ed25519' }, false, ['sign', 'verify'])`, keep the pair's
81
- * private half (structured-clone it into IndexedDB if it must survive a reload),
82
- * and pass it here with its public half.
83
- *
84
- * @param privateKey the signing key — extractable or not, it is never exported
85
- * @param publicKey its public half: 64 hex, or the pair's `publicKey` (which
86
- * WebCrypto leaves exportable even when the private half is not)
87
- */
88
- export async function signerFromCryptoKey(privateKey, publicKey) {
89
- if (privateKey.type !== 'private') {
90
- throw new Error(`signerFromCryptoKey needs the private half of the pair, got a ${privateKey.type} key`);
91
- }
92
- if (!privateKey.usages.includes('sign')) {
93
- throw new Error("this CryptoKey cannot sign: generate it with usages ['sign', 'verify']");
94
- }
95
- let publicHex;
96
- if (typeof publicKey === 'string') {
97
- // Validated rather than trusted: a wrong public key produces signatures
98
- // that verify nowhere, and the refusal arrives at a relay as a 403 —
99
- // nowhere near this call.
100
- fromHex(publicKey, 'publicKey', 32);
101
- publicHex = publicKey.trim().toLowerCase();
102
- }
103
- else {
104
- try {
105
- publicHex = hex(new Uint8Array(await crypto.subtle.exportKey('raw', publicKey)));
106
- }
107
- catch (cause) {
108
- const detail = cause instanceof Error ? `: ${cause.message}` : '';
109
- throw new Error('could not read the public half of this key pair; pass the public key ' +
110
- 'as 64 hex instead' +
111
- detail);
112
- }
113
- }
114
- return fromKeyAndPublic(privateKey, publicHex);
115
- }
116
- /** The shared tail of both constructors. */
117
- function fromKeyAndPublic(privateKey, publicKey) {
118
- return {
119
- publicKey,
120
- async sign(payload) {
121
- try {
122
- return new Uint8Array(await crypto.subtle.sign({ name: 'Ed25519' }, privateKey, payload));
123
- }
124
- catch (cause) {
125
- throw unsupported(cause);
126
- }
127
- },
128
- };
129
- }
130
- /**
131
- * Resolve the signer an input describes, accepting either form.
132
- *
133
- * Internal. Every signing entry point takes an optional secret *and* an
134
- * optional signer, and exactly one must be present — passing both is refused
135
- * rather than silently preferring one, because the two could name different
136
- * keys and the caller would have no way to tell which signed.
137
- */
138
- export async function resolveSigner(secret, signer, label) {
139
- if (signer && secret !== undefined) {
140
- throw new Error(`pass either ${label} or signer, not both — they may name different keys`);
141
- }
142
- if (signer)
143
- return signer;
144
- if (secret !== undefined)
145
- return signerFromSecret(secret, label);
146
- throw new Error(`${label} or signer is required`);
147
- }
@@ -1,27 +0,0 @@
1
- import type { TokenData } from '../mero-js.js';
2
- export interface TokenStore {
3
- getTokens(): TokenData | null;
4
- setTokens(data: TokenData): void;
5
- clear(): void;
6
- /**
7
- * Run `fn` holding a lock that every process sharing this store respects.
8
- * MeroJs holds it around a token refresh, so two processes on one store never
9
- * spend the same single-use refresh token. Web Locks cover tabs of one
10
- * browser; a store shared across processes (a file read by several CLIs, say)
11
- * needs its own lock, and implements this. Takes precedence over Web Locks.
12
- */
13
- withLock?<T>(fn: () => Promise<T>): Promise<T>;
14
- }
15
- export declare class MemoryTokenStore implements TokenStore {
16
- private tokens;
17
- getTokens(): TokenData | null;
18
- setTokens(data: TokenData): void;
19
- clear(): void;
20
- }
21
- export declare class LocalStorageTokenStore implements TokenStore {
22
- private readonly key;
23
- constructor(key?: string);
24
- getTokens(): TokenData | null;
25
- setTokens(data: TokenData): void;
26
- clear(): void;
27
- }
@@ -1,62 +0,0 @@
1
- export class MemoryTokenStore {
2
- constructor() {
3
- this.tokens = null;
4
- }
5
- getTokens() {
6
- return this.tokens;
7
- }
8
- setTokens(data) {
9
- this.tokens = data;
10
- }
11
- clear() {
12
- this.tokens = null;
13
- }
14
- }
15
- const STORAGE_KEY = 'mero-tokens';
16
- export class LocalStorageTokenStore {
17
- constructor(key = STORAGE_KEY) {
18
- this.key = key;
19
- }
20
- getTokens() {
21
- try {
22
- if (typeof localStorage === 'undefined')
23
- return null;
24
- const raw = localStorage.getItem(this.key);
25
- if (!raw)
26
- return null;
27
- const parsed = JSON.parse(raw);
28
- if (parsed && parsed.access_token && parsed.refresh_token) {
29
- return {
30
- access_token: parsed.access_token,
31
- refresh_token: parsed.refresh_token,
32
- expires_at: typeof parsed.expires_at === 'number' ? parsed.expires_at : Date.now() + 3600000,
33
- ...(typeof parsed.node_origin === 'string' && { node_origin: parsed.node_origin }),
34
- };
35
- }
36
- return null;
37
- }
38
- catch {
39
- return null;
40
- }
41
- }
42
- setTokens(data) {
43
- try {
44
- if (typeof localStorage === 'undefined')
45
- return;
46
- localStorage.setItem(this.key, JSON.stringify(data));
47
- }
48
- catch {
49
- // Storage unavailable
50
- }
51
- }
52
- clear() {
53
- try {
54
- if (typeof localStorage === 'undefined')
55
- return;
56
- localStorage.removeItem(this.key);
57
- }
58
- catch {
59
- // Storage unavailable
60
- }
61
- }
62
- }
@@ -1,6 +0,0 @@
1
- export { MeroClient, createMeroClient } from './mero-client.js';
2
- export type { MeroClientConfig, NodeTransportConfig, RelayTransportConfig, } from './mero-client.js';
3
- export { RelayTransport } from './relay-transport.js';
4
- export { RelayObserver, defaultAudience } from './relay-observer.js';
5
- export type { RelayObserveConfig } from './relay-observer.js';
6
- export type { ExecuteTransport, ExecuteResult, TransportKind } from './types.js';
@@ -1,3 +0,0 @@
1
- export { MeroClient, createMeroClient } from './mero-client.js';
2
- export { RelayTransport } from './relay-transport.js';
3
- export { RelayObserver, defaultAudience } from './relay-observer.js';