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

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 +499 -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 +525 -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 +516 -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 +526 -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/index.js CHANGED
@@ -17443,7 +17443,7 @@ import {
17443
17443
  canonicalizeRecapCaveats as canonicalizeRecapCaveats3,
17444
17444
  isCapabilitySubset as isCapabilitySubset3,
17445
17445
  principalDid,
17446
- parseSpaceUri as parseSpaceUri2
17446
+ parseSpaceUri as parseSpaceUri3
17447
17447
  } from "@tinycloud/sdk-core";
17448
17448
  import { parseUcanGrant as replicaParseUcanGrant } from "@tinycloud/replica";
17449
17449
  function reportReplicaDenial(result, reason, onEvent, context) {
@@ -17625,7 +17625,7 @@ function replicationScopeFromSignedSession(input) {
17625
17625
  for (const resource of Object.keys(grant.att)) {
17626
17626
  const space = resource.match(/^(?:tinycloud:\/\/)?(.+?)\/kv(?:\/|$)/)?.[1];
17627
17627
  if (space !== input.persistedSpace) continue;
17628
- const owner = parseSpaceUri2(space);
17628
+ const owner = parseSpaceUri3(space);
17629
17629
  if (owner?.owner && owner.address && owner.chainId) return { space, principal: owner.owner };
17630
17630
  }
17631
17631
  return void 0;
@@ -18151,6 +18151,7 @@ import {
18151
18151
  decodeBase64Url,
18152
18152
  principalDidEquals
18153
18153
  } from "@tinycloud/sdk-core";
18154
+ import { routingFailure } from "@tinycloud/sdk-services";
18154
18155
  var RUNTIME_DELEGATION_REJECTIONS = [
18155
18156
  "host_mismatch",
18156
18157
  "expired",
@@ -18192,7 +18193,8 @@ var SERVICE_BY_SHORT_NAME = {
18192
18193
  kv: "tinycloud.kv",
18193
18194
  sql: "tinycloud.sql",
18194
18195
  duckdb: "tinycloud.duckdb",
18195
- hooks: "tinycloud.hooks"
18196
+ hooks: "tinycloud.hooks",
18197
+ capabilities: "tinycloud.capabilities"
18196
18198
  };
18197
18199
  function authorizationWithoutBearer(authorization) {
18198
18200
  return authorization.replace(/^Bearer /i, "");
@@ -18323,8 +18325,8 @@ function signedAuthorityFromCompactUcan(authorization) {
18323
18325
  ));
18324
18326
  continue;
18325
18327
  }
18326
- const match = /^(?:tinycloud:\/\/([^/]+)|(tinycloud:[^/]+))\/(kv|sql|duckdb|hooks)(?:\/|$)(.*)$/.exec(resource);
18327
- if (!match) {
18328
+ const match = /^(?:tinycloud:\/\/([^/]+)|(tinycloud:[^/]+))\/([^/]+)(?:\/|$)(.*)$/.exec(resource);
18329
+ if (!match || !Object.prototype.hasOwnProperty.call(SERVICE_BY_SHORT_NAME, match[3])) {
18328
18330
  throw new Error(
18329
18331
  `Validated runtime delegation has an unsupported signed resource '${resource}'.`
18330
18332
  );
@@ -18469,13 +18471,8 @@ function resourcesMatch(actual, expected) {
18469
18471
  return JSON.stringify(actual.map(comparable)) === JSON.stringify(expected.map(comparable));
18470
18472
  }
18471
18473
  async function activateValidatedRuntimeDelegation(node, delegation, options) {
18472
- const host = normalizedHost(options.host);
18473
- if (delegation.host !== void 0 && normalizedHost(delegation.host) !== host) {
18474
- reject(
18475
- "host_mismatch",
18476
- `Runtime delegation host '${delegation.host}' does not match expected host '${options.host}'.`
18477
- );
18478
- }
18474
+ let host = normalizedHost(options.host);
18475
+ const differentHost = delegation.host !== void 0 && normalizedHost(delegation.host) !== host;
18479
18476
  if (!(delegation.expiry instanceof Date) || Number.isNaN(delegation.expiry.getTime())) {
18480
18477
  reject("authority_invalid", "Runtime delegation has an invalid expiry.");
18481
18478
  }
@@ -18519,6 +18516,13 @@ async function activateValidatedRuntimeDelegation(node, delegation, options) {
18519
18516
  if (options.authorize !== void 0 && !options.authorize(signed2.permissions)) {
18520
18517
  throw new Error("Runtime delegation is not authorized for this runtime.");
18521
18518
  }
18519
+ if (differentHost) {
18520
+ const registeredHost = await node.resolveRegisteredDelegationHost?.(signed2.permissions, host);
18521
+ if (registeredHost === void 0) {
18522
+ reject("host_mismatch", `Runtime delegation host '${delegation.host}' does not match expected host '${options.host}'.`);
18523
+ }
18524
+ host = normalizedHost(registeredHost);
18525
+ }
18522
18526
  const previouslyInstalled = node.getRuntimePermissionDelegations().find((candidate) => candidate.cid === cid);
18523
18527
  if (previouslyInstalled !== void 0 && previouslyInstalled.delegationHeader.Authorization !== authorization) {
18524
18528
  reject(
@@ -18562,6 +18566,7 @@ async function activateValidatedRuntimeDelegation(node, delegation, options) {
18562
18566
  await node.useRuntimeDelegation(installedCandidate);
18563
18567
  }
18564
18568
  } catch (error) {
18569
+ if (routingFailure(error)) throw error;
18565
18570
  reject(
18566
18571
  activationRejectionReason(error),
18567
18572
  error instanceof Error ? error.message : String(error)
@@ -18580,6 +18585,7 @@ async function activateValidatedRuntimeDelegation(node, delegation, options) {
18580
18585
  "Runtime delegation activation installed authority that differs from the validated authority."
18581
18586
  );
18582
18587
  }
18588
+ node.refreshRegistryForCapabilities?.(signed2.permissions);
18583
18589
  return {
18584
18590
  cid,
18585
18591
  delegation: installed,
@@ -18689,6 +18695,15 @@ function deserializeDelegation(data) {
18689
18695
  }
18690
18696
 
18691
18697
  // src/TinyCloudNode.ts
18698
+ import {
18699
+ createResourceRoutingFetch,
18700
+ verifyResourceDestination as verifyResourceDestination2,
18701
+ NodeNotRespondingError as NodeNotRespondingError2,
18702
+ NodeIdentityMismatchError,
18703
+ PartialNodeWriteError as PartialNodeWriteError2,
18704
+ ResourceNotRoutedError,
18705
+ routingFailure as routingFailure3
18706
+ } from "@tinycloud/sdk-services";
18692
18707
  import { generateNonce, SiweMessage as SiweMessage3 } from "siwe";
18693
18708
  import { didKeyFromEd25519PublicKey } from "@tinycloud/share-envelope";
18694
18709
 
@@ -18762,9 +18777,15 @@ import {
18762
18777
  verifyDidKeyEd25519Signature,
18763
18778
  canonicalizeAddress as canonicalizeAddress2,
18764
18779
  pkhDid as pkhDid2,
18765
- resolveTinyCloudHosts as resolveTinyCloudHosts2,
18766
- resolveFailClosedTinyCloudHosts as resolveFailClosedTinyCloudHosts2,
18780
+ parseSpaceUri as parseSpaceUri2,
18781
+ resolveRegistryResource as resolveRegistryResource2,
18782
+ warnRegistryDefaultNode as warnRegistryDefaultNode2,
18783
+ NodeNotFoundError as NodeNotFoundError2,
18784
+ exactHttpOrigin as exactHttpOrigin2,
18767
18785
  createInMemoryRegistrySequenceStore as createInMemoryRegistrySequenceStore2,
18786
+ createInMemoryRegistryCacheStore as createInMemoryRegistryCacheStore2,
18787
+ refreshOwnerRegistry,
18788
+ registryOwnerKeyV2,
18768
18789
  publishLocationRecord,
18769
18790
  principalDidEquals as principalDidEquals3,
18770
18791
  parseNetworkId as parseNetworkId2,
@@ -18811,11 +18832,16 @@ import {
18811
18832
  principalDidEquals as principalDidEquals2,
18812
18833
  resourceCapabilitiesToAbilitiesMap,
18813
18834
  resourceCapabilitiesToSpaceAbilitiesMap,
18814
- resolveTinyCloudHosts,
18815
- resolveFailClosedTinyCloudHosts,
18816
- lookupOwnerRegistryVerdict,
18835
+ resolveRegistryResource,
18836
+ warnRegistryDefaultNode,
18837
+ parseSpaceUri,
18838
+ NodeNotFoundError,
18839
+ RegistryUnavailableError,
18840
+ discoverLocalTinyCloudNode,
18841
+ exactHttpOrigin,
18817
18842
  assertSpaceCreationAllowed,
18818
18843
  createInMemoryRegistrySequenceStore,
18844
+ createInMemoryRegistryCacheStore,
18819
18845
  registryOwnerSubjectV1,
18820
18846
  EXPIRY,
18821
18847
  assertExpiryWithinTechnicalLimit,
@@ -18838,7 +18864,13 @@ import {
18838
18864
  ENCRYPTION
18839
18865
  } from "@tinycloud/sdk-core";
18840
18866
  import { parseEncryptionResource } from "@tinycloud/sdk-services/encryption/resource";
18841
- import { parseNetworkId } from "@tinycloud/sdk-services";
18867
+ import {
18868
+ parseNetworkId,
18869
+ verifyResourceDestination,
18870
+ NodeNotRespondingError,
18871
+ PartialNodeWriteError,
18872
+ routingFailure as routingFailure2
18873
+ } from "@tinycloud/sdk-services";
18842
18874
 
18843
18875
  // src/authorization/appKeyHostBinding.ts
18844
18876
  var DOMAIN_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
@@ -19116,11 +19148,12 @@ var NodeUserAuthorization = class {
19116
19148
  this.tinycloudHosts = config.tinycloudHosts;
19117
19149
  this.tinycloudRegistryUrl = config.tinycloudRegistryUrl;
19118
19150
  this.tinycloudFallbackHosts = config.tinycloudFallbackHosts;
19119
- this.registryPolicy = config.registryPolicy;
19120
19151
  this.newUserHost = config.newUserHost;
19121
19152
  this.registryChainId = config.registryChainId;
19122
19153
  this.registrySequenceStore = config.registrySequenceStore ?? createInMemoryRegistrySequenceStore();
19154
+ this.registryCacheStore = config.registryCacheStore ?? createInMemoryRegistryCacheStore();
19123
19155
  this.onNodeNotFound = config.onNodeNotFound;
19156
+ this.onRegistryFallback = config.onRegistryFallback;
19124
19157
  this.autoDiscoverLocalNode = config.autoDiscoverLocalNode;
19125
19158
  this.localNodeUrl = config.localNodeUrl;
19126
19159
  this.localLinkName = config.localLinkName;
@@ -19188,13 +19221,8 @@ var NodeUserAuthorization = class {
19188
19221
  * undefined for missing SIWEs and the SDK then treats the session as
19189
19222
  * expired-at-epoch-zero.
19190
19223
  *
19191
- * @param hosts - The TinyCloud hosts this session was created against,
19192
- * as persisted in {@link PersistedSessionData.tinycloudHosts}. When
19193
- * present (and non-empty) they are adopted directly so the restored
19194
- * session resolves to the same node as the original sign-in without
19195
- * re-running registry/fallback resolution. When absent (old session)
19196
- * hosts are resolved lazily on the first host-needing call via
19197
- * {@link ensureTinyCloudHosts}.
19224
+ * @param hosts - Persisted host hints for genuine no-entry onboarding.
19225
+ * Current signed registry membership always takes precedence.
19198
19226
  */
19199
19227
  setRestoredTinyCloudSession(session, hosts) {
19200
19228
  const address = canonicalizeAddress(session.address);
@@ -19256,89 +19284,84 @@ var NodeUserAuthorization = class {
19256
19284
  signature: session.signature
19257
19285
  };
19258
19286
  }
19259
- /**
19260
- * Ensure `tinycloudHosts` are resolved before a host-needing call.
19261
- *
19262
- * Fresh sign-in resolves hosts up front; a restored session may not have
19263
- * (old persisted sessions predate {@link PersistedSessionData.tinycloudHosts}).
19264
- * This guard makes a restored session resolve the node exactly like a fresh
19265
- * sign-in — persisted hosts are preferred (already set by
19266
- * {@link setRestoredTinyCloudSession}), otherwise the registry/fallback
19267
- * resolution runs lazily here. Idempotent: {@link resolveTinyCloudHostsForSignIn}
19268
- * early-returns when hosts are already set.
19269
- *
19270
- * Throws if hosts are unset and the restored session has no address/chainId
19271
- * to resolve from — a real failure that must surface, not be masked.
19272
- */
19287
+ /** Refresh the primary space's registry destinations, including after restore. */
19273
19288
  async ensureTinyCloudHosts() {
19274
- if (this.tinycloudHosts && this.tinycloudHosts.length > 0) {
19275
- return;
19276
- }
19277
19289
  if (this._address === void 0 || this._chainId === void 0) {
19278
- throw new Error(
19279
- "Cannot resolve TinyCloud hosts: no address/chainId available. Sign in or restore a session first."
19280
- );
19290
+ throw new Error("Cannot resolve TinyCloud hosts: sign in or restore a session first.");
19281
19291
  }
19282
- await this.resolveTinyCloudHostsForSignIn(this._address, this._chainId);
19292
+ await this.resolveTinyCloudHostsForSignIn(this._address, this._chainId, this._tinyCloudSession?.spaceId);
19283
19293
  }
19284
- async resolveTinyCloudHostsForSignIn(address, chainId) {
19285
- if (this.tinycloudHosts && this.tinycloudHosts.length > 0) {
19286
- return;
19287
- }
19288
- if (this.registryPolicy === "fail-closed") {
19289
- const resolved2 = await resolveFailClosedTinyCloudHosts(address, {
19290
- registryUrl: this.tinycloudRegistryUrl,
19291
- registryChainId: this.registryChainId,
19292
- newUserHost: this.newUserHost,
19293
- sequenceStore: this.registrySequenceStore,
19294
- onNodeNotFound: this.onNodeNotFound,
19295
- autoDiscoverLocalNode: this.autoDiscoverLocalNode,
19296
- localNodeUrl: this.localNodeUrl,
19297
- localLinkName: this.localLinkName,
19298
- expectedNodeDid: this.expectedNodeDid,
19299
- localNodeIdentityStore: this.localNodeIdentityStore
19300
- });
19301
- this.tinycloudHosts = resolved2.hosts;
19302
- this.registryVerdict = resolved2.verdict;
19303
- return;
19304
- }
19305
- const subject = pkhDid(address, chainId);
19306
- const resolved = await resolveTinyCloudHosts(subject, {
19307
- registryUrl: this.tinycloudRegistryUrl,
19308
- fallbackHosts: this.tinycloudFallbackHosts,
19309
- autoDiscoverLocalNode: this.autoDiscoverLocalNode,
19310
- localNodeUrl: this.localNodeUrl,
19311
- localLinkName: this.localLinkName,
19312
- expectedNodeDid: this.expectedNodeDid,
19313
- localNodeIdentityStore: this.localNodeIdentityStore
19314
- });
19315
- this.tinycloudHosts = resolved.hosts;
19294
+ async resolveTinyCloudHostsForSignIn(address, chainId, restoredSpaceId) {
19295
+ const spaceId = restoredSpaceId ?? this.resolveSignInCapabilities(address, chainId).spaceId;
19296
+ const destinations = await this.resolveResourceDestinations(spaceId, "read");
19297
+ for (const destination of destinations) await verifyResourceDestination(destination, globalThis.fetch.bind(globalThis));
19298
+ this.tinycloudHosts = destinations.map(({ url }) => url);
19316
19299
  }
19317
- /**
19318
- * Space-creation guard (TC-1443). Under `registryPolicy: "fail-closed"`,
19319
- * throws unless the owner's registry verdict allows a space on the primary
19320
- * host: `NodeRegistryEntryExistsError` when the owner's record does not
19321
- * list it, `RegistryUnavailableError` when the registry gives no
19322
- * verdict. Reuses this session's sign-in verdict unless it was
19323
- * `unavailable`, else looks it up now (explicit-host apps). Under the
19324
- * default policy it returns at once and performs no lookup.
19325
- */
19326
- async assertMayCreateSpace() {
19327
- if (this.registryPolicy !== "fail-closed") return;
19328
- if (this._address === void 0) {
19329
- throw new Error("Must be signed in to create a space");
19300
+ /** Current full-resource registry routing; saved hosts are only no-entry hints. */
19301
+ async resolveResourceDestinations(resource, access, noEntryHosts = this.tinycloudHosts ?? []) {
19302
+ const owner = resource.startsWith("urn:tinycloud:encryption:") ? parseNetworkId(resource.split("/")[0]).ownerDid : parseSpaceUri(resource)?.owner ?? (() => {
19303
+ const key2 = /^tinycloud:key:(z[^:]+):[^/]+$/.exec(resource);
19304
+ return key2 ? `did:key:${key2[1]}` : void 0;
19305
+ })();
19306
+ if (!owner) throw new Error(`Cannot determine registry owner for ${resource}`);
19307
+ const subject = owner.startsWith("did:pkh:") ? registryOwnerSubjectV1(owner.slice(owner.lastIndexOf(":") + 1), this.registryChainId) : owner;
19308
+ for (let attempt = 1; ; attempt++) {
19309
+ try {
19310
+ const resolved = await resolveRegistryResource(subject, resource, access, {
19311
+ registryUrl: this.tinycloudRegistryUrl,
19312
+ registryChainId: this.registryChainId,
19313
+ sequenceStore: this.registrySequenceStore,
19314
+ cacheStore: this.registryCacheStore,
19315
+ onRegistryFallback: this.onRegistryFallback
19316
+ });
19317
+ this.registryVerdict = resolved.verdict;
19318
+ if (resolved.verdict.status === "unregistered") {
19319
+ if (noEntryHosts.length === 0 && (this.autoDiscoverLocalNode ?? true)) {
19320
+ const local = await discoverLocalTinyCloudNode({
19321
+ localNodeUrl: this.localNodeUrl,
19322
+ localLinkName: this.localLinkName,
19323
+ expectedNodeDid: this.expectedNodeDid,
19324
+ identityStore: this.localNodeIdentityStore
19325
+ });
19326
+ if (local) {
19327
+ warnRegistryDefaultNode(resolved.verdict, this.registryCacheStore, this.onRegistryFallback);
19328
+ return [{ url: local.url }];
19329
+ }
19330
+ }
19331
+ const host = noEntryHosts[0] ?? this.newUserHost ?? (this.tinycloudFallbackHosts === void 0 ? "https://node.tinycloud.xyz" : this.tinycloudFallbackHosts?.[0]);
19332
+ if (!host || this.newUserHost === null && noEntryHosts.length === 0) throw new NodeNotFoundError(subject, resolved.verdict);
19333
+ const url = exactHttpOrigin(host, "new-user host");
19334
+ warnRegistryDefaultNode(resolved.verdict, this.registryCacheStore, this.onRegistryFallback);
19335
+ return [{ url }];
19336
+ }
19337
+ const destinations = resolved.hosts.flatMap((host) => host.urls.map((url) => ({ url, nodeDid: host.nodeDid })));
19338
+ return destinations;
19339
+ } catch (error) {
19340
+ if (!(error instanceof NodeNotFoundError) && !(error instanceof RegistryUnavailableError)) throw error;
19341
+ if (!this.onNodeNotFound) throw error;
19342
+ const decision = await this.onNodeNotFound({ error, attempt });
19343
+ if (decision.action === "retry") continue;
19344
+ if (decision.action === "use-host" && error.verdict?.status === "unregistered") {
19345
+ const url = exactHttpOrigin(decision.host, "new-user host");
19346
+ warnRegistryDefaultNode(error.verdict, this.registryCacheStore, this.onRegistryFallback);
19347
+ return [{ url }];
19348
+ }
19349
+ throw error;
19350
+ }
19330
19351
  }
19331
- await this.ensureTinyCloudHosts();
19332
- const subject = registryOwnerSubjectV1(this._address, this.registryChainId);
19333
- let verdict = this.registryVerdict;
19334
- if (verdict?.subject !== subject || verdict.status === "unavailable") {
19335
- verdict = await lookupOwnerRegistryVerdict(subject, {
19336
- registryUrl: this.tinycloudRegistryUrl,
19337
- sequenceStore: this.registrySequenceStore
19338
- });
19339
- this.registryVerdict = verdict;
19352
+ }
19353
+ get registryRecordPresent() {
19354
+ return this.registryVerdict?.status === "registered";
19355
+ }
19356
+ /** Refresh membership before hosting; a saved no-entry verdict never authorizes a later creation. */
19357
+ async assertMayCreateSpace(spaceId = this._tinyCloudSession?.spaceId, host = this.primaryTinyCloudHost) {
19358
+ if (!spaceId) throw new Error("Must be signed in to create a space");
19359
+ const destinations = await this.resolveResourceDestinations(spaceId, "host");
19360
+ if (!destinations.some((destination) => destination.url === host)) {
19361
+ const subject = parseSpaceUri(spaceId)?.owner;
19362
+ if (!subject || !this.registryVerdict) throw new Error("Cannot resolve space owner");
19363
+ assertSpaceCreationAllowed(this.registryVerdict, host, spaceId);
19340
19364
  }
19341
- assertSpaceCreationAllowed(verdict, this.primaryTinyCloudHost);
19342
19365
  }
19343
19366
  requireTinyCloudHosts() {
19344
19367
  if (!this.tinycloudHosts || this.tinycloudHosts.length === 0) {
@@ -19784,6 +19807,31 @@ var NodeUserAuthorization = class {
19784
19807
  checkCurrent();
19785
19808
  const host = context?.host ?? this.primaryTinyCloudHost;
19786
19809
  const spaceId = context?.spaceId ?? (typeof targetSpaceId === "string" ? targetSpaceId : this._tinyCloudSession.spaceId);
19810
+ const destinations = await this.resolveResourceDestinations(spaceId, "host");
19811
+ if (this.registryVerdict?.status === "registered") {
19812
+ const results = [];
19813
+ for (const destination of destinations) {
19814
+ checkCurrent();
19815
+ try {
19816
+ await verifyResourceDestination(destination, globalThis.fetch.bind(globalThis));
19817
+ const result = await activateSessionWithHost(destination.url, this._tinyCloudSession.delegationHeader, signal);
19818
+ results.push(result.skipped?.includes(spaceId) ? { ...result, success: false, status: 403, error: "The session does not activate the requested space" } : result);
19819
+ } catch (error) {
19820
+ if (routingFailure2(error) || signal?.aborted) throw error;
19821
+ throw new NodeNotRespondingError([destination.url], error);
19822
+ }
19823
+ }
19824
+ const failed = results.flatMap((result, index) => result.success ? [] : [{ url: destinations[index].url, status: result.status }]);
19825
+ if (failed.length) {
19826
+ if (destinations.length === 1) return results[0];
19827
+ throw new PartialNodeWriteError(destinations.filter((_, index) => results[index].success).map(({ url }) => url), failed);
19828
+ }
19829
+ return {
19830
+ ...results[0],
19831
+ activated: [...new Set(results.flatMap((result) => result.activated ?? []))],
19832
+ skipped: [...new Set(results.flatMap((result) => result.skipped ?? []))]
19833
+ };
19834
+ }
19787
19835
  const peerId = await fetchPeerId(host, spaceId, signal);
19788
19836
  checkCurrent();
19789
19837
  const siwe = this.wasm.generateHostSIWEMessage(
@@ -19854,13 +19902,45 @@ var NodeUserAuthorization = class {
19854
19902
  if (!this._tinyCloudSession) {
19855
19903
  throw new Error("Must be signed in to ensure space exists");
19856
19904
  }
19857
- await this.ensureTinyCloudHosts();
19858
- const host = this.primaryTinyCloudHost;
19859
19905
  const primarySpaceId = this._tinyCloudSession.spaceId;
19860
- const result = await activateSessionWithHost(
19861
- host,
19862
- this._tinyCloudSession.delegationHeader
19863
- );
19906
+ const primary = await this.resolveResourceDestinations(primarySpaceId, "read");
19907
+ this.tinycloudHosts = primary.map(({ url }) => url);
19908
+ const targets = new Map(primary.map((destination) => [destination.url, destination]));
19909
+ if (this.registryVerdict?.status === "registered" && this.registryVerdict.record) {
19910
+ const record = this.registryVerdict.record;
19911
+ const resources = Object.keys(extractRecapAttenuations(this._tinyCloudSession.siwe ?? ""));
19912
+ for (const space of record.spaces) {
19913
+ const registeredSpace = parseSpaceUri(space.space);
19914
+ if (!resources.some((resource) => {
19915
+ const fullSpace = resource.split("/")[0];
19916
+ const requestedSpace = parseSpaceUri(fullSpace);
19917
+ return fullSpace === space.space || registeredSpace !== null && requestedSpace !== null && registeredSpace.owner === requestedSpace.owner && registeredSpace.name === requestedSpace.name;
19918
+ })) continue;
19919
+ for (const membership of space.nodes) {
19920
+ const node = record.nodes.find((candidate) => candidate.did === membership.did);
19921
+ targets.set(node.url, { url: node.url, nodeDid: node.did });
19922
+ }
19923
+ }
19924
+ if (record.encryptionNode && resources.some((resource) => resource.startsWith("urn:tinycloud:encryption:"))) {
19925
+ const node = record.nodes.find((candidate) => candidate.did === record.encryptionNode);
19926
+ targets.set(node.url, { url: node.url, nodeDid: node.did });
19927
+ }
19928
+ }
19929
+ let result;
19930
+ for (const destination of targets.values()) {
19931
+ await verifyResourceDestination(destination, globalThis.fetch.bind(globalThis));
19932
+ let activated;
19933
+ try {
19934
+ activated = await activateSessionWithHost(destination.url, this._tinyCloudSession.delegationHeader);
19935
+ } catch (error) {
19936
+ throw new NodeNotRespondingError([destination.url], error);
19937
+ }
19938
+ if (!activated.success && activated.status !== 404) {
19939
+ throw Object.assign(new Error(`Failed to activate session: ${describeHostFailure(activated)}`), { cause: activated });
19940
+ }
19941
+ if (destination.url === primary[0].url) result = activated;
19942
+ }
19943
+ if (!result) throw new Error("Primary space activation did not return a result");
19864
19944
  this.recordActivationSkippedSpaces(result, primarySpaceId);
19865
19945
  if (!result.success && result.status !== 404) {
19866
19946
  throw Object.assign(
@@ -19908,7 +19988,7 @@ var NodeUserAuthorization = class {
19908
19988
  /** {@link assertMayCreateSpace}, reporting a refusal to the creation handler before rethrowing it. */
19909
19989
  async guardSpaceCreation(handler, context) {
19910
19990
  try {
19911
- await this.assertMayCreateSpace();
19991
+ await this.assertMayCreateSpace(context.spaceId, context.host);
19912
19992
  } catch (error) {
19913
19993
  const failure = error instanceof Error ? error : new Error(String(error));
19914
19994
  handler.onSpaceCreationFailed?.(context, failure);
@@ -21830,6 +21910,7 @@ var _TinyCloudNode = class _TinyCloudNode {
21830
21910
  * ```
21831
21911
  */
21832
21912
  constructor(config = {}) {
21913
+ this.registryRefreshOwners = /* @__PURE__ */ new Set();
21833
21914
  this.signer = null;
21834
21915
  this.auth = null;
21835
21916
  this.consumedOpenKeyPendings = /* @__PURE__ */ new WeakSet();
@@ -21993,8 +22074,10 @@ var _TinyCloudNode = class _TinyCloudNode {
21993
22074
  this.config = {
21994
22075
  ...config,
21995
22076
  host: config.host ?? DEFAULT_HOST,
22077
+ registryPolicy: "fail-closed",
21996
22078
  // One store for every authorization this client creates.
21997
- registrySequenceStore: config.registrySequenceStore ?? createInMemoryRegistrySequenceStore2()
22079
+ registrySequenceStore: config.registrySequenceStore ?? createInMemoryRegistrySequenceStore2(),
22080
+ registryCacheStore: config.registryCacheStore ?? createInMemoryRegistryCacheStore2()
21998
22081
  };
21999
22082
  if (this.config.replication?.enabled) {
22000
22083
  const runtime = getNodeReplicationLoaders().runtime().then(({ ReplicationRuntime: ReplicationRuntime2 }) => {
@@ -22298,7 +22381,11 @@ var _TinyCloudNode = class _TinyCloudNode {
22298
22381
  // A session saved but never registered on this node is refused: it registers once.
22299
22382
  activate: (signal) => {
22300
22383
  assertCurrent();
22301
- return activateSessionWithHost2(host, session.delegationHeader, AbortSignal.any([signal, graph.signal]));
22384
+ const activationSignal = AbortSignal.any([signal, graph.signal]);
22385
+ if (primary && primary.delegationCid === session.delegationCid) {
22386
+ return this.activateSignedSession(primary, primary.siwe, host, activationSignal);
22387
+ }
22388
+ return this.activateResourceSession(session.delegationHeader, [spaceId], host, activationSignal);
22302
22389
  }
22303
22390
  });
22304
22391
  assertCurrent();
@@ -22434,14 +22521,38 @@ var _TinyCloudNode = class _TinyCloudNode {
22434
22521
  get requests() {
22435
22522
  return this._requestLog === void 0 ? void 0 : readOnlyRequestLog(this._requestLog);
22436
22523
  }
22437
- createServiceGraphLifetime() {
22524
+ throwRegistryRefusal(status, text) {
22525
+ if (status !== 403 && status !== 503 || !text.startsWith("registry_unavailable:") && !text.startsWith("registry_hosting_denied:")) return;
22526
+ const code = status === 503 ? "registry_unavailable" : "node_registry_entry_exists";
22527
+ 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 } });
22528
+ }
22529
+ createServiceGraphLifetime(session) {
22530
+ const routedFetch = createResourceRoutingFetch(
22531
+ async (url, init2) => {
22532
+ try {
22533
+ const response = await globalThis.fetch(url, init2);
22534
+ if (response.status === 403 || response.status === 503) {
22535
+ this.throwRegistryRefusal(response.status, await response.clone().text());
22536
+ }
22537
+ return response;
22538
+ } catch (error) {
22539
+ if (routingFailure3(error)) throw error;
22540
+ if (new URL(url).pathname.startsWith("/encryption/") && !init2?.signal?.aborted) {
22541
+ throw new NodeNotRespondingError2([new URL(url).origin], error);
22542
+ }
22543
+ throw error;
22544
+ }
22545
+ },
22546
+ {
22547
+ resolveResource: (resource, access) => this.routingDestinations(resource, access),
22548
+ bindAudience: (authorization, nodeDid) => this.bindRoutedAudience(authorization, nodeDid, session),
22549
+ verifyDestination: (target) => verifyResourceDestination2(target, globalThis.fetch.bind(globalThis))
22550
+ }
22551
+ );
22438
22552
  return new ServiceGraphLifetime(
22439
22553
  this.invokeWithRuntimePermissions,
22440
22554
  this.invokeAnyWithRuntimePermissions,
22441
- // Resolve fetch at request time. This keeps the graph-owned lifetime
22442
- // signal while preserving the SDK's existing injectable global fetch
22443
- // behavior (including adapters that install it after construction).
22444
- (url, init2) => globalThis.fetch(url, init2),
22555
+ routedFetch,
22445
22556
  (context) => {
22446
22557
  this.storageFull.observe(context);
22447
22558
  if (this._requestLog) observeReplicaReads(context, this._requestLog);
@@ -22449,6 +22560,75 @@ var _TinyCloudNode = class _TinyCloudNode {
22449
22560
  this._requestLog
22450
22561
  );
22451
22562
  }
22563
+ async routingDestinations(resource, access, noEntryHosts) {
22564
+ const session = this.currentTinyCloudSession();
22565
+ if (session) this.refreshRegistryOwner(pkhDid2(session.address, session.chainId));
22566
+ if (this.auth) return this.auth.resolveResourceDestinations(resource, access, noEntryHosts);
22567
+ const owner = resource.startsWith("urn:tinycloud:encryption:") ? parseNetworkId2(resource.split("/")[0]).ownerDid : parseSpaceUri2(resource)?.owner ?? (() => {
22568
+ const key2 = /^tinycloud:key:(z[^:]+):[^/]+$/.exec(resource);
22569
+ return key2 ? `did:key:${key2[1]}` : void 0;
22570
+ })();
22571
+ if (!owner) throw new Error(`Cannot determine registry owner for ${resource}`);
22572
+ const subject = owner.startsWith("did:pkh:") ? pkhDid2(owner.slice(owner.lastIndexOf(":") + 1), this.config.registryChainId ?? 1) : owner;
22573
+ const resolved = await resolveRegistryResource2(subject, resource, access, {
22574
+ registryUrl: this.config.tinycloudRegistryUrl,
22575
+ registryChainId: this.config.registryChainId,
22576
+ sequenceStore: this.config.registrySequenceStore,
22577
+ cacheStore: this.config.registryCacheStore,
22578
+ onRegistryFallback: this.config.onRegistryFallback
22579
+ });
22580
+ if (resolved.verdict.status === "unregistered") {
22581
+ const hint = noEntryHosts === void 0 ? this.explicitHost ?? this.config.host : noEntryHosts[0];
22582
+ const host = hint ?? this.config.newUserHost ?? (this.config.tinycloudFallbackHosts === void 0 ? DEFAULT_HOST : this.config.tinycloudFallbackHosts?.[0]);
22583
+ if (!host || !hint && this.config.newUserHost === null) throw new NodeNotFoundError2(subject, resolved.verdict);
22584
+ const url = exactHttpOrigin2(host, "new-user host");
22585
+ warnRegistryDefaultNode2(resolved.verdict, this.config.registryCacheStore, this.config.onRegistryFallback);
22586
+ return [{ url }];
22587
+ }
22588
+ const destinations = resolved.hosts.flatMap((host) => host.urls.map((url) => ({ url, nodeDid: host.nodeDid })));
22589
+ return destinations;
22590
+ }
22591
+ /** Transport hints cannot override verified full-resource registry routing. */
22592
+ async resolveRegisteredDelegationHost(permissions, noEntryHost) {
22593
+ const resources = /* @__PURE__ */ new Map();
22594
+ let hasData = false;
22595
+ for (const permission of permissions) {
22596
+ const encryption = this.isEncryptionNetworkOperation(this.invocationServiceName(permission.service), permission.path, permission.space);
22597
+ const resource = encryption ? permission.path.split("/")[0] : permission.space;
22598
+ if (!resource) throw new ResourceNotRoutedError("delegation");
22599
+ resources.set(resource, encryption ? "encryption" : "host");
22600
+ hasData || (hasData = !encryption);
22601
+ }
22602
+ let dataHost;
22603
+ let encryptionHost;
22604
+ let unserved;
22605
+ for (const [resource, access] of resources) {
22606
+ try {
22607
+ const destinations = await this.routingDestinations(resource, access, [noEntryHost]);
22608
+ if (destinations.some(({ nodeDid }) => nodeDid === void 0)) return void 0;
22609
+ if (access === "host") dataHost ?? (dataHost = destinations[0]?.url);
22610
+ else encryptionHost ?? (encryptionHost = destinations[0]?.url);
22611
+ } catch (error) {
22612
+ if (!(error instanceof NodeNotFoundError2) || error.verdict.status !== "registered") throw error;
22613
+ unserved ?? (unserved = error);
22614
+ }
22615
+ }
22616
+ const host = hasData ? dataHost : encryptionHost;
22617
+ if (!host) throw unserved ?? new ResourceNotRoutedError("delegation");
22618
+ return host;
22619
+ }
22620
+ async bindRoutedAudience(authorization, nodeDid, stagedSession) {
22621
+ const payload = JSON.parse(new TextDecoder().decode(base64UrlDecode(authorization.split(".")[1])));
22622
+ const issuer = typeof payload === "object" && payload !== null && "iss" in payload ? payload.iss : void 0;
22623
+ const session = [
22624
+ stagedSession,
22625
+ this._serviceContext?.session,
22626
+ this.auth?.tinyCloudSession,
22627
+ ...this.runtimePermissionGrants.map((grant) => grant.session)
22628
+ ].find((candidate) => candidate?.verificationMethod === issuer);
22629
+ if (!session) throw new Error("Invocation signing session is not active");
22630
+ return rewriteInvocationAudience(authorization, nodeDid, session.jwk);
22631
+ }
22452
22632
  syncResolvedHostFromAuth() {
22453
22633
  const host = this.auth?.hosts[0];
22454
22634
  if (host) {
@@ -22562,6 +22742,29 @@ var _TinyCloudNode = class _TinyCloudNode {
22562
22742
  const session = this.currentTinyCloudSession();
22563
22743
  return session?.siwe ? this.projectSignedRecapCapabilities(session.siwe) : [];
22564
22744
  }
22745
+ /** Start one background refresh for each owner derived from verified authority. */
22746
+ refreshRegistryForCapabilities(permissions) {
22747
+ for (const permission of permissions) {
22748
+ try {
22749
+ 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;
22750
+ if (owner) this.refreshRegistryOwner(owner);
22751
+ } catch {
22752
+ }
22753
+ }
22754
+ }
22755
+ refreshRegistryOwner(owner) {
22756
+ if (this.config.tinycloudRegistryUrl === null) return;
22757
+ const subject = owner.startsWith("did:pkh:") ? pkhDid2(owner.slice(owner.lastIndexOf(":") + 1), this.config.registryChainId ?? 1) : owner;
22758
+ const key2 = registryOwnerKeyV2(subject);
22759
+ if (this.registryRefreshOwners.has(key2)) return;
22760
+ this.registryRefreshOwners.add(key2);
22761
+ void refreshOwnerRegistry(subject, {
22762
+ registryUrl: this.config.tinycloudRegistryUrl,
22763
+ registryChainId: this.config.registryChainId,
22764
+ sequenceStore: this.config.registrySequenceStore,
22765
+ cacheStore: this.config.registryCacheStore
22766
+ }).catch(() => void 0);
22767
+ }
22565
22768
  /** Normalize signed capabilities without collapsing nested encryption into raw authority. */
22566
22769
  projectSignedRecapCapabilities(siwe) {
22567
22770
  const projected = this.parseRecapWithCaveats(siwe).map((entry) => {
@@ -22993,6 +23196,8 @@ var _TinyCloudNode = class _TinyCloudNode {
22993
23196
  const primarySession = this.currentTinyCloudSession();
22994
23197
  if (primarySession) {
22995
23198
  this.registerPrimarySessionGrant(primarySession);
23199
+ this.refreshRegistryOwner(pkhDid2(primarySession.address, primarySession.chainId));
23200
+ this.refreshRegistryForCapabilities(this.getVerifiedSessionCapabilities());
22996
23201
  if (this.auth?.spaceHosting === "unknown") {
22997
23202
  await this.checkSpaceHosting();
22998
23203
  this.assertSessionEpoch(epoch, "Sign-in");
@@ -23215,8 +23420,8 @@ var _TinyCloudNode = class _TinyCloudNode {
23215
23420
  const primarySession = auth.tinyCloudSession;
23216
23421
  const defaultSpaceId = this.ownedSpaceId("default");
23217
23422
  const canReusePrimaryBootstrapSession = primarySession?.spaceId === defaultSpaceId && auth.capabilityRequest === BOOTSTRAP_SESSION_REQUESTS.default;
23218
- if (steps.some((step) => step.kind === "host")) {
23219
- await this.assertMayCreateSpace();
23423
+ for (const step of steps) {
23424
+ if (step.kind === "host") await this.assertMayCreateSpace(step.spaceId);
23220
23425
  }
23221
23426
  for (const step of steps) {
23222
23427
  if (step.kind !== "session") continue;
@@ -23265,10 +23470,20 @@ var _TinyCloudNode = class _TinyCloudNode {
23265
23470
  if (!session) {
23266
23471
  throw new Error(`Missing bootstrap session for ${step.space}`);
23267
23472
  }
23268
- const activated = await activateSessionWithHost2(host, session.delegationHeader, graph.signal).catch((error) => {
23269
- graph.assertActive();
23270
- throw error;
23271
- });
23473
+ const destinations = await this.routingDestinations(step.spaceId, "host");
23474
+ let activated;
23475
+ for (const destination of destinations) {
23476
+ try {
23477
+ await verifyResourceDestination2(destination, globalThis.fetch.bind(globalThis));
23478
+ const result = await activateSessionWithHost2(destination.url, session.delegationHeader, graph.signal);
23479
+ if (!result.success) throw Object.assign(new Error("Bootstrap activation failed"), { cause: result });
23480
+ activated = result;
23481
+ } catch (error) {
23482
+ graph.assertActive();
23483
+ throw error;
23484
+ }
23485
+ }
23486
+ if (!activated) throw new Error("Bootstrap space has no destination");
23272
23487
  graph.assertActive();
23273
23488
  if (!activated.success || activated.skipped?.includes(step.spaceId)) {
23274
23489
  throw Object.assign(
@@ -23851,6 +24066,13 @@ var _TinyCloudNode = class _TinyCloudNode {
23851
24066
  if (!session) {
23852
24067
  throw new Error("Owned space hosting requires an active session");
23853
24068
  }
24069
+ await this.assertMayCreateSpace(spaceId);
24070
+ if (this.auth.registryRecordPresent) {
24071
+ const hosted = await this.hostOwnedSpaceOnce(spaceId, signal);
24072
+ if (!hosted.success) throw Object.assign(new Error(`Failed to activate listed space ${spaceId}: ${describeHostFailure2(hosted)}`), { cause: hosted });
24073
+ if (this.auth.tinyCloudSession === session) this.confirmedHostedSpaceIds.add(spaceId);
24074
+ return;
24075
+ }
23854
24076
  if (this.confirmedHostedSpaceIds.has(spaceId) || this.hostedOwnedSpaceIds.has(spaceId)) {
23855
24077
  return;
23856
24078
  }
@@ -23869,7 +24091,7 @@ var _TinyCloudNode = class _TinyCloudNode {
23869
24091
  { cause: activation }
23870
24092
  );
23871
24093
  }
23872
- await this.assertMayCreateSpace();
24094
+ await this.assertMayCreateSpace(spaceId);
23873
24095
  if (signal?.aborted) throw signal.reason;
23874
24096
  const created = await this.hostOwnedSpaceOnce(spaceId, signal);
23875
24097
  if (!created.success) {
@@ -23930,17 +24152,19 @@ var _TinyCloudNode = class _TinyCloudNode {
23930
24152
  { cause: hosted }
23931
24153
  );
23932
24154
  }
23933
- const activation = await activateSessionWithHost2(
23934
- host,
23935
- session.delegationHeader
23936
- );
23937
- if (!activation.success || activation.skipped?.includes(spaceId)) {
23938
- throw Object.assign(
23939
- new Error(
23940
- `Failed to activate session for owned space ${spaceId}: ${activation.success ? "space was skipped" : describeHostFailure2(activation)}`
23941
- ),
23942
- { cause: activation }
24155
+ if (!this.auth.registryRecordPresent) {
24156
+ const activation = await activateSessionWithHost2(
24157
+ host,
24158
+ session.delegationHeader
23943
24159
  );
24160
+ if (!activation.success || activation.skipped?.includes(spaceId)) {
24161
+ throw Object.assign(
24162
+ new Error(
24163
+ `Failed to activate session for owned space ${spaceId}: ${activation.success ? "space was skipped" : describeHostFailure2(activation)}`
24164
+ ),
24165
+ { cause: activation }
24166
+ );
24167
+ }
23944
24168
  }
23945
24169
  if (this.auth?.tinyCloudSession === session) this.hostedOwnedSpaceIds.add(spaceId);
23946
24170
  try {
@@ -23984,11 +24208,9 @@ var _TinyCloudNode = class _TinyCloudNode {
23984
24208
  * session's recap doesn't cover it and the registry has no record; the host
23985
24209
  * SIWE is idempotent server-side. Must be called after {@link signIn}.
23986
24210
  *
23987
- * Under `registryPolicy: "fail-closed"`, hosting is refused with
23988
- * `NodeRegistryEntryExistsError` when the owner's registry record does not
23989
- * list the host, or `RegistryUnavailableError` when the registry gives no
23990
- * verdict. {@link hostOwnedSpace} itself is an explicit action and is not
23991
- * guarded.
24211
+ * Current signed per-space membership is checked before both automatic and
24212
+ * explicit hosting. Registered replicas use the session grant; no-entry
24213
+ * onboarding retains the host-SIWE flow.
23992
24214
  *
23993
24215
  * @param name - The owned space name (e.g. `"secrets"`).
23994
24216
  * @returns The hosted owned-space URI.
@@ -23998,27 +24220,20 @@ var _TinyCloudNode = class _TinyCloudNode {
23998
24220
  throw new Error("Not signed in. Call signIn() first.");
23999
24221
  }
24000
24222
  const spaceId = this.ownedSpaceId(name);
24223
+ await this.assertMayCreateSpace(spaceId);
24001
24224
  if (this.hostedOwnedSpaceIds.has(spaceId) || await this.isOwnedSpaceRegistered(spaceId)) {
24002
24225
  return spaceId;
24003
24226
  }
24004
- await this.assertMayCreateSpace();
24005
24227
  return this.hostOwnedSpace(name);
24006
24228
  }
24007
- /**
24008
- * Space-creation guard (TC-1443) for the automatic hosting paths: sign-in's
24009
- * owned spaces, account bootstrap, {@link ensureOwnedSpaceHosted} and
24010
- * {@link ensurePublicSpace}. Only `registryPolicy: "fail-closed"` consults
24011
- * the registry ({@link NodeUserAuthorization.assertMayCreateSpace}); the
24012
- * default policy performs no lookup.
24013
- */
24014
- async assertMayCreateSpace() {
24015
- if (this.config.registryPolicy !== "fail-closed") return;
24016
- await this.auth.assertMayCreateSpace();
24229
+ /** Refresh full-space membership before any automatic or explicit hosting. */
24230
+ async assertMayCreateSpace(spaceId) {
24231
+ await this.routingDestinations(spaceId, "host");
24017
24232
  }
24018
24233
  /** Registry-policy settings shared by every authorization this client creates. */
24019
24234
  registryPolicyConfig() {
24020
- const { registryPolicy, newUserHost, registryChainId, registrySequenceStore, onNodeNotFound } = this.config;
24021
- return { registryPolicy, newUserHost, registryChainId, registrySequenceStore, onNodeNotFound };
24235
+ const { registryPolicy, newUserHost, registryChainId, registrySequenceStore, registryCacheStore, onNodeNotFound, onRegistryFallback } = this.config;
24236
+ return { registryPolicy, newUserHost, registryChainId, registrySequenceStore, registryCacheStore, onNodeNotFound, onRegistryFallback };
24022
24237
  }
24023
24238
  /**
24024
24239
  * Submit the host delegation for an owned space, sharing one in-flight
@@ -24224,12 +24439,8 @@ var _TinyCloudNode = class _TinyCloudNode {
24224
24439
  }
24225
24440
  stagedSessionExpiry = signedExpiry ?? persistedPolicyExpiry ?? persistedExpiry(void 0);
24226
24441
  }
24227
- const resolvedHost = await this.resolveRestoredHost(
24228
- sessionData.tinycloudHosts,
24229
- restoredAddress,
24230
- sessionData.chainId
24231
- );
24232
- const stagedHost = resolvedHost ?? this.config.host;
24442
+ const restoreHints = this.explicitHost ? [this.explicitHost] : sessionData.tinycloudHosts ?? [];
24443
+ const stagedHost = exactHttpOrigin2(restoreHints[0] ?? this.config.host, "restored host hint");
24233
24444
  let restoredNodeFeatures;
24234
24445
  let replicaProtocolMismatch = false;
24235
24446
  if (this.config.replication?.enabled) {
@@ -24346,7 +24557,7 @@ var _TinyCloudNode = class _TinyCloudNode {
24346
24557
  this.sessionManager = stagedManager;
24347
24558
  this.sessionKeyId = stagedKeyId;
24348
24559
  this.sessionKeyJwk = stagedJwk;
24349
- if (resolvedHost) this.config.host = resolvedHost;
24560
+ this.config.host = stagedHost;
24350
24561
  if (spaceOwnerKey(serviceSession.spaceId) !== this.currentStorageOwner()) {
24351
24562
  this.storageFull.clear();
24352
24563
  this._requestLog?.clear();
@@ -24413,6 +24624,10 @@ var _TinyCloudNode = class _TinyCloudNode {
24413
24624
  if (attempt === this.latestSessionAttempt) await this.endSession();
24414
24625
  this.assertSessionEpoch(epoch, "Session restore");
24415
24626
  }
24627
+ if (this.config.replication?.enabled) {
24628
+ if (stagedAddress !== void 0) this.refreshRegistryOwner(pkhDid2(stagedAddress, stagedChainId));
24629
+ this.refreshRegistryForCapabilities(this.getVerifiedSessionCapabilities());
24630
+ }
24416
24631
  }
24417
24632
  stagePrimarySessionState(session, verificationMethod, host, sessionExpiry, recap) {
24418
24633
  if (!session?.siwe) return { cache: void 0, grants: [] };
@@ -24461,7 +24676,7 @@ var _TinyCloudNode = class _TinyCloudNode {
24461
24676
  };
24462
24677
  }
24463
24678
  stageRestoredServiceGraph(input) {
24464
- const graph = this.createServiceGraphLifetime();
24679
+ const graph = this.createServiceGraphLifetime(input.serviceSession);
24465
24680
  const core = this.auth ? new TinyCloud(this.auth, {
24466
24681
  invokeAny: graph.invokeAny,
24467
24682
  telemetry: this.config.telemetry,
@@ -24686,56 +24901,6 @@ var _TinyCloudNode = class _TinyCloudNode {
24686
24901
  spaceService.updateConfig({ sharingService });
24687
24902
  return { core, graph, serviceContext, kv, sql, duckdb, hooks, vault, encryption, capabilityRegistry, keyProvider, sharingService, delegationManager, spaceService };
24688
24903
  }
24689
- /**
24690
- * Resolve the host a restored session should target.
24691
- *
24692
- * Mirrors fresh sign-in host resolution but for the restore path:
24693
- * an explicit/pinned host always wins, then the hosts the session was
24694
- * persisted with, then a lazy registry/fallback resolution for sessions
24695
- * that predate the persisted `tinycloudHosts` field. Returns `undefined`
24696
- * only when there's nothing to resolve from (no explicit host, no
24697
- * persisted hosts, and no address/chainId) — in which case the existing
24698
- * `config.host` (default) is left in place.
24699
- *
24700
- * Resolution failures are surfaced, not swallowed: a genuinely broken
24701
- * registry lookup throws rather than silently falling back to a wrong host.
24702
- */
24703
- async resolveRestoredHost(persistedHosts, address, chainId) {
24704
- if (this.explicitHost) {
24705
- return this.explicitHost;
24706
- }
24707
- if (persistedHosts && persistedHosts.length > 0) {
24708
- return persistedHosts[0];
24709
- }
24710
- if (address === void 0 || chainId === void 0) {
24711
- return void 0;
24712
- }
24713
- if (this.config.registryPolicy === "fail-closed") {
24714
- const resolved2 = await resolveFailClosedTinyCloudHosts2(address, {
24715
- registryUrl: this.config.tinycloudRegistryUrl,
24716
- registryChainId: this.config.registryChainId,
24717
- newUserHost: this.config.newUserHost,
24718
- sequenceStore: this.config.registrySequenceStore,
24719
- onNodeNotFound: this.config.onNodeNotFound,
24720
- autoDiscoverLocalNode: this.config.autoDiscoverLocalNode,
24721
- localNodeUrl: this.config.localNodeUrl,
24722
- localLinkName: this.config.localLinkName,
24723
- expectedNodeDid: this.config.expectedNodeDid,
24724
- localNodeIdentityStore: this.config.localNodeIdentityStore
24725
- });
24726
- return resolved2.hosts[0];
24727
- }
24728
- const resolved = await resolveTinyCloudHosts2(pkhDid2(address, chainId), {
24729
- registryUrl: this.config.tinycloudRegistryUrl,
24730
- fallbackHosts: this.config.tinycloudFallbackHosts,
24731
- autoDiscoverLocalNode: this.config.autoDiscoverLocalNode,
24732
- localNodeUrl: this.config.localNodeUrl,
24733
- localLinkName: this.config.localLinkName,
24734
- expectedNodeDid: this.config.expectedNodeDid,
24735
- localNodeIdentityStore: this.config.localNodeIdentityStore
24736
- });
24737
- return resolved.hosts[0];
24738
- }
24739
24904
  /**
24740
24905
  * Resolve the currently-active TinyCloudSession, preferring the auth
24741
24906
  * layer's value (wallet mode) and falling back to the node-level
@@ -25210,10 +25375,10 @@ var _TinyCloudNode = class _TinyCloudNode {
25210
25375
  * second time. Only a value recorded for this exact host qualifies; older
25211
25376
  * nodes that omit `nodeId` fall through to the fetch.
25212
25377
  */
25213
- async fetchNodeId() {
25214
- const cached = this.auth?.nodeIdForHost(this.config.host);
25378
+ async fetchNodeId(host = this.config.host) {
25379
+ const cached = this.auth?.nodeIdForHost(host);
25215
25380
  if (cached) return cached;
25216
- const response = await fetch(`${this.config.host}/info`);
25381
+ const response = await fetch(`${host}/info`);
25217
25382
  if (!response.ok) {
25218
25383
  throw await httpResponseError(response, "Failed to fetch node info");
25219
25384
  }
@@ -25261,10 +25426,24 @@ var _TinyCloudNode = class _TinyCloudNode {
25261
25426
  )
25262
25427
  };
25263
25428
  }
25264
- async fetchEncryptionNetworkAt(host, networkId, fetchFn) {
25265
- const response = await fetchFn(
25266
- `${host}/encryption/networks/${encodeURIComponent(networkId)}`
25267
- );
25429
+ async encryptionDestination(networkId) {
25430
+ const destination = (await this.routingDestinations(networkId, "encryption"))[0];
25431
+ if (!destination) throw new Error("No encryption destination is registered");
25432
+ await verifyResourceDestination2(destination, globalThis.fetch.bind(globalThis));
25433
+ return { ...destination, nodeDid: destination.nodeDid ?? await this.fetchNodeId(destination.url) };
25434
+ }
25435
+ async fetchEncryptionNetworkAt(host, networkId, fetchFn, callerSignal) {
25436
+ let response;
25437
+ const timeout = AbortSignal.timeout(5e3);
25438
+ try {
25439
+ response = await fetchFn(`${host}/encryption/networks/${encodeURIComponent(networkId)}`, {
25440
+ signal: callerSignal ? AbortSignal.any([callerSignal, timeout]) : timeout,
25441
+ redirect: "error"
25442
+ });
25443
+ } catch (error) {
25444
+ if (routingFailure3(error) || callerSignal?.aborted || !timeout.aborted && error instanceof Error && error.name === "AbortError") throw error;
25445
+ throw new NodeNotRespondingError2([host], error);
25446
+ }
25268
25447
  if (response.status === 404) return null;
25269
25448
  if (!response.ok) {
25270
25449
  throw await httpResponseError(response, `Failed to fetch encryption network ${networkId}`);
@@ -25274,18 +25453,18 @@ var _TinyCloudNode = class _TinyCloudNode {
25274
25453
  }
25275
25454
  createEncryptionService(binding) {
25276
25455
  const graph = binding?.graph ?? this._serviceGraph;
25277
- const host = binding?.host ?? this.config.host;
25278
25456
  const session = binding?.session ?? this.requireEncryptionSession();
25279
25457
  const did = binding?.did ?? this.did;
25280
25458
  const address = binding?.address ?? this._address;
25281
25459
  const chainId = binding?.chainId ?? this._chainId;
25282
25460
  const crypto3 = this.createEncryptionCrypto();
25283
- const descriptorCache = /* @__PURE__ */ new Map();
25284
25461
  const transport = {
25285
- postDecrypt: async ({ networkId, authorization, canonicalBody, signal }) => {
25462
+ postDecrypt: async ({ targetNode, networkId, authorization, canonicalBody, signal }) => {
25286
25463
  graph.assertActive();
25464
+ const destination = await this.encryptionDestination(networkId);
25465
+ if (destination.nodeDid !== targetNode) throw new NodeIdentityMismatchError(destination.url, destination.nodeDid, targetNode);
25287
25466
  const response = await graph.fetch(
25288
- `${host}/encryption/networks/${encodeURIComponent(networkId)}/decrypt`,
25467
+ `${destination.url}/encryption/networks/${encodeURIComponent(networkId)}/decrypt`,
25289
25468
  {
25290
25469
  method: "POST",
25291
25470
  headers: {
@@ -25313,6 +25492,7 @@ var _TinyCloudNode = class _TinyCloudNode {
25313
25492
  }
25314
25493
  };
25315
25494
  return new EncryptionService({
25495
+ resolveTargetNode: async (networkId) => (await this.encryptionDestination(networkId)).nodeDid,
25316
25496
  crypto: crypto3,
25317
25497
  signer: {
25318
25498
  signDecryptInvocation: async (input) => {
@@ -25336,18 +25516,8 @@ var _TinyCloudNode = class _TinyCloudNode {
25336
25516
  node: {
25337
25517
  fetchByNetworkId: async (networkId) => {
25338
25518
  graph.assertActive();
25339
- let pending = descriptorCache.get(networkId);
25340
- if (!pending) {
25341
- pending = this.fetchEncryptionNetworkAt(host, networkId, graph.fetch).then((descriptor2) => {
25342
- if (descriptor2 === null) descriptorCache.delete(networkId);
25343
- return descriptor2;
25344
- }).catch((error) => {
25345
- descriptorCache.delete(networkId);
25346
- throw error;
25347
- });
25348
- descriptorCache.set(networkId, pending);
25349
- }
25350
- const descriptor = await pending;
25519
+ const destination = await this.encryptionDestination(networkId);
25520
+ const descriptor = await this.fetchEncryptionNetworkAt(destination.url, networkId, graph.fetch, graph.signal);
25351
25521
  graph.assertActive();
25352
25522
  return descriptor;
25353
25523
  }
@@ -25355,13 +25525,15 @@ var _TinyCloudNode = class _TinyCloudNode {
25355
25525
  wellKnown: {
25356
25526
  fetchWellKnown: async (principal, discoveryKey) => {
25357
25527
  graph.assertActive();
25528
+ const destination = await this.encryptionDestination(this.getDefaultEncryptionNetworkId());
25529
+ if (this.auth?.registryRecordPresent) return null;
25358
25530
  if (!address || !didPrincipalMatches2(principal, did)) {
25359
25531
  return null;
25360
25532
  }
25361
25533
  const publicSpaceId = makePublicSpaceId(address, chainId);
25362
25534
  const encodedKey = discoveryKey.split("/").map(encodeURIComponent).join("/");
25363
25535
  const response = await graph.fetch(
25364
- `${host}/public/${encodeURIComponent(publicSpaceId)}/kv/${encodedKey}`,
25536
+ `${destination.url}/public/${encodeURIComponent(publicSpaceId)}/kv/${encodedKey}`,
25365
25537
  { method: "GET" }
25366
25538
  );
25367
25539
  graph.assertActive();
@@ -25731,7 +25903,7 @@ var _TinyCloudNode = class _TinyCloudNode {
25731
25903
  const signature2 = await this.signer.signMessage(prepared.siwe);
25732
25904
  assertOwnerGraphActive();
25733
25905
  const delegationSession = this.wasmBindings.completeSessionSetup({ ...prepared, signature: signature2 });
25734
- const activation = await activateSessionWithHost2(host, delegationSession.delegationHeader);
25906
+ const activation = await this.activateSignedSession(delegationSession, prepared.siwe, host);
25735
25907
  assertOwnerGraphActive();
25736
25908
  if (!activation.success) {
25737
25909
  throw Object.assign(
@@ -26050,15 +26222,16 @@ var _TinyCloudNode = class _TinyCloudNode {
26050
26222
  async getEncryptionNetwork(nameOrNetworkId = this.getDefaultEncryptionNetworkId()) {
26051
26223
  const networkId = nameOrNetworkId.startsWith("urn:tinycloud:encryption:") ? nameOrNetworkId : this.getDefaultEncryptionNetworkId(nameOrNetworkId);
26052
26224
  return this.fetchEncryptionNetworkAt(
26053
- this.config.host,
26225
+ (await this.encryptionDestination(networkId)).url,
26054
26226
  networkId,
26055
26227
  globalThis.fetch.bind(globalThis)
26056
26228
  );
26057
26229
  }
26058
26230
  async createEncryptionNetwork(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
26059
- const targetNode = await this.fetchNodeId();
26060
26231
  const ownerDid = this.did;
26061
26232
  const networkId = this.getDefaultEncryptionNetworkId(name);
26233
+ const destination = await this.encryptionDestination(networkId);
26234
+ const targetNode = destination.nodeDid;
26062
26235
  const body = {
26063
26236
  name,
26064
26237
  ownerDid,
@@ -26081,7 +26254,7 @@ var _TinyCloudNode = class _TinyCloudNode {
26081
26254
  action: NETWORK_CREATE_ACTION2,
26082
26255
  facts
26083
26256
  });
26084
- const response = await fetch(`${this.config.host}/encryption/networks`, {
26257
+ const response = await this._serviceGraph.fetch(`${destination.url}/encryption/networks`, {
26085
26258
  method: "POST",
26086
26259
  headers: {
26087
26260
  Authorization: signed2.authorization,
@@ -26421,6 +26594,97 @@ var _TinyCloudNode = class _TinyCloudNode {
26421
26594
  }
26422
26595
  return { recap: exactVerifiedRecap(verified), expiresAt };
26423
26596
  }
26597
+ async activateResourceSession(delegationHeader, resources, hostHint, signal) {
26598
+ const targets = /* @__PURE__ */ new Map();
26599
+ const routedSpaces = [];
26600
+ let unserved;
26601
+ let requestedResource;
26602
+ for (const resource of new Set(resources)) {
26603
+ requestedResource ?? (requestedResource = resource);
26604
+ signal?.throwIfAborted();
26605
+ try {
26606
+ const encryption = resource.startsWith("urn:tinycloud:encryption:");
26607
+ const destinations = await this.routingDestinations(
26608
+ resource,
26609
+ encryption ? "encryption" : "host",
26610
+ [hostHint ?? this.config.host]
26611
+ );
26612
+ if (!encryption && destinations.some(({ nodeDid }) => nodeDid !== void 0)) routedSpaces.push(resource);
26613
+ for (const destination of destinations) {
26614
+ let target = targets.get(destination.url);
26615
+ if (!target) {
26616
+ target = { destination, spaces: [] };
26617
+ targets.set(destination.url, target);
26618
+ }
26619
+ if (!encryption && destination.nodeDid !== void 0) target.spaces.push(resource);
26620
+ }
26621
+ } catch (error) {
26622
+ if (!(error instanceof NodeNotFoundError2)) throw error;
26623
+ unserved ?? (unserved = error);
26624
+ }
26625
+ }
26626
+ if (targets.size === 0) throw unserved ?? new ResourceNotRoutedError(requestedResource ?? "session grant");
26627
+ const succeeded = [];
26628
+ const accepted = [];
26629
+ const failed = [];
26630
+ let refusal;
26631
+ for (const { destination: target, spaces } of targets.values()) {
26632
+ signal?.throwIfAborted();
26633
+ try {
26634
+ await verifyResourceDestination2(target, globalThis.fetch.bind(globalThis));
26635
+ const timeout = AbortSignal.timeout(5e3);
26636
+ const result = await activateSessionWithHost2(
26637
+ target.url,
26638
+ delegationHeader,
26639
+ signal ? AbortSignal.any([signal, timeout]) : timeout
26640
+ );
26641
+ this.throwRegistryRefusal(result.status, result.error ?? "");
26642
+ if (result.success) {
26643
+ const skipped = result.skipped?.find((space) => spaces.some((expected) => this.spaceIdsEqual(space, expected)));
26644
+ if (skipped) {
26645
+ failed.push({ url: target.url, status: result.status, cause: new ResourceNotRoutedError(skipped) });
26646
+ continue;
26647
+ }
26648
+ succeeded.push(target.url);
26649
+ accepted.push(result);
26650
+ } else {
26651
+ refusal = result;
26652
+ failed.push({ url: target.url, status: result.status, cause: result });
26653
+ }
26654
+ } catch (error) {
26655
+ if (signal?.aborted) throw error;
26656
+ failed.push({ url: target.url, cause: routingFailure3(error) ? error : new NodeNotRespondingError2([target.url], error) });
26657
+ }
26658
+ }
26659
+ if (failed.length > 0) {
26660
+ if (targets.size > 1) throw new PartialNodeWriteError2(succeeded, failed);
26661
+ if (refusal) return refusal;
26662
+ throw failed[0].cause;
26663
+ }
26664
+ const activated = new Set(accepted.flatMap((result) => result.activated ?? []));
26665
+ return {
26666
+ ...accepted[0],
26667
+ activated: [...activated],
26668
+ skipped: [...new Set(accepted.flatMap((result) => result.skipped ?? []))].filter((space) => !activated.has(space) && !routedSpaces.some((expected) => this.spaceIdsEqual(space, expected)))
26669
+ };
26670
+ }
26671
+ activateRoutedDelegation(delegation, signal) {
26672
+ const resources = this.operationsFromDelegation(delegation).map(
26673
+ (operation) => operation.resource?.startsWith("urn:tinycloud:encryption:") ? operation.resource.split("/")[0] : operation.spaceId ?? delegation.spaceId
26674
+ );
26675
+ return this.activateResourceSession(delegation.delegationHeader, resources, delegation.host, signal);
26676
+ }
26677
+ activateSignedSession(session, siwe, hostHint, signal) {
26678
+ const resources = this.parseRecapWithCaveats(siwe).map(
26679
+ (entry) => this.isEncryptionNetworkOperation(this.invocationServiceName(entry.service), entry.path, entry.space) ? entry.path.split("/")[0] : entry.space.startsWith("tinycloud:") ? entry.space : session.spaceId
26680
+ );
26681
+ return this.activateResourceSession(
26682
+ session.delegationHeader,
26683
+ resources.length > 0 ? resources : [session.spaceId],
26684
+ hostHint,
26685
+ signal
26686
+ );
26687
+ }
26424
26688
  /**
26425
26689
  * Install a portable runtime permission delegation into this SDK instance so
26426
26690
  * matching service calls and downstream `delegateTo()` calls can use it.
@@ -26440,16 +26704,12 @@ var _TinyCloudNode = class _TinyCloudNode {
26440
26704
  `Runtime delegation targets ${delegation.delegateDID} but this session key is ${session.verificationMethod}.`
26441
26705
  );
26442
26706
  }
26443
- const targetHost = delegation.host ?? this.config.host;
26444
26707
  let activateResult;
26445
26708
  try {
26446
- activateResult = await activateSessionWithHost2(
26447
- targetHost,
26448
- delegation.delegationHeader,
26449
- graph.signal
26450
- );
26709
+ activateResult = await this.activateRoutedDelegation(delegation, graph.signal);
26451
26710
  } catch (cause) {
26452
26711
  graph.assertActive();
26712
+ if (routingFailure3(cause)) throw cause;
26453
26713
  throw Object.assign(
26454
26714
  new Error("The TinyCloud host could not be reached to activate the delegation."),
26455
26715
  { code: "ACTIVATION_TRANSPORT_FAILED", cause }
@@ -26605,11 +26865,15 @@ var _TinyCloudNode = class _TinyCloudNode {
26605
26865
  ...prepared,
26606
26866
  signature: signature2
26607
26867
  });
26608
- const activateResult = await activateSessionWithHost2(
26609
- this.config.host,
26610
- delegatedSession.delegationHeader,
26611
- graph.signal
26612
- ).catch((error) => {
26868
+ const delegation = this.runtimeDelegationFromSession(
26869
+ delegatedSession,
26870
+ delegatedEntries,
26871
+ spaceId,
26872
+ captured,
26873
+ expiresAt,
26874
+ { siwe: prepared.siwe, signature: signature2 }
26875
+ );
26876
+ const activateResult = await this.activateRoutedDelegation(delegation, graph.signal).catch((error) => {
26613
26877
  graph.assertActive();
26614
26878
  throw error;
26615
26879
  });
@@ -26625,14 +26889,6 @@ var _TinyCloudNode = class _TinyCloudNode {
26625
26889
  { cause: activateResult }
26626
26890
  );
26627
26891
  }
26628
- const delegation = this.runtimeDelegationFromSession(
26629
- delegatedSession,
26630
- delegatedEntries,
26631
- spaceId,
26632
- captured,
26633
- expiresAt,
26634
- { siwe: prepared.siwe, signature: signature2 }
26635
- );
26636
26892
  graph.assertActive();
26637
26893
  assertCapturedKey?.();
26638
26894
  this.runtimePermissionGrants.push({
@@ -26777,7 +27033,7 @@ var _TinyCloudNode = class _TinyCloudNode {
26777
27033
  if (!publicSpaceId) {
26778
27034
  throw new Error("Public space not enabled. Set enablePublicSpace: true in config.");
26779
27035
  }
26780
- await this.assertMayCreateSpace();
27036
+ await this.assertMayCreateSpace(publicSpaceId);
26781
27037
  await this.auth.hostPublicSpace(publicSpaceId);
26782
27038
  const kvActions = [KV3.PUT, KV3.GET, KV3.DEL, KV3.LIST, KV3.METADATA];
26783
27039
  const abilities = { kv: { "": kvActions } };
@@ -26800,10 +27056,7 @@ var _TinyCloudNode = class _TinyCloudNode {
26800
27056
  ...prepared,
26801
27057
  signature: signature2
26802
27058
  });
26803
- const activateResult = await activateSessionWithHost2(
26804
- this.config.host,
26805
- delegationSession.delegationHeader
26806
- );
27059
+ const activateResult = await this.activateSignedSession(delegationSession, prepared.siwe);
26807
27060
  if (!activateResult.success) {
26808
27061
  throw Object.assign(
26809
27062
  new Error(`Failed to activate public space delegation: ${describeHostFailure2(activateResult)}`),
@@ -27353,10 +27606,7 @@ var _TinyCloudNode = class _TinyCloudNode {
27353
27606
  if (onPrepared) {
27354
27607
  await onPrepared(delegation);
27355
27608
  }
27356
- const activateResult = await activateSessionWithHost2(
27357
- this.config.host,
27358
- delegationHeader
27359
- );
27609
+ const activateResult = await this.activateRoutedDelegation(delegation);
27360
27610
  assertDelegationActivated(activateResult, delegation, requireActivation);
27361
27611
  return delegation;
27362
27612
  }
@@ -27390,10 +27640,7 @@ var _TinyCloudNode = class _TinyCloudNode {
27390
27640
  if (onPrepared) {
27391
27641
  await onPrepared(delegation);
27392
27642
  }
27393
- const activateResult = await activateSessionWithHost2(
27394
- targetHost,
27395
- delegationHeader
27396
- );
27643
+ const activateResult = await this.activateRoutedDelegation(delegation);
27397
27644
  assertDelegationActivated(activateResult, delegation, requireActivation);
27398
27645
  return delegation;
27399
27646
  }
@@ -28045,10 +28292,7 @@ var _TinyCloudNode = class _TinyCloudNode {
28045
28292
  ...prepared,
28046
28293
  signature: signature2
28047
28294
  });
28048
- const activateResult = await activateSessionWithHost2(
28049
- this.config.host,
28050
- delegationSession.delegationHeader
28051
- );
28295
+ const activateResult = await this.activateSignedSession(delegationSession, prepared.siwe);
28052
28296
  if (!activateResult.success) {
28053
28297
  throw Object.assign(
28054
28298
  new Error(`Failed to activate delegation: ${describeHostFailure2(activateResult)}`),
@@ -28093,10 +28337,7 @@ var _TinyCloudNode = class _TinyCloudNode {
28093
28337
  ...publicPrepared,
28094
28338
  signature: publicSignature
28095
28339
  });
28096
- const publicActivateResult = await activateSessionWithHost2(
28097
- this.config.host,
28098
- publicSession.delegationHeader
28099
- );
28340
+ const publicActivateResult = await this.activateSignedSession(publicSession, publicPrepared.siwe);
28100
28341
  if (publicActivateResult.success) {
28101
28342
  result.publicDelegation = {
28102
28343
  cid: publicSession.delegationCid,
@@ -28202,11 +28443,7 @@ var _TinyCloudNode = class _TinyCloudNode {
28202
28443
  ...prepared,
28203
28444
  signature: signature2
28204
28445
  });
28205
- const activateResult = await activateSessionWithHost2(
28206
- targetHost,
28207
- invokerSession.delegationHeader,
28208
- graph.signal
28209
- ).catch((error) => {
28446
+ const activateResult = await this.activateSignedSession(invokerSession, prepared.siwe, targetHost, graph.signal).catch((error) => {
28210
28447
  graph.assertActive();
28211
28448
  throw error;
28212
28449
  });
@@ -28333,10 +28570,7 @@ var _TinyCloudNode = class _TinyCloudNode {
28333
28570
  ...prepared,
28334
28571
  signature: signature2
28335
28572
  });
28336
- const activateResult = await activateSessionWithHost2(
28337
- targetHost,
28338
- subDelegationSession.delegationHeader
28339
- );
28573
+ const activateResult = await this.activateSignedSession(subDelegationSession, prepared.siwe, targetHost);
28340
28574
  if (!activateResult.success) {
28341
28575
  throw Object.assign(
28342
28576
  new Error(`Failed to activate sub-delegation: ${describeHostFailure2(activateResult)}`),
@@ -28400,6 +28634,7 @@ registerReplicationLoaders({
28400
28634
 
28401
28635
  // src/index.ts
28402
28636
  import { TinyCloud as TinyCloud2 } from "@tinycloud/sdk-core";
28637
+ import { NodeNotRespondingError as NodeNotRespondingError3, NodeIdentityMismatchError as NodeIdentityMismatchError2, PartialNodeWriteError as PartialNodeWriteError3 } from "@tinycloud/sdk-services";
28403
28638
  import {
28404
28639
  SilentNotificationHandler as SilentNotificationHandler2,
28405
28640
  AutoApproveSpaceCreationHandler as AutoApproveSpaceCreationHandler2,
@@ -28717,7 +28952,7 @@ import {
28717
28952
  SpaceService as SpaceService2,
28718
28953
  SpaceErrorCodes,
28719
28954
  createSpaceService,
28720
- parseSpaceUri as parseSpaceUri3,
28955
+ parseSpaceUri as parseSpaceUri4,
28721
28956
  buildSpaceUri,
28722
28957
  makePublicSpaceId as makePublicSpaceId2,
28723
28958
  Space
@@ -31288,8 +31523,11 @@ export {
31288
31523
  MemorySessionStorage,
31289
31524
  NETWORK_NAME_PATTERN,
31290
31525
  NetworkIdError,
31526
+ NodeIdentityMismatchError2 as NodeIdentityMismatchError,
31527
+ NodeNotRespondingError3 as NodeNotRespondingError,
31291
31528
  NodeUserAuthorization,
31292
31529
  NodeWasmBindings,
31530
+ PartialNodeWriteError3 as PartialNodeWriteError,
31293
31531
  PermissionNotInManifestError2 as PermissionNotInManifestError,
31294
31532
  PrefixedKVService,
31295
31533
  PrivateKeySigner,
@@ -31397,7 +31635,7 @@ export {
31397
31635
  parseNetworkId3 as parseNetworkId,
31398
31636
  parsePkhDid,
31399
31637
  parseSecretCatalogKey,
31400
- parseSpaceUri3 as parseSpaceUri,
31638
+ parseSpaceUri4 as parseSpaceUri,
31401
31639
  pkhDid4 as pkhDid,
31402
31640
  principalDid3 as principalDid,
31403
31641
  principalDidEquals4 as principalDidEquals,