@tinycloud/node-sdk 3.12.0-beta.2 → 3.12.0-beta.3

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 (32) hide show
  1. package/README.md +39 -0
  2. package/dist/{NodeUserAuthorization-B05Wjjpb.d.cts → NodeUserAuthorization-B_TFUlua.d.cts} +18 -52
  3. package/dist/{NodeUserAuthorization-B05Wjjpb.d.ts → NodeUserAuthorization-B_TFUlua.d.ts} +18 -52
  4. package/dist/{TinyCloudNode-CEFGtSWn.d.ts → TinyCloudNode-Brcu8QoX.d.cts} +31 -59
  5. package/dist/{TinyCloudNode-6_IumWu0.d.cts → TinyCloudNode-DBJUnsMN.d.ts} +31 -59
  6. package/dist/{authority-DXpnuId1.d.ts → authority-BFc5lesl.d.ts} +1 -1
  7. package/dist/{authority-DODDXxoP.d.cts → authority-DQ6R1HEG.d.cts} +1 -1
  8. package/dist/core.cjs +500 -283
  9. package/dist/core.cjs.map +1 -1
  10. package/dist/core.d.cts +5 -5
  11. package/dist/core.d.ts +5 -5
  12. package/dist/core.js +526 -287
  13. package/dist/core.js.map +1 -1
  14. package/dist/credentials-core.d.cts +1 -1
  15. package/dist/credentials-core.d.ts +1 -1
  16. package/dist/credentials.d.cts +1 -1
  17. package/dist/credentials.d.ts +1 -1
  18. package/dist/{delegation-BQbAuKad.d.cts → delegation-CeOWDnBB.d.cts} +7 -0
  19. package/dist/{delegation-BQbAuKad.d.ts → delegation-CeOWDnBB.d.ts} +7 -0
  20. package/dist/encryption-keys.d.cts +3 -3
  21. package/dist/encryption-keys.d.ts +3 -3
  22. package/dist/index.cjs +517 -300
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +5 -4
  25. package/dist/index.d.ts +5 -4
  26. package/dist/index.js +527 -288
  27. package/dist/index.js.map +1 -1
  28. package/dist/replication/authority.d.cts +4 -4
  29. package/dist/replication/authority.d.ts +4 -4
  30. package/dist/space-hosting.d.cts +1 -1
  31. package/dist/space-hosting.d.ts +1 -1
  32. package/package.json +4 -4
package/dist/core.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/core.ts
2
2
  import { TinyCloud as TinyCloud2 } from "@tinycloud/sdk-core";
3
+ import { NodeNotRespondingError as NodeNotRespondingError3, NodeIdentityMismatchError as NodeIdentityMismatchError2, PartialNodeWriteError as PartialNodeWriteError3 } from "@tinycloud/sdk-services";
3
4
 
4
5
  // src/replication/module-registry.ts
5
6
  var nodeLoaders;
@@ -259,11 +260,16 @@ import {
259
260
  principalDidEquals,
260
261
  resourceCapabilitiesToAbilitiesMap,
261
262
  resourceCapabilitiesToSpaceAbilitiesMap,
262
- resolveTinyCloudHosts,
263
- resolveFailClosedTinyCloudHosts,
264
- lookupOwnerRegistryVerdict,
263
+ resolveRegistryResource,
264
+ warnRegistryDefaultNode,
265
+ parseSpaceUri,
266
+ NodeNotFoundError,
267
+ RegistryUnavailableError,
268
+ discoverLocalTinyCloudNode,
269
+ exactHttpOrigin,
265
270
  assertSpaceCreationAllowed,
266
271
  createInMemoryRegistrySequenceStore,
272
+ createInMemoryRegistryCacheStore,
267
273
  registryOwnerSubjectV1,
268
274
  EXPIRY,
269
275
  assertExpiryWithinTechnicalLimit,
@@ -286,7 +292,13 @@ import {
286
292
  ENCRYPTION
287
293
  } from "@tinycloud/sdk-core";
288
294
  import { parseEncryptionResource } from "@tinycloud/sdk-services/encryption/resource";
289
- import { parseNetworkId } from "@tinycloud/sdk-services";
295
+ import {
296
+ parseNetworkId,
297
+ verifyResourceDestination,
298
+ NodeNotRespondingError,
299
+ PartialNodeWriteError,
300
+ routingFailure
301
+ } from "@tinycloud/sdk-services";
290
302
 
291
303
  // src/authorization/appKeyHostBinding.ts
292
304
  var DOMAIN_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
@@ -541,11 +553,12 @@ var NodeUserAuthorization = class {
541
553
  this.tinycloudHosts = config.tinycloudHosts;
542
554
  this.tinycloudRegistryUrl = config.tinycloudRegistryUrl;
543
555
  this.tinycloudFallbackHosts = config.tinycloudFallbackHosts;
544
- this.registryPolicy = config.registryPolicy;
545
556
  this.newUserHost = config.newUserHost;
546
557
  this.registryChainId = config.registryChainId;
547
558
  this.registrySequenceStore = config.registrySequenceStore ?? createInMemoryRegistrySequenceStore();
559
+ this.registryCacheStore = config.registryCacheStore ?? createInMemoryRegistryCacheStore();
548
560
  this.onNodeNotFound = config.onNodeNotFound;
561
+ this.onRegistryFallback = config.onRegistryFallback;
549
562
  this.autoDiscoverLocalNode = config.autoDiscoverLocalNode;
550
563
  this.localNodeUrl = config.localNodeUrl;
551
564
  this.localLinkName = config.localLinkName;
@@ -613,13 +626,8 @@ var NodeUserAuthorization = class {
613
626
  * undefined for missing SIWEs and the SDK then treats the session as
614
627
  * expired-at-epoch-zero.
615
628
  *
616
- * @param hosts - The TinyCloud hosts this session was created against,
617
- * as persisted in {@link PersistedSessionData.tinycloudHosts}. When
618
- * present (and non-empty) they are adopted directly so the restored
619
- * session resolves to the same node as the original sign-in without
620
- * re-running registry/fallback resolution. When absent (old session)
621
- * hosts are resolved lazily on the first host-needing call via
622
- * {@link ensureTinyCloudHosts}.
629
+ * @param hosts - Persisted host hints for genuine no-entry onboarding.
630
+ * Current signed registry membership always takes precedence.
623
631
  */
624
632
  setRestoredTinyCloudSession(session, hosts) {
625
633
  const address = canonicalizeAddress(session.address);
@@ -681,89 +689,84 @@ var NodeUserAuthorization = class {
681
689
  signature: session.signature
682
690
  };
683
691
  }
684
- /**
685
- * Ensure `tinycloudHosts` are resolved before a host-needing call.
686
- *
687
- * Fresh sign-in resolves hosts up front; a restored session may not have
688
- * (old persisted sessions predate {@link PersistedSessionData.tinycloudHosts}).
689
- * This guard makes a restored session resolve the node exactly like a fresh
690
- * sign-in — persisted hosts are preferred (already set by
691
- * {@link setRestoredTinyCloudSession}), otherwise the registry/fallback
692
- * resolution runs lazily here. Idempotent: {@link resolveTinyCloudHostsForSignIn}
693
- * early-returns when hosts are already set.
694
- *
695
- * Throws if hosts are unset and the restored session has no address/chainId
696
- * to resolve from — a real failure that must surface, not be masked.
697
- */
692
+ /** Refresh the primary space's registry destinations, including after restore. */
698
693
  async ensureTinyCloudHosts() {
699
- if (this.tinycloudHosts && this.tinycloudHosts.length > 0) {
700
- return;
701
- }
702
694
  if (this._address === void 0 || this._chainId === void 0) {
703
- throw new Error(
704
- "Cannot resolve TinyCloud hosts: no address/chainId available. Sign in or restore a session first."
705
- );
695
+ throw new Error("Cannot resolve TinyCloud hosts: sign in or restore a session first.");
696
+ }
697
+ await this.resolveTinyCloudHostsForSignIn(this._address, this._chainId, this._tinyCloudSession?.spaceId);
698
+ }
699
+ async resolveTinyCloudHostsForSignIn(address, chainId, restoredSpaceId) {
700
+ const spaceId = restoredSpaceId ?? this.resolveSignInCapabilities(address, chainId).spaceId;
701
+ const destinations = await this.resolveResourceDestinations(spaceId, "read");
702
+ for (const destination of destinations) await verifyResourceDestination(destination, globalThis.fetch.bind(globalThis));
703
+ this.tinycloudHosts = destinations.map(({ url }) => url);
704
+ }
705
+ /** Current full-resource registry routing; saved hosts are only no-entry hints. */
706
+ async resolveResourceDestinations(resource, access, noEntryHosts = this.tinycloudHosts ?? []) {
707
+ const owner = resource.startsWith("urn:tinycloud:encryption:") ? parseNetworkId(resource.split("/")[0]).ownerDid : parseSpaceUri(resource)?.owner ?? (() => {
708
+ const key = /^tinycloud:key:(z[^:]+):[^/]+$/.exec(resource);
709
+ return key ? `did:key:${key[1]}` : void 0;
710
+ })();
711
+ if (!owner) throw new Error(`Cannot determine registry owner for ${resource}`);
712
+ const subject = owner.startsWith("did:pkh:") ? registryOwnerSubjectV1(owner.slice(owner.lastIndexOf(":") + 1), this.registryChainId) : owner;
713
+ for (let attempt = 1; ; attempt++) {
714
+ try {
715
+ const resolved = await resolveRegistryResource(subject, resource, access, {
716
+ registryUrl: this.tinycloudRegistryUrl,
717
+ registryChainId: this.registryChainId,
718
+ sequenceStore: this.registrySequenceStore,
719
+ cacheStore: this.registryCacheStore,
720
+ onRegistryFallback: this.onRegistryFallback
721
+ });
722
+ this.registryVerdict = resolved.verdict;
723
+ if (resolved.verdict.status === "unregistered") {
724
+ if (noEntryHosts.length === 0 && (this.autoDiscoverLocalNode ?? true)) {
725
+ const local = await discoverLocalTinyCloudNode({
726
+ localNodeUrl: this.localNodeUrl,
727
+ localLinkName: this.localLinkName,
728
+ expectedNodeDid: this.expectedNodeDid,
729
+ identityStore: this.localNodeIdentityStore
730
+ });
731
+ if (local) {
732
+ warnRegistryDefaultNode(resolved.verdict, this.registryCacheStore, this.onRegistryFallback);
733
+ return [{ url: local.url }];
734
+ }
735
+ }
736
+ const host = noEntryHosts[0] ?? this.newUserHost ?? (this.tinycloudFallbackHosts === void 0 ? "https://node.tinycloud.xyz" : this.tinycloudFallbackHosts?.[0]);
737
+ if (!host || this.newUserHost === null && noEntryHosts.length === 0) throw new NodeNotFoundError(subject, resolved.verdict);
738
+ const url = exactHttpOrigin(host, "new-user host");
739
+ warnRegistryDefaultNode(resolved.verdict, this.registryCacheStore, this.onRegistryFallback);
740
+ return [{ url }];
741
+ }
742
+ const destinations = resolved.hosts.flatMap((host) => host.urls.map((url) => ({ url, nodeDid: host.nodeDid })));
743
+ return destinations;
744
+ } catch (error) {
745
+ if (!(error instanceof NodeNotFoundError) && !(error instanceof RegistryUnavailableError)) throw error;
746
+ if (!this.onNodeNotFound) throw error;
747
+ const decision = await this.onNodeNotFound({ error, attempt });
748
+ if (decision.action === "retry") continue;
749
+ if (decision.action === "use-host" && error.verdict?.status === "unregistered") {
750
+ const url = exactHttpOrigin(decision.host, "new-user host");
751
+ warnRegistryDefaultNode(error.verdict, this.registryCacheStore, this.onRegistryFallback);
752
+ return [{ url }];
753
+ }
754
+ throw error;
755
+ }
706
756
  }
707
- await this.resolveTinyCloudHostsForSignIn(this._address, this._chainId);
708
757
  }
709
- async resolveTinyCloudHostsForSignIn(address, chainId) {
710
- if (this.tinycloudHosts && this.tinycloudHosts.length > 0) {
711
- return;
712
- }
713
- if (this.registryPolicy === "fail-closed") {
714
- const resolved2 = await resolveFailClosedTinyCloudHosts(address, {
715
- registryUrl: this.tinycloudRegistryUrl,
716
- registryChainId: this.registryChainId,
717
- newUserHost: this.newUserHost,
718
- sequenceStore: this.registrySequenceStore,
719
- onNodeNotFound: this.onNodeNotFound,
720
- autoDiscoverLocalNode: this.autoDiscoverLocalNode,
721
- localNodeUrl: this.localNodeUrl,
722
- localLinkName: this.localLinkName,
723
- expectedNodeDid: this.expectedNodeDid,
724
- localNodeIdentityStore: this.localNodeIdentityStore
725
- });
726
- this.tinycloudHosts = resolved2.hosts;
727
- this.registryVerdict = resolved2.verdict;
728
- return;
729
- }
730
- const subject = pkhDid(address, chainId);
731
- const resolved = await resolveTinyCloudHosts(subject, {
732
- registryUrl: this.tinycloudRegistryUrl,
733
- fallbackHosts: this.tinycloudFallbackHosts,
734
- autoDiscoverLocalNode: this.autoDiscoverLocalNode,
735
- localNodeUrl: this.localNodeUrl,
736
- localLinkName: this.localLinkName,
737
- expectedNodeDid: this.expectedNodeDid,
738
- localNodeIdentityStore: this.localNodeIdentityStore
739
- });
740
- this.tinycloudHosts = resolved.hosts;
758
+ get registryRecordPresent() {
759
+ return this.registryVerdict?.status === "registered";
741
760
  }
742
- /**
743
- * Space-creation guard (TC-1443). Under `registryPolicy: "fail-closed"`,
744
- * throws unless the owner's registry verdict allows a space on the primary
745
- * host: `NodeRegistryEntryExistsError` when the owner's record does not
746
- * list it, `RegistryUnavailableError` when the registry gives no
747
- * verdict. Reuses this session's sign-in verdict unless it was
748
- * `unavailable`, else looks it up now (explicit-host apps). Under the
749
- * default policy it returns at once and performs no lookup.
750
- */
751
- async assertMayCreateSpace() {
752
- if (this.registryPolicy !== "fail-closed") return;
753
- if (this._address === void 0) {
754
- throw new Error("Must be signed in to create a space");
761
+ /** Refresh membership before hosting; a saved no-entry verdict never authorizes a later creation. */
762
+ async assertMayCreateSpace(spaceId = this._tinyCloudSession?.spaceId, host = this.primaryTinyCloudHost) {
763
+ if (!spaceId) throw new Error("Must be signed in to create a space");
764
+ const destinations = await this.resolveResourceDestinations(spaceId, "host");
765
+ if (!destinations.some((destination) => destination.url === host)) {
766
+ const subject = parseSpaceUri(spaceId)?.owner;
767
+ if (!subject || !this.registryVerdict) throw new Error("Cannot resolve space owner");
768
+ assertSpaceCreationAllowed(this.registryVerdict, host, spaceId);
755
769
  }
756
- await this.ensureTinyCloudHosts();
757
- const subject = registryOwnerSubjectV1(this._address, this.registryChainId);
758
- let verdict = this.registryVerdict;
759
- if (verdict?.subject !== subject || verdict.status === "unavailable") {
760
- verdict = await lookupOwnerRegistryVerdict(subject, {
761
- registryUrl: this.tinycloudRegistryUrl,
762
- sequenceStore: this.registrySequenceStore
763
- });
764
- this.registryVerdict = verdict;
765
- }
766
- assertSpaceCreationAllowed(verdict, this.primaryTinyCloudHost);
767
770
  }
768
771
  requireTinyCloudHosts() {
769
772
  if (!this.tinycloudHosts || this.tinycloudHosts.length === 0) {
@@ -1209,6 +1212,31 @@ var NodeUserAuthorization = class {
1209
1212
  checkCurrent();
1210
1213
  const host = context?.host ?? this.primaryTinyCloudHost;
1211
1214
  const spaceId = context?.spaceId ?? (typeof targetSpaceId === "string" ? targetSpaceId : this._tinyCloudSession.spaceId);
1215
+ const destinations = await this.resolveResourceDestinations(spaceId, "host");
1216
+ if (this.registryVerdict?.status === "registered") {
1217
+ const results = [];
1218
+ for (const destination of destinations) {
1219
+ checkCurrent();
1220
+ try {
1221
+ await verifyResourceDestination(destination, globalThis.fetch.bind(globalThis));
1222
+ const result = await activateSessionWithHost(destination.url, this._tinyCloudSession.delegationHeader, signal);
1223
+ results.push(result.skipped?.includes(spaceId) ? { ...result, success: false, status: 403, error: "The session does not activate the requested space" } : result);
1224
+ } catch (error) {
1225
+ if (routingFailure(error) || signal?.aborted) throw error;
1226
+ throw new NodeNotRespondingError([destination.url], error);
1227
+ }
1228
+ }
1229
+ const failed = results.flatMap((result, index) => result.success ? [] : [{ url: destinations[index].url, status: result.status }]);
1230
+ if (failed.length) {
1231
+ if (destinations.length === 1) return results[0];
1232
+ throw new PartialNodeWriteError(destinations.filter((_, index) => results[index].success).map(({ url }) => url), failed);
1233
+ }
1234
+ return {
1235
+ ...results[0],
1236
+ activated: [...new Set(results.flatMap((result) => result.activated ?? []))],
1237
+ skipped: [...new Set(results.flatMap((result) => result.skipped ?? []))]
1238
+ };
1239
+ }
1212
1240
  const peerId = await fetchPeerId(host, spaceId, signal);
1213
1241
  checkCurrent();
1214
1242
  const siwe = this.wasm.generateHostSIWEMessage(
@@ -1279,13 +1307,45 @@ var NodeUserAuthorization = class {
1279
1307
  if (!this._tinyCloudSession) {
1280
1308
  throw new Error("Must be signed in to ensure space exists");
1281
1309
  }
1282
- await this.ensureTinyCloudHosts();
1283
- const host = this.primaryTinyCloudHost;
1284
1310
  const primarySpaceId = this._tinyCloudSession.spaceId;
1285
- const result = await activateSessionWithHost(
1286
- host,
1287
- this._tinyCloudSession.delegationHeader
1288
- );
1311
+ const primary = await this.resolveResourceDestinations(primarySpaceId, "read");
1312
+ this.tinycloudHosts = primary.map(({ url }) => url);
1313
+ const targets = new Map(primary.map((destination) => [destination.url, destination]));
1314
+ if (this.registryVerdict?.status === "registered" && this.registryVerdict.record) {
1315
+ const record = this.registryVerdict.record;
1316
+ const resources = Object.keys(extractRecapAttenuations(this._tinyCloudSession.siwe ?? ""));
1317
+ for (const space of record.spaces) {
1318
+ const registeredSpace = parseSpaceUri(space.space);
1319
+ if (!resources.some((resource) => {
1320
+ const fullSpace = resource.split("/")[0];
1321
+ const requestedSpace = parseSpaceUri(fullSpace);
1322
+ return fullSpace === space.space || registeredSpace !== null && requestedSpace !== null && registeredSpace.owner === requestedSpace.owner && registeredSpace.name === requestedSpace.name;
1323
+ })) continue;
1324
+ for (const membership of space.nodes) {
1325
+ const node = record.nodes.find((candidate) => candidate.did === membership.did);
1326
+ targets.set(node.url, { url: node.url, nodeDid: node.did });
1327
+ }
1328
+ }
1329
+ if (record.encryptionNode && resources.some((resource) => resource.startsWith("urn:tinycloud:encryption:"))) {
1330
+ const node = record.nodes.find((candidate) => candidate.did === record.encryptionNode);
1331
+ targets.set(node.url, { url: node.url, nodeDid: node.did });
1332
+ }
1333
+ }
1334
+ let result;
1335
+ for (const destination of targets.values()) {
1336
+ await verifyResourceDestination(destination, globalThis.fetch.bind(globalThis));
1337
+ let activated;
1338
+ try {
1339
+ activated = await activateSessionWithHost(destination.url, this._tinyCloudSession.delegationHeader);
1340
+ } catch (error) {
1341
+ throw new NodeNotRespondingError([destination.url], error);
1342
+ }
1343
+ if (!activated.success && activated.status !== 404) {
1344
+ throw Object.assign(new Error(`Failed to activate session: ${describeHostFailure(activated)}`), { cause: activated });
1345
+ }
1346
+ if (destination.url === primary[0].url) result = activated;
1347
+ }
1348
+ if (!result) throw new Error("Primary space activation did not return a result");
1289
1349
  this.recordActivationSkippedSpaces(result, primarySpaceId);
1290
1350
  if (!result.success && result.status !== 404) {
1291
1351
  throw Object.assign(
@@ -1333,7 +1393,7 @@ var NodeUserAuthorization = class {
1333
1393
  /** {@link assertMayCreateSpace}, reporting a refusal to the creation handler before rethrowing it. */
1334
1394
  async guardSpaceCreation(handler, context) {
1335
1395
  try {
1336
- await this.assertMayCreateSpace();
1396
+ await this.assertMayCreateSpace(context.spaceId, context.host);
1337
1397
  } catch (error) {
1338
1398
  const failure = error instanceof Error ? error : new Error(String(error));
1339
1399
  handler.onSpaceCreationFailed?.(context, failure);
@@ -2487,6 +2547,7 @@ import {
2487
2547
  decodeBase64Url,
2488
2548
  principalDidEquals as principalDidEquals2
2489
2549
  } from "@tinycloud/sdk-core";
2550
+ import { routingFailure as routingFailure2 } from "@tinycloud/sdk-services";
2490
2551
  var RuntimeDelegationRejectedError = class extends Error {
2491
2552
  constructor(reason, message) {
2492
2553
  super(message);
@@ -2513,7 +2574,8 @@ var SERVICE_BY_SHORT_NAME = {
2513
2574
  kv: "tinycloud.kv",
2514
2575
  sql: "tinycloud.sql",
2515
2576
  duckdb: "tinycloud.duckdb",
2516
- hooks: "tinycloud.hooks"
2577
+ hooks: "tinycloud.hooks",
2578
+ capabilities: "tinycloud.capabilities"
2517
2579
  };
2518
2580
  function authorizationWithoutBearer(authorization) {
2519
2581
  return authorization.replace(/^Bearer /i, "");
@@ -2644,8 +2706,8 @@ function signedAuthorityFromCompactUcan(authorization) {
2644
2706
  ));
2645
2707
  continue;
2646
2708
  }
2647
- const match = /^(?:tinycloud:\/\/([^/]+)|(tinycloud:[^/]+))\/(kv|sql|duckdb|hooks)(?:\/|$)(.*)$/.exec(resource);
2648
- if (!match) {
2709
+ const match = /^(?:tinycloud:\/\/([^/]+)|(tinycloud:[^/]+))\/([^/]+)(?:\/|$)(.*)$/.exec(resource);
2710
+ if (!match || !Object.prototype.hasOwnProperty.call(SERVICE_BY_SHORT_NAME, match[3])) {
2649
2711
  throw new Error(
2650
2712
  `Validated runtime delegation has an unsupported signed resource '${resource}'.`
2651
2713
  );
@@ -2790,13 +2852,8 @@ function resourcesMatch(actual, expected) {
2790
2852
  return JSON.stringify(actual.map(comparable)) === JSON.stringify(expected.map(comparable));
2791
2853
  }
2792
2854
  async function activateValidatedRuntimeDelegation(node, delegation, options) {
2793
- const host = normalizedHost(options.host);
2794
- if (delegation.host !== void 0 && normalizedHost(delegation.host) !== host) {
2795
- reject(
2796
- "host_mismatch",
2797
- `Runtime delegation host '${delegation.host}' does not match expected host '${options.host}'.`
2798
- );
2799
- }
2855
+ let host = normalizedHost(options.host);
2856
+ const differentHost = delegation.host !== void 0 && normalizedHost(delegation.host) !== host;
2800
2857
  if (!(delegation.expiry instanceof Date) || Number.isNaN(delegation.expiry.getTime())) {
2801
2858
  reject("authority_invalid", "Runtime delegation has an invalid expiry.");
2802
2859
  }
@@ -2840,6 +2897,13 @@ async function activateValidatedRuntimeDelegation(node, delegation, options) {
2840
2897
  if (options.authorize !== void 0 && !options.authorize(signed.permissions)) {
2841
2898
  throw new Error("Runtime delegation is not authorized for this runtime.");
2842
2899
  }
2900
+ if (differentHost) {
2901
+ const registeredHost = await node.resolveRegisteredDelegationHost?.(signed.permissions, host);
2902
+ if (registeredHost === void 0) {
2903
+ reject("host_mismatch", `Runtime delegation host '${delegation.host}' does not match expected host '${options.host}'.`);
2904
+ }
2905
+ host = normalizedHost(registeredHost);
2906
+ }
2843
2907
  const previouslyInstalled = node.getRuntimePermissionDelegations().find((candidate) => candidate.cid === cid);
2844
2908
  if (previouslyInstalled !== void 0 && previouslyInstalled.delegationHeader.Authorization !== authorization) {
2845
2909
  reject(
@@ -2883,6 +2947,7 @@ async function activateValidatedRuntimeDelegation(node, delegation, options) {
2883
2947
  await node.useRuntimeDelegation(installedCandidate);
2884
2948
  }
2885
2949
  } catch (error) {
2950
+ if (routingFailure2(error)) throw error;
2886
2951
  reject(
2887
2952
  activationRejectionReason(error),
2888
2953
  error instanceof Error ? error.message : String(error)
@@ -2901,6 +2966,7 @@ async function activateValidatedRuntimeDelegation(node, delegation, options) {
2901
2966
  "Runtime delegation activation installed authority that differs from the validated authority."
2902
2967
  );
2903
2968
  }
2969
+ node.refreshRegistryForCapabilities?.(signed.permissions);
2904
2970
  return {
2905
2971
  cid,
2906
2972
  delegation: installed,
@@ -2964,6 +3030,15 @@ function deserializeDelegation(data) {
2964
3030
  }
2965
3031
 
2966
3032
  // src/TinyCloudNode.ts
3033
+ import {
3034
+ createResourceRoutingFetch,
3035
+ verifyResourceDestination as verifyResourceDestination2,
3036
+ NodeNotRespondingError as NodeNotRespondingError2,
3037
+ NodeIdentityMismatchError,
3038
+ PartialNodeWriteError as PartialNodeWriteError2,
3039
+ ResourceNotRoutedError,
3040
+ routingFailure as routingFailure3
3041
+ } from "@tinycloud/sdk-services";
2967
3042
  import { generateNonce, SiweMessage as SiweMessage3 } from "siwe";
2968
3043
  import { didKeyFromEd25519PublicKey } from "@tinycloud/share-envelope";
2969
3044
  import {
@@ -3023,9 +3098,15 @@ import {
3023
3098
  verifyDidKeyEd25519Signature,
3024
3099
  canonicalizeAddress as canonicalizeAddress2,
3025
3100
  pkhDid as pkhDid2,
3026
- resolveTinyCloudHosts as resolveTinyCloudHosts2,
3027
- resolveFailClosedTinyCloudHosts as resolveFailClosedTinyCloudHosts2,
3101
+ parseSpaceUri as parseSpaceUri2,
3102
+ resolveRegistryResource as resolveRegistryResource2,
3103
+ warnRegistryDefaultNode as warnRegistryDefaultNode2,
3104
+ NodeNotFoundError as NodeNotFoundError2,
3105
+ exactHttpOrigin as exactHttpOrigin2,
3028
3106
  createInMemoryRegistrySequenceStore as createInMemoryRegistrySequenceStore2,
3107
+ createInMemoryRegistryCacheStore as createInMemoryRegistryCacheStore2,
3108
+ refreshOwnerRegistry,
3109
+ registryOwnerKeyV2,
3029
3110
  publishLocationRecord,
3030
3111
  principalDidEquals as principalDidEquals3,
3031
3112
  parseNetworkId as parseNetworkId2,
@@ -4043,6 +4124,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4043
4124
  * ```
4044
4125
  */
4045
4126
  constructor(config = {}) {
4127
+ this.registryRefreshOwners = /* @__PURE__ */ new Set();
4046
4128
  this.signer = null;
4047
4129
  this.auth = null;
4048
4130
  this.consumedOpenKeyPendings = /* @__PURE__ */ new WeakSet();
@@ -4206,8 +4288,10 @@ var _TinyCloudNode = class _TinyCloudNode {
4206
4288
  this.config = {
4207
4289
  ...config,
4208
4290
  host: config.host ?? DEFAULT_HOST,
4291
+ registryPolicy: "fail-closed",
4209
4292
  // One store for every authorization this client creates.
4210
- registrySequenceStore: config.registrySequenceStore ?? createInMemoryRegistrySequenceStore2()
4293
+ registrySequenceStore: config.registrySequenceStore ?? createInMemoryRegistrySequenceStore2(),
4294
+ registryCacheStore: config.registryCacheStore ?? createInMemoryRegistryCacheStore2()
4211
4295
  };
4212
4296
  if (this.config.replication?.enabled) {
4213
4297
  const runtime = getNodeReplicationLoaders().runtime().then(({ ReplicationRuntime }) => {
@@ -4511,7 +4595,11 @@ var _TinyCloudNode = class _TinyCloudNode {
4511
4595
  // A session saved but never registered on this node is refused: it registers once.
4512
4596
  activate: (signal) => {
4513
4597
  assertCurrent();
4514
- return activateSessionWithHost2(host, session.delegationHeader, AbortSignal.any([signal, graph.signal]));
4598
+ const activationSignal = AbortSignal.any([signal, graph.signal]);
4599
+ if (primary && primary.delegationCid === session.delegationCid) {
4600
+ return this.activateSignedSession(primary, primary.siwe, host, activationSignal);
4601
+ }
4602
+ return this.activateResourceSession(session.delegationHeader, [spaceId], host, activationSignal);
4515
4603
  }
4516
4604
  });
4517
4605
  assertCurrent();
@@ -4647,14 +4735,38 @@ var _TinyCloudNode = class _TinyCloudNode {
4647
4735
  get requests() {
4648
4736
  return this._requestLog === void 0 ? void 0 : readOnlyRequestLog(this._requestLog);
4649
4737
  }
4650
- createServiceGraphLifetime() {
4738
+ throwRegistryRefusal(status, text) {
4739
+ if (status !== 403 && status !== 503 || !text.startsWith("registry_unavailable:") && !text.startsWith("registry_hosting_denied:")) return;
4740
+ const code = status === 503 ? "registry_unavailable" : "node_registry_entry_exists";
4741
+ throw Object.assign(new Error(status === 503 ? "The location registry is unavailable for this resource." : "The registry does not authorize this node for the requested resource."), { code, meta: { status } });
4742
+ }
4743
+ createServiceGraphLifetime(session) {
4744
+ const routedFetch = createResourceRoutingFetch(
4745
+ async (url, init) => {
4746
+ try {
4747
+ const response = await globalThis.fetch(url, init);
4748
+ if (response.status === 403 || response.status === 503) {
4749
+ this.throwRegistryRefusal(response.status, await response.clone().text());
4750
+ }
4751
+ return response;
4752
+ } catch (error) {
4753
+ if (routingFailure3(error)) throw error;
4754
+ if (new URL(url).pathname.startsWith("/encryption/") && !init?.signal?.aborted) {
4755
+ throw new NodeNotRespondingError2([new URL(url).origin], error);
4756
+ }
4757
+ throw error;
4758
+ }
4759
+ },
4760
+ {
4761
+ resolveResource: (resource, access) => this.routingDestinations(resource, access),
4762
+ bindAudience: (authorization, nodeDid) => this.bindRoutedAudience(authorization, nodeDid, session),
4763
+ verifyDestination: (target) => verifyResourceDestination2(target, globalThis.fetch.bind(globalThis))
4764
+ }
4765
+ );
4651
4766
  return new ServiceGraphLifetime(
4652
4767
  this.invokeWithRuntimePermissions,
4653
4768
  this.invokeAnyWithRuntimePermissions,
4654
- // Resolve fetch at request time. This keeps the graph-owned lifetime
4655
- // signal while preserving the SDK's existing injectable global fetch
4656
- // behavior (including adapters that install it after construction).
4657
- (url, init) => globalThis.fetch(url, init),
4769
+ routedFetch,
4658
4770
  (context) => {
4659
4771
  this.storageFull.observe(context);
4660
4772
  if (this._requestLog) observeReplicaReads(context, this._requestLog);
@@ -4662,6 +4774,75 @@ var _TinyCloudNode = class _TinyCloudNode {
4662
4774
  this._requestLog
4663
4775
  );
4664
4776
  }
4777
+ async routingDestinations(resource, access, noEntryHosts) {
4778
+ const session = this.currentTinyCloudSession();
4779
+ if (session) this.refreshRegistryOwner(pkhDid2(session.address, session.chainId));
4780
+ if (this.auth) return this.auth.resolveResourceDestinations(resource, access, noEntryHosts);
4781
+ const owner = resource.startsWith("urn:tinycloud:encryption:") ? parseNetworkId2(resource.split("/")[0]).ownerDid : parseSpaceUri2(resource)?.owner ?? (() => {
4782
+ const key = /^tinycloud:key:(z[^:]+):[^/]+$/.exec(resource);
4783
+ return key ? `did:key:${key[1]}` : void 0;
4784
+ })();
4785
+ if (!owner) throw new Error(`Cannot determine registry owner for ${resource}`);
4786
+ const subject = owner.startsWith("did:pkh:") ? pkhDid2(owner.slice(owner.lastIndexOf(":") + 1), this.config.registryChainId ?? 1) : owner;
4787
+ const resolved = await resolveRegistryResource2(subject, resource, access, {
4788
+ registryUrl: this.config.tinycloudRegistryUrl,
4789
+ registryChainId: this.config.registryChainId,
4790
+ sequenceStore: this.config.registrySequenceStore,
4791
+ cacheStore: this.config.registryCacheStore,
4792
+ onRegistryFallback: this.config.onRegistryFallback
4793
+ });
4794
+ if (resolved.verdict.status === "unregistered") {
4795
+ const hint = noEntryHosts === void 0 ? this.explicitHost ?? this.config.host : noEntryHosts[0];
4796
+ const host = hint ?? this.config.newUserHost ?? (this.config.tinycloudFallbackHosts === void 0 ? DEFAULT_HOST : this.config.tinycloudFallbackHosts?.[0]);
4797
+ if (!host || !hint && this.config.newUserHost === null) throw new NodeNotFoundError2(subject, resolved.verdict);
4798
+ const url = exactHttpOrigin2(host, "new-user host");
4799
+ warnRegistryDefaultNode2(resolved.verdict, this.config.registryCacheStore, this.config.onRegistryFallback);
4800
+ return [{ url }];
4801
+ }
4802
+ const destinations = resolved.hosts.flatMap((host) => host.urls.map((url) => ({ url, nodeDid: host.nodeDid })));
4803
+ return destinations;
4804
+ }
4805
+ /** Transport hints cannot override verified full-resource registry routing. */
4806
+ async resolveRegisteredDelegationHost(permissions, noEntryHost) {
4807
+ const resources = /* @__PURE__ */ new Map();
4808
+ let hasData = false;
4809
+ for (const permission of permissions) {
4810
+ const encryption = this.isEncryptionNetworkOperation(this.invocationServiceName(permission.service), permission.path, permission.space);
4811
+ const resource = encryption ? permission.path.split("/")[0] : permission.space;
4812
+ if (!resource) throw new ResourceNotRoutedError("delegation");
4813
+ resources.set(resource, encryption ? "encryption" : "host");
4814
+ hasData || (hasData = !encryption);
4815
+ }
4816
+ let dataHost;
4817
+ let encryptionHost;
4818
+ let unserved;
4819
+ for (const [resource, access] of resources) {
4820
+ try {
4821
+ const destinations = await this.routingDestinations(resource, access, [noEntryHost]);
4822
+ if (destinations.some(({ nodeDid }) => nodeDid === void 0)) return void 0;
4823
+ if (access === "host") dataHost ?? (dataHost = destinations[0]?.url);
4824
+ else encryptionHost ?? (encryptionHost = destinations[0]?.url);
4825
+ } catch (error) {
4826
+ if (!(error instanceof NodeNotFoundError2) || error.verdict.status !== "registered") throw error;
4827
+ unserved ?? (unserved = error);
4828
+ }
4829
+ }
4830
+ const host = hasData ? dataHost : encryptionHost;
4831
+ if (!host) throw unserved ?? new ResourceNotRoutedError("delegation");
4832
+ return host;
4833
+ }
4834
+ async bindRoutedAudience(authorization, nodeDid, stagedSession) {
4835
+ const payload = JSON.parse(new TextDecoder().decode(base64UrlDecode(authorization.split(".")[1])));
4836
+ const issuer = typeof payload === "object" && payload !== null && "iss" in payload ? payload.iss : void 0;
4837
+ const session = [
4838
+ stagedSession,
4839
+ this._serviceContext?.session,
4840
+ this.auth?.tinyCloudSession,
4841
+ ...this.runtimePermissionGrants.map((grant) => grant.session)
4842
+ ].find((candidate) => candidate?.verificationMethod === issuer);
4843
+ if (!session) throw new Error("Invocation signing session is not active");
4844
+ return rewriteInvocationAudience(authorization, nodeDid, session.jwk);
4845
+ }
4665
4846
  syncResolvedHostFromAuth() {
4666
4847
  const host = this.auth?.hosts[0];
4667
4848
  if (host) {
@@ -4775,6 +4956,29 @@ var _TinyCloudNode = class _TinyCloudNode {
4775
4956
  const session = this.currentTinyCloudSession();
4776
4957
  return session?.siwe ? this.projectSignedRecapCapabilities(session.siwe) : [];
4777
4958
  }
4959
+ /** Start one background refresh for each owner derived from verified authority. */
4960
+ refreshRegistryForCapabilities(permissions) {
4961
+ for (const permission of permissions) {
4962
+ try {
4963
+ const owner = permission.service === "tinycloud.encryption" && permission.path.startsWith("urn:tinycloud:encryption:") ? parseNetworkId2(permission.path.split("/")[0]).ownerDid : permission.space ? this.ownerDidFromSpaceId(permission.space) : void 0;
4964
+ if (owner) this.refreshRegistryOwner(owner);
4965
+ } catch {
4966
+ }
4967
+ }
4968
+ }
4969
+ refreshRegistryOwner(owner) {
4970
+ if (this.config.tinycloudRegistryUrl === null) return;
4971
+ const subject = owner.startsWith("did:pkh:") ? pkhDid2(owner.slice(owner.lastIndexOf(":") + 1), this.config.registryChainId ?? 1) : owner;
4972
+ const key = registryOwnerKeyV2(subject);
4973
+ if (this.registryRefreshOwners.has(key)) return;
4974
+ this.registryRefreshOwners.add(key);
4975
+ void refreshOwnerRegistry(subject, {
4976
+ registryUrl: this.config.tinycloudRegistryUrl,
4977
+ registryChainId: this.config.registryChainId,
4978
+ sequenceStore: this.config.registrySequenceStore,
4979
+ cacheStore: this.config.registryCacheStore
4980
+ }).catch(() => void 0);
4981
+ }
4778
4982
  /** Normalize signed capabilities without collapsing nested encryption into raw authority. */
4779
4983
  projectSignedRecapCapabilities(siwe) {
4780
4984
  const projected = this.parseRecapWithCaveats(siwe).map((entry) => {
@@ -5206,6 +5410,8 @@ var _TinyCloudNode = class _TinyCloudNode {
5206
5410
  const primarySession = this.currentTinyCloudSession();
5207
5411
  if (primarySession) {
5208
5412
  this.registerPrimarySessionGrant(primarySession);
5413
+ this.refreshRegistryOwner(pkhDid2(primarySession.address, primarySession.chainId));
5414
+ this.refreshRegistryForCapabilities(this.getVerifiedSessionCapabilities());
5209
5415
  if (this.auth?.spaceHosting === "unknown") {
5210
5416
  await this.checkSpaceHosting();
5211
5417
  this.assertSessionEpoch(epoch, "Sign-in");
@@ -5428,8 +5634,8 @@ var _TinyCloudNode = class _TinyCloudNode {
5428
5634
  const primarySession = auth.tinyCloudSession;
5429
5635
  const defaultSpaceId = this.ownedSpaceId("default");
5430
5636
  const canReusePrimaryBootstrapSession = primarySession?.spaceId === defaultSpaceId && auth.capabilityRequest === BOOTSTRAP_SESSION_REQUESTS.default;
5431
- if (steps.some((step) => step.kind === "host")) {
5432
- await this.assertMayCreateSpace();
5637
+ for (const step of steps) {
5638
+ if (step.kind === "host") await this.assertMayCreateSpace(step.spaceId);
5433
5639
  }
5434
5640
  for (const step of steps) {
5435
5641
  if (step.kind !== "session") continue;
@@ -5478,10 +5684,20 @@ var _TinyCloudNode = class _TinyCloudNode {
5478
5684
  if (!session) {
5479
5685
  throw new Error(`Missing bootstrap session for ${step.space}`);
5480
5686
  }
5481
- const activated = await activateSessionWithHost2(host, session.delegationHeader, graph.signal).catch((error) => {
5482
- graph.assertActive();
5483
- throw error;
5484
- });
5687
+ const destinations = await this.routingDestinations(step.spaceId, "host");
5688
+ let activated;
5689
+ for (const destination of destinations) {
5690
+ try {
5691
+ await verifyResourceDestination2(destination, globalThis.fetch.bind(globalThis));
5692
+ const result = await activateSessionWithHost2(destination.url, session.delegationHeader, graph.signal);
5693
+ if (!result.success) throw Object.assign(new Error("Bootstrap activation failed"), { cause: result });
5694
+ activated = result;
5695
+ } catch (error) {
5696
+ graph.assertActive();
5697
+ throw error;
5698
+ }
5699
+ }
5700
+ if (!activated) throw new Error("Bootstrap space has no destination");
5485
5701
  graph.assertActive();
5486
5702
  if (!activated.success || activated.skipped?.includes(step.spaceId)) {
5487
5703
  throw Object.assign(
@@ -6064,6 +6280,13 @@ var _TinyCloudNode = class _TinyCloudNode {
6064
6280
  if (!session) {
6065
6281
  throw new Error("Owned space hosting requires an active session");
6066
6282
  }
6283
+ await this.assertMayCreateSpace(spaceId);
6284
+ if (this.auth.registryRecordPresent) {
6285
+ const hosted = await this.hostOwnedSpaceOnce(spaceId, signal);
6286
+ if (!hosted.success) throw Object.assign(new Error(`Failed to activate listed space ${spaceId}: ${describeHostFailure2(hosted)}`), { cause: hosted });
6287
+ if (this.auth.tinyCloudSession === session) this.confirmedHostedSpaceIds.add(spaceId);
6288
+ return;
6289
+ }
6067
6290
  if (this.confirmedHostedSpaceIds.has(spaceId) || this.hostedOwnedSpaceIds.has(spaceId)) {
6068
6291
  return;
6069
6292
  }
@@ -6082,7 +6305,7 @@ var _TinyCloudNode = class _TinyCloudNode {
6082
6305
  { cause: activation }
6083
6306
  );
6084
6307
  }
6085
- await this.assertMayCreateSpace();
6308
+ await this.assertMayCreateSpace(spaceId);
6086
6309
  if (signal?.aborted) throw signal.reason;
6087
6310
  const created = await this.hostOwnedSpaceOnce(spaceId, signal);
6088
6311
  if (!created.success) {
@@ -6143,17 +6366,19 @@ var _TinyCloudNode = class _TinyCloudNode {
6143
6366
  { cause: hosted }
6144
6367
  );
6145
6368
  }
6146
- const activation = await activateSessionWithHost2(
6147
- host,
6148
- session.delegationHeader
6149
- );
6150
- if (!activation.success || activation.skipped?.includes(spaceId)) {
6151
- throw Object.assign(
6152
- new Error(
6153
- `Failed to activate session for owned space ${spaceId}: ${activation.success ? "space was skipped" : describeHostFailure2(activation)}`
6154
- ),
6155
- { cause: activation }
6369
+ if (!this.auth.registryRecordPresent) {
6370
+ const activation = await activateSessionWithHost2(
6371
+ host,
6372
+ session.delegationHeader
6156
6373
  );
6374
+ if (!activation.success || activation.skipped?.includes(spaceId)) {
6375
+ throw Object.assign(
6376
+ new Error(
6377
+ `Failed to activate session for owned space ${spaceId}: ${activation.success ? "space was skipped" : describeHostFailure2(activation)}`
6378
+ ),
6379
+ { cause: activation }
6380
+ );
6381
+ }
6157
6382
  }
6158
6383
  if (this.auth?.tinyCloudSession === session) this.hostedOwnedSpaceIds.add(spaceId);
6159
6384
  try {
@@ -6197,11 +6422,9 @@ var _TinyCloudNode = class _TinyCloudNode {
6197
6422
  * session's recap doesn't cover it and the registry has no record; the host
6198
6423
  * SIWE is idempotent server-side. Must be called after {@link signIn}.
6199
6424
  *
6200
- * Under `registryPolicy: "fail-closed"`, hosting is refused with
6201
- * `NodeRegistryEntryExistsError` when the owner's registry record does not
6202
- * list the host, or `RegistryUnavailableError` when the registry gives no
6203
- * verdict. {@link hostOwnedSpace} itself is an explicit action and is not
6204
- * guarded.
6425
+ * Current signed per-space membership is checked before both automatic and
6426
+ * explicit hosting. Registered replicas use the session grant; no-entry
6427
+ * onboarding retains the host-SIWE flow.
6205
6428
  *
6206
6429
  * @param name - The owned space name (e.g. `"secrets"`).
6207
6430
  * @returns The hosted owned-space URI.
@@ -6211,27 +6434,20 @@ var _TinyCloudNode = class _TinyCloudNode {
6211
6434
  throw new Error("Not signed in. Call signIn() first.");
6212
6435
  }
6213
6436
  const spaceId = this.ownedSpaceId(name);
6437
+ await this.assertMayCreateSpace(spaceId);
6214
6438
  if (this.hostedOwnedSpaceIds.has(spaceId) || await this.isOwnedSpaceRegistered(spaceId)) {
6215
6439
  return spaceId;
6216
6440
  }
6217
- await this.assertMayCreateSpace();
6218
6441
  return this.hostOwnedSpace(name);
6219
6442
  }
6220
- /**
6221
- * Space-creation guard (TC-1443) for the automatic hosting paths: sign-in's
6222
- * owned spaces, account bootstrap, {@link ensureOwnedSpaceHosted} and
6223
- * {@link ensurePublicSpace}. Only `registryPolicy: "fail-closed"` consults
6224
- * the registry ({@link NodeUserAuthorization.assertMayCreateSpace}); the
6225
- * default policy performs no lookup.
6226
- */
6227
- async assertMayCreateSpace() {
6228
- if (this.config.registryPolicy !== "fail-closed") return;
6229
- await this.auth.assertMayCreateSpace();
6443
+ /** Refresh full-space membership before any automatic or explicit hosting. */
6444
+ async assertMayCreateSpace(spaceId) {
6445
+ await this.routingDestinations(spaceId, "host");
6230
6446
  }
6231
6447
  /** Registry-policy settings shared by every authorization this client creates. */
6232
6448
  registryPolicyConfig() {
6233
- const { registryPolicy, newUserHost, registryChainId, registrySequenceStore, onNodeNotFound } = this.config;
6234
- return { registryPolicy, newUserHost, registryChainId, registrySequenceStore, onNodeNotFound };
6449
+ const { registryPolicy, newUserHost, registryChainId, registrySequenceStore, registryCacheStore, onNodeNotFound, onRegistryFallback } = this.config;
6450
+ return { registryPolicy, newUserHost, registryChainId, registrySequenceStore, registryCacheStore, onNodeNotFound, onRegistryFallback };
6235
6451
  }
6236
6452
  /**
6237
6453
  * Submit the host delegation for an owned space, sharing one in-flight
@@ -6437,12 +6653,9 @@ var _TinyCloudNode = class _TinyCloudNode {
6437
6653
  }
6438
6654
  stagedSessionExpiry = signedExpiry ?? persistedPolicyExpiry ?? persistedExpiry(void 0);
6439
6655
  }
6440
- const resolvedHost = await this.resolveRestoredHost(
6441
- sessionData.tinycloudHosts,
6442
- restoredAddress,
6443
- sessionData.chainId
6444
- );
6445
- const stagedHost = resolvedHost ?? this.config.host;
6656
+ const restoreHints = this.explicitHost ? [this.explicitHost] : sessionData.tinycloudHosts ?? [];
6657
+ const resolvedHost = this.config.replication?.enabled ? (await this.routingDestinations(sessionData.spaceId, "read", restoreHints))[0]?.url : void 0;
6658
+ const stagedHost = resolvedHost ?? exactHttpOrigin2(restoreHints[0] ?? this.config.host, "restored host hint");
6446
6659
  let restoredNodeFeatures;
6447
6660
  let replicaProtocolMismatch = false;
6448
6661
  if (this.config.replication?.enabled) {
@@ -6559,7 +6772,7 @@ var _TinyCloudNode = class _TinyCloudNode {
6559
6772
  this.sessionManager = stagedManager;
6560
6773
  this.sessionKeyId = stagedKeyId;
6561
6774
  this.sessionKeyJwk = stagedJwk;
6562
- if (resolvedHost) this.config.host = resolvedHost;
6775
+ this.config.host = stagedHost;
6563
6776
  if (spaceOwnerKey(serviceSession.spaceId) !== this.currentStorageOwner()) {
6564
6777
  this.storageFull.clear();
6565
6778
  this._requestLog?.clear();
@@ -6626,6 +6839,10 @@ var _TinyCloudNode = class _TinyCloudNode {
6626
6839
  if (attempt === this.latestSessionAttempt) await this.endSession();
6627
6840
  this.assertSessionEpoch(epoch, "Session restore");
6628
6841
  }
6842
+ if (this.config.replication?.enabled) {
6843
+ if (stagedAddress !== void 0) this.refreshRegistryOwner(pkhDid2(stagedAddress, stagedChainId));
6844
+ this.refreshRegistryForCapabilities(this.getVerifiedSessionCapabilities());
6845
+ }
6629
6846
  }
6630
6847
  stagePrimarySessionState(session, verificationMethod, host, sessionExpiry, recap) {
6631
6848
  if (!session?.siwe) return { cache: void 0, grants: [] };
@@ -6674,7 +6891,7 @@ var _TinyCloudNode = class _TinyCloudNode {
6674
6891
  };
6675
6892
  }
6676
6893
  stageRestoredServiceGraph(input) {
6677
- const graph = this.createServiceGraphLifetime();
6894
+ const graph = this.createServiceGraphLifetime(input.serviceSession);
6678
6895
  const core = this.auth ? new TinyCloud(this.auth, {
6679
6896
  invokeAny: graph.invokeAny,
6680
6897
  telemetry: this.config.telemetry,
@@ -6899,56 +7116,6 @@ var _TinyCloudNode = class _TinyCloudNode {
6899
7116
  spaceService.updateConfig({ sharingService });
6900
7117
  return { core, graph, serviceContext, kv, sql, duckdb, hooks, vault, encryption, capabilityRegistry, keyProvider, sharingService, delegationManager, spaceService };
6901
7118
  }
6902
- /**
6903
- * Resolve the host a restored session should target.
6904
- *
6905
- * Mirrors fresh sign-in host resolution but for the restore path:
6906
- * an explicit/pinned host always wins, then the hosts the session was
6907
- * persisted with, then a lazy registry/fallback resolution for sessions
6908
- * that predate the persisted `tinycloudHosts` field. Returns `undefined`
6909
- * only when there's nothing to resolve from (no explicit host, no
6910
- * persisted hosts, and no address/chainId) — in which case the existing
6911
- * `config.host` (default) is left in place.
6912
- *
6913
- * Resolution failures are surfaced, not swallowed: a genuinely broken
6914
- * registry lookup throws rather than silently falling back to a wrong host.
6915
- */
6916
- async resolveRestoredHost(persistedHosts, address, chainId) {
6917
- if (this.explicitHost) {
6918
- return this.explicitHost;
6919
- }
6920
- if (persistedHosts && persistedHosts.length > 0) {
6921
- return persistedHosts[0];
6922
- }
6923
- if (address === void 0 || chainId === void 0) {
6924
- return void 0;
6925
- }
6926
- if (this.config.registryPolicy === "fail-closed") {
6927
- const resolved2 = await resolveFailClosedTinyCloudHosts2(address, {
6928
- registryUrl: this.config.tinycloudRegistryUrl,
6929
- registryChainId: this.config.registryChainId,
6930
- newUserHost: this.config.newUserHost,
6931
- sequenceStore: this.config.registrySequenceStore,
6932
- onNodeNotFound: this.config.onNodeNotFound,
6933
- autoDiscoverLocalNode: this.config.autoDiscoverLocalNode,
6934
- localNodeUrl: this.config.localNodeUrl,
6935
- localLinkName: this.config.localLinkName,
6936
- expectedNodeDid: this.config.expectedNodeDid,
6937
- localNodeIdentityStore: this.config.localNodeIdentityStore
6938
- });
6939
- return resolved2.hosts[0];
6940
- }
6941
- const resolved = await resolveTinyCloudHosts2(pkhDid2(address, chainId), {
6942
- registryUrl: this.config.tinycloudRegistryUrl,
6943
- fallbackHosts: this.config.tinycloudFallbackHosts,
6944
- autoDiscoverLocalNode: this.config.autoDiscoverLocalNode,
6945
- localNodeUrl: this.config.localNodeUrl,
6946
- localLinkName: this.config.localLinkName,
6947
- expectedNodeDid: this.config.expectedNodeDid,
6948
- localNodeIdentityStore: this.config.localNodeIdentityStore
6949
- });
6950
- return resolved.hosts[0];
6951
- }
6952
7119
  /**
6953
7120
  * Resolve the currently-active TinyCloudSession, preferring the auth
6954
7121
  * layer's value (wallet mode) and falling back to the node-level
@@ -7423,10 +7590,10 @@ var _TinyCloudNode = class _TinyCloudNode {
7423
7590
  * second time. Only a value recorded for this exact host qualifies; older
7424
7591
  * nodes that omit `nodeId` fall through to the fetch.
7425
7592
  */
7426
- async fetchNodeId() {
7427
- const cached = this.auth?.nodeIdForHost(this.config.host);
7593
+ async fetchNodeId(host = this.config.host) {
7594
+ const cached = this.auth?.nodeIdForHost(host);
7428
7595
  if (cached) return cached;
7429
- const response = await fetch(`${this.config.host}/info`);
7596
+ const response = await fetch(`${host}/info`);
7430
7597
  if (!response.ok) {
7431
7598
  throw await httpResponseError(response, "Failed to fetch node info");
7432
7599
  }
@@ -7474,10 +7641,24 @@ var _TinyCloudNode = class _TinyCloudNode {
7474
7641
  )
7475
7642
  };
7476
7643
  }
7477
- async fetchEncryptionNetworkAt(host, networkId, fetchFn) {
7478
- const response = await fetchFn(
7479
- `${host}/encryption/networks/${encodeURIComponent(networkId)}`
7480
- );
7644
+ async encryptionDestination(networkId) {
7645
+ const destination = (await this.routingDestinations(networkId, "encryption"))[0];
7646
+ if (!destination) throw new Error("No encryption destination is registered");
7647
+ await verifyResourceDestination2(destination, globalThis.fetch.bind(globalThis));
7648
+ return { ...destination, nodeDid: destination.nodeDid ?? await this.fetchNodeId(destination.url) };
7649
+ }
7650
+ async fetchEncryptionNetworkAt(host, networkId, fetchFn, callerSignal) {
7651
+ let response;
7652
+ const timeout = AbortSignal.timeout(5e3);
7653
+ try {
7654
+ response = await fetchFn(`${host}/encryption/networks/${encodeURIComponent(networkId)}`, {
7655
+ signal: callerSignal ? AbortSignal.any([callerSignal, timeout]) : timeout,
7656
+ redirect: "error"
7657
+ });
7658
+ } catch (error) {
7659
+ if (routingFailure3(error) || callerSignal?.aborted || !timeout.aborted && error instanceof Error && error.name === "AbortError") throw error;
7660
+ throw new NodeNotRespondingError2([host], error);
7661
+ }
7481
7662
  if (response.status === 404) return null;
7482
7663
  if (!response.ok) {
7483
7664
  throw await httpResponseError(response, `Failed to fetch encryption network ${networkId}`);
@@ -7487,18 +7668,18 @@ var _TinyCloudNode = class _TinyCloudNode {
7487
7668
  }
7488
7669
  createEncryptionService(binding) {
7489
7670
  const graph = binding?.graph ?? this._serviceGraph;
7490
- const host = binding?.host ?? this.config.host;
7491
7671
  const session = binding?.session ?? this.requireEncryptionSession();
7492
7672
  const did = binding?.did ?? this.did;
7493
7673
  const address = binding?.address ?? this._address;
7494
7674
  const chainId = binding?.chainId ?? this._chainId;
7495
7675
  const crypto2 = this.createEncryptionCrypto();
7496
- const descriptorCache = /* @__PURE__ */ new Map();
7497
7676
  const transport = {
7498
- postDecrypt: async ({ networkId, authorization, canonicalBody, signal }) => {
7677
+ postDecrypt: async ({ targetNode, networkId, authorization, canonicalBody, signal }) => {
7499
7678
  graph.assertActive();
7679
+ const destination = await this.encryptionDestination(networkId);
7680
+ if (destination.nodeDid !== targetNode) throw new NodeIdentityMismatchError(destination.url, destination.nodeDid, targetNode);
7500
7681
  const response = await graph.fetch(
7501
- `${host}/encryption/networks/${encodeURIComponent(networkId)}/decrypt`,
7682
+ `${destination.url}/encryption/networks/${encodeURIComponent(networkId)}/decrypt`,
7502
7683
  {
7503
7684
  method: "POST",
7504
7685
  headers: {
@@ -7526,6 +7707,7 @@ var _TinyCloudNode = class _TinyCloudNode {
7526
7707
  }
7527
7708
  };
7528
7709
  return new EncryptionService({
7710
+ resolveTargetNode: async (networkId) => (await this.encryptionDestination(networkId)).nodeDid,
7529
7711
  crypto: crypto2,
7530
7712
  signer: {
7531
7713
  signDecryptInvocation: async (input) => {
@@ -7549,18 +7731,8 @@ var _TinyCloudNode = class _TinyCloudNode {
7549
7731
  node: {
7550
7732
  fetchByNetworkId: async (networkId) => {
7551
7733
  graph.assertActive();
7552
- let pending = descriptorCache.get(networkId);
7553
- if (!pending) {
7554
- pending = this.fetchEncryptionNetworkAt(host, networkId, graph.fetch).then((descriptor2) => {
7555
- if (descriptor2 === null) descriptorCache.delete(networkId);
7556
- return descriptor2;
7557
- }).catch((error) => {
7558
- descriptorCache.delete(networkId);
7559
- throw error;
7560
- });
7561
- descriptorCache.set(networkId, pending);
7562
- }
7563
- const descriptor = await pending;
7734
+ const destination = await this.encryptionDestination(networkId);
7735
+ const descriptor = await this.fetchEncryptionNetworkAt(destination.url, networkId, graph.fetch, graph.signal);
7564
7736
  graph.assertActive();
7565
7737
  return descriptor;
7566
7738
  }
@@ -7568,13 +7740,15 @@ var _TinyCloudNode = class _TinyCloudNode {
7568
7740
  wellKnown: {
7569
7741
  fetchWellKnown: async (principal, discoveryKey) => {
7570
7742
  graph.assertActive();
7743
+ const destination = await this.encryptionDestination(this.getDefaultEncryptionNetworkId());
7744
+ if (this.auth?.registryRecordPresent) return null;
7571
7745
  if (!address || !didPrincipalMatches2(principal, did)) {
7572
7746
  return null;
7573
7747
  }
7574
7748
  const publicSpaceId = makePublicSpaceId(address, chainId);
7575
7749
  const encodedKey = discoveryKey.split("/").map(encodeURIComponent).join("/");
7576
7750
  const response = await graph.fetch(
7577
- `${host}/public/${encodeURIComponent(publicSpaceId)}/kv/${encodedKey}`,
7751
+ `${destination.url}/public/${encodeURIComponent(publicSpaceId)}/kv/${encodedKey}`,
7578
7752
  { method: "GET" }
7579
7753
  );
7580
7754
  graph.assertActive();
@@ -7944,7 +8118,7 @@ var _TinyCloudNode = class _TinyCloudNode {
7944
8118
  const signature = await this.signer.signMessage(prepared.siwe);
7945
8119
  assertOwnerGraphActive();
7946
8120
  const delegationSession = this.wasmBindings.completeSessionSetup({ ...prepared, signature });
7947
- const activation = await activateSessionWithHost2(host, delegationSession.delegationHeader);
8121
+ const activation = await this.activateSignedSession(delegationSession, prepared.siwe, host);
7948
8122
  assertOwnerGraphActive();
7949
8123
  if (!activation.success) {
7950
8124
  throw Object.assign(
@@ -8263,15 +8437,16 @@ var _TinyCloudNode = class _TinyCloudNode {
8263
8437
  async getEncryptionNetwork(nameOrNetworkId = this.getDefaultEncryptionNetworkId()) {
8264
8438
  const networkId = nameOrNetworkId.startsWith("urn:tinycloud:encryption:") ? nameOrNetworkId : this.getDefaultEncryptionNetworkId(nameOrNetworkId);
8265
8439
  return this.fetchEncryptionNetworkAt(
8266
- this.config.host,
8440
+ (await this.encryptionDestination(networkId)).url,
8267
8441
  networkId,
8268
8442
  globalThis.fetch.bind(globalThis)
8269
8443
  );
8270
8444
  }
8271
8445
  async createEncryptionNetwork(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
8272
- const targetNode = await this.fetchNodeId();
8273
8446
  const ownerDid = this.did;
8274
8447
  const networkId = this.getDefaultEncryptionNetworkId(name);
8448
+ const destination = await this.encryptionDestination(networkId);
8449
+ const targetNode = destination.nodeDid;
8275
8450
  const body = {
8276
8451
  name,
8277
8452
  ownerDid,
@@ -8294,7 +8469,7 @@ var _TinyCloudNode = class _TinyCloudNode {
8294
8469
  action: NETWORK_CREATE_ACTION2,
8295
8470
  facts
8296
8471
  });
8297
- const response = await fetch(`${this.config.host}/encryption/networks`, {
8472
+ const response = await this._serviceGraph.fetch(`${destination.url}/encryption/networks`, {
8298
8473
  method: "POST",
8299
8474
  headers: {
8300
8475
  Authorization: signed.authorization,
@@ -8634,6 +8809,97 @@ var _TinyCloudNode = class _TinyCloudNode {
8634
8809
  }
8635
8810
  return { recap: exactVerifiedRecap(verified), expiresAt };
8636
8811
  }
8812
+ async activateResourceSession(delegationHeader, resources, hostHint, signal) {
8813
+ const targets = /* @__PURE__ */ new Map();
8814
+ const routedSpaces = [];
8815
+ let unserved;
8816
+ let requestedResource;
8817
+ for (const resource of new Set(resources)) {
8818
+ requestedResource ?? (requestedResource = resource);
8819
+ signal?.throwIfAborted();
8820
+ try {
8821
+ const encryption = resource.startsWith("urn:tinycloud:encryption:");
8822
+ const destinations = await this.routingDestinations(
8823
+ resource,
8824
+ encryption ? "encryption" : "host",
8825
+ [hostHint ?? this.config.host]
8826
+ );
8827
+ if (!encryption && destinations.some(({ nodeDid }) => nodeDid !== void 0)) routedSpaces.push(resource);
8828
+ for (const destination of destinations) {
8829
+ let target = targets.get(destination.url);
8830
+ if (!target) {
8831
+ target = { destination, spaces: [] };
8832
+ targets.set(destination.url, target);
8833
+ }
8834
+ if (!encryption && destination.nodeDid !== void 0) target.spaces.push(resource);
8835
+ }
8836
+ } catch (error) {
8837
+ if (!(error instanceof NodeNotFoundError2)) throw error;
8838
+ unserved ?? (unserved = error);
8839
+ }
8840
+ }
8841
+ if (targets.size === 0) throw unserved ?? new ResourceNotRoutedError(requestedResource ?? "session grant");
8842
+ const succeeded = [];
8843
+ const accepted = [];
8844
+ const failed = [];
8845
+ let refusal;
8846
+ for (const { destination: target, spaces } of targets.values()) {
8847
+ signal?.throwIfAborted();
8848
+ try {
8849
+ await verifyResourceDestination2(target, globalThis.fetch.bind(globalThis));
8850
+ const timeout = AbortSignal.timeout(5e3);
8851
+ const result = await activateSessionWithHost2(
8852
+ target.url,
8853
+ delegationHeader,
8854
+ signal ? AbortSignal.any([signal, timeout]) : timeout
8855
+ );
8856
+ this.throwRegistryRefusal(result.status, result.error ?? "");
8857
+ if (result.success) {
8858
+ const skipped = result.skipped?.find((space) => spaces.some((expected) => this.spaceIdsEqual(space, expected)));
8859
+ if (skipped) {
8860
+ failed.push({ url: target.url, status: result.status, cause: new ResourceNotRoutedError(skipped) });
8861
+ continue;
8862
+ }
8863
+ succeeded.push(target.url);
8864
+ accepted.push(result);
8865
+ } else {
8866
+ refusal = result;
8867
+ failed.push({ url: target.url, status: result.status, cause: result });
8868
+ }
8869
+ } catch (error) {
8870
+ if (signal?.aborted) throw error;
8871
+ failed.push({ url: target.url, cause: routingFailure3(error) ? error : new NodeNotRespondingError2([target.url], error) });
8872
+ }
8873
+ }
8874
+ if (failed.length > 0) {
8875
+ if (targets.size > 1) throw new PartialNodeWriteError2(succeeded, failed);
8876
+ if (refusal) return refusal;
8877
+ throw failed[0].cause;
8878
+ }
8879
+ const activated = new Set(accepted.flatMap((result) => result.activated ?? []));
8880
+ return {
8881
+ ...accepted[0],
8882
+ activated: [...activated],
8883
+ skipped: [...new Set(accepted.flatMap((result) => result.skipped ?? []))].filter((space) => !activated.has(space) && !routedSpaces.some((expected) => this.spaceIdsEqual(space, expected)))
8884
+ };
8885
+ }
8886
+ activateRoutedDelegation(delegation, signal) {
8887
+ const resources = this.operationsFromDelegation(delegation).map(
8888
+ (operation) => operation.resource?.startsWith("urn:tinycloud:encryption:") ? operation.resource.split("/")[0] : operation.spaceId ?? delegation.spaceId
8889
+ );
8890
+ return this.activateResourceSession(delegation.delegationHeader, resources, delegation.host, signal);
8891
+ }
8892
+ activateSignedSession(session, siwe, hostHint, signal) {
8893
+ const resources = this.parseRecapWithCaveats(siwe).map(
8894
+ (entry) => this.isEncryptionNetworkOperation(this.invocationServiceName(entry.service), entry.path, entry.space) ? entry.path.split("/")[0] : entry.space.startsWith("tinycloud:") ? entry.space : session.spaceId
8895
+ );
8896
+ return this.activateResourceSession(
8897
+ session.delegationHeader,
8898
+ resources.length > 0 ? resources : [session.spaceId],
8899
+ hostHint,
8900
+ signal
8901
+ );
8902
+ }
8637
8903
  /**
8638
8904
  * Install a portable runtime permission delegation into this SDK instance so
8639
8905
  * matching service calls and downstream `delegateTo()` calls can use it.
@@ -8653,16 +8919,12 @@ var _TinyCloudNode = class _TinyCloudNode {
8653
8919
  `Runtime delegation targets ${delegation.delegateDID} but this session key is ${session.verificationMethod}.`
8654
8920
  );
8655
8921
  }
8656
- const targetHost = delegation.host ?? this.config.host;
8657
8922
  let activateResult;
8658
8923
  try {
8659
- activateResult = await activateSessionWithHost2(
8660
- targetHost,
8661
- delegation.delegationHeader,
8662
- graph.signal
8663
- );
8924
+ activateResult = await this.activateRoutedDelegation(delegation, graph.signal);
8664
8925
  } catch (cause) {
8665
8926
  graph.assertActive();
8927
+ if (routingFailure3(cause)) throw cause;
8666
8928
  throw Object.assign(
8667
8929
  new Error("The TinyCloud host could not be reached to activate the delegation."),
8668
8930
  { code: "ACTIVATION_TRANSPORT_FAILED", cause }
@@ -8818,11 +9080,15 @@ var _TinyCloudNode = class _TinyCloudNode {
8818
9080
  ...prepared,
8819
9081
  signature
8820
9082
  });
8821
- const activateResult = await activateSessionWithHost2(
8822
- this.config.host,
8823
- delegatedSession.delegationHeader,
8824
- graph.signal
8825
- ).catch((error) => {
9083
+ const delegation = this.runtimeDelegationFromSession(
9084
+ delegatedSession,
9085
+ delegatedEntries,
9086
+ spaceId,
9087
+ captured,
9088
+ expiresAt,
9089
+ { siwe: prepared.siwe, signature }
9090
+ );
9091
+ const activateResult = await this.activateRoutedDelegation(delegation, graph.signal).catch((error) => {
8826
9092
  graph.assertActive();
8827
9093
  throw error;
8828
9094
  });
@@ -8838,14 +9104,6 @@ var _TinyCloudNode = class _TinyCloudNode {
8838
9104
  { cause: activateResult }
8839
9105
  );
8840
9106
  }
8841
- const delegation = this.runtimeDelegationFromSession(
8842
- delegatedSession,
8843
- delegatedEntries,
8844
- spaceId,
8845
- captured,
8846
- expiresAt,
8847
- { siwe: prepared.siwe, signature }
8848
- );
8849
9107
  graph.assertActive();
8850
9108
  assertCapturedKey?.();
8851
9109
  this.runtimePermissionGrants.push({
@@ -8990,7 +9248,7 @@ var _TinyCloudNode = class _TinyCloudNode {
8990
9248
  if (!publicSpaceId) {
8991
9249
  throw new Error("Public space not enabled. Set enablePublicSpace: true in config.");
8992
9250
  }
8993
- await this.assertMayCreateSpace();
9251
+ await this.assertMayCreateSpace(publicSpaceId);
8994
9252
  await this.auth.hostPublicSpace(publicSpaceId);
8995
9253
  const kvActions = [KV3.PUT, KV3.GET, KV3.DEL, KV3.LIST, KV3.METADATA];
8996
9254
  const abilities = { kv: { "": kvActions } };
@@ -9013,10 +9271,7 @@ var _TinyCloudNode = class _TinyCloudNode {
9013
9271
  ...prepared,
9014
9272
  signature
9015
9273
  });
9016
- const activateResult = await activateSessionWithHost2(
9017
- this.config.host,
9018
- delegationSession.delegationHeader
9019
- );
9274
+ const activateResult = await this.activateSignedSession(delegationSession, prepared.siwe);
9020
9275
  if (!activateResult.success) {
9021
9276
  throw Object.assign(
9022
9277
  new Error(`Failed to activate public space delegation: ${describeHostFailure2(activateResult)}`),
@@ -9566,10 +9821,7 @@ var _TinyCloudNode = class _TinyCloudNode {
9566
9821
  if (onPrepared) {
9567
9822
  await onPrepared(delegation);
9568
9823
  }
9569
- const activateResult = await activateSessionWithHost2(
9570
- this.config.host,
9571
- delegationHeader
9572
- );
9824
+ const activateResult = await this.activateRoutedDelegation(delegation);
9573
9825
  assertDelegationActivated(activateResult, delegation, requireActivation);
9574
9826
  return delegation;
9575
9827
  }
@@ -9603,10 +9855,7 @@ var _TinyCloudNode = class _TinyCloudNode {
9603
9855
  if (onPrepared) {
9604
9856
  await onPrepared(delegation);
9605
9857
  }
9606
- const activateResult = await activateSessionWithHost2(
9607
- targetHost,
9608
- delegationHeader
9609
- );
9858
+ const activateResult = await this.activateRoutedDelegation(delegation);
9610
9859
  assertDelegationActivated(activateResult, delegation, requireActivation);
9611
9860
  return delegation;
9612
9861
  }
@@ -10258,10 +10507,7 @@ var _TinyCloudNode = class _TinyCloudNode {
10258
10507
  ...prepared,
10259
10508
  signature
10260
10509
  });
10261
- const activateResult = await activateSessionWithHost2(
10262
- this.config.host,
10263
- delegationSession.delegationHeader
10264
- );
10510
+ const activateResult = await this.activateSignedSession(delegationSession, prepared.siwe);
10265
10511
  if (!activateResult.success) {
10266
10512
  throw Object.assign(
10267
10513
  new Error(`Failed to activate delegation: ${describeHostFailure2(activateResult)}`),
@@ -10306,10 +10552,7 @@ var _TinyCloudNode = class _TinyCloudNode {
10306
10552
  ...publicPrepared,
10307
10553
  signature: publicSignature
10308
10554
  });
10309
- const publicActivateResult = await activateSessionWithHost2(
10310
- this.config.host,
10311
- publicSession.delegationHeader
10312
- );
10555
+ const publicActivateResult = await this.activateSignedSession(publicSession, publicPrepared.siwe);
10313
10556
  if (publicActivateResult.success) {
10314
10557
  result.publicDelegation = {
10315
10558
  cid: publicSession.delegationCid,
@@ -10415,11 +10658,7 @@ var _TinyCloudNode = class _TinyCloudNode {
10415
10658
  ...prepared,
10416
10659
  signature
10417
10660
  });
10418
- const activateResult = await activateSessionWithHost2(
10419
- targetHost,
10420
- invokerSession.delegationHeader,
10421
- graph.signal
10422
- ).catch((error) => {
10661
+ const activateResult = await this.activateSignedSession(invokerSession, prepared.siwe, targetHost, graph.signal).catch((error) => {
10423
10662
  graph.assertActive();
10424
10663
  throw error;
10425
10664
  });
@@ -10546,10 +10785,7 @@ var _TinyCloudNode = class _TinyCloudNode {
10546
10785
  ...prepared,
10547
10786
  signature
10548
10787
  });
10549
- const activateResult = await activateSessionWithHost2(
10550
- targetHost,
10551
- subDelegationSession.delegationHeader
10552
- );
10788
+ const activateResult = await this.activateSignedSession(subDelegationSession, prepared.siwe, targetHost);
10553
10789
  if (!activateResult.success) {
10554
10790
  throw Object.assign(
10555
10791
  new Error(`Failed to activate sub-delegation: ${describeHostFailure2(activateResult)}`),
@@ -10652,7 +10888,7 @@ import {
10652
10888
  SpaceService as SpaceService2,
10653
10889
  SpaceErrorCodes,
10654
10890
  createSpaceService,
10655
- parseSpaceUri as parseSpaceUri2,
10891
+ parseSpaceUri as parseSpaceUri3,
10656
10892
  buildSpaceUri,
10657
10893
  makePublicSpaceId as makePublicSpaceId2,
10658
10894
  Space
@@ -10689,7 +10925,10 @@ export {
10689
10925
  KVService3 as KVService,
10690
10926
  ManifestValidationError3 as ManifestValidationError,
10691
10927
  MemorySessionStorage,
10928
+ NodeIdentityMismatchError2 as NodeIdentityMismatchError,
10929
+ NodeNotRespondingError3 as NodeNotRespondingError,
10692
10930
  NodeUserAuthorization,
10931
+ PartialNodeWriteError3 as PartialNodeWriteError,
10693
10932
  PermissionNotInManifestError2 as PermissionNotInManifestError,
10694
10933
  PrefixedKVService,
10695
10934
  ProtocolMismatchError2 as ProtocolMismatchError,
@@ -10756,7 +10995,7 @@ export {
10756
10995
  parseExpiry2 as parseExpiry,
10757
10996
  parsePkhDid,
10758
10997
  parseSecretCatalogKey,
10759
- parseSpaceUri2 as parseSpaceUri,
10998
+ parseSpaceUri3 as parseSpaceUri,
10760
10999
  pkhDid3 as pkhDid,
10761
11000
  principalDid,
10762
11001
  principalDidEquals4 as principalDidEquals,