@calimero-network/mero-js 19.26.0 → 19.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cloud/connect.d.ts +19 -0
- package/dist/cloud/connect.js +17 -1
- package/dist/index.browser.mjs +2 -2
- package/dist/index.browser.mjs.map +4 -4
- package/dist/index.cjs +121 -86
- package/dist/index.cjs.map +3 -3
- package/dist/index.mjs +121 -86
- package/dist/index.mjs.map +3 -3
- package/dist/sealed/index.d.ts +2 -2
- package/dist/sealed/index.js +1 -1
- package/dist/sealed/sealed.d.ts +32 -0
- package/dist/sealed/sealed.js +49 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -54,6 +54,7 @@ __export(index_exports, {
|
|
|
54
54
|
connectCloud: () => connectCloud,
|
|
55
55
|
createAdminApiClient: () => createAdminApiClient,
|
|
56
56
|
createAdminApiClientFromHttpClient: () => createAdminApiClientFromHttpClient,
|
|
57
|
+
createAttestedSealedFetch: () => createAttestedSealedFetch,
|
|
57
58
|
createAuthApiClient: () => createAuthApiClient,
|
|
58
59
|
createAuthApiClientFromHttpClient: () => createAuthApiClientFromHttpClient,
|
|
59
60
|
createBrowserAdminApiClient: () => createBrowserAdminApiClient,
|
|
@@ -6574,92 +6575,6 @@ function createLocalStorageNonceSource(key, storage) {
|
|
|
6574
6575
|
};
|
|
6575
6576
|
}
|
|
6576
6577
|
|
|
6577
|
-
// src/cloud/connect.ts
|
|
6578
|
-
async function connectCloud(options) {
|
|
6579
|
-
if (!options.sessionToken && !options.googleIdToken) {
|
|
6580
|
-
throw new Error("connectCloud needs either a sessionToken or a googleIdToken");
|
|
6581
|
-
}
|
|
6582
|
-
const cloud = new CloudClient({
|
|
6583
|
-
cloudBaseUrl: options.cloudBaseUrl,
|
|
6584
|
-
sessionToken: options.sessionToken,
|
|
6585
|
-
onSession: options.onSession,
|
|
6586
|
-
fetch: options.fetch,
|
|
6587
|
-
timeoutMs: options.timeoutMs
|
|
6588
|
-
});
|
|
6589
|
-
if (!options.sessionToken && options.googleIdToken) {
|
|
6590
|
-
await cloud.signInWithGoogle(options.googleIdToken);
|
|
6591
|
-
}
|
|
6592
|
-
const namespaces = await cloud.getMyNamespaces();
|
|
6593
|
-
const namespace = pickNamespace(namespaces, options.namespaceId);
|
|
6594
|
-
const relays = await cloud.getNamespaceRelays(namespace.namespaceId);
|
|
6595
|
-
const relayInfo = relays.find((r) => r.authorshipReady && r.relayUrl !== null && r.executorAccount !== null) ?? null;
|
|
6596
|
-
if (!relayInfo) {
|
|
6597
|
-
throw new Error(explainNoRelay(namespace, relays));
|
|
6598
|
-
}
|
|
6599
|
-
const relay = new RelayClient({
|
|
6600
|
-
// Checked by `findExecutingRelay`, which is the only thing that returns a
|
|
6601
|
-
// relay — narrowed here rather than re-validated, so the invariant lives in
|
|
6602
|
-
// one place.
|
|
6603
|
-
relayUrl: relayInfo.relayUrl,
|
|
6604
|
-
executorAccount: relayInfo.executorAccount,
|
|
6605
|
-
authorAccount: options.authorAccount,
|
|
6606
|
-
authorProof: options.authorProof,
|
|
6607
|
-
deviceSecret: options.deviceSecret,
|
|
6608
|
-
nonces: options.nonces ?? await defaultNonceSource(options.deviceSecret),
|
|
6609
|
-
ttlSeconds: options.ttlSeconds,
|
|
6610
|
-
fetch: options.fetch,
|
|
6611
|
-
timeoutMs: options.timeoutMs
|
|
6612
|
-
});
|
|
6613
|
-
return {
|
|
6614
|
-
cloud,
|
|
6615
|
-
relay,
|
|
6616
|
-
namespaceId: namespace.namespaceId,
|
|
6617
|
-
namespace,
|
|
6618
|
-
relayInfo,
|
|
6619
|
-
execute: (contextId, method, argsJson) => relay.execute(contextId, method, argsJson)
|
|
6620
|
-
};
|
|
6621
|
-
}
|
|
6622
|
-
function pickNamespace(namespaces, requested) {
|
|
6623
|
-
if (requested) {
|
|
6624
|
-
const found = namespaces.find((n) => n.namespaceId === requested);
|
|
6625
|
-
if (!found) {
|
|
6626
|
-
throw new Error(
|
|
6627
|
-
`this account owns no namespace ${requested}; it owns: ${namespaces.map((n) => n.namespaceId).join(", ") || "(none)"}`
|
|
6628
|
-
);
|
|
6629
|
-
}
|
|
6630
|
-
return found;
|
|
6631
|
-
}
|
|
6632
|
-
if (namespaces.length === 0) {
|
|
6633
|
-
throw new Error(
|
|
6634
|
-
"this account owns no namespaces in the cloud: claim one first, then enable HA on it so a relay is assigned"
|
|
6635
|
-
);
|
|
6636
|
-
}
|
|
6637
|
-
if (namespaces.length > 1) {
|
|
6638
|
-
throw new Error(
|
|
6639
|
-
`this account owns ${namespaces.length} namespaces \u2014 pass namespaceId to say which: ${namespaces.map((n) => n.namespaceId).join(", ")}`
|
|
6640
|
-
);
|
|
6641
|
-
}
|
|
6642
|
-
return namespaces[0];
|
|
6643
|
-
}
|
|
6644
|
-
function explainNoRelay(namespace, relays) {
|
|
6645
|
-
if (relays.length === 0) {
|
|
6646
|
-
return namespace.haStatus === "enabled" ? `namespace ${namespace.namespaceId} has HA enabled but no relay has been assigned to it yet \u2014 retry shortly` : `namespace ${namespace.namespaceId} has no relays: enable HA on it in the cloud to have an attested node assigned`;
|
|
6647
|
-
}
|
|
6648
|
-
const waitingOnGrant = relays.filter((r) => !r.authorshipReady);
|
|
6649
|
-
if (waitingOnGrant.length === relays.length) {
|
|
6650
|
-
return `every relay on namespace ${namespace.namespaceId} is waiting for the authorship grant: an admin of the namespace must grant CAN_AUTHOR_ON_BEHALF to ${waitingOnGrant.map((r) => r.executorAccount ?? r.peerId).join(", ") || "the relay account"} \u2014 with AdminClient.grantAuthorship(namespaceId, account) for these relays, and AdminClient.openToDelegatedExecution(namespaceId) so relays assigned later need no further op`;
|
|
6651
|
-
}
|
|
6652
|
-
return `namespace ${namespace.namespaceId} has ${relays.length} relay(s), but none reported both a URL and an executor account yet \u2014 retry shortly`;
|
|
6653
|
-
}
|
|
6654
|
-
async function defaultNonceSource(deviceSecret) {
|
|
6655
|
-
const key = `mero-js:warrant-nonce:${hex(await derivePublicKey(deviceSecret))}`;
|
|
6656
|
-
try {
|
|
6657
|
-
return createLocalStorageNonceSource(key);
|
|
6658
|
-
} catch {
|
|
6659
|
-
return createMemoryNonceSource(1);
|
|
6660
|
-
}
|
|
6661
|
-
}
|
|
6662
|
-
|
|
6663
6578
|
// src/sealed/noise.ts
|
|
6664
6579
|
var PROTOCOL_NAME = "Noise_NK_25519_AESGCM_SHA256";
|
|
6665
6580
|
var DH_LEN = 32;
|
|
@@ -6879,6 +6794,33 @@ async function fetchAttestedTransportKey(admin, verify, options = {}) {
|
|
|
6879
6794
|
}
|
|
6880
6795
|
return transportKey;
|
|
6881
6796
|
}
|
|
6797
|
+
function createAttestedSealedFetch(options) {
|
|
6798
|
+
const baseFetch = options.fetch ?? ((input, init) => fetch(input, init));
|
|
6799
|
+
const attestUrl = `${options.baseUrl.replace(/\/+$/, "")}/admin-api/tee/attest`;
|
|
6800
|
+
const admin = {
|
|
6801
|
+
async teeAttest(request) {
|
|
6802
|
+
const response = await baseFetch(attestUrl, {
|
|
6803
|
+
method: "POST",
|
|
6804
|
+
headers: { "content-type": "application/json" },
|
|
6805
|
+
body: JSON.stringify(request)
|
|
6806
|
+
});
|
|
6807
|
+
if (!response.ok) {
|
|
6808
|
+
throw new Error(`The node refused to attest: HTTP ${response.status}`);
|
|
6809
|
+
}
|
|
6810
|
+
const body = await response.json();
|
|
6811
|
+
if (!body.data) throw new Error("The attestation response is malformed");
|
|
6812
|
+
return body.data;
|
|
6813
|
+
}
|
|
6814
|
+
};
|
|
6815
|
+
return createSealedFetch({
|
|
6816
|
+
baseUrl: options.baseUrl,
|
|
6817
|
+
transportPublicKey: () => fetchAttestedTransportKey(admin, options.verify, {
|
|
6818
|
+
applicationId: options.applicationId,
|
|
6819
|
+
applicationHash: options.applicationHash
|
|
6820
|
+
}),
|
|
6821
|
+
fetch: baseFetch
|
|
6822
|
+
});
|
|
6823
|
+
}
|
|
6882
6824
|
function createSealedFetch(options) {
|
|
6883
6825
|
const base = new URL(options.baseUrl);
|
|
6884
6826
|
const basePath = base.pathname.replace(/\/+$/, "");
|
|
@@ -6886,6 +6828,7 @@ function createSealedFetch(options) {
|
|
|
6886
6828
|
const baseFetch = options.fetch ?? ((input, init) => fetch(input, init));
|
|
6887
6829
|
const attest = typeof options.transportPublicKey === "function" ? options.transportPublicKey : void 0;
|
|
6888
6830
|
let transportKey = attest ? Promise.resolve().then(attest) : Promise.resolve(options.transportPublicKey);
|
|
6831
|
+
transportKey.catch(() => void 0);
|
|
6889
6832
|
let session;
|
|
6890
6833
|
const openSession = async () => {
|
|
6891
6834
|
try {
|
|
@@ -7136,6 +7079,98 @@ async function envelopeRefusal(response) {
|
|
|
7136
7079
|
return new SealedTransportError(response.status, code, message, retryAfterMs);
|
|
7137
7080
|
}
|
|
7138
7081
|
|
|
7082
|
+
// src/cloud/connect.ts
|
|
7083
|
+
async function connectCloud(options) {
|
|
7084
|
+
if (!options.sessionToken && !options.googleIdToken) {
|
|
7085
|
+
throw new Error("connectCloud needs either a sessionToken or a googleIdToken");
|
|
7086
|
+
}
|
|
7087
|
+
const cloud = new CloudClient({
|
|
7088
|
+
cloudBaseUrl: options.cloudBaseUrl,
|
|
7089
|
+
sessionToken: options.sessionToken,
|
|
7090
|
+
onSession: options.onSession,
|
|
7091
|
+
fetch: options.fetch,
|
|
7092
|
+
timeoutMs: options.timeoutMs
|
|
7093
|
+
});
|
|
7094
|
+
if (!options.sessionToken && options.googleIdToken) {
|
|
7095
|
+
await cloud.signInWithGoogle(options.googleIdToken);
|
|
7096
|
+
}
|
|
7097
|
+
const namespaces = await cloud.getMyNamespaces();
|
|
7098
|
+
const namespace = pickNamespace(namespaces, options.namespaceId);
|
|
7099
|
+
const relays = await cloud.getNamespaceRelays(namespace.namespaceId);
|
|
7100
|
+
const relayInfo = relays.find((r) => r.authorshipReady && r.relayUrl !== null && r.executorAccount !== null) ?? null;
|
|
7101
|
+
if (!relayInfo) {
|
|
7102
|
+
throw new Error(explainNoRelay(namespace, relays));
|
|
7103
|
+
}
|
|
7104
|
+
const relay = new RelayClient({
|
|
7105
|
+
// Checked by `findExecutingRelay`, which is the only thing that returns a
|
|
7106
|
+
// relay — narrowed here rather than re-validated, so the invariant lives in
|
|
7107
|
+
// one place.
|
|
7108
|
+
relayUrl: relayInfo.relayUrl,
|
|
7109
|
+
executorAccount: relayInfo.executorAccount,
|
|
7110
|
+
authorAccount: options.authorAccount,
|
|
7111
|
+
authorProof: options.authorProof,
|
|
7112
|
+
deviceSecret: options.deviceSecret,
|
|
7113
|
+
nonces: options.nonces ?? await defaultNonceSource(options.deviceSecret),
|
|
7114
|
+
ttlSeconds: options.ttlSeconds,
|
|
7115
|
+
// Only the relay is sealed: a sealed fetch is bound to one node and refuses
|
|
7116
|
+
// any other URL, and the cloud calls above carry no intent.
|
|
7117
|
+
fetch: options.seal ? createAttestedSealedFetch({
|
|
7118
|
+
baseUrl: relayInfo.relayUrl,
|
|
7119
|
+
verify: options.seal,
|
|
7120
|
+
fetch: options.fetch
|
|
7121
|
+
}) : options.fetch,
|
|
7122
|
+
timeoutMs: options.timeoutMs
|
|
7123
|
+
});
|
|
7124
|
+
return {
|
|
7125
|
+
cloud,
|
|
7126
|
+
relay,
|
|
7127
|
+
namespaceId: namespace.namespaceId,
|
|
7128
|
+
namespace,
|
|
7129
|
+
relayInfo,
|
|
7130
|
+
execute: (contextId, method, argsJson) => relay.execute(contextId, method, argsJson)
|
|
7131
|
+
};
|
|
7132
|
+
}
|
|
7133
|
+
function pickNamespace(namespaces, requested) {
|
|
7134
|
+
if (requested) {
|
|
7135
|
+
const found = namespaces.find((n) => n.namespaceId === requested);
|
|
7136
|
+
if (!found) {
|
|
7137
|
+
throw new Error(
|
|
7138
|
+
`this account owns no namespace ${requested}; it owns: ${namespaces.map((n) => n.namespaceId).join(", ") || "(none)"}`
|
|
7139
|
+
);
|
|
7140
|
+
}
|
|
7141
|
+
return found;
|
|
7142
|
+
}
|
|
7143
|
+
if (namespaces.length === 0) {
|
|
7144
|
+
throw new Error(
|
|
7145
|
+
"this account owns no namespaces in the cloud: claim one first, then enable HA on it so a relay is assigned"
|
|
7146
|
+
);
|
|
7147
|
+
}
|
|
7148
|
+
if (namespaces.length > 1) {
|
|
7149
|
+
throw new Error(
|
|
7150
|
+
`this account owns ${namespaces.length} namespaces \u2014 pass namespaceId to say which: ${namespaces.map((n) => n.namespaceId).join(", ")}`
|
|
7151
|
+
);
|
|
7152
|
+
}
|
|
7153
|
+
return namespaces[0];
|
|
7154
|
+
}
|
|
7155
|
+
function explainNoRelay(namespace, relays) {
|
|
7156
|
+
if (relays.length === 0) {
|
|
7157
|
+
return namespace.haStatus === "enabled" ? `namespace ${namespace.namespaceId} has HA enabled but no relay has been assigned to it yet \u2014 retry shortly` : `namespace ${namespace.namespaceId} has no relays: enable HA on it in the cloud to have an attested node assigned`;
|
|
7158
|
+
}
|
|
7159
|
+
const waitingOnGrant = relays.filter((r) => !r.authorshipReady);
|
|
7160
|
+
if (waitingOnGrant.length === relays.length) {
|
|
7161
|
+
return `every relay on namespace ${namespace.namespaceId} is waiting for the authorship grant: an admin of the namespace must grant CAN_AUTHOR_ON_BEHALF to ${waitingOnGrant.map((r) => r.executorAccount ?? r.peerId).join(", ") || "the relay account"} \u2014 with AdminClient.grantAuthorship(namespaceId, account) for these relays, and AdminClient.openToDelegatedExecution(namespaceId) so relays assigned later need no further op`;
|
|
7162
|
+
}
|
|
7163
|
+
return `namespace ${namespace.namespaceId} has ${relays.length} relay(s), but none reported both a URL and an executor account yet \u2014 retry shortly`;
|
|
7164
|
+
}
|
|
7165
|
+
async function defaultNonceSource(deviceSecret) {
|
|
7166
|
+
const key = `mero-js:warrant-nonce:${hex(await derivePublicKey(deviceSecret))}`;
|
|
7167
|
+
try {
|
|
7168
|
+
return createLocalStorageNonceSource(key);
|
|
7169
|
+
} catch {
|
|
7170
|
+
return createMemoryNonceSource(1);
|
|
7171
|
+
}
|
|
7172
|
+
}
|
|
7173
|
+
|
|
7139
7174
|
// src/sealed/verify.ts
|
|
7140
7175
|
var MEASUREMENT_HEX = /^[0-9a-f]{96}$/;
|
|
7141
7176
|
function createQuoteVerifier(options) {
|