@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.
- package/README.md +39 -0
- package/dist/{NodeUserAuthorization-B05Wjjpb.d.cts → NodeUserAuthorization-B_TFUlua.d.cts} +18 -52
- package/dist/{NodeUserAuthorization-B05Wjjpb.d.ts → NodeUserAuthorization-B_TFUlua.d.ts} +18 -52
- package/dist/{TinyCloudNode-CEFGtSWn.d.ts → TinyCloudNode-Brcu8QoX.d.cts} +31 -59
- package/dist/{TinyCloudNode-6_IumWu0.d.cts → TinyCloudNode-DBJUnsMN.d.ts} +31 -59
- package/dist/{authority-DXpnuId1.d.ts → authority-BFc5lesl.d.ts} +1 -1
- package/dist/{authority-DODDXxoP.d.cts → authority-DQ6R1HEG.d.cts} +1 -1
- package/dist/core.cjs +500 -283
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +5 -5
- package/dist/core.d.ts +5 -5
- package/dist/core.js +526 -287
- package/dist/core.js.map +1 -1
- package/dist/credentials-core.d.cts +1 -1
- package/dist/credentials-core.d.ts +1 -1
- package/dist/credentials.d.cts +1 -1
- package/dist/credentials.d.ts +1 -1
- package/dist/{delegation-BQbAuKad.d.cts → delegation-CeOWDnBB.d.cts} +7 -0
- package/dist/{delegation-BQbAuKad.d.ts → delegation-CeOWDnBB.d.ts} +7 -0
- package/dist/encryption-keys.d.cts +3 -3
- package/dist/encryption-keys.d.ts +3 -3
- package/dist/index.cjs +517 -300
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.js +527 -288
- package/dist/index.js.map +1 -1
- package/dist/replication/authority.d.cts +4 -4
- package/dist/replication/authority.d.ts +4 -4
- package/dist/space-hosting.d.cts +1 -1
- package/dist/space-hosting.d.ts +1 -1
- 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
|
|
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 =
|
|
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:[^/]+))\/(
|
|
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
|
-
|
|
18473
|
-
|
|
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
|
-
|
|
18766
|
-
|
|
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
|
-
|
|
18815
|
-
|
|
18816
|
-
|
|
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 {
|
|
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 -
|
|
19192
|
-
*
|
|
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
|
-
|
|
19286
|
-
|
|
19287
|
-
|
|
19288
|
-
|
|
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
|
-
|
|
19319
|
-
|
|
19320
|
-
|
|
19321
|
-
|
|
19322
|
-
|
|
19323
|
-
|
|
19324
|
-
|
|
19325
|
-
|
|
19326
|
-
|
|
19327
|
-
|
|
19328
|
-
|
|
19329
|
-
|
|
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
|
-
|
|
19332
|
-
|
|
19333
|
-
|
|
19334
|
-
|
|
19335
|
-
|
|
19336
|
-
|
|
19337
|
-
|
|
19338
|
-
|
|
19339
|
-
|
|
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
|
|
19861
|
-
|
|
19862
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
23269
|
-
|
|
23270
|
-
|
|
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
|
-
|
|
23934
|
-
|
|
23935
|
-
|
|
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
|
-
*
|
|
23988
|
-
*
|
|
23989
|
-
*
|
|
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
|
-
|
|
24009
|
-
|
|
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,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24224
24439
|
}
|
|
24225
24440
|
stagedSessionExpiry = signedExpiry ?? persistedPolicyExpiry ?? persistedExpiry(void 0);
|
|
24226
24441
|
}
|
|
24227
|
-
const
|
|
24228
|
-
|
|
24229
|
-
|
|
24230
|
-
sessionData.chainId
|
|
24231
|
-
);
|
|
24232
|
-
const stagedHost = resolvedHost ?? this.config.host;
|
|
24442
|
+
const restoreHints = this.explicitHost ? [this.explicitHost] : sessionData.tinycloudHosts ?? [];
|
|
24443
|
+
const resolvedHost = this.config.replication?.enabled ? (await this.routingDestinations(sessionData.spaceId, "read", restoreHints))[0]?.url : void 0;
|
|
24444
|
+
const stagedHost = resolvedHost ?? exactHttpOrigin2(restoreHints[0] ?? this.config.host, "restored host hint");
|
|
24233
24445
|
let restoredNodeFeatures;
|
|
24234
24446
|
let replicaProtocolMismatch = false;
|
|
24235
24447
|
if (this.config.replication?.enabled) {
|
|
@@ -24346,7 +24558,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24346
24558
|
this.sessionManager = stagedManager;
|
|
24347
24559
|
this.sessionKeyId = stagedKeyId;
|
|
24348
24560
|
this.sessionKeyJwk = stagedJwk;
|
|
24349
|
-
|
|
24561
|
+
this.config.host = stagedHost;
|
|
24350
24562
|
if (spaceOwnerKey(serviceSession.spaceId) !== this.currentStorageOwner()) {
|
|
24351
24563
|
this.storageFull.clear();
|
|
24352
24564
|
this._requestLog?.clear();
|
|
@@ -24413,6 +24625,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24413
24625
|
if (attempt === this.latestSessionAttempt) await this.endSession();
|
|
24414
24626
|
this.assertSessionEpoch(epoch, "Session restore");
|
|
24415
24627
|
}
|
|
24628
|
+
if (this.config.replication?.enabled) {
|
|
24629
|
+
if (stagedAddress !== void 0) this.refreshRegistryOwner(pkhDid2(stagedAddress, stagedChainId));
|
|
24630
|
+
this.refreshRegistryForCapabilities(this.getVerifiedSessionCapabilities());
|
|
24631
|
+
}
|
|
24416
24632
|
}
|
|
24417
24633
|
stagePrimarySessionState(session, verificationMethod, host, sessionExpiry, recap) {
|
|
24418
24634
|
if (!session?.siwe) return { cache: void 0, grants: [] };
|
|
@@ -24461,7 +24677,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24461
24677
|
};
|
|
24462
24678
|
}
|
|
24463
24679
|
stageRestoredServiceGraph(input) {
|
|
24464
|
-
const graph = this.createServiceGraphLifetime();
|
|
24680
|
+
const graph = this.createServiceGraphLifetime(input.serviceSession);
|
|
24465
24681
|
const core = this.auth ? new TinyCloud(this.auth, {
|
|
24466
24682
|
invokeAny: graph.invokeAny,
|
|
24467
24683
|
telemetry: this.config.telemetry,
|
|
@@ -24686,56 +24902,6 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24686
24902
|
spaceService.updateConfig({ sharingService });
|
|
24687
24903
|
return { core, graph, serviceContext, kv, sql, duckdb, hooks, vault, encryption, capabilityRegistry, keyProvider, sharingService, delegationManager, spaceService };
|
|
24688
24904
|
}
|
|
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
24905
|
/**
|
|
24740
24906
|
* Resolve the currently-active TinyCloudSession, preferring the auth
|
|
24741
24907
|
* layer's value (wallet mode) and falling back to the node-level
|
|
@@ -25210,10 +25376,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25210
25376
|
* second time. Only a value recorded for this exact host qualifies; older
|
|
25211
25377
|
* nodes that omit `nodeId` fall through to the fetch.
|
|
25212
25378
|
*/
|
|
25213
|
-
async fetchNodeId() {
|
|
25214
|
-
const cached = this.auth?.nodeIdForHost(
|
|
25379
|
+
async fetchNodeId(host = this.config.host) {
|
|
25380
|
+
const cached = this.auth?.nodeIdForHost(host);
|
|
25215
25381
|
if (cached) return cached;
|
|
25216
|
-
const response = await fetch(`${
|
|
25382
|
+
const response = await fetch(`${host}/info`);
|
|
25217
25383
|
if (!response.ok) {
|
|
25218
25384
|
throw await httpResponseError(response, "Failed to fetch node info");
|
|
25219
25385
|
}
|
|
@@ -25261,10 +25427,24 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25261
25427
|
)
|
|
25262
25428
|
};
|
|
25263
25429
|
}
|
|
25264
|
-
async
|
|
25265
|
-
const
|
|
25266
|
-
|
|
25267
|
-
);
|
|
25430
|
+
async encryptionDestination(networkId) {
|
|
25431
|
+
const destination = (await this.routingDestinations(networkId, "encryption"))[0];
|
|
25432
|
+
if (!destination) throw new Error("No encryption destination is registered");
|
|
25433
|
+
await verifyResourceDestination2(destination, globalThis.fetch.bind(globalThis));
|
|
25434
|
+
return { ...destination, nodeDid: destination.nodeDid ?? await this.fetchNodeId(destination.url) };
|
|
25435
|
+
}
|
|
25436
|
+
async fetchEncryptionNetworkAt(host, networkId, fetchFn, callerSignal) {
|
|
25437
|
+
let response;
|
|
25438
|
+
const timeout = AbortSignal.timeout(5e3);
|
|
25439
|
+
try {
|
|
25440
|
+
response = await fetchFn(`${host}/encryption/networks/${encodeURIComponent(networkId)}`, {
|
|
25441
|
+
signal: callerSignal ? AbortSignal.any([callerSignal, timeout]) : timeout,
|
|
25442
|
+
redirect: "error"
|
|
25443
|
+
});
|
|
25444
|
+
} catch (error) {
|
|
25445
|
+
if (routingFailure3(error) || callerSignal?.aborted || !timeout.aborted && error instanceof Error && error.name === "AbortError") throw error;
|
|
25446
|
+
throw new NodeNotRespondingError2([host], error);
|
|
25447
|
+
}
|
|
25268
25448
|
if (response.status === 404) return null;
|
|
25269
25449
|
if (!response.ok) {
|
|
25270
25450
|
throw await httpResponseError(response, `Failed to fetch encryption network ${networkId}`);
|
|
@@ -25274,18 +25454,18 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25274
25454
|
}
|
|
25275
25455
|
createEncryptionService(binding) {
|
|
25276
25456
|
const graph = binding?.graph ?? this._serviceGraph;
|
|
25277
|
-
const host = binding?.host ?? this.config.host;
|
|
25278
25457
|
const session = binding?.session ?? this.requireEncryptionSession();
|
|
25279
25458
|
const did = binding?.did ?? this.did;
|
|
25280
25459
|
const address = binding?.address ?? this._address;
|
|
25281
25460
|
const chainId = binding?.chainId ?? this._chainId;
|
|
25282
25461
|
const crypto3 = this.createEncryptionCrypto();
|
|
25283
|
-
const descriptorCache = /* @__PURE__ */ new Map();
|
|
25284
25462
|
const transport = {
|
|
25285
|
-
postDecrypt: async ({ networkId, authorization, canonicalBody, signal }) => {
|
|
25463
|
+
postDecrypt: async ({ targetNode, networkId, authorization, canonicalBody, signal }) => {
|
|
25286
25464
|
graph.assertActive();
|
|
25465
|
+
const destination = await this.encryptionDestination(networkId);
|
|
25466
|
+
if (destination.nodeDid !== targetNode) throw new NodeIdentityMismatchError(destination.url, destination.nodeDid, targetNode);
|
|
25287
25467
|
const response = await graph.fetch(
|
|
25288
|
-
`${
|
|
25468
|
+
`${destination.url}/encryption/networks/${encodeURIComponent(networkId)}/decrypt`,
|
|
25289
25469
|
{
|
|
25290
25470
|
method: "POST",
|
|
25291
25471
|
headers: {
|
|
@@ -25313,6 +25493,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25313
25493
|
}
|
|
25314
25494
|
};
|
|
25315
25495
|
return new EncryptionService({
|
|
25496
|
+
resolveTargetNode: async (networkId) => (await this.encryptionDestination(networkId)).nodeDid,
|
|
25316
25497
|
crypto: crypto3,
|
|
25317
25498
|
signer: {
|
|
25318
25499
|
signDecryptInvocation: async (input) => {
|
|
@@ -25336,18 +25517,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25336
25517
|
node: {
|
|
25337
25518
|
fetchByNetworkId: async (networkId) => {
|
|
25338
25519
|
graph.assertActive();
|
|
25339
|
-
|
|
25340
|
-
|
|
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;
|
|
25520
|
+
const destination = await this.encryptionDestination(networkId);
|
|
25521
|
+
const descriptor = await this.fetchEncryptionNetworkAt(destination.url, networkId, graph.fetch, graph.signal);
|
|
25351
25522
|
graph.assertActive();
|
|
25352
25523
|
return descriptor;
|
|
25353
25524
|
}
|
|
@@ -25355,13 +25526,15 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25355
25526
|
wellKnown: {
|
|
25356
25527
|
fetchWellKnown: async (principal, discoveryKey) => {
|
|
25357
25528
|
graph.assertActive();
|
|
25529
|
+
const destination = await this.encryptionDestination(this.getDefaultEncryptionNetworkId());
|
|
25530
|
+
if (this.auth?.registryRecordPresent) return null;
|
|
25358
25531
|
if (!address || !didPrincipalMatches2(principal, did)) {
|
|
25359
25532
|
return null;
|
|
25360
25533
|
}
|
|
25361
25534
|
const publicSpaceId = makePublicSpaceId(address, chainId);
|
|
25362
25535
|
const encodedKey = discoveryKey.split("/").map(encodeURIComponent).join("/");
|
|
25363
25536
|
const response = await graph.fetch(
|
|
25364
|
-
`${
|
|
25537
|
+
`${destination.url}/public/${encodeURIComponent(publicSpaceId)}/kv/${encodedKey}`,
|
|
25365
25538
|
{ method: "GET" }
|
|
25366
25539
|
);
|
|
25367
25540
|
graph.assertActive();
|
|
@@ -25731,7 +25904,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25731
25904
|
const signature2 = await this.signer.signMessage(prepared.siwe);
|
|
25732
25905
|
assertOwnerGraphActive();
|
|
25733
25906
|
const delegationSession = this.wasmBindings.completeSessionSetup({ ...prepared, signature: signature2 });
|
|
25734
|
-
const activation = await
|
|
25907
|
+
const activation = await this.activateSignedSession(delegationSession, prepared.siwe, host);
|
|
25735
25908
|
assertOwnerGraphActive();
|
|
25736
25909
|
if (!activation.success) {
|
|
25737
25910
|
throw Object.assign(
|
|
@@ -26050,15 +26223,16 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26050
26223
|
async getEncryptionNetwork(nameOrNetworkId = this.getDefaultEncryptionNetworkId()) {
|
|
26051
26224
|
const networkId = nameOrNetworkId.startsWith("urn:tinycloud:encryption:") ? nameOrNetworkId : this.getDefaultEncryptionNetworkId(nameOrNetworkId);
|
|
26052
26225
|
return this.fetchEncryptionNetworkAt(
|
|
26053
|
-
this.
|
|
26226
|
+
(await this.encryptionDestination(networkId)).url,
|
|
26054
26227
|
networkId,
|
|
26055
26228
|
globalThis.fetch.bind(globalThis)
|
|
26056
26229
|
);
|
|
26057
26230
|
}
|
|
26058
26231
|
async createEncryptionNetwork(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
|
|
26059
|
-
const targetNode = await this.fetchNodeId();
|
|
26060
26232
|
const ownerDid = this.did;
|
|
26061
26233
|
const networkId = this.getDefaultEncryptionNetworkId(name);
|
|
26234
|
+
const destination = await this.encryptionDestination(networkId);
|
|
26235
|
+
const targetNode = destination.nodeDid;
|
|
26062
26236
|
const body = {
|
|
26063
26237
|
name,
|
|
26064
26238
|
ownerDid,
|
|
@@ -26081,7 +26255,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26081
26255
|
action: NETWORK_CREATE_ACTION2,
|
|
26082
26256
|
facts
|
|
26083
26257
|
});
|
|
26084
|
-
const response = await fetch(`${
|
|
26258
|
+
const response = await this._serviceGraph.fetch(`${destination.url}/encryption/networks`, {
|
|
26085
26259
|
method: "POST",
|
|
26086
26260
|
headers: {
|
|
26087
26261
|
Authorization: signed2.authorization,
|
|
@@ -26421,6 +26595,97 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26421
26595
|
}
|
|
26422
26596
|
return { recap: exactVerifiedRecap(verified), expiresAt };
|
|
26423
26597
|
}
|
|
26598
|
+
async activateResourceSession(delegationHeader, resources, hostHint, signal) {
|
|
26599
|
+
const targets = /* @__PURE__ */ new Map();
|
|
26600
|
+
const routedSpaces = [];
|
|
26601
|
+
let unserved;
|
|
26602
|
+
let requestedResource;
|
|
26603
|
+
for (const resource of new Set(resources)) {
|
|
26604
|
+
requestedResource ?? (requestedResource = resource);
|
|
26605
|
+
signal?.throwIfAborted();
|
|
26606
|
+
try {
|
|
26607
|
+
const encryption = resource.startsWith("urn:tinycloud:encryption:");
|
|
26608
|
+
const destinations = await this.routingDestinations(
|
|
26609
|
+
resource,
|
|
26610
|
+
encryption ? "encryption" : "host",
|
|
26611
|
+
[hostHint ?? this.config.host]
|
|
26612
|
+
);
|
|
26613
|
+
if (!encryption && destinations.some(({ nodeDid }) => nodeDid !== void 0)) routedSpaces.push(resource);
|
|
26614
|
+
for (const destination of destinations) {
|
|
26615
|
+
let target = targets.get(destination.url);
|
|
26616
|
+
if (!target) {
|
|
26617
|
+
target = { destination, spaces: [] };
|
|
26618
|
+
targets.set(destination.url, target);
|
|
26619
|
+
}
|
|
26620
|
+
if (!encryption && destination.nodeDid !== void 0) target.spaces.push(resource);
|
|
26621
|
+
}
|
|
26622
|
+
} catch (error) {
|
|
26623
|
+
if (!(error instanceof NodeNotFoundError2)) throw error;
|
|
26624
|
+
unserved ?? (unserved = error);
|
|
26625
|
+
}
|
|
26626
|
+
}
|
|
26627
|
+
if (targets.size === 0) throw unserved ?? new ResourceNotRoutedError(requestedResource ?? "session grant");
|
|
26628
|
+
const succeeded = [];
|
|
26629
|
+
const accepted = [];
|
|
26630
|
+
const failed = [];
|
|
26631
|
+
let refusal;
|
|
26632
|
+
for (const { destination: target, spaces } of targets.values()) {
|
|
26633
|
+
signal?.throwIfAborted();
|
|
26634
|
+
try {
|
|
26635
|
+
await verifyResourceDestination2(target, globalThis.fetch.bind(globalThis));
|
|
26636
|
+
const timeout = AbortSignal.timeout(5e3);
|
|
26637
|
+
const result = await activateSessionWithHost2(
|
|
26638
|
+
target.url,
|
|
26639
|
+
delegationHeader,
|
|
26640
|
+
signal ? AbortSignal.any([signal, timeout]) : timeout
|
|
26641
|
+
);
|
|
26642
|
+
this.throwRegistryRefusal(result.status, result.error ?? "");
|
|
26643
|
+
if (result.success) {
|
|
26644
|
+
const skipped = result.skipped?.find((space) => spaces.some((expected) => this.spaceIdsEqual(space, expected)));
|
|
26645
|
+
if (skipped) {
|
|
26646
|
+
failed.push({ url: target.url, status: result.status, cause: new ResourceNotRoutedError(skipped) });
|
|
26647
|
+
continue;
|
|
26648
|
+
}
|
|
26649
|
+
succeeded.push(target.url);
|
|
26650
|
+
accepted.push(result);
|
|
26651
|
+
} else {
|
|
26652
|
+
refusal = result;
|
|
26653
|
+
failed.push({ url: target.url, status: result.status, cause: result });
|
|
26654
|
+
}
|
|
26655
|
+
} catch (error) {
|
|
26656
|
+
if (signal?.aborted) throw error;
|
|
26657
|
+
failed.push({ url: target.url, cause: routingFailure3(error) ? error : new NodeNotRespondingError2([target.url], error) });
|
|
26658
|
+
}
|
|
26659
|
+
}
|
|
26660
|
+
if (failed.length > 0) {
|
|
26661
|
+
if (targets.size > 1) throw new PartialNodeWriteError2(succeeded, failed);
|
|
26662
|
+
if (refusal) return refusal;
|
|
26663
|
+
throw failed[0].cause;
|
|
26664
|
+
}
|
|
26665
|
+
const activated = new Set(accepted.flatMap((result) => result.activated ?? []));
|
|
26666
|
+
return {
|
|
26667
|
+
...accepted[0],
|
|
26668
|
+
activated: [...activated],
|
|
26669
|
+
skipped: [...new Set(accepted.flatMap((result) => result.skipped ?? []))].filter((space) => !activated.has(space) && !routedSpaces.some((expected) => this.spaceIdsEqual(space, expected)))
|
|
26670
|
+
};
|
|
26671
|
+
}
|
|
26672
|
+
activateRoutedDelegation(delegation, signal) {
|
|
26673
|
+
const resources = this.operationsFromDelegation(delegation).map(
|
|
26674
|
+
(operation) => operation.resource?.startsWith("urn:tinycloud:encryption:") ? operation.resource.split("/")[0] : operation.spaceId ?? delegation.spaceId
|
|
26675
|
+
);
|
|
26676
|
+
return this.activateResourceSession(delegation.delegationHeader, resources, delegation.host, signal);
|
|
26677
|
+
}
|
|
26678
|
+
activateSignedSession(session, siwe, hostHint, signal) {
|
|
26679
|
+
const resources = this.parseRecapWithCaveats(siwe).map(
|
|
26680
|
+
(entry) => this.isEncryptionNetworkOperation(this.invocationServiceName(entry.service), entry.path, entry.space) ? entry.path.split("/")[0] : entry.space.startsWith("tinycloud:") ? entry.space : session.spaceId
|
|
26681
|
+
);
|
|
26682
|
+
return this.activateResourceSession(
|
|
26683
|
+
session.delegationHeader,
|
|
26684
|
+
resources.length > 0 ? resources : [session.spaceId],
|
|
26685
|
+
hostHint,
|
|
26686
|
+
signal
|
|
26687
|
+
);
|
|
26688
|
+
}
|
|
26424
26689
|
/**
|
|
26425
26690
|
* Install a portable runtime permission delegation into this SDK instance so
|
|
26426
26691
|
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
@@ -26440,16 +26705,12 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26440
26705
|
`Runtime delegation targets ${delegation.delegateDID} but this session key is ${session.verificationMethod}.`
|
|
26441
26706
|
);
|
|
26442
26707
|
}
|
|
26443
|
-
const targetHost = delegation.host ?? this.config.host;
|
|
26444
26708
|
let activateResult;
|
|
26445
26709
|
try {
|
|
26446
|
-
activateResult = await
|
|
26447
|
-
targetHost,
|
|
26448
|
-
delegation.delegationHeader,
|
|
26449
|
-
graph.signal
|
|
26450
|
-
);
|
|
26710
|
+
activateResult = await this.activateRoutedDelegation(delegation, graph.signal);
|
|
26451
26711
|
} catch (cause) {
|
|
26452
26712
|
graph.assertActive();
|
|
26713
|
+
if (routingFailure3(cause)) throw cause;
|
|
26453
26714
|
throw Object.assign(
|
|
26454
26715
|
new Error("The TinyCloud host could not be reached to activate the delegation."),
|
|
26455
26716
|
{ code: "ACTIVATION_TRANSPORT_FAILED", cause }
|
|
@@ -26605,11 +26866,15 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26605
26866
|
...prepared,
|
|
26606
26867
|
signature: signature2
|
|
26607
26868
|
});
|
|
26608
|
-
const
|
|
26609
|
-
|
|
26610
|
-
|
|
26611
|
-
|
|
26612
|
-
|
|
26869
|
+
const delegation = this.runtimeDelegationFromSession(
|
|
26870
|
+
delegatedSession,
|
|
26871
|
+
delegatedEntries,
|
|
26872
|
+
spaceId,
|
|
26873
|
+
captured,
|
|
26874
|
+
expiresAt,
|
|
26875
|
+
{ siwe: prepared.siwe, signature: signature2 }
|
|
26876
|
+
);
|
|
26877
|
+
const activateResult = await this.activateRoutedDelegation(delegation, graph.signal).catch((error) => {
|
|
26613
26878
|
graph.assertActive();
|
|
26614
26879
|
throw error;
|
|
26615
26880
|
});
|
|
@@ -26625,14 +26890,6 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26625
26890
|
{ cause: activateResult }
|
|
26626
26891
|
);
|
|
26627
26892
|
}
|
|
26628
|
-
const delegation = this.runtimeDelegationFromSession(
|
|
26629
|
-
delegatedSession,
|
|
26630
|
-
delegatedEntries,
|
|
26631
|
-
spaceId,
|
|
26632
|
-
captured,
|
|
26633
|
-
expiresAt,
|
|
26634
|
-
{ siwe: prepared.siwe, signature: signature2 }
|
|
26635
|
-
);
|
|
26636
26893
|
graph.assertActive();
|
|
26637
26894
|
assertCapturedKey?.();
|
|
26638
26895
|
this.runtimePermissionGrants.push({
|
|
@@ -26777,7 +27034,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26777
27034
|
if (!publicSpaceId) {
|
|
26778
27035
|
throw new Error("Public space not enabled. Set enablePublicSpace: true in config.");
|
|
26779
27036
|
}
|
|
26780
|
-
await this.assertMayCreateSpace();
|
|
27037
|
+
await this.assertMayCreateSpace(publicSpaceId);
|
|
26781
27038
|
await this.auth.hostPublicSpace(publicSpaceId);
|
|
26782
27039
|
const kvActions = [KV3.PUT, KV3.GET, KV3.DEL, KV3.LIST, KV3.METADATA];
|
|
26783
27040
|
const abilities = { kv: { "": kvActions } };
|
|
@@ -26800,10 +27057,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26800
27057
|
...prepared,
|
|
26801
27058
|
signature: signature2
|
|
26802
27059
|
});
|
|
26803
|
-
const activateResult = await
|
|
26804
|
-
this.config.host,
|
|
26805
|
-
delegationSession.delegationHeader
|
|
26806
|
-
);
|
|
27060
|
+
const activateResult = await this.activateSignedSession(delegationSession, prepared.siwe);
|
|
26807
27061
|
if (!activateResult.success) {
|
|
26808
27062
|
throw Object.assign(
|
|
26809
27063
|
new Error(`Failed to activate public space delegation: ${describeHostFailure2(activateResult)}`),
|
|
@@ -27353,10 +27607,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
27353
27607
|
if (onPrepared) {
|
|
27354
27608
|
await onPrepared(delegation);
|
|
27355
27609
|
}
|
|
27356
|
-
const activateResult = await
|
|
27357
|
-
this.config.host,
|
|
27358
|
-
delegationHeader
|
|
27359
|
-
);
|
|
27610
|
+
const activateResult = await this.activateRoutedDelegation(delegation);
|
|
27360
27611
|
assertDelegationActivated(activateResult, delegation, requireActivation);
|
|
27361
27612
|
return delegation;
|
|
27362
27613
|
}
|
|
@@ -27390,10 +27641,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
27390
27641
|
if (onPrepared) {
|
|
27391
27642
|
await onPrepared(delegation);
|
|
27392
27643
|
}
|
|
27393
|
-
const activateResult = await
|
|
27394
|
-
targetHost,
|
|
27395
|
-
delegationHeader
|
|
27396
|
-
);
|
|
27644
|
+
const activateResult = await this.activateRoutedDelegation(delegation);
|
|
27397
27645
|
assertDelegationActivated(activateResult, delegation, requireActivation);
|
|
27398
27646
|
return delegation;
|
|
27399
27647
|
}
|
|
@@ -28045,10 +28293,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
28045
28293
|
...prepared,
|
|
28046
28294
|
signature: signature2
|
|
28047
28295
|
});
|
|
28048
|
-
const activateResult = await
|
|
28049
|
-
this.config.host,
|
|
28050
|
-
delegationSession.delegationHeader
|
|
28051
|
-
);
|
|
28296
|
+
const activateResult = await this.activateSignedSession(delegationSession, prepared.siwe);
|
|
28052
28297
|
if (!activateResult.success) {
|
|
28053
28298
|
throw Object.assign(
|
|
28054
28299
|
new Error(`Failed to activate delegation: ${describeHostFailure2(activateResult)}`),
|
|
@@ -28093,10 +28338,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
28093
28338
|
...publicPrepared,
|
|
28094
28339
|
signature: publicSignature
|
|
28095
28340
|
});
|
|
28096
|
-
const publicActivateResult = await
|
|
28097
|
-
this.config.host,
|
|
28098
|
-
publicSession.delegationHeader
|
|
28099
|
-
);
|
|
28341
|
+
const publicActivateResult = await this.activateSignedSession(publicSession, publicPrepared.siwe);
|
|
28100
28342
|
if (publicActivateResult.success) {
|
|
28101
28343
|
result.publicDelegation = {
|
|
28102
28344
|
cid: publicSession.delegationCid,
|
|
@@ -28202,11 +28444,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
28202
28444
|
...prepared,
|
|
28203
28445
|
signature: signature2
|
|
28204
28446
|
});
|
|
28205
|
-
const activateResult = await
|
|
28206
|
-
targetHost,
|
|
28207
|
-
invokerSession.delegationHeader,
|
|
28208
|
-
graph.signal
|
|
28209
|
-
).catch((error) => {
|
|
28447
|
+
const activateResult = await this.activateSignedSession(invokerSession, prepared.siwe, targetHost, graph.signal).catch((error) => {
|
|
28210
28448
|
graph.assertActive();
|
|
28211
28449
|
throw error;
|
|
28212
28450
|
});
|
|
@@ -28333,10 +28571,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
28333
28571
|
...prepared,
|
|
28334
28572
|
signature: signature2
|
|
28335
28573
|
});
|
|
28336
|
-
const activateResult = await
|
|
28337
|
-
targetHost,
|
|
28338
|
-
subDelegationSession.delegationHeader
|
|
28339
|
-
);
|
|
28574
|
+
const activateResult = await this.activateSignedSession(subDelegationSession, prepared.siwe, targetHost);
|
|
28340
28575
|
if (!activateResult.success) {
|
|
28341
28576
|
throw Object.assign(
|
|
28342
28577
|
new Error(`Failed to activate sub-delegation: ${describeHostFailure2(activateResult)}`),
|
|
@@ -28400,6 +28635,7 @@ registerReplicationLoaders({
|
|
|
28400
28635
|
|
|
28401
28636
|
// src/index.ts
|
|
28402
28637
|
import { TinyCloud as TinyCloud2 } from "@tinycloud/sdk-core";
|
|
28638
|
+
import { NodeNotRespondingError as NodeNotRespondingError3, NodeIdentityMismatchError as NodeIdentityMismatchError2, PartialNodeWriteError as PartialNodeWriteError3 } from "@tinycloud/sdk-services";
|
|
28403
28639
|
import {
|
|
28404
28640
|
SilentNotificationHandler as SilentNotificationHandler2,
|
|
28405
28641
|
AutoApproveSpaceCreationHandler as AutoApproveSpaceCreationHandler2,
|
|
@@ -28717,7 +28953,7 @@ import {
|
|
|
28717
28953
|
SpaceService as SpaceService2,
|
|
28718
28954
|
SpaceErrorCodes,
|
|
28719
28955
|
createSpaceService,
|
|
28720
|
-
parseSpaceUri as
|
|
28956
|
+
parseSpaceUri as parseSpaceUri4,
|
|
28721
28957
|
buildSpaceUri,
|
|
28722
28958
|
makePublicSpaceId as makePublicSpaceId2,
|
|
28723
28959
|
Space
|
|
@@ -31288,8 +31524,11 @@ export {
|
|
|
31288
31524
|
MemorySessionStorage,
|
|
31289
31525
|
NETWORK_NAME_PATTERN,
|
|
31290
31526
|
NetworkIdError,
|
|
31527
|
+
NodeIdentityMismatchError2 as NodeIdentityMismatchError,
|
|
31528
|
+
NodeNotRespondingError3 as NodeNotRespondingError,
|
|
31291
31529
|
NodeUserAuthorization,
|
|
31292
31530
|
NodeWasmBindings,
|
|
31531
|
+
PartialNodeWriteError3 as PartialNodeWriteError,
|
|
31293
31532
|
PermissionNotInManifestError2 as PermissionNotInManifestError,
|
|
31294
31533
|
PrefixedKVService,
|
|
31295
31534
|
PrivateKeySigner,
|
|
@@ -31397,7 +31636,7 @@ export {
|
|
|
31397
31636
|
parseNetworkId3 as parseNetworkId,
|
|
31398
31637
|
parsePkhDid,
|
|
31399
31638
|
parseSecretCatalogKey,
|
|
31400
|
-
|
|
31639
|
+
parseSpaceUri4 as parseSpaceUri,
|
|
31401
31640
|
pkhDid4 as pkhDid,
|
|
31402
31641
|
principalDid3 as principalDid,
|
|
31403
31642
|
principalDidEquals4 as principalDidEquals,
|