@calimero-network/mero-js 24.5.0 → 24.7.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/index.d.ts CHANGED
@@ -28,7 +28,7 @@ export { signerFromSecret, signerFromCryptoKey } from "./signer/index.js";
28
28
  export type { Signer } from "./signer/index.js";
29
29
  export { generateAccountRoot, accountRootFromPhrase, accountRootFromSecret, createAccountRootSigner, accountRootSignerFromPhrase, signAccountLink, signAccountLogin, resolveRoot, resolveRootPair, } from "./account/index.js";
30
30
  export type { AccountRoot, RecoverableAccountRoot, AccountRootSigner, NewAccountRootSigner, AccountLinkInput, AccountLoginInput, RootSource, ResolvedRoot, } from "./account/index.js";
31
- export { readDelegatedSession, saveDelegatedSession, clearDelegatedSession, readDelegatedCredential, saveDelegatedCredential, clearDelegatedCredential, persistedNonces, markContextNonceSpent, pinRelayNodeKey, readPinnedRelayNodeKey, attemptRelayNodeKey, resolveRelayNodeKey, learnRelayNodeKey, RELAY_NODE_KEY_RETRY_MS, forgetMethodKinds, buildDelegatedClient, readRelayMap, rememberRelay, relayForContext, carryExecutorAccount, knownRelays, listDelegatedContexts, listDelegatedNamespaces, resolveRelayFromInvitation, normaliseAccount, joinWithNode, bootstrapFromInvitation, joinAsAccount, createDelegatedContext, createDelegatedPrivateContext, foundDelegatedNamespace, delegatedGovernance, latestPublishedVersion, HA_ACCOUNT_NOT_LINKED_MESSAGE, HA_REFUSAL_MESSAGES, governGroup, governRoot, namespaceOfGroup, rememberGroupNamespace, applicationIdForBundle, fetchRegistryBundles, resolveApplicationIdFromRegistry, selectLatestBundle, createAccountAdmin, InvitationNotClaimableError, NoRelayError, NotForAccountError, createNodeAdmin, } from "./account/index.js";
31
+ export { readDelegatedSession, saveDelegatedSession, clearDelegatedSession, readDelegatedCredential, saveDelegatedCredential, clearDelegatedCredential, persistedNonces, markContextNonceSpent, pinRelayNodeKey, readPinnedRelayNodeKey, attemptRelayNodeKey, resolveRelayNodeKey, learnRelayNodeKey, RELAY_NODE_KEY_RETRY_MS, forgetMethodKinds, forgetRelaySealing, relayTransportFetch, buildDelegatedClient, readRelayMap, rememberRelay, relayForContext, carryExecutorAccount, knownRelays, listDelegatedContexts, listDelegatedNamespaces, resolveRelayFromInvitation, normaliseAccount, joinWithNode, bootstrapFromInvitation, joinAsAccount, createDelegatedContext, createDelegatedPrivateContext, foundDelegatedNamespace, delegatedGovernance, latestPublishedVersion, HA_ACCOUNT_NOT_LINKED_MESSAGE, HA_REFUSAL_MESSAGES, governGroup, governRoot, namespaceOfGroup, rememberGroupNamespace, applicationIdForBundle, fetchRegistryBundles, resolveApplicationIdFromRegistry, selectLatestBundle, createAccountAdmin, InvitationNotClaimableError, NoRelayError, NotForAccountError, createNodeAdmin, } from "./account/index.js";
32
32
  export type { DelegatedCredential, DelegatedAccountSession, RelayMap, RelayNodeKeyAttempt, RelayResolutionStep, RelayResolutionFailure, ResolvedInvitationRelay, ResolveRelayResult, ResolveRelayFromInvitationInput, JoinWithNodeInput, JoinWithNodeResult, JoinWithNodeSuccess, JoinWithNodeFailure, JoinWithNodeStep, BootstrapStep, BootstrapResult, BootstrapSuccess, BootstrapFailure, BootstrapFromInvitationInput, CreateDelegatedContextRequest, FoundedDelegatedNamespace, RegistryBundle, ResolvedApplication, AccountAdminDeps, NodeAdminDeps, } from "./account/index.js";
33
33
  export { signDeviceCert, mintDeviceId, deviceCertPayload, accountForRoot, accountForRootPublicKey, parseDeviceCredential, verifyDeviceCredential, } from "./device-cert/index.js";
34
34
  export type { DeviceCertInput, DeviceCredential } from "./device-cert/index.js";
package/dist/index.js CHANGED
@@ -52,7 +52,7 @@ resolveRoot, resolveRootPair, } from "./account/index.js";
52
52
  // Framework-free; mero-react re-exports every one of these under the same name
53
53
  // (its `DelegatedSession` is `DelegatedAccountSession` here: the login module
54
54
  // already owns the bare name for the session `login()` mints).
55
- export { readDelegatedSession, saveDelegatedSession, clearDelegatedSession, readDelegatedCredential, saveDelegatedCredential, clearDelegatedCredential, persistedNonces, markContextNonceSpent, pinRelayNodeKey, readPinnedRelayNodeKey, attemptRelayNodeKey, resolveRelayNodeKey, learnRelayNodeKey, RELAY_NODE_KEY_RETRY_MS, forgetMethodKinds, buildDelegatedClient, readRelayMap, rememberRelay, relayForContext, carryExecutorAccount, knownRelays, listDelegatedContexts, listDelegatedNamespaces, resolveRelayFromInvitation, normaliseAccount, joinWithNode, bootstrapFromInvitation, joinAsAccount, createDelegatedContext, createDelegatedPrivateContext, foundDelegatedNamespace, delegatedGovernance, latestPublishedVersion, HA_ACCOUNT_NOT_LINKED_MESSAGE, HA_REFUSAL_MESSAGES, governGroup, governRoot, namespaceOfGroup, rememberGroupNamespace, applicationIdForBundle, fetchRegistryBundles, resolveApplicationIdFromRegistry, selectLatestBundle, createAccountAdmin, InvitationNotClaimableError, NoRelayError, NotForAccountError, createNodeAdmin, } from "./account/index.js";
55
+ export { readDelegatedSession, saveDelegatedSession, clearDelegatedSession, readDelegatedCredential, saveDelegatedCredential, clearDelegatedCredential, persistedNonces, markContextNonceSpent, pinRelayNodeKey, readPinnedRelayNodeKey, attemptRelayNodeKey, resolveRelayNodeKey, learnRelayNodeKey, RELAY_NODE_KEY_RETRY_MS, forgetMethodKinds, forgetRelaySealing, relayTransportFetch, buildDelegatedClient, readRelayMap, rememberRelay, relayForContext, carryExecutorAccount, knownRelays, listDelegatedContexts, listDelegatedNamespaces, resolveRelayFromInvitation, normaliseAccount, joinWithNode, bootstrapFromInvitation, joinAsAccount, createDelegatedContext, createDelegatedPrivateContext, foundDelegatedNamespace, delegatedGovernance, latestPublishedVersion, HA_ACCOUNT_NOT_LINKED_MESSAGE, HA_REFUSAL_MESSAGES, governGroup, governRoot, namespaceOfGroup, rememberGroupNamespace, applicationIdForBundle, fetchRegistryBundles, resolveApplicationIdFromRegistry, selectLatestBundle, createAccountAdmin, InvitationNotClaimableError, NoRelayError, NotForAccountError, createNodeAdmin, } from "./account/index.js";
56
56
  export { signDeviceCert, mintDeviceId, deviceCertPayload, accountForRoot, accountForRootPublicKey, parseDeviceCredential, verifyDeviceCredential, } from "./device-cert/index.js";
57
57
  // A device's scope — signed by the root beside its certificate, and what a relay
58
58
  // needs with it to bind the device of an account that has no node.
package/dist/index.mjs CHANGED
@@ -8836,7 +8836,7 @@ function missingNodeKey(surface) {
8836
8836
  `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.`
8837
8837
  );
8838
8838
  }
8839
- function buildObserver(relay, observe) {
8839
+ function buildObserver(relay, observe, fetchImpl) {
8840
8840
  const nodeKey = observe?.nodeKey;
8841
8841
  if (!nodeKey) return null;
8842
8842
  return new RelayObserver({
@@ -8847,7 +8847,7 @@ function buildObserver(relay, observe) {
8847
8847
  signer: observe?.signer ? async () => observe.signer : () => relay.authorSigner(),
8848
8848
  clientName: observe?.clientName,
8849
8849
  ttlSeconds: observe?.ttlSeconds,
8850
- fetch: observe?.fetch,
8850
+ fetch: observe?.fetch ?? fetchImpl,
8851
8851
  timeoutMs: observe?.timeoutMs
8852
8852
  });
8853
8853
  }
@@ -8868,12 +8868,16 @@ var MeroClient = class {
8868
8868
  this.proofAuthorizer = null;
8869
8869
  this.relayUrl = null;
8870
8870
  this.proofEvents = null;
8871
+ /** The relay transport's `fetch`, if one was given. */
8872
+ this.relayFetch = void 0;
8871
8873
  this.relayPresence = null;
8872
8874
  if (config.transport === "relay") {
8873
8875
  this.transport = "relay";
8874
8876
  this.nodeClient = null;
8875
- const relay = config.relay instanceof RelayClient ? config.relay : new RelayClient(config.relay);
8876
- this.relayObserver = buildObserver(relay, config.observe);
8877
+ const fetchImpl = config.fetch;
8878
+ this.relayFetch = fetchImpl;
8879
+ const relay = config.relay instanceof RelayClient ? config.relay : new RelayClient({ ...config.relay, fetch: config.relay.fetch ?? fetchImpl });
8880
+ this.relayObserver = buildObserver(relay, config.observe, fetchImpl);
8877
8881
  this.rpcTransport = new RelayTransport(relay, this.relayObserver);
8878
8882
  this.relayUrl = relay.relayUrl;
8879
8883
  if (config.proof) {
@@ -8883,13 +8887,14 @@ var MeroClient = class {
8883
8887
  const token = config.session;
8884
8888
  this.relayAdmin = createAdminApiClient({
8885
8889
  baseUrl: relay.relayUrl,
8886
- getAuthToken: typeof token === "string" ? async () => token : token
8890
+ getAuthToken: typeof token === "string" ? async () => token : token,
8891
+ fetch: fetchImpl
8887
8892
  });
8888
8893
  } else if (this.proofAuthorizer) {
8889
8894
  const authorizeRequest = this.proofAuthorizer;
8890
8895
  this.relayAdmin = createAdminApiClientFromHttpClient(
8891
8896
  createHttpClient({
8892
- fetch: (url, init) => globalThis.fetch(url, init),
8897
+ fetch: (url, init) => fetchImpl ? fetchImpl(url, init) : globalThis.fetch(url, init),
8893
8898
  baseUrl: relay.relayUrl,
8894
8899
  authorizeRequest
8895
8900
  }),
@@ -9003,7 +9008,8 @@ var MeroClient = class {
9003
9008
  if (this.proofAuthorizer && this.relayUrl) {
9004
9009
  this.proofEvents ?? (this.proofEvents = new SseClient({
9005
9010
  baseUrl: this.relayUrl,
9006
- authorize: this.proofAuthorizer
9011
+ authorize: this.proofAuthorizer,
9012
+ fetch: this.relayFetch
9007
9013
  }));
9008
9014
  return this.proofEvents;
9009
9015
  }
@@ -9253,8 +9259,8 @@ function relayQuoteVerifier(relayUrl, fetchRelay) {
9253
9259
  };
9254
9260
  return Object.assign(verify, { includeCollateral: true });
9255
9261
  }
9256
- async function attemptRelayNodeKey(relayUrl) {
9257
- const pinned = readPinnedRelayNodeKey(relayUrl);
9262
+ async function attemptRelayNodeKey(relayUrl, options = {}) {
9263
+ const pinned = options.fresh ? null : readPinnedRelayNodeKey(relayUrl);
9258
9264
  if (pinned) return { kind: "learned", nodeKey: pinned };
9259
9265
  const marked = { failure: null };
9260
9266
  const fetchRelay = relayFetch(marked);
@@ -9318,6 +9324,54 @@ function jwtExpiryMs2(token) {
9318
9324
  return null;
9319
9325
  }
9320
9326
  }
9327
+ var sealedFetches = /* @__PURE__ */ new Map();
9328
+ function forgetRelaySealing() {
9329
+ sealedFetches.clear();
9330
+ }
9331
+ async function isUnguardedRefusal(response) {
9332
+ if (response.status !== 403) return false;
9333
+ try {
9334
+ const body = await response.clone().json();
9335
+ return body.error?.code === "sealed_route_unguarded";
9336
+ } catch {
9337
+ return false;
9338
+ }
9339
+ }
9340
+ function needsSession(request) {
9341
+ return request.headers.has("authorization") || new URL(request.url).pathname.startsWith("/auth/");
9342
+ }
9343
+ function sealedRelayFetch(relayUrl) {
9344
+ const plain = (input, init) => globalThis.fetch(input, init);
9345
+ const sealed = createAttestedSealedFetch({
9346
+ baseUrl: relayUrl,
9347
+ verify: relayQuoteVerifier(relayUrl, plain),
9348
+ fetch: plain
9349
+ });
9350
+ let unguarded = false;
9351
+ return async (input, init) => {
9352
+ const request = new Request(input, init);
9353
+ if (unguarded && needsSession(request)) return plain(request);
9354
+ const response = await sealed(request.clone());
9355
+ if (!await isUnguardedRefusal(response)) return response;
9356
+ if (!unguarded) {
9357
+ unguarded = true;
9358
+ console.warn(
9359
+ `[mero-js] ${relayOrigin(relayUrl)} cannot guard logged-in calls sealed (no forward_auth); they go over its TLS until it does`
9360
+ );
9361
+ }
9362
+ return plain(request);
9363
+ };
9364
+ }
9365
+ function relayTransportFetch(relayUrl) {
9366
+ if (isLoopback(relayUrl)) return void 0;
9367
+ const origin2 = relayOrigin(relayUrl);
9368
+ let found = sealedFetches.get(origin2);
9369
+ if (!found) {
9370
+ found = sealedRelayFetch(relayUrl);
9371
+ sealedFetches.set(origin2, found);
9372
+ }
9373
+ return found;
9374
+ }
9321
9375
  function relaySession(s, nodeKey) {
9322
9376
  let token = null;
9323
9377
  let inflight = null;
@@ -9327,6 +9381,8 @@ function relaySession(s, nodeKey) {
9327
9381
  if (!inflight) {
9328
9382
  inflight = login({
9329
9383
  nodeUrl: s.relayUrl,
9384
+ // Sealed: the token is the session, and the TLS ends outside the TD.
9385
+ fetch: relayTransportFetch(s.relayUrl),
9330
9386
  node: nodeKey,
9331
9387
  deviceSecret: s.deviceSecret,
9332
9388
  accountProof: s.credential,
@@ -9422,6 +9478,9 @@ function buildDelegatedClient(s, _contextId) {
9422
9478
  const nodeKey = readPinnedRelayNodeKey(s.relayUrl);
9423
9479
  const client2 = createMeroClient({
9424
9480
  transport: "relay",
9481
+ // Every request sealed to the relay's attested TD: intents, admin reads,
9482
+ // queries, the login and events (see `relayTransportFetch`).
9483
+ fetch: relayTransportFetch(s.relayUrl),
9425
9484
  relay: {
9426
9485
  relayUrl: s.relayUrl,
9427
9486
  authorAccount: s.account,
@@ -10511,7 +10570,7 @@ function client(s, relayUrl, nonces, fetch2) {
10511
10570
  authorProof: s.credential,
10512
10571
  deviceSecret: s.deviceSecret,
10513
10572
  nonces,
10514
- fetch: fetch2
10573
+ fetch: fetch2 ?? relayTransportFetch(relayUrl)
10515
10574
  });
10516
10575
  }
10517
10576
  async function createDelegatedContext(s, req, deps = {}) {
@@ -10598,10 +10657,10 @@ async function foundingExecutor(s, relay, fetch2) {
10598
10657
  return { executorAccount: described.executorAccount, executorKey };
10599
10658
  }
10600
10659
  async function attestedNodeKey(relay) {
10601
- const key = await attemptRelayNodeKey(relay);
10660
+ const key = await attemptRelayNodeKey(relay, { fresh: true });
10602
10661
  if (key.kind === "learned") return key.nodeKey;
10603
10662
  throw new Error(
10604
- 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 (only a key from its operator, pinned with pinRelayNodeKey, can stand in)"
10663
+ 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"
10605
10664
  );
10606
10665
  }
10607
10666
  var HA_ACCOUNT_NOT_LINKED_MESSAGE = "link this account to your cloud user in the wallet so invitees can find this namespace";
@@ -12202,8 +12261,12 @@ var MeroJs = class {
12202
12261
  }
12203
12262
  }
12204
12263
  }
12205
- /** Run `fn` under the cross-tab refresh lock, or directly if Web Locks are unavailable. */
12264
+ /**
12265
+ * Run `fn` under the refresh lock: the token store's own (shared across
12266
+ * processes), else the cross-tab Web Lock, else directly.
12267
+ */
12206
12268
  withRefreshLock(fn) {
12269
+ if (this.tokenStore?.withLock) return this.tokenStore.withLock(fn);
12207
12270
  const locks = getLockManager();
12208
12271
  return locks ? locks.request(REFRESH_LOCK_NAME, fn) : fn();
12209
12272
  }
@@ -12717,6 +12780,7 @@ export {
12717
12780
  fetchNodeReleaseVersion,
12718
12781
  fetchRegistryBundles,
12719
12782
  forgetMethodKinds,
12783
+ forgetRelaySealing,
12720
12784
  foundDelegatedNamespace,
12721
12785
  foundedNamespaceId,
12722
12786
  generateAccountRoot,
@@ -12777,6 +12841,7 @@ export {
12777
12841
  readRelayMap,
12778
12842
  redeemInvitation,
12779
12843
  relayForContext,
12844
+ relayTransportFetch,
12780
12845
  rememberGroupNamespace,
12781
12846
  rememberRelay,
12782
12847
  reportDataOf,