@calimero-network/mero-js 24.6.0 → 25.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/account/create-context.js +17 -6
- package/dist/account/index.d.ts +1 -1
- package/dist/account/index.js +1 -1
- package/dist/account/session.d.ts +23 -1
- package/dist/account/session.js +96 -12
- package/dist/admin-api/admin-types.d.ts +2 -0
- package/dist/index.browser.mjs +2 -2
- package/dist/index.browser.mjs.map +3 -3
- package/dist/index.cjs +89 -14
- package/dist/index.cjs.map +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +89 -14
- 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/release.d.ts +2 -4
- package/dist/sealed/release.js +5 -1
- package/dist/sealed/verify.d.ts +16 -2
- package/dist/sealed/verify.js +19 -1
- package/dist/transport/mero-client.d.ts +9 -0
- package/dist/transport/mero-client.js +13 -5
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -61,6 +61,7 @@ __export(index_exports, {
|
|
|
61
61
|
NotForAccountError: () => NotForAccountError,
|
|
62
62
|
OWNER_OP_KIND: () => OWNER_OP_KIND,
|
|
63
63
|
PRESENCE_DOMAIN: () => PRESENCE_DOMAIN,
|
|
64
|
+
PRODUCTION_PROFILE: () => PRODUCTION_PROFILE,
|
|
64
65
|
RELAY_NODE_KEY_RETRY_MS: () => RELAY_NODE_KEY_RETRY_MS,
|
|
65
66
|
REQUEST_PROOF_HEADER: () => REQUEST_PROOF_HEADER,
|
|
66
67
|
RelayClient: () => RelayClient,
|
|
@@ -166,6 +167,7 @@ __export(index_exports, {
|
|
|
166
167
|
fetchNodeReleaseVersion: () => fetchNodeReleaseVersion,
|
|
167
168
|
fetchRegistryBundles: () => fetchRegistryBundles,
|
|
168
169
|
forgetMethodKinds: () => forgetMethodKinds,
|
|
170
|
+
forgetRelaySealing: () => forgetRelaySealing,
|
|
169
171
|
foundDelegatedNamespace: () => foundDelegatedNamespace,
|
|
170
172
|
foundedNamespaceId: () => foundedNamespaceId,
|
|
171
173
|
generateAccountRoot: () => generateAccountRoot,
|
|
@@ -226,6 +228,7 @@ __export(index_exports, {
|
|
|
226
228
|
readRelayMap: () => readRelayMap,
|
|
227
229
|
redeemInvitation: () => redeemInvitation,
|
|
228
230
|
relayForContext: () => relayForContext,
|
|
231
|
+
relayTransportFetch: () => relayTransportFetch,
|
|
229
232
|
rememberGroupNamespace: () => rememberGroupNamespace,
|
|
230
233
|
rememberRelay: () => rememberRelay,
|
|
231
234
|
reportDataOf: () => reportDataOf,
|
|
@@ -6844,6 +6847,9 @@ function createQuoteVerifier(options) {
|
|
|
6844
6847
|
throw new Error(`The platform's TCB status is ${verified.status}, which is not accepted`);
|
|
6845
6848
|
}
|
|
6846
6849
|
const td = tdReport(verified.report);
|
|
6850
|
+
if (td.tdAttributes?.length !== 8 || (td.tdAttributes[0] & 1) !== 0) {
|
|
6851
|
+
throw new Error("The quote is from a debug TD, or its TD attributes cannot be read");
|
|
6852
|
+
}
|
|
6847
6853
|
const measured = [td.mrTd, td.rtMr0, td.rtMr1, td.rtMr2, td.rtMr3].map((register) => hex(register));
|
|
6848
6854
|
if (allowedImages.length > 0 && !allowedImages.includes(measured.join())) {
|
|
6849
6855
|
throw new Error(
|
|
@@ -6896,7 +6902,8 @@ var TCB_STATUSES = [
|
|
|
6896
6902
|
"OutOfDate",
|
|
6897
6903
|
"OutOfDateConfigurationNeeded"
|
|
6898
6904
|
];
|
|
6899
|
-
|
|
6905
|
+
var PRODUCTION_PROFILE = "locked-read-only";
|
|
6906
|
+
function trustedMeasurementsFromReleases(releases, { profile, allowDebugProfile = false }) {
|
|
6900
6907
|
if (releases.length === 0) {
|
|
6901
6908
|
throw new Error("No release given, so no image would be trusted");
|
|
6902
6909
|
}
|
|
@@ -6911,6 +6918,11 @@ function trustedMeasurementsFromReleases(releases, { profile }) {
|
|
|
6911
6918
|
const known = Object.keys(release?.profiles ?? {}).join(", ") || "none";
|
|
6912
6919
|
throw new Error(`${name} has no profile ${JSON.stringify(profile)} (it has: ${known})`);
|
|
6913
6920
|
}
|
|
6921
|
+
if (profile !== PRODUCTION_PROFILE && !allowDebugProfile) {
|
|
6922
|
+
throw new Error(
|
|
6923
|
+
`${JSON.stringify(profile)} is not a production profile: its image is readable by whoever operates the node. Trust ${JSON.stringify(PRODUCTION_PROFILE)}, or pass allowDebugProfile to test`
|
|
6924
|
+
);
|
|
6925
|
+
}
|
|
6914
6926
|
const accepted = (image.allowed_tcb_statuses ?? ["uptodate"]).map((status) => tcbStatus(status, name));
|
|
6915
6927
|
statuses = statuses === void 0 ? accepted : statuses.filter((status) => accepted.includes(status));
|
|
6916
6928
|
return {
|
|
@@ -7399,7 +7411,10 @@ async function trustSignedRelease(options) {
|
|
|
7399
7411
|
if (compareReleaseVersions(version, minimum) < 0) {
|
|
7400
7412
|
throw new Error(`Release ${version} is older than the minimum trusted, ${minimum}`);
|
|
7401
7413
|
}
|
|
7402
|
-
return trustedMeasurementsFromReleases([published], {
|
|
7414
|
+
return trustedMeasurementsFromReleases([published], {
|
|
7415
|
+
profile: options.profile,
|
|
7416
|
+
allowDebugProfile: options.allowDebugProfile
|
|
7417
|
+
});
|
|
7403
7418
|
}
|
|
7404
7419
|
function createSignedReleaseVerifier(options) {
|
|
7405
7420
|
releaseVersion(options.minReleaseVersion, "minReleaseVersion");
|
|
@@ -7407,6 +7422,7 @@ function createSignedReleaseVerifier(options) {
|
|
|
7407
7422
|
const trusted = await trustSignedRelease({
|
|
7408
7423
|
release: await options.release(),
|
|
7409
7424
|
profile: options.profile,
|
|
7425
|
+
allowDebugProfile: options.allowDebugProfile,
|
|
7410
7426
|
minReleaseVersion: options.minReleaseVersion,
|
|
7411
7427
|
identity: options.identity,
|
|
7412
7428
|
issuer: options.issuer
|
|
@@ -9118,7 +9134,7 @@ function missingNodeKey(surface) {
|
|
|
9118
9134
|
`this relay-transport client cannot open ${surface}: it was given no relay node key. A relay is an ordinary node and does serve /sse and /ws \u2014 but the login statement must be signed against the node's device signing key, learned out of band (never read from the node, and never derived from its peerId). Pass it as \`observe: { nodeKey }\`. For a cloud-hosted relay the cloud does not publish it yet \u2014 see mdma #312.`
|
|
9119
9135
|
);
|
|
9120
9136
|
}
|
|
9121
|
-
function buildObserver(relay, observe) {
|
|
9137
|
+
function buildObserver(relay, observe, fetchImpl) {
|
|
9122
9138
|
const nodeKey = observe?.nodeKey;
|
|
9123
9139
|
if (!nodeKey) return null;
|
|
9124
9140
|
return new RelayObserver({
|
|
@@ -9129,7 +9145,7 @@ function buildObserver(relay, observe) {
|
|
|
9129
9145
|
signer: observe?.signer ? async () => observe.signer : () => relay.authorSigner(),
|
|
9130
9146
|
clientName: observe?.clientName,
|
|
9131
9147
|
ttlSeconds: observe?.ttlSeconds,
|
|
9132
|
-
fetch: observe?.fetch,
|
|
9148
|
+
fetch: observe?.fetch ?? fetchImpl,
|
|
9133
9149
|
timeoutMs: observe?.timeoutMs
|
|
9134
9150
|
});
|
|
9135
9151
|
}
|
|
@@ -9150,12 +9166,16 @@ var MeroClient = class {
|
|
|
9150
9166
|
this.proofAuthorizer = null;
|
|
9151
9167
|
this.relayUrl = null;
|
|
9152
9168
|
this.proofEvents = null;
|
|
9169
|
+
/** The relay transport's `fetch`, if one was given. */
|
|
9170
|
+
this.relayFetch = void 0;
|
|
9153
9171
|
this.relayPresence = null;
|
|
9154
9172
|
if (config.transport === "relay") {
|
|
9155
9173
|
this.transport = "relay";
|
|
9156
9174
|
this.nodeClient = null;
|
|
9157
|
-
const
|
|
9158
|
-
this.
|
|
9175
|
+
const fetchImpl = config.fetch;
|
|
9176
|
+
this.relayFetch = fetchImpl;
|
|
9177
|
+
const relay = config.relay instanceof RelayClient ? config.relay : new RelayClient({ ...config.relay, fetch: config.relay.fetch ?? fetchImpl });
|
|
9178
|
+
this.relayObserver = buildObserver(relay, config.observe, fetchImpl);
|
|
9159
9179
|
this.rpcTransport = new RelayTransport(relay, this.relayObserver);
|
|
9160
9180
|
this.relayUrl = relay.relayUrl;
|
|
9161
9181
|
if (config.proof) {
|
|
@@ -9165,13 +9185,14 @@ var MeroClient = class {
|
|
|
9165
9185
|
const token = config.session;
|
|
9166
9186
|
this.relayAdmin = createAdminApiClient({
|
|
9167
9187
|
baseUrl: relay.relayUrl,
|
|
9168
|
-
getAuthToken: typeof token === "string" ? async () => token : token
|
|
9188
|
+
getAuthToken: typeof token === "string" ? async () => token : token,
|
|
9189
|
+
fetch: fetchImpl
|
|
9169
9190
|
});
|
|
9170
9191
|
} else if (this.proofAuthorizer) {
|
|
9171
9192
|
const authorizeRequest = this.proofAuthorizer;
|
|
9172
9193
|
this.relayAdmin = createAdminApiClientFromHttpClient(
|
|
9173
9194
|
createHttpClient({
|
|
9174
|
-
fetch: (url, init) => globalThis.fetch(url, init),
|
|
9195
|
+
fetch: (url, init) => fetchImpl ? fetchImpl(url, init) : globalThis.fetch(url, init),
|
|
9175
9196
|
baseUrl: relay.relayUrl,
|
|
9176
9197
|
authorizeRequest
|
|
9177
9198
|
}),
|
|
@@ -9285,7 +9306,8 @@ var MeroClient = class {
|
|
|
9285
9306
|
if (this.proofAuthorizer && this.relayUrl) {
|
|
9286
9307
|
this.proofEvents ?? (this.proofEvents = new SseClient({
|
|
9287
9308
|
baseUrl: this.relayUrl,
|
|
9288
|
-
authorize: this.proofAuthorizer
|
|
9309
|
+
authorize: this.proofAuthorizer,
|
|
9310
|
+
fetch: this.relayFetch
|
|
9289
9311
|
}));
|
|
9290
9312
|
return this.proofEvents;
|
|
9291
9313
|
}
|
|
@@ -9535,8 +9557,8 @@ function relayQuoteVerifier(relayUrl, fetchRelay) {
|
|
|
9535
9557
|
};
|
|
9536
9558
|
return Object.assign(verify, { includeCollateral: true });
|
|
9537
9559
|
}
|
|
9538
|
-
async function attemptRelayNodeKey(relayUrl) {
|
|
9539
|
-
const pinned = readPinnedRelayNodeKey(relayUrl);
|
|
9560
|
+
async function attemptRelayNodeKey(relayUrl, options = {}) {
|
|
9561
|
+
const pinned = options.fresh ? null : readPinnedRelayNodeKey(relayUrl);
|
|
9540
9562
|
if (pinned) return { kind: "learned", nodeKey: pinned };
|
|
9541
9563
|
const marked = { failure: null };
|
|
9542
9564
|
const fetchRelay = relayFetch(marked);
|
|
@@ -9600,6 +9622,54 @@ function jwtExpiryMs2(token) {
|
|
|
9600
9622
|
return null;
|
|
9601
9623
|
}
|
|
9602
9624
|
}
|
|
9625
|
+
var sealedFetches = /* @__PURE__ */ new Map();
|
|
9626
|
+
function forgetRelaySealing() {
|
|
9627
|
+
sealedFetches.clear();
|
|
9628
|
+
}
|
|
9629
|
+
async function isUnguardedRefusal(response) {
|
|
9630
|
+
if (response.status !== 403) return false;
|
|
9631
|
+
try {
|
|
9632
|
+
const body = await response.clone().json();
|
|
9633
|
+
return body.error?.code === "sealed_route_unguarded";
|
|
9634
|
+
} catch {
|
|
9635
|
+
return false;
|
|
9636
|
+
}
|
|
9637
|
+
}
|
|
9638
|
+
function needsSession(request) {
|
|
9639
|
+
return request.headers.has("authorization") || new URL(request.url).pathname.startsWith("/auth/");
|
|
9640
|
+
}
|
|
9641
|
+
function sealedRelayFetch(relayUrl) {
|
|
9642
|
+
const plain = (input, init) => globalThis.fetch(input, init);
|
|
9643
|
+
const sealed = createAttestedSealedFetch({
|
|
9644
|
+
baseUrl: relayUrl,
|
|
9645
|
+
verify: relayQuoteVerifier(relayUrl, plain),
|
|
9646
|
+
fetch: plain
|
|
9647
|
+
});
|
|
9648
|
+
let unguarded = false;
|
|
9649
|
+
return async (input, init) => {
|
|
9650
|
+
const request = new Request(input, init);
|
|
9651
|
+
if (unguarded && needsSession(request)) return plain(request);
|
|
9652
|
+
const response = await sealed(request.clone());
|
|
9653
|
+
if (!await isUnguardedRefusal(response)) return response;
|
|
9654
|
+
if (!unguarded) {
|
|
9655
|
+
unguarded = true;
|
|
9656
|
+
console.warn(
|
|
9657
|
+
`[mero-js] ${relayOrigin(relayUrl)} cannot guard logged-in calls sealed (no forward_auth); they go over its TLS until it does`
|
|
9658
|
+
);
|
|
9659
|
+
}
|
|
9660
|
+
return plain(request);
|
|
9661
|
+
};
|
|
9662
|
+
}
|
|
9663
|
+
function relayTransportFetch(relayUrl) {
|
|
9664
|
+
if (isLoopback(relayUrl)) return void 0;
|
|
9665
|
+
const origin2 = relayOrigin(relayUrl);
|
|
9666
|
+
let found = sealedFetches.get(origin2);
|
|
9667
|
+
if (!found) {
|
|
9668
|
+
found = sealedRelayFetch(relayUrl);
|
|
9669
|
+
sealedFetches.set(origin2, found);
|
|
9670
|
+
}
|
|
9671
|
+
return found;
|
|
9672
|
+
}
|
|
9603
9673
|
function relaySession(s, nodeKey) {
|
|
9604
9674
|
let token = null;
|
|
9605
9675
|
let inflight = null;
|
|
@@ -9609,6 +9679,8 @@ function relaySession(s, nodeKey) {
|
|
|
9609
9679
|
if (!inflight) {
|
|
9610
9680
|
inflight = login({
|
|
9611
9681
|
nodeUrl: s.relayUrl,
|
|
9682
|
+
// Sealed: the token is the session, and the TLS ends outside the TD.
|
|
9683
|
+
fetch: relayTransportFetch(s.relayUrl),
|
|
9612
9684
|
node: nodeKey,
|
|
9613
9685
|
deviceSecret: s.deviceSecret,
|
|
9614
9686
|
accountProof: s.credential,
|
|
@@ -9704,6 +9776,9 @@ function buildDelegatedClient(s, _contextId) {
|
|
|
9704
9776
|
const nodeKey = readPinnedRelayNodeKey(s.relayUrl);
|
|
9705
9777
|
const client2 = createMeroClient({
|
|
9706
9778
|
transport: "relay",
|
|
9779
|
+
// Every request sealed to the relay's attested TD: intents, admin reads,
|
|
9780
|
+
// queries, the login and events (see `relayTransportFetch`).
|
|
9781
|
+
fetch: relayTransportFetch(s.relayUrl),
|
|
9707
9782
|
relay: {
|
|
9708
9783
|
relayUrl: s.relayUrl,
|
|
9709
9784
|
authorAccount: s.account,
|
|
@@ -10793,7 +10868,7 @@ function client(s, relayUrl, nonces, fetch2) {
|
|
|
10793
10868
|
authorProof: s.credential,
|
|
10794
10869
|
deviceSecret: s.deviceSecret,
|
|
10795
10870
|
nonces,
|
|
10796
|
-
fetch: fetch2
|
|
10871
|
+
fetch: fetch2 ?? relayTransportFetch(relayUrl)
|
|
10797
10872
|
});
|
|
10798
10873
|
}
|
|
10799
10874
|
async function createDelegatedContext(s, req, deps = {}) {
|
|
@@ -10880,10 +10955,10 @@ async function foundingExecutor(s, relay, fetch2) {
|
|
|
10880
10955
|
return { executorAccount: described.executorAccount, executorKey };
|
|
10881
10956
|
}
|
|
10882
10957
|
async function attestedNodeKey(relay) {
|
|
10883
|
-
const key = await attemptRelayNodeKey(relay);
|
|
10958
|
+
const key = await attemptRelayNodeKey(relay, { fresh: true });
|
|
10884
10959
|
if (key.kind === "learned") return key.nodeKey;
|
|
10885
10960
|
throw new Error(
|
|
10886
|
-
key.kind === "unavailable" ? "the relay's signing key could not be learned: its attestation did not answer, try again" : "the relay's attestation was refused, so founding through it is not possible
|
|
10961
|
+
key.kind === "unavailable" ? "the relay's signing key could not be learned: its attestation did not answer, try again" : "the relay's attestation was refused, so founding through it is not possible: it is not a signed locked-read-only release"
|
|
10887
10962
|
);
|
|
10888
10963
|
}
|
|
10889
10964
|
var HA_ACCOUNT_NOT_LINKED_MESSAGE = "link this account to your cloud user in the wallet so invitees can find this namespace";
|