@tinycloud/node-sdk 3.12.0-beta.1 → 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-BtoDI9in.d.cts → NodeUserAuthorization-B_TFUlua.d.cts} +19 -53
- package/dist/{NodeUserAuthorization-BtoDI9in.d.ts → NodeUserAuthorization-B_TFUlua.d.ts} +19 -53
- package/dist/{TinyCloudNode-DLrINr98.d.ts → TinyCloudNode-Brcu8QoX.d.cts} +33 -59
- package/dist/{TinyCloudNode-C24728N_.d.cts → TinyCloudNode-DBJUnsMN.d.ts} +33 -59
- package/dist/{authority-BQN2Z3st.d.ts → authority-BFc5lesl.d.ts} +1 -1
- package/dist/{authority-DDdheOL6.d.cts → authority-DQ6R1HEG.d.cts} +1 -1
- package/dist/core.cjs +555 -320
- 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 +581 -324
- 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 +572 -337
- 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 +582 -325
- 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.cjs +48 -6
- package/dist/space-hosting.cjs.map +1 -1
- package/dist/space-hosting.d.cts +23 -8
- package/dist/space-hosting.d.ts +23 -8
- package/dist/space-hosting.js +48 -6
- package/dist/space-hosting.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -17045,7 +17045,7 @@ function augmentSignInEntriesWithReplication(input) {
|
|
|
17045
17045
|
for (const prefix of replication.prefixes) {
|
|
17046
17046
|
(0, import_prefix.assertFolderPrefix)(prefix);
|
|
17047
17047
|
const path = prefix === "/" ? "" : prefix;
|
|
17048
|
-
if ((0,
|
|
17048
|
+
if ((0, import_sdk_services3.requiresSecretsOptIn)(input.primarySpaceId, prefix) && replication.allowSecrets !== true) {
|
|
17049
17049
|
continue;
|
|
17050
17050
|
}
|
|
17051
17051
|
if (!hasUnrestrictedGetCoverage(input.entries, input.primarySpaceId, path)) {
|
|
@@ -17071,12 +17071,12 @@ function augmentSignInEntriesWithReplication(input) {
|
|
|
17071
17071
|
}
|
|
17072
17072
|
return out;
|
|
17073
17073
|
}
|
|
17074
|
-
var import_sdk_core2,
|
|
17074
|
+
var import_sdk_core2, import_sdk_services3, import_prefix, KV_GET, KV_SYNC, KV_SERVICE;
|
|
17075
17075
|
var init_authority_sign_in = __esm({
|
|
17076
17076
|
"src/replication/authority-sign-in.ts"() {
|
|
17077
17077
|
"use strict";
|
|
17078
17078
|
import_sdk_core2 = require("@tinycloud/sdk-core");
|
|
17079
|
-
|
|
17079
|
+
import_sdk_services3 = require("@tinycloud/sdk-services");
|
|
17080
17080
|
import_prefix = require("@tinycloud/sdk-services/kv/replication/prefix");
|
|
17081
17081
|
KV_GET = import_sdk_core2.KV.GET;
|
|
17082
17082
|
KV_SYNC = import_sdk_core2.KV.SYNC;
|
|
@@ -17875,8 +17875,11 @@ __export(src_exports, {
|
|
|
17875
17875
|
MemorySessionStorage: () => MemorySessionStorage,
|
|
17876
17876
|
NETWORK_NAME_PATTERN: () => import_sdk_core22.NETWORK_NAME_PATTERN,
|
|
17877
17877
|
NetworkIdError: () => import_sdk_core22.NetworkIdError,
|
|
17878
|
+
NodeIdentityMismatchError: () => import_sdk_services8.NodeIdentityMismatchError,
|
|
17879
|
+
NodeNotRespondingError: () => import_sdk_services8.NodeNotRespondingError,
|
|
17878
17880
|
NodeUserAuthorization: () => NodeUserAuthorization,
|
|
17879
17881
|
NodeWasmBindings: () => NodeWasmBindings,
|
|
17882
|
+
PartialNodeWriteError: () => import_sdk_services8.PartialNodeWriteError,
|
|
17880
17883
|
PermissionNotInManifestError: () => import_sdk_core17.PermissionNotInManifestError,
|
|
17881
17884
|
PrefixedKVService: () => import_sdk_core18.PrefixedKVService,
|
|
17882
17885
|
PrivateKeySigner: () => PrivateKeySigner,
|
|
@@ -18275,6 +18278,7 @@ function rememberInvokeAny(invokeAny2, sink, pending) {
|
|
|
18275
18278
|
|
|
18276
18279
|
// src/delegation.ts
|
|
18277
18280
|
var import_sdk_core = require("@tinycloud/sdk-core");
|
|
18281
|
+
var import_sdk_services2 = require("@tinycloud/sdk-services");
|
|
18278
18282
|
var RUNTIME_DELEGATION_REJECTIONS = [
|
|
18279
18283
|
"host_mismatch",
|
|
18280
18284
|
"expired",
|
|
@@ -18316,7 +18320,8 @@ var SERVICE_BY_SHORT_NAME = {
|
|
|
18316
18320
|
kv: "tinycloud.kv",
|
|
18317
18321
|
sql: "tinycloud.sql",
|
|
18318
18322
|
duckdb: "tinycloud.duckdb",
|
|
18319
|
-
hooks: "tinycloud.hooks"
|
|
18323
|
+
hooks: "tinycloud.hooks",
|
|
18324
|
+
capabilities: "tinycloud.capabilities"
|
|
18320
18325
|
};
|
|
18321
18326
|
function authorizationWithoutBearer(authorization) {
|
|
18322
18327
|
return authorization.replace(/^Bearer /i, "");
|
|
@@ -18447,8 +18452,8 @@ function signedAuthorityFromCompactUcan(authorization) {
|
|
|
18447
18452
|
));
|
|
18448
18453
|
continue;
|
|
18449
18454
|
}
|
|
18450
|
-
const match = /^(?:tinycloud:\/\/([^/]+)|(tinycloud:[^/]+))\/(
|
|
18451
|
-
if (!match) {
|
|
18455
|
+
const match = /^(?:tinycloud:\/\/([^/]+)|(tinycloud:[^/]+))\/([^/]+)(?:\/|$)(.*)$/.exec(resource);
|
|
18456
|
+
if (!match || !Object.prototype.hasOwnProperty.call(SERVICE_BY_SHORT_NAME, match[3])) {
|
|
18452
18457
|
throw new Error(
|
|
18453
18458
|
`Validated runtime delegation has an unsupported signed resource '${resource}'.`
|
|
18454
18459
|
);
|
|
@@ -18593,13 +18598,8 @@ function resourcesMatch(actual, expected) {
|
|
|
18593
18598
|
return JSON.stringify(actual.map(comparable)) === JSON.stringify(expected.map(comparable));
|
|
18594
18599
|
}
|
|
18595
18600
|
async function activateValidatedRuntimeDelegation(node, delegation, options) {
|
|
18596
|
-
|
|
18597
|
-
|
|
18598
|
-
reject(
|
|
18599
|
-
"host_mismatch",
|
|
18600
|
-
`Runtime delegation host '${delegation.host}' does not match expected host '${options.host}'.`
|
|
18601
|
-
);
|
|
18602
|
-
}
|
|
18601
|
+
let host = normalizedHost(options.host);
|
|
18602
|
+
const differentHost = delegation.host !== void 0 && normalizedHost(delegation.host) !== host;
|
|
18603
18603
|
if (!(delegation.expiry instanceof Date) || Number.isNaN(delegation.expiry.getTime())) {
|
|
18604
18604
|
reject("authority_invalid", "Runtime delegation has an invalid expiry.");
|
|
18605
18605
|
}
|
|
@@ -18643,6 +18643,13 @@ async function activateValidatedRuntimeDelegation(node, delegation, options) {
|
|
|
18643
18643
|
if (options.authorize !== void 0 && !options.authorize(signed2.permissions)) {
|
|
18644
18644
|
throw new Error("Runtime delegation is not authorized for this runtime.");
|
|
18645
18645
|
}
|
|
18646
|
+
if (differentHost) {
|
|
18647
|
+
const registeredHost = await node.resolveRegisteredDelegationHost?.(signed2.permissions, host);
|
|
18648
|
+
if (registeredHost === void 0) {
|
|
18649
|
+
reject("host_mismatch", `Runtime delegation host '${delegation.host}' does not match expected host '${options.host}'.`);
|
|
18650
|
+
}
|
|
18651
|
+
host = normalizedHost(registeredHost);
|
|
18652
|
+
}
|
|
18646
18653
|
const previouslyInstalled = node.getRuntimePermissionDelegations().find((candidate) => candidate.cid === cid);
|
|
18647
18654
|
if (previouslyInstalled !== void 0 && previouslyInstalled.delegationHeader.Authorization !== authorization) {
|
|
18648
18655
|
reject(
|
|
@@ -18686,6 +18693,7 @@ async function activateValidatedRuntimeDelegation(node, delegation, options) {
|
|
|
18686
18693
|
await node.useRuntimeDelegation(installedCandidate);
|
|
18687
18694
|
}
|
|
18688
18695
|
} catch (error) {
|
|
18696
|
+
if ((0, import_sdk_services2.routingFailure)(error)) throw error;
|
|
18689
18697
|
reject(
|
|
18690
18698
|
activationRejectionReason(error),
|
|
18691
18699
|
error instanceof Error ? error.message : String(error)
|
|
@@ -18704,6 +18712,7 @@ async function activateValidatedRuntimeDelegation(node, delegation, options) {
|
|
|
18704
18712
|
"Runtime delegation activation installed authority that differs from the validated authority."
|
|
18705
18713
|
);
|
|
18706
18714
|
}
|
|
18715
|
+
node.refreshRegistryForCapabilities?.(signed2.permissions);
|
|
18707
18716
|
return {
|
|
18708
18717
|
cid,
|
|
18709
18718
|
delegation: installed,
|
|
@@ -18813,6 +18822,7 @@ function deserializeDelegation(data) {
|
|
|
18813
18822
|
}
|
|
18814
18823
|
|
|
18815
18824
|
// src/TinyCloudNode.ts
|
|
18825
|
+
var import_sdk_services5 = require("@tinycloud/sdk-services");
|
|
18816
18826
|
var import_siwe2 = require("siwe");
|
|
18817
18827
|
var import_share_envelope = require("@tinycloud/share-envelope");
|
|
18818
18828
|
|
|
@@ -18830,7 +18840,7 @@ function getNodeReplicationLoaders() {
|
|
|
18830
18840
|
|
|
18831
18841
|
// src/TinyCloudNode.ts
|
|
18832
18842
|
var import_sdk_core10 = require("@tinycloud/sdk-core");
|
|
18833
|
-
var
|
|
18843
|
+
var import_sdk_services6 = require("@tinycloud/sdk-services");
|
|
18834
18844
|
var import_requestLog2 = require("@tinycloud/sdk-services/telemetry/requestLog");
|
|
18835
18845
|
var import_resource2 = require("@tinycloud/sdk-services/encryption/resource");
|
|
18836
18846
|
|
|
@@ -18839,7 +18849,7 @@ var import_node_crypto = require("crypto");
|
|
|
18839
18849
|
var import_siwe = require("siwe");
|
|
18840
18850
|
var import_sdk_core5 = require("@tinycloud/sdk-core");
|
|
18841
18851
|
var import_resource = require("@tinycloud/sdk-services/encryption/resource");
|
|
18842
|
-
var
|
|
18852
|
+
var import_sdk_services4 = require("@tinycloud/sdk-services");
|
|
18843
18853
|
|
|
18844
18854
|
// src/authorization/appKeyHostBinding.ts
|
|
18845
18855
|
var DOMAIN_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
@@ -19114,11 +19124,12 @@ var NodeUserAuthorization = class {
|
|
|
19114
19124
|
this.tinycloudHosts = config.tinycloudHosts;
|
|
19115
19125
|
this.tinycloudRegistryUrl = config.tinycloudRegistryUrl;
|
|
19116
19126
|
this.tinycloudFallbackHosts = config.tinycloudFallbackHosts;
|
|
19117
|
-
this.registryPolicy = config.registryPolicy;
|
|
19118
19127
|
this.newUserHost = config.newUserHost;
|
|
19119
19128
|
this.registryChainId = config.registryChainId;
|
|
19120
19129
|
this.registrySequenceStore = config.registrySequenceStore ?? (0, import_sdk_core5.createInMemoryRegistrySequenceStore)();
|
|
19130
|
+
this.registryCacheStore = config.registryCacheStore ?? (0, import_sdk_core5.createInMemoryRegistryCacheStore)();
|
|
19121
19131
|
this.onNodeNotFound = config.onNodeNotFound;
|
|
19132
|
+
this.onRegistryFallback = config.onRegistryFallback;
|
|
19122
19133
|
this.autoDiscoverLocalNode = config.autoDiscoverLocalNode;
|
|
19123
19134
|
this.localNodeUrl = config.localNodeUrl;
|
|
19124
19135
|
this.localLinkName = config.localLinkName;
|
|
@@ -19186,13 +19197,8 @@ var NodeUserAuthorization = class {
|
|
|
19186
19197
|
* undefined for missing SIWEs and the SDK then treats the session as
|
|
19187
19198
|
* expired-at-epoch-zero.
|
|
19188
19199
|
*
|
|
19189
|
-
* @param hosts -
|
|
19190
|
-
*
|
|
19191
|
-
* present (and non-empty) they are adopted directly so the restored
|
|
19192
|
-
* session resolves to the same node as the original sign-in without
|
|
19193
|
-
* re-running registry/fallback resolution. When absent (old session)
|
|
19194
|
-
* hosts are resolved lazily on the first host-needing call via
|
|
19195
|
-
* {@link ensureTinyCloudHosts}.
|
|
19200
|
+
* @param hosts - Persisted host hints for genuine no-entry onboarding.
|
|
19201
|
+
* Current signed registry membership always takes precedence.
|
|
19196
19202
|
*/
|
|
19197
19203
|
setRestoredTinyCloudSession(session, hosts) {
|
|
19198
19204
|
const address = (0, import_sdk_core5.canonicalizeAddress)(session.address);
|
|
@@ -19254,89 +19260,84 @@ var NodeUserAuthorization = class {
|
|
|
19254
19260
|
signature: session.signature
|
|
19255
19261
|
};
|
|
19256
19262
|
}
|
|
19257
|
-
/**
|
|
19258
|
-
* Ensure `tinycloudHosts` are resolved before a host-needing call.
|
|
19259
|
-
*
|
|
19260
|
-
* Fresh sign-in resolves hosts up front; a restored session may not have
|
|
19261
|
-
* (old persisted sessions predate {@link PersistedSessionData.tinycloudHosts}).
|
|
19262
|
-
* This guard makes a restored session resolve the node exactly like a fresh
|
|
19263
|
-
* sign-in — persisted hosts are preferred (already set by
|
|
19264
|
-
* {@link setRestoredTinyCloudSession}), otherwise the registry/fallback
|
|
19265
|
-
* resolution runs lazily here. Idempotent: {@link resolveTinyCloudHostsForSignIn}
|
|
19266
|
-
* early-returns when hosts are already set.
|
|
19267
|
-
*
|
|
19268
|
-
* Throws if hosts are unset and the restored session has no address/chainId
|
|
19269
|
-
* to resolve from — a real failure that must surface, not be masked.
|
|
19270
|
-
*/
|
|
19263
|
+
/** Refresh the primary space's registry destinations, including after restore. */
|
|
19271
19264
|
async ensureTinyCloudHosts() {
|
|
19272
|
-
if (this.tinycloudHosts && this.tinycloudHosts.length > 0) {
|
|
19273
|
-
return;
|
|
19274
|
-
}
|
|
19275
19265
|
if (this._address === void 0 || this._chainId === void 0) {
|
|
19276
|
-
throw new Error(
|
|
19277
|
-
"Cannot resolve TinyCloud hosts: no address/chainId available. Sign in or restore a session first."
|
|
19278
|
-
);
|
|
19266
|
+
throw new Error("Cannot resolve TinyCloud hosts: sign in or restore a session first.");
|
|
19279
19267
|
}
|
|
19280
|
-
await this.resolveTinyCloudHostsForSignIn(this._address, this._chainId);
|
|
19268
|
+
await this.resolveTinyCloudHostsForSignIn(this._address, this._chainId, this._tinyCloudSession?.spaceId);
|
|
19281
19269
|
}
|
|
19282
|
-
async resolveTinyCloudHostsForSignIn(address, chainId) {
|
|
19283
|
-
|
|
19284
|
-
|
|
19285
|
-
|
|
19286
|
-
|
|
19287
|
-
const resolved2 = await (0, import_sdk_core5.resolveFailClosedTinyCloudHosts)(address, {
|
|
19288
|
-
registryUrl: this.tinycloudRegistryUrl,
|
|
19289
|
-
registryChainId: this.registryChainId,
|
|
19290
|
-
newUserHost: this.newUserHost,
|
|
19291
|
-
sequenceStore: this.registrySequenceStore,
|
|
19292
|
-
onNodeNotFound: this.onNodeNotFound,
|
|
19293
|
-
autoDiscoverLocalNode: this.autoDiscoverLocalNode,
|
|
19294
|
-
localNodeUrl: this.localNodeUrl,
|
|
19295
|
-
localLinkName: this.localLinkName,
|
|
19296
|
-
expectedNodeDid: this.expectedNodeDid,
|
|
19297
|
-
localNodeIdentityStore: this.localNodeIdentityStore
|
|
19298
|
-
});
|
|
19299
|
-
this.tinycloudHosts = resolved2.hosts;
|
|
19300
|
-
this.registryVerdict = resolved2.verdict;
|
|
19301
|
-
return;
|
|
19302
|
-
}
|
|
19303
|
-
const subject = (0, import_sdk_core5.pkhDid)(address, chainId);
|
|
19304
|
-
const resolved = await (0, import_sdk_core5.resolveTinyCloudHosts)(subject, {
|
|
19305
|
-
registryUrl: this.tinycloudRegistryUrl,
|
|
19306
|
-
fallbackHosts: this.tinycloudFallbackHosts,
|
|
19307
|
-
autoDiscoverLocalNode: this.autoDiscoverLocalNode,
|
|
19308
|
-
localNodeUrl: this.localNodeUrl,
|
|
19309
|
-
localLinkName: this.localLinkName,
|
|
19310
|
-
expectedNodeDid: this.expectedNodeDid,
|
|
19311
|
-
localNodeIdentityStore: this.localNodeIdentityStore
|
|
19312
|
-
});
|
|
19313
|
-
this.tinycloudHosts = resolved.hosts;
|
|
19270
|
+
async resolveTinyCloudHostsForSignIn(address, chainId, restoredSpaceId) {
|
|
19271
|
+
const spaceId = restoredSpaceId ?? this.resolveSignInCapabilities(address, chainId).spaceId;
|
|
19272
|
+
const destinations = await this.resolveResourceDestinations(spaceId, "read");
|
|
19273
|
+
for (const destination of destinations) await (0, import_sdk_services4.verifyResourceDestination)(destination, globalThis.fetch.bind(globalThis));
|
|
19274
|
+
this.tinycloudHosts = destinations.map(({ url }) => url);
|
|
19314
19275
|
}
|
|
19315
|
-
/**
|
|
19316
|
-
|
|
19317
|
-
|
|
19318
|
-
|
|
19319
|
-
|
|
19320
|
-
|
|
19321
|
-
|
|
19322
|
-
|
|
19323
|
-
|
|
19324
|
-
|
|
19325
|
-
|
|
19326
|
-
|
|
19327
|
-
|
|
19276
|
+
/** Current full-resource registry routing; saved hosts are only no-entry hints. */
|
|
19277
|
+
async resolveResourceDestinations(resource, access, noEntryHosts = this.tinycloudHosts ?? []) {
|
|
19278
|
+
const owner = resource.startsWith("urn:tinycloud:encryption:") ? (0, import_sdk_services4.parseNetworkId)(resource.split("/")[0]).ownerDid : (0, import_sdk_core5.parseSpaceUri)(resource)?.owner ?? (() => {
|
|
19279
|
+
const key2 = /^tinycloud:key:(z[^:]+):[^/]+$/.exec(resource);
|
|
19280
|
+
return key2 ? `did:key:${key2[1]}` : void 0;
|
|
19281
|
+
})();
|
|
19282
|
+
if (!owner) throw new Error(`Cannot determine registry owner for ${resource}`);
|
|
19283
|
+
const subject = owner.startsWith("did:pkh:") ? (0, import_sdk_core5.registryOwnerSubjectV1)(owner.slice(owner.lastIndexOf(":") + 1), this.registryChainId) : owner;
|
|
19284
|
+
for (let attempt = 1; ; attempt++) {
|
|
19285
|
+
try {
|
|
19286
|
+
const resolved = await (0, import_sdk_core5.resolveRegistryResource)(subject, resource, access, {
|
|
19287
|
+
registryUrl: this.tinycloudRegistryUrl,
|
|
19288
|
+
registryChainId: this.registryChainId,
|
|
19289
|
+
sequenceStore: this.registrySequenceStore,
|
|
19290
|
+
cacheStore: this.registryCacheStore,
|
|
19291
|
+
onRegistryFallback: this.onRegistryFallback
|
|
19292
|
+
});
|
|
19293
|
+
this.registryVerdict = resolved.verdict;
|
|
19294
|
+
if (resolved.verdict.status === "unregistered") {
|
|
19295
|
+
if (noEntryHosts.length === 0 && (this.autoDiscoverLocalNode ?? true)) {
|
|
19296
|
+
const local = await (0, import_sdk_core5.discoverLocalTinyCloudNode)({
|
|
19297
|
+
localNodeUrl: this.localNodeUrl,
|
|
19298
|
+
localLinkName: this.localLinkName,
|
|
19299
|
+
expectedNodeDid: this.expectedNodeDid,
|
|
19300
|
+
identityStore: this.localNodeIdentityStore
|
|
19301
|
+
});
|
|
19302
|
+
if (local) {
|
|
19303
|
+
(0, import_sdk_core5.warnRegistryDefaultNode)(resolved.verdict, this.registryCacheStore, this.onRegistryFallback);
|
|
19304
|
+
return [{ url: local.url }];
|
|
19305
|
+
}
|
|
19306
|
+
}
|
|
19307
|
+
const host = noEntryHosts[0] ?? this.newUserHost ?? (this.tinycloudFallbackHosts === void 0 ? "https://node.tinycloud.xyz" : this.tinycloudFallbackHosts?.[0]);
|
|
19308
|
+
if (!host || this.newUserHost === null && noEntryHosts.length === 0) throw new import_sdk_core5.NodeNotFoundError(subject, resolved.verdict);
|
|
19309
|
+
const url = (0, import_sdk_core5.exactHttpOrigin)(host, "new-user host");
|
|
19310
|
+
(0, import_sdk_core5.warnRegistryDefaultNode)(resolved.verdict, this.registryCacheStore, this.onRegistryFallback);
|
|
19311
|
+
return [{ url }];
|
|
19312
|
+
}
|
|
19313
|
+
const destinations = resolved.hosts.flatMap((host) => host.urls.map((url) => ({ url, nodeDid: host.nodeDid })));
|
|
19314
|
+
return destinations;
|
|
19315
|
+
} catch (error) {
|
|
19316
|
+
if (!(error instanceof import_sdk_core5.NodeNotFoundError) && !(error instanceof import_sdk_core5.RegistryUnavailableError)) throw error;
|
|
19317
|
+
if (!this.onNodeNotFound) throw error;
|
|
19318
|
+
const decision = await this.onNodeNotFound({ error, attempt });
|
|
19319
|
+
if (decision.action === "retry") continue;
|
|
19320
|
+
if (decision.action === "use-host" && error.verdict?.status === "unregistered") {
|
|
19321
|
+
const url = (0, import_sdk_core5.exactHttpOrigin)(decision.host, "new-user host");
|
|
19322
|
+
(0, import_sdk_core5.warnRegistryDefaultNode)(error.verdict, this.registryCacheStore, this.onRegistryFallback);
|
|
19323
|
+
return [{ url }];
|
|
19324
|
+
}
|
|
19325
|
+
throw error;
|
|
19326
|
+
}
|
|
19328
19327
|
}
|
|
19329
|
-
|
|
19330
|
-
|
|
19331
|
-
|
|
19332
|
-
|
|
19333
|
-
|
|
19334
|
-
|
|
19335
|
-
|
|
19336
|
-
|
|
19337
|
-
|
|
19328
|
+
}
|
|
19329
|
+
get registryRecordPresent() {
|
|
19330
|
+
return this.registryVerdict?.status === "registered";
|
|
19331
|
+
}
|
|
19332
|
+
/** Refresh membership before hosting; a saved no-entry verdict never authorizes a later creation. */
|
|
19333
|
+
async assertMayCreateSpace(spaceId = this._tinyCloudSession?.spaceId, host = this.primaryTinyCloudHost) {
|
|
19334
|
+
if (!spaceId) throw new Error("Must be signed in to create a space");
|
|
19335
|
+
const destinations = await this.resolveResourceDestinations(spaceId, "host");
|
|
19336
|
+
if (!destinations.some((destination) => destination.url === host)) {
|
|
19337
|
+
const subject = (0, import_sdk_core5.parseSpaceUri)(spaceId)?.owner;
|
|
19338
|
+
if (!subject || !this.registryVerdict) throw new Error("Cannot resolve space owner");
|
|
19339
|
+
(0, import_sdk_core5.assertSpaceCreationAllowed)(this.registryVerdict, host, spaceId);
|
|
19338
19340
|
}
|
|
19339
|
-
(0, import_sdk_core5.assertSpaceCreationAllowed)(verdict, this.primaryTinyCloudHost);
|
|
19340
19341
|
}
|
|
19341
19342
|
requireTinyCloudHosts() {
|
|
19342
19343
|
if (!this.tinycloudHosts || this.tinycloudHosts.length === 0) {
|
|
@@ -19653,7 +19654,7 @@ var NodeUserAuthorization = class {
|
|
|
19653
19654
|
if (entry.service !== import_sdk_core5.ENCRYPTION_PERMISSION_SERVICE) {
|
|
19654
19655
|
return true;
|
|
19655
19656
|
}
|
|
19656
|
-
const parsed = entry.path.includes("/") ? (0, import_resource.parseEncryptionResource)(entry.path) : { ...(0,
|
|
19657
|
+
const parsed = entry.path.includes("/") ? (0, import_resource.parseEncryptionResource)(entry.path) : { ...(0, import_sdk_services4.parseNetworkId)(entry.path), kind: "network" };
|
|
19657
19658
|
const sharePrefix = parsed.kind === "prefix" && parsed.network === "default" && parsed.prefix === "share" && entry.actions.length > 0 && entry.actions.every((action) => action === DECRYPT_ACTION || action === KEY_CREATE_ACTION) && didPrincipalMatches(parsed.ownerDid, currentDid);
|
|
19658
19659
|
if (parsed.kind !== "network" && !sharePrefix) {
|
|
19659
19660
|
throw new import_sdk_core5.ManifestValidationError("named keys go in manifest.keys");
|
|
@@ -19766,23 +19767,60 @@ var NodeUserAuthorization = class {
|
|
|
19766
19767
|
* Create the space on the TinyCloud server (host delegation).
|
|
19767
19768
|
* This registers the user as the owner of the space.
|
|
19768
19769
|
*/
|
|
19769
|
-
async hostSpace(targetSpaceId, purpose, signal) {
|
|
19770
|
-
|
|
19770
|
+
async hostSpace(targetSpaceId, purpose, signal, assertCurrent, siweParameters) {
|
|
19771
|
+
const context = typeof targetSpaceId === "object" ? targetSpaceId : void 0;
|
|
19772
|
+
const address = context?.address ?? this._address;
|
|
19773
|
+
const chainId = context?.chainId ?? this._chainId;
|
|
19774
|
+
if (!address || chainId === void 0 || !this._tinyCloudSession) {
|
|
19771
19775
|
throw new Error("Must be signed in to host space");
|
|
19772
19776
|
}
|
|
19777
|
+
const checkCurrent = () => {
|
|
19778
|
+
signal?.throwIfAborted();
|
|
19779
|
+
assertCurrent?.();
|
|
19780
|
+
};
|
|
19781
|
+
checkCurrent();
|
|
19773
19782
|
await this.ensureTinyCloudHosts();
|
|
19774
|
-
|
|
19775
|
-
const
|
|
19783
|
+
checkCurrent();
|
|
19784
|
+
const host = context?.host ?? this.primaryTinyCloudHost;
|
|
19785
|
+
const spaceId = context?.spaceId ?? (typeof targetSpaceId === "string" ? targetSpaceId : this._tinyCloudSession.spaceId);
|
|
19786
|
+
const destinations = await this.resolveResourceDestinations(spaceId, "host");
|
|
19787
|
+
if (this.registryVerdict?.status === "registered") {
|
|
19788
|
+
const results = [];
|
|
19789
|
+
for (const destination of destinations) {
|
|
19790
|
+
checkCurrent();
|
|
19791
|
+
try {
|
|
19792
|
+
await (0, import_sdk_services4.verifyResourceDestination)(destination, globalThis.fetch.bind(globalThis));
|
|
19793
|
+
const result = await (0, import_sdk_core5.activateSessionWithHost)(destination.url, this._tinyCloudSession.delegationHeader, signal);
|
|
19794
|
+
results.push(result.skipped?.includes(spaceId) ? { ...result, success: false, status: 403, error: "The session does not activate the requested space" } : result);
|
|
19795
|
+
} catch (error) {
|
|
19796
|
+
if ((0, import_sdk_services4.routingFailure)(error) || signal?.aborted) throw error;
|
|
19797
|
+
throw new import_sdk_services4.NodeNotRespondingError([destination.url], error);
|
|
19798
|
+
}
|
|
19799
|
+
}
|
|
19800
|
+
const failed = results.flatMap((result, index) => result.success ? [] : [{ url: destinations[index].url, status: result.status }]);
|
|
19801
|
+
if (failed.length) {
|
|
19802
|
+
if (destinations.length === 1) return results[0];
|
|
19803
|
+
throw new import_sdk_services4.PartialNodeWriteError(destinations.filter((_, index) => results[index].success).map(({ url }) => url), failed);
|
|
19804
|
+
}
|
|
19805
|
+
return {
|
|
19806
|
+
...results[0],
|
|
19807
|
+
activated: [...new Set(results.flatMap((result) => result.activated ?? []))],
|
|
19808
|
+
skipped: [...new Set(results.flatMap((result) => result.skipped ?? []))]
|
|
19809
|
+
};
|
|
19810
|
+
}
|
|
19776
19811
|
const peerId = await (0, import_sdk_core5.fetchPeerId)(host, spaceId, signal);
|
|
19777
|
-
|
|
19778
|
-
|
|
19779
|
-
|
|
19780
|
-
|
|
19781
|
-
|
|
19782
|
-
|
|
19783
|
-
|
|
19784
|
-
|
|
19785
|
-
|
|
19812
|
+
checkCurrent();
|
|
19813
|
+
const siwe = this.wasm.generateHostSIWEMessage(
|
|
19814
|
+
context && siweParameters ? siweParameters(peerId) : {
|
|
19815
|
+
address,
|
|
19816
|
+
chainId,
|
|
19817
|
+
domain: this.domain,
|
|
19818
|
+
issuedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19819
|
+
spaceId,
|
|
19820
|
+
peerId
|
|
19821
|
+
}
|
|
19822
|
+
);
|
|
19823
|
+
checkCurrent();
|
|
19786
19824
|
const signing = this.signMessage(siwe, purpose);
|
|
19787
19825
|
let signature2;
|
|
19788
19826
|
if (!signal) {
|
|
@@ -19801,8 +19839,9 @@ var NodeUserAuthorization = class {
|
|
|
19801
19839
|
signal.removeEventListener("abort", onAbort);
|
|
19802
19840
|
}
|
|
19803
19841
|
}
|
|
19804
|
-
|
|
19842
|
+
checkCurrent();
|
|
19805
19843
|
const headers = this.wasm.siweToDelegationHeaders({ siwe, signature: signature2 });
|
|
19844
|
+
checkCurrent();
|
|
19806
19845
|
return (0, import_sdk_core5.submitHostDelegation)(host, headers, signal);
|
|
19807
19846
|
}
|
|
19808
19847
|
/**
|
|
@@ -19839,13 +19878,45 @@ var NodeUserAuthorization = class {
|
|
|
19839
19878
|
if (!this._tinyCloudSession) {
|
|
19840
19879
|
throw new Error("Must be signed in to ensure space exists");
|
|
19841
19880
|
}
|
|
19842
|
-
await this.ensureTinyCloudHosts();
|
|
19843
|
-
const host = this.primaryTinyCloudHost;
|
|
19844
19881
|
const primarySpaceId = this._tinyCloudSession.spaceId;
|
|
19845
|
-
const
|
|
19846
|
-
|
|
19847
|
-
|
|
19848
|
-
)
|
|
19882
|
+
const primary = await this.resolveResourceDestinations(primarySpaceId, "read");
|
|
19883
|
+
this.tinycloudHosts = primary.map(({ url }) => url);
|
|
19884
|
+
const targets = new Map(primary.map((destination) => [destination.url, destination]));
|
|
19885
|
+
if (this.registryVerdict?.status === "registered" && this.registryVerdict.record) {
|
|
19886
|
+
const record = this.registryVerdict.record;
|
|
19887
|
+
const resources = Object.keys((0, import_sdk_core5.extractRecapAttenuations)(this._tinyCloudSession.siwe ?? ""));
|
|
19888
|
+
for (const space of record.spaces) {
|
|
19889
|
+
const registeredSpace = (0, import_sdk_core5.parseSpaceUri)(space.space);
|
|
19890
|
+
if (!resources.some((resource) => {
|
|
19891
|
+
const fullSpace = resource.split("/")[0];
|
|
19892
|
+
const requestedSpace = (0, import_sdk_core5.parseSpaceUri)(fullSpace);
|
|
19893
|
+
return fullSpace === space.space || registeredSpace !== null && requestedSpace !== null && registeredSpace.owner === requestedSpace.owner && registeredSpace.name === requestedSpace.name;
|
|
19894
|
+
})) continue;
|
|
19895
|
+
for (const membership of space.nodes) {
|
|
19896
|
+
const node = record.nodes.find((candidate) => candidate.did === membership.did);
|
|
19897
|
+
targets.set(node.url, { url: node.url, nodeDid: node.did });
|
|
19898
|
+
}
|
|
19899
|
+
}
|
|
19900
|
+
if (record.encryptionNode && resources.some((resource) => resource.startsWith("urn:tinycloud:encryption:"))) {
|
|
19901
|
+
const node = record.nodes.find((candidate) => candidate.did === record.encryptionNode);
|
|
19902
|
+
targets.set(node.url, { url: node.url, nodeDid: node.did });
|
|
19903
|
+
}
|
|
19904
|
+
}
|
|
19905
|
+
let result;
|
|
19906
|
+
for (const destination of targets.values()) {
|
|
19907
|
+
await (0, import_sdk_services4.verifyResourceDestination)(destination, globalThis.fetch.bind(globalThis));
|
|
19908
|
+
let activated;
|
|
19909
|
+
try {
|
|
19910
|
+
activated = await (0, import_sdk_core5.activateSessionWithHost)(destination.url, this._tinyCloudSession.delegationHeader);
|
|
19911
|
+
} catch (error) {
|
|
19912
|
+
throw new import_sdk_services4.NodeNotRespondingError([destination.url], error);
|
|
19913
|
+
}
|
|
19914
|
+
if (!activated.success && activated.status !== 404) {
|
|
19915
|
+
throw Object.assign(new Error(`Failed to activate session: ${describeHostFailure(activated)}`), { cause: activated });
|
|
19916
|
+
}
|
|
19917
|
+
if (destination.url === primary[0].url) result = activated;
|
|
19918
|
+
}
|
|
19919
|
+
if (!result) throw new Error("Primary space activation did not return a result");
|
|
19849
19920
|
this.recordActivationSkippedSpaces(result, primarySpaceId);
|
|
19850
19921
|
if (!result.success && result.status !== 404) {
|
|
19851
19922
|
throw Object.assign(
|
|
@@ -19864,43 +19935,36 @@ var NodeUserAuthorization = class {
|
|
|
19864
19935
|
*
|
|
19865
19936
|
* @throws Error if the creation handler fails or times out, or hosting fails
|
|
19866
19937
|
*/
|
|
19867
|
-
async ensurePrimarySpaceHosted(hosting) {
|
|
19868
|
-
if (hosting !== "not-hosted") {
|
|
19869
|
-
return;
|
|
19870
|
-
}
|
|
19938
|
+
async ensurePrimarySpaceHosted(hosting, signal) {
|
|
19871
19939
|
const session = this._tinyCloudSession;
|
|
19872
|
-
if (
|
|
19873
|
-
|
|
19874
|
-
}
|
|
19940
|
+
if (hosting !== "not-hosted") return;
|
|
19941
|
+
if (!session) throw new Error("Must be signed in to host the primary space");
|
|
19875
19942
|
const handler = this.spaceCreationHandler ?? (this.autoCreateSpace ? new import_sdk_core5.AutoApproveSpaceCreationHandler() : void 0);
|
|
19876
|
-
if (!handler)
|
|
19877
|
-
return;
|
|
19878
|
-
}
|
|
19879
|
-
await this.ensureTinyCloudHosts();
|
|
19880
|
-
const host = this.primaryTinyCloudHost;
|
|
19881
|
-
const primarySpaceId = session.spaceId;
|
|
19882
|
-
const creationContext = {
|
|
19883
|
-
spaceId: primarySpaceId,
|
|
19884
|
-
address: this._address,
|
|
19885
|
-
chainId: this._chainId,
|
|
19886
|
-
host
|
|
19887
|
-
};
|
|
19943
|
+
if (!handler) return;
|
|
19888
19944
|
const { createPrimarySpace } = await import("./space-hosting.cjs");
|
|
19889
19945
|
await createPrimarySpace({
|
|
19890
19946
|
handler,
|
|
19891
|
-
|
|
19892
|
-
|
|
19893
|
-
|
|
19947
|
+
session,
|
|
19948
|
+
currentSession: () => this._tinyCloudSession,
|
|
19949
|
+
signerAddress: () => this.signer.getAddress(),
|
|
19950
|
+
resolveHost: async () => {
|
|
19951
|
+
await this.ensureTinyCloudHosts();
|
|
19952
|
+
return this.primaryTinyCloudHost;
|
|
19953
|
+
},
|
|
19954
|
+
domain: this.domain,
|
|
19955
|
+
guard: (context) => this.guardSpaceCreation(handler, context),
|
|
19956
|
+
host: (context, assertCurrent, siweParameters) => this.hostSpace(context, void 0, signal, assertCurrent, siweParameters),
|
|
19894
19957
|
onHosted: () => {
|
|
19895
19958
|
this._spaceHosting = "hosted";
|
|
19896
19959
|
},
|
|
19897
|
-
activate: () => (0, import_sdk_core5.activateSessionWithHost)(host, session.delegationHeader)
|
|
19960
|
+
activate: (context) => (0, import_sdk_core5.activateSessionWithHost)(context.host, session.delegationHeader),
|
|
19961
|
+
signal
|
|
19898
19962
|
});
|
|
19899
19963
|
}
|
|
19900
19964
|
/** {@link assertMayCreateSpace}, reporting a refusal to the creation handler before rethrowing it. */
|
|
19901
19965
|
async guardSpaceCreation(handler, context) {
|
|
19902
19966
|
try {
|
|
19903
|
-
await this.assertMayCreateSpace();
|
|
19967
|
+
await this.assertMayCreateSpace(context.spaceId, context.host);
|
|
19904
19968
|
} catch (error) {
|
|
19905
19969
|
const failure = error instanceof Error ? error : new Error(String(error));
|
|
19906
19970
|
handler.onSpaceCreationFailed?.(context, failure);
|
|
@@ -21807,6 +21871,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21807
21871
|
* ```
|
|
21808
21872
|
*/
|
|
21809
21873
|
constructor(config = {}) {
|
|
21874
|
+
this.registryRefreshOwners = /* @__PURE__ */ new Set();
|
|
21810
21875
|
this.signer = null;
|
|
21811
21876
|
this.auth = null;
|
|
21812
21877
|
this.consumedOpenKeyPendings = /* @__PURE__ */ new WeakSet();
|
|
@@ -21970,8 +22035,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21970
22035
|
this.config = {
|
|
21971
22036
|
...config,
|
|
21972
22037
|
host: config.host ?? DEFAULT_HOST,
|
|
22038
|
+
registryPolicy: "fail-closed",
|
|
21973
22039
|
// One store for every authorization this client creates.
|
|
21974
|
-
registrySequenceStore: config.registrySequenceStore ?? (0, import_sdk_core10.createInMemoryRegistrySequenceStore)()
|
|
22040
|
+
registrySequenceStore: config.registrySequenceStore ?? (0, import_sdk_core10.createInMemoryRegistrySequenceStore)(),
|
|
22041
|
+
registryCacheStore: config.registryCacheStore ?? (0, import_sdk_core10.createInMemoryRegistryCacheStore)()
|
|
21975
22042
|
};
|
|
21976
22043
|
if (this.config.replication?.enabled) {
|
|
21977
22044
|
const runtime = getNodeReplicationLoaders().runtime().then(({ ReplicationRuntime: ReplicationRuntime2 }) => {
|
|
@@ -22218,6 +22285,18 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22218
22285
|
return "unknown";
|
|
22219
22286
|
}
|
|
22220
22287
|
}
|
|
22288
|
+
/** Run the existing primary-space creation flow for the restored session. */
|
|
22289
|
+
async ensurePrimarySpaceHosted(hosting) {
|
|
22290
|
+
const graph = this._serviceGraph;
|
|
22291
|
+
const epoch = this.sessionEpoch;
|
|
22292
|
+
graph.assertActive();
|
|
22293
|
+
this.assertSessionEpoch(epoch, "Space hosting");
|
|
22294
|
+
const auth = this.auth;
|
|
22295
|
+
if (!auth || !this.session) return;
|
|
22296
|
+
await auth.ensurePrimarySpaceHosted(hosting, graph.signal);
|
|
22297
|
+
graph.assertActive();
|
|
22298
|
+
this.assertSessionEpoch(epoch, "Space hosting");
|
|
22299
|
+
}
|
|
22221
22300
|
/**
|
|
22222
22301
|
* The node's last answer for the primary space: recorded at sign-in from the
|
|
22223
22302
|
* activation, or by {@link checkSpaceHosting}. `unknown` after a restore until
|
|
@@ -22263,7 +22342,11 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22263
22342
|
// A session saved but never registered on this node is refused: it registers once.
|
|
22264
22343
|
activate: (signal) => {
|
|
22265
22344
|
assertCurrent();
|
|
22266
|
-
|
|
22345
|
+
const activationSignal = AbortSignal.any([signal, graph.signal]);
|
|
22346
|
+
if (primary && primary.delegationCid === session.delegationCid) {
|
|
22347
|
+
return this.activateSignedSession(primary, primary.siwe, host, activationSignal);
|
|
22348
|
+
}
|
|
22349
|
+
return this.activateResourceSession(session.delegationHeader, [spaceId], host, activationSignal);
|
|
22267
22350
|
}
|
|
22268
22351
|
});
|
|
22269
22352
|
assertCurrent();
|
|
@@ -22399,14 +22482,38 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22399
22482
|
get requests() {
|
|
22400
22483
|
return this._requestLog === void 0 ? void 0 : (0, import_requestLog2.readOnlyRequestLog)(this._requestLog);
|
|
22401
22484
|
}
|
|
22402
|
-
|
|
22485
|
+
throwRegistryRefusal(status, text) {
|
|
22486
|
+
if (status !== 403 && status !== 503 || !text.startsWith("registry_unavailable:") && !text.startsWith("registry_hosting_denied:")) return;
|
|
22487
|
+
const code = status === 503 ? "registry_unavailable" : "node_registry_entry_exists";
|
|
22488
|
+
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 } });
|
|
22489
|
+
}
|
|
22490
|
+
createServiceGraphLifetime(session) {
|
|
22491
|
+
const routedFetch = (0, import_sdk_services5.createResourceRoutingFetch)(
|
|
22492
|
+
async (url, init2) => {
|
|
22493
|
+
try {
|
|
22494
|
+
const response = await globalThis.fetch(url, init2);
|
|
22495
|
+
if (response.status === 403 || response.status === 503) {
|
|
22496
|
+
this.throwRegistryRefusal(response.status, await response.clone().text());
|
|
22497
|
+
}
|
|
22498
|
+
return response;
|
|
22499
|
+
} catch (error) {
|
|
22500
|
+
if ((0, import_sdk_services5.routingFailure)(error)) throw error;
|
|
22501
|
+
if (new URL(url).pathname.startsWith("/encryption/") && !init2?.signal?.aborted) {
|
|
22502
|
+
throw new import_sdk_services5.NodeNotRespondingError([new URL(url).origin], error);
|
|
22503
|
+
}
|
|
22504
|
+
throw error;
|
|
22505
|
+
}
|
|
22506
|
+
},
|
|
22507
|
+
{
|
|
22508
|
+
resolveResource: (resource, access) => this.routingDestinations(resource, access),
|
|
22509
|
+
bindAudience: (authorization, nodeDid) => this.bindRoutedAudience(authorization, nodeDid, session),
|
|
22510
|
+
verifyDestination: (target) => (0, import_sdk_services5.verifyResourceDestination)(target, globalThis.fetch.bind(globalThis))
|
|
22511
|
+
}
|
|
22512
|
+
);
|
|
22403
22513
|
return new ServiceGraphLifetime(
|
|
22404
22514
|
this.invokeWithRuntimePermissions,
|
|
22405
22515
|
this.invokeAnyWithRuntimePermissions,
|
|
22406
|
-
|
|
22407
|
-
// signal while preserving the SDK's existing injectable global fetch
|
|
22408
|
-
// behavior (including adapters that install it after construction).
|
|
22409
|
-
(url, init2) => globalThis.fetch(url, init2),
|
|
22516
|
+
routedFetch,
|
|
22410
22517
|
(context) => {
|
|
22411
22518
|
this.storageFull.observe(context);
|
|
22412
22519
|
if (this._requestLog) (0, import_requestLog2.observeReplicaReads)(context, this._requestLog);
|
|
@@ -22414,6 +22521,75 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22414
22521
|
this._requestLog
|
|
22415
22522
|
);
|
|
22416
22523
|
}
|
|
22524
|
+
async routingDestinations(resource, access, noEntryHosts) {
|
|
22525
|
+
const session = this.currentTinyCloudSession();
|
|
22526
|
+
if (session) this.refreshRegistryOwner((0, import_sdk_core10.pkhDid)(session.address, session.chainId));
|
|
22527
|
+
if (this.auth) return this.auth.resolveResourceDestinations(resource, access, noEntryHosts);
|
|
22528
|
+
const owner = resource.startsWith("urn:tinycloud:encryption:") ? (0, import_sdk_core10.parseNetworkId)(resource.split("/")[0]).ownerDid : (0, import_sdk_core10.parseSpaceUri)(resource)?.owner ?? (() => {
|
|
22529
|
+
const key2 = /^tinycloud:key:(z[^:]+):[^/]+$/.exec(resource);
|
|
22530
|
+
return key2 ? `did:key:${key2[1]}` : void 0;
|
|
22531
|
+
})();
|
|
22532
|
+
if (!owner) throw new Error(`Cannot determine registry owner for ${resource}`);
|
|
22533
|
+
const subject = owner.startsWith("did:pkh:") ? (0, import_sdk_core10.pkhDid)(owner.slice(owner.lastIndexOf(":") + 1), this.config.registryChainId ?? 1) : owner;
|
|
22534
|
+
const resolved = await (0, import_sdk_core10.resolveRegistryResource)(subject, resource, access, {
|
|
22535
|
+
registryUrl: this.config.tinycloudRegistryUrl,
|
|
22536
|
+
registryChainId: this.config.registryChainId,
|
|
22537
|
+
sequenceStore: this.config.registrySequenceStore,
|
|
22538
|
+
cacheStore: this.config.registryCacheStore,
|
|
22539
|
+
onRegistryFallback: this.config.onRegistryFallback
|
|
22540
|
+
});
|
|
22541
|
+
if (resolved.verdict.status === "unregistered") {
|
|
22542
|
+
const hint = noEntryHosts === void 0 ? this.explicitHost ?? this.config.host : noEntryHosts[0];
|
|
22543
|
+
const host = hint ?? this.config.newUserHost ?? (this.config.tinycloudFallbackHosts === void 0 ? DEFAULT_HOST : this.config.tinycloudFallbackHosts?.[0]);
|
|
22544
|
+
if (!host || !hint && this.config.newUserHost === null) throw new import_sdk_core10.NodeNotFoundError(subject, resolved.verdict);
|
|
22545
|
+
const url = (0, import_sdk_core10.exactHttpOrigin)(host, "new-user host");
|
|
22546
|
+
(0, import_sdk_core10.warnRegistryDefaultNode)(resolved.verdict, this.config.registryCacheStore, this.config.onRegistryFallback);
|
|
22547
|
+
return [{ url }];
|
|
22548
|
+
}
|
|
22549
|
+
const destinations = resolved.hosts.flatMap((host) => host.urls.map((url) => ({ url, nodeDid: host.nodeDid })));
|
|
22550
|
+
return destinations;
|
|
22551
|
+
}
|
|
22552
|
+
/** Transport hints cannot override verified full-resource registry routing. */
|
|
22553
|
+
async resolveRegisteredDelegationHost(permissions, noEntryHost) {
|
|
22554
|
+
const resources = /* @__PURE__ */ new Map();
|
|
22555
|
+
let hasData = false;
|
|
22556
|
+
for (const permission of permissions) {
|
|
22557
|
+
const encryption = this.isEncryptionNetworkOperation(this.invocationServiceName(permission.service), permission.path, permission.space);
|
|
22558
|
+
const resource = encryption ? permission.path.split("/")[0] : permission.space;
|
|
22559
|
+
if (!resource) throw new import_sdk_services5.ResourceNotRoutedError("delegation");
|
|
22560
|
+
resources.set(resource, encryption ? "encryption" : "host");
|
|
22561
|
+
hasData || (hasData = !encryption);
|
|
22562
|
+
}
|
|
22563
|
+
let dataHost;
|
|
22564
|
+
let encryptionHost;
|
|
22565
|
+
let unserved;
|
|
22566
|
+
for (const [resource, access] of resources) {
|
|
22567
|
+
try {
|
|
22568
|
+
const destinations = await this.routingDestinations(resource, access, [noEntryHost]);
|
|
22569
|
+
if (destinations.some(({ nodeDid }) => nodeDid === void 0)) return void 0;
|
|
22570
|
+
if (access === "host") dataHost ?? (dataHost = destinations[0]?.url);
|
|
22571
|
+
else encryptionHost ?? (encryptionHost = destinations[0]?.url);
|
|
22572
|
+
} catch (error) {
|
|
22573
|
+
if (!(error instanceof import_sdk_core10.NodeNotFoundError) || error.verdict.status !== "registered") throw error;
|
|
22574
|
+
unserved ?? (unserved = error);
|
|
22575
|
+
}
|
|
22576
|
+
}
|
|
22577
|
+
const host = hasData ? dataHost : encryptionHost;
|
|
22578
|
+
if (!host) throw unserved ?? new import_sdk_services5.ResourceNotRoutedError("delegation");
|
|
22579
|
+
return host;
|
|
22580
|
+
}
|
|
22581
|
+
async bindRoutedAudience(authorization, nodeDid, stagedSession) {
|
|
22582
|
+
const payload = JSON.parse(new TextDecoder().decode(base64UrlDecode(authorization.split(".")[1])));
|
|
22583
|
+
const issuer = typeof payload === "object" && payload !== null && "iss" in payload ? payload.iss : void 0;
|
|
22584
|
+
const session = [
|
|
22585
|
+
stagedSession,
|
|
22586
|
+
this._serviceContext?.session,
|
|
22587
|
+
this.auth?.tinyCloudSession,
|
|
22588
|
+
...this.runtimePermissionGrants.map((grant) => grant.session)
|
|
22589
|
+
].find((candidate) => candidate?.verificationMethod === issuer);
|
|
22590
|
+
if (!session) throw new Error("Invocation signing session is not active");
|
|
22591
|
+
return rewriteInvocationAudience(authorization, nodeDid, session.jwk);
|
|
22592
|
+
}
|
|
22417
22593
|
syncResolvedHostFromAuth() {
|
|
22418
22594
|
const host = this.auth?.hosts[0];
|
|
22419
22595
|
if (host) {
|
|
@@ -22527,6 +22703,29 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22527
22703
|
const session = this.currentTinyCloudSession();
|
|
22528
22704
|
return session?.siwe ? this.projectSignedRecapCapabilities(session.siwe) : [];
|
|
22529
22705
|
}
|
|
22706
|
+
/** Start one background refresh for each owner derived from verified authority. */
|
|
22707
|
+
refreshRegistryForCapabilities(permissions) {
|
|
22708
|
+
for (const permission of permissions) {
|
|
22709
|
+
try {
|
|
22710
|
+
const owner = permission.service === "tinycloud.encryption" && permission.path.startsWith("urn:tinycloud:encryption:") ? (0, import_sdk_core10.parseNetworkId)(permission.path.split("/")[0]).ownerDid : permission.space ? this.ownerDidFromSpaceId(permission.space) : void 0;
|
|
22711
|
+
if (owner) this.refreshRegistryOwner(owner);
|
|
22712
|
+
} catch {
|
|
22713
|
+
}
|
|
22714
|
+
}
|
|
22715
|
+
}
|
|
22716
|
+
refreshRegistryOwner(owner) {
|
|
22717
|
+
if (this.config.tinycloudRegistryUrl === null) return;
|
|
22718
|
+
const subject = owner.startsWith("did:pkh:") ? (0, import_sdk_core10.pkhDid)(owner.slice(owner.lastIndexOf(":") + 1), this.config.registryChainId ?? 1) : owner;
|
|
22719
|
+
const key2 = (0, import_sdk_core10.registryOwnerKeyV2)(subject);
|
|
22720
|
+
if (this.registryRefreshOwners.has(key2)) return;
|
|
22721
|
+
this.registryRefreshOwners.add(key2);
|
|
22722
|
+
void (0, import_sdk_core10.refreshOwnerRegistry)(subject, {
|
|
22723
|
+
registryUrl: this.config.tinycloudRegistryUrl,
|
|
22724
|
+
registryChainId: this.config.registryChainId,
|
|
22725
|
+
sequenceStore: this.config.registrySequenceStore,
|
|
22726
|
+
cacheStore: this.config.registryCacheStore
|
|
22727
|
+
}).catch(() => void 0);
|
|
22728
|
+
}
|
|
22530
22729
|
/** Normalize signed capabilities without collapsing nested encryption into raw authority. */
|
|
22531
22730
|
projectSignedRecapCapabilities(siwe) {
|
|
22532
22731
|
const projected = this.parseRecapWithCaveats(siwe).map((entry) => {
|
|
@@ -22958,6 +23157,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22958
23157
|
const primarySession = this.currentTinyCloudSession();
|
|
22959
23158
|
if (primarySession) {
|
|
22960
23159
|
this.registerPrimarySessionGrant(primarySession);
|
|
23160
|
+
this.refreshRegistryOwner((0, import_sdk_core10.pkhDid)(primarySession.address, primarySession.chainId));
|
|
23161
|
+
this.refreshRegistryForCapabilities(this.getVerifiedSessionCapabilities());
|
|
22961
23162
|
if (this.auth?.spaceHosting === "unknown") {
|
|
22962
23163
|
await this.checkSpaceHosting();
|
|
22963
23164
|
this.assertSessionEpoch(epoch, "Sign-in");
|
|
@@ -23180,8 +23381,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23180
23381
|
const primarySession = auth.tinyCloudSession;
|
|
23181
23382
|
const defaultSpaceId = this.ownedSpaceId("default");
|
|
23182
23383
|
const canReusePrimaryBootstrapSession = primarySession?.spaceId === defaultSpaceId && auth.capabilityRequest === import_sdk_core10.BOOTSTRAP_SESSION_REQUESTS.default;
|
|
23183
|
-
|
|
23184
|
-
await this.assertMayCreateSpace();
|
|
23384
|
+
for (const step of steps) {
|
|
23385
|
+
if (step.kind === "host") await this.assertMayCreateSpace(step.spaceId);
|
|
23185
23386
|
}
|
|
23186
23387
|
for (const step of steps) {
|
|
23187
23388
|
if (step.kind !== "session") continue;
|
|
@@ -23230,10 +23431,20 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23230
23431
|
if (!session) {
|
|
23231
23432
|
throw new Error(`Missing bootstrap session for ${step.space}`);
|
|
23232
23433
|
}
|
|
23233
|
-
const
|
|
23234
|
-
|
|
23235
|
-
|
|
23236
|
-
|
|
23434
|
+
const destinations = await this.routingDestinations(step.spaceId, "host");
|
|
23435
|
+
let activated;
|
|
23436
|
+
for (const destination of destinations) {
|
|
23437
|
+
try {
|
|
23438
|
+
await (0, import_sdk_services5.verifyResourceDestination)(destination, globalThis.fetch.bind(globalThis));
|
|
23439
|
+
const result = await (0, import_sdk_core10.activateSessionWithHost)(destination.url, session.delegationHeader, graph.signal);
|
|
23440
|
+
if (!result.success) throw Object.assign(new Error("Bootstrap activation failed"), { cause: result });
|
|
23441
|
+
activated = result;
|
|
23442
|
+
} catch (error) {
|
|
23443
|
+
graph.assertActive();
|
|
23444
|
+
throw error;
|
|
23445
|
+
}
|
|
23446
|
+
}
|
|
23447
|
+
if (!activated) throw new Error("Bootstrap space has no destination");
|
|
23237
23448
|
graph.assertActive();
|
|
23238
23449
|
if (!activated.success || activated.skipped?.includes(step.spaceId)) {
|
|
23239
23450
|
throw Object.assign(
|
|
@@ -23563,7 +23774,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23563
23774
|
if (!this.currentSessionCoversAccountRegistry()) {
|
|
23564
23775
|
if (!_TinyCloudNode.accountRegistrySkipLogged) {
|
|
23565
23776
|
_TinyCloudNode.accountRegistrySkipLogged = true;
|
|
23566
|
-
|
|
23777
|
+
import_sdk_services6.tinyCloudDebugLogger.log("registry.skip");
|
|
23567
23778
|
}
|
|
23568
23779
|
return;
|
|
23569
23780
|
}
|
|
@@ -23816,6 +24027,13 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23816
24027
|
if (!session) {
|
|
23817
24028
|
throw new Error("Owned space hosting requires an active session");
|
|
23818
24029
|
}
|
|
24030
|
+
await this.assertMayCreateSpace(spaceId);
|
|
24031
|
+
if (this.auth.registryRecordPresent) {
|
|
24032
|
+
const hosted = await this.hostOwnedSpaceOnce(spaceId, signal);
|
|
24033
|
+
if (!hosted.success) throw Object.assign(new Error(`Failed to activate listed space ${spaceId}: ${describeHostFailure2(hosted)}`), { cause: hosted });
|
|
24034
|
+
if (this.auth.tinyCloudSession === session) this.confirmedHostedSpaceIds.add(spaceId);
|
|
24035
|
+
return;
|
|
24036
|
+
}
|
|
23819
24037
|
if (this.confirmedHostedSpaceIds.has(spaceId) || this.hostedOwnedSpaceIds.has(spaceId)) {
|
|
23820
24038
|
return;
|
|
23821
24039
|
}
|
|
@@ -23834,7 +24052,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23834
24052
|
{ cause: activation }
|
|
23835
24053
|
);
|
|
23836
24054
|
}
|
|
23837
|
-
await this.assertMayCreateSpace();
|
|
24055
|
+
await this.assertMayCreateSpace(spaceId);
|
|
23838
24056
|
if (signal?.aborted) throw signal.reason;
|
|
23839
24057
|
const created = await this.hostOwnedSpaceOnce(spaceId, signal);
|
|
23840
24058
|
if (!created.success) {
|
|
@@ -23895,17 +24113,19 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23895
24113
|
{ cause: hosted }
|
|
23896
24114
|
);
|
|
23897
24115
|
}
|
|
23898
|
-
|
|
23899
|
-
|
|
23900
|
-
|
|
23901
|
-
|
|
23902
|
-
if (!activation.success || activation.skipped?.includes(spaceId)) {
|
|
23903
|
-
throw Object.assign(
|
|
23904
|
-
new Error(
|
|
23905
|
-
`Failed to activate session for owned space ${spaceId}: ${activation.success ? "space was skipped" : describeHostFailure2(activation)}`
|
|
23906
|
-
),
|
|
23907
|
-
{ cause: activation }
|
|
24116
|
+
if (!this.auth.registryRecordPresent) {
|
|
24117
|
+
const activation = await (0, import_sdk_core10.activateSessionWithHost)(
|
|
24118
|
+
host,
|
|
24119
|
+
session.delegationHeader
|
|
23908
24120
|
);
|
|
24121
|
+
if (!activation.success || activation.skipped?.includes(spaceId)) {
|
|
24122
|
+
throw Object.assign(
|
|
24123
|
+
new Error(
|
|
24124
|
+
`Failed to activate session for owned space ${spaceId}: ${activation.success ? "space was skipped" : describeHostFailure2(activation)}`
|
|
24125
|
+
),
|
|
24126
|
+
{ cause: activation }
|
|
24127
|
+
);
|
|
24128
|
+
}
|
|
23909
24129
|
}
|
|
23910
24130
|
if (this.auth?.tinyCloudSession === session) this.hostedOwnedSpaceIds.add(spaceId);
|
|
23911
24131
|
try {
|
|
@@ -23949,11 +24169,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23949
24169
|
* session's recap doesn't cover it and the registry has no record; the host
|
|
23950
24170
|
* SIWE is idempotent server-side. Must be called after {@link signIn}.
|
|
23951
24171
|
*
|
|
23952
|
-
*
|
|
23953
|
-
*
|
|
23954
|
-
*
|
|
23955
|
-
* verdict. {@link hostOwnedSpace} itself is an explicit action and is not
|
|
23956
|
-
* guarded.
|
|
24172
|
+
* Current signed per-space membership is checked before both automatic and
|
|
24173
|
+
* explicit hosting. Registered replicas use the session grant; no-entry
|
|
24174
|
+
* onboarding retains the host-SIWE flow.
|
|
23957
24175
|
*
|
|
23958
24176
|
* @param name - The owned space name (e.g. `"secrets"`).
|
|
23959
24177
|
* @returns The hosted owned-space URI.
|
|
@@ -23963,27 +24181,20 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23963
24181
|
throw new Error("Not signed in. Call signIn() first.");
|
|
23964
24182
|
}
|
|
23965
24183
|
const spaceId = this.ownedSpaceId(name);
|
|
24184
|
+
await this.assertMayCreateSpace(spaceId);
|
|
23966
24185
|
if (this.hostedOwnedSpaceIds.has(spaceId) || await this.isOwnedSpaceRegistered(spaceId)) {
|
|
23967
24186
|
return spaceId;
|
|
23968
24187
|
}
|
|
23969
|
-
await this.assertMayCreateSpace();
|
|
23970
24188
|
return this.hostOwnedSpace(name);
|
|
23971
24189
|
}
|
|
23972
|
-
/**
|
|
23973
|
-
|
|
23974
|
-
|
|
23975
|
-
* {@link ensurePublicSpace}. Only `registryPolicy: "fail-closed"` consults
|
|
23976
|
-
* the registry ({@link NodeUserAuthorization.assertMayCreateSpace}); the
|
|
23977
|
-
* default policy performs no lookup.
|
|
23978
|
-
*/
|
|
23979
|
-
async assertMayCreateSpace() {
|
|
23980
|
-
if (this.config.registryPolicy !== "fail-closed") return;
|
|
23981
|
-
await this.auth.assertMayCreateSpace();
|
|
24190
|
+
/** Refresh full-space membership before any automatic or explicit hosting. */
|
|
24191
|
+
async assertMayCreateSpace(spaceId) {
|
|
24192
|
+
await this.routingDestinations(spaceId, "host");
|
|
23982
24193
|
}
|
|
23983
24194
|
/** Registry-policy settings shared by every authorization this client creates. */
|
|
23984
24195
|
registryPolicyConfig() {
|
|
23985
|
-
const { registryPolicy, newUserHost, registryChainId, registrySequenceStore, onNodeNotFound } = this.config;
|
|
23986
|
-
return { registryPolicy, newUserHost, registryChainId, registrySequenceStore, onNodeNotFound };
|
|
24196
|
+
const { registryPolicy, newUserHost, registryChainId, registrySequenceStore, registryCacheStore, onNodeNotFound, onRegistryFallback } = this.config;
|
|
24197
|
+
return { registryPolicy, newUserHost, registryChainId, registrySequenceStore, registryCacheStore, onNodeNotFound, onRegistryFallback };
|
|
23987
24198
|
}
|
|
23988
24199
|
/**
|
|
23989
24200
|
* Submit the host delegation for an owned space, sharing one in-flight
|
|
@@ -24189,12 +24400,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24189
24400
|
}
|
|
24190
24401
|
stagedSessionExpiry = signedExpiry ?? persistedPolicyExpiry ?? persistedExpiry(void 0);
|
|
24191
24402
|
}
|
|
24192
|
-
const
|
|
24193
|
-
|
|
24194
|
-
|
|
24195
|
-
sessionData.chainId
|
|
24196
|
-
);
|
|
24197
|
-
const stagedHost = resolvedHost ?? this.config.host;
|
|
24403
|
+
const restoreHints = this.explicitHost ? [this.explicitHost] : sessionData.tinycloudHosts ?? [];
|
|
24404
|
+
const resolvedHost = this.config.replication?.enabled ? (await this.routingDestinations(sessionData.spaceId, "read", restoreHints))[0]?.url : void 0;
|
|
24405
|
+
const stagedHost = resolvedHost ?? (0, import_sdk_core10.exactHttpOrigin)(restoreHints[0] ?? this.config.host, "restored host hint");
|
|
24198
24406
|
let restoredNodeFeatures;
|
|
24199
24407
|
let replicaProtocolMismatch = false;
|
|
24200
24408
|
if (this.config.replication?.enabled) {
|
|
@@ -24311,7 +24519,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24311
24519
|
this.sessionManager = stagedManager;
|
|
24312
24520
|
this.sessionKeyId = stagedKeyId;
|
|
24313
24521
|
this.sessionKeyJwk = stagedJwk;
|
|
24314
|
-
|
|
24522
|
+
this.config.host = stagedHost;
|
|
24315
24523
|
if (spaceOwnerKey(serviceSession.spaceId) !== this.currentStorageOwner()) {
|
|
24316
24524
|
this.storageFull.clear();
|
|
24317
24525
|
this._requestLog?.clear();
|
|
@@ -24378,6 +24586,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24378
24586
|
if (attempt === this.latestSessionAttempt) await this.endSession();
|
|
24379
24587
|
this.assertSessionEpoch(epoch, "Session restore");
|
|
24380
24588
|
}
|
|
24589
|
+
if (this.config.replication?.enabled) {
|
|
24590
|
+
if (stagedAddress !== void 0) this.refreshRegistryOwner((0, import_sdk_core10.pkhDid)(stagedAddress, stagedChainId));
|
|
24591
|
+
this.refreshRegistryForCapabilities(this.getVerifiedSessionCapabilities());
|
|
24592
|
+
}
|
|
24381
24593
|
}
|
|
24382
24594
|
stagePrimarySessionState(session, verificationMethod, host, sessionExpiry, recap) {
|
|
24383
24595
|
if (!session?.siwe) return { cache: void 0, grants: [] };
|
|
@@ -24426,7 +24638,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24426
24638
|
};
|
|
24427
24639
|
}
|
|
24428
24640
|
stageRestoredServiceGraph(input) {
|
|
24429
|
-
const graph = this.createServiceGraphLifetime();
|
|
24641
|
+
const graph = this.createServiceGraphLifetime(input.serviceSession);
|
|
24430
24642
|
const core = this.auth ? new import_sdk_core10.TinyCloud(this.auth, {
|
|
24431
24643
|
invokeAny: graph.invokeAny,
|
|
24432
24644
|
telemetry: this.config.telemetry,
|
|
@@ -24651,56 +24863,6 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24651
24863
|
spaceService.updateConfig({ sharingService });
|
|
24652
24864
|
return { core, graph, serviceContext, kv, sql, duckdb, hooks, vault, encryption, capabilityRegistry, keyProvider, sharingService, delegationManager, spaceService };
|
|
24653
24865
|
}
|
|
24654
|
-
/**
|
|
24655
|
-
* Resolve the host a restored session should target.
|
|
24656
|
-
*
|
|
24657
|
-
* Mirrors fresh sign-in host resolution but for the restore path:
|
|
24658
|
-
* an explicit/pinned host always wins, then the hosts the session was
|
|
24659
|
-
* persisted with, then a lazy registry/fallback resolution for sessions
|
|
24660
|
-
* that predate the persisted `tinycloudHosts` field. Returns `undefined`
|
|
24661
|
-
* only when there's nothing to resolve from (no explicit host, no
|
|
24662
|
-
* persisted hosts, and no address/chainId) — in which case the existing
|
|
24663
|
-
* `config.host` (default) is left in place.
|
|
24664
|
-
*
|
|
24665
|
-
* Resolution failures are surfaced, not swallowed: a genuinely broken
|
|
24666
|
-
* registry lookup throws rather than silently falling back to a wrong host.
|
|
24667
|
-
*/
|
|
24668
|
-
async resolveRestoredHost(persistedHosts, address, chainId) {
|
|
24669
|
-
if (this.explicitHost) {
|
|
24670
|
-
return this.explicitHost;
|
|
24671
|
-
}
|
|
24672
|
-
if (persistedHosts && persistedHosts.length > 0) {
|
|
24673
|
-
return persistedHosts[0];
|
|
24674
|
-
}
|
|
24675
|
-
if (address === void 0 || chainId === void 0) {
|
|
24676
|
-
return void 0;
|
|
24677
|
-
}
|
|
24678
|
-
if (this.config.registryPolicy === "fail-closed") {
|
|
24679
|
-
const resolved2 = await (0, import_sdk_core10.resolveFailClosedTinyCloudHosts)(address, {
|
|
24680
|
-
registryUrl: this.config.tinycloudRegistryUrl,
|
|
24681
|
-
registryChainId: this.config.registryChainId,
|
|
24682
|
-
newUserHost: this.config.newUserHost,
|
|
24683
|
-
sequenceStore: this.config.registrySequenceStore,
|
|
24684
|
-
onNodeNotFound: this.config.onNodeNotFound,
|
|
24685
|
-
autoDiscoverLocalNode: this.config.autoDiscoverLocalNode,
|
|
24686
|
-
localNodeUrl: this.config.localNodeUrl,
|
|
24687
|
-
localLinkName: this.config.localLinkName,
|
|
24688
|
-
expectedNodeDid: this.config.expectedNodeDid,
|
|
24689
|
-
localNodeIdentityStore: this.config.localNodeIdentityStore
|
|
24690
|
-
});
|
|
24691
|
-
return resolved2.hosts[0];
|
|
24692
|
-
}
|
|
24693
|
-
const resolved = await (0, import_sdk_core10.resolveTinyCloudHosts)((0, import_sdk_core10.pkhDid)(address, chainId), {
|
|
24694
|
-
registryUrl: this.config.tinycloudRegistryUrl,
|
|
24695
|
-
fallbackHosts: this.config.tinycloudFallbackHosts,
|
|
24696
|
-
autoDiscoverLocalNode: this.config.autoDiscoverLocalNode,
|
|
24697
|
-
localNodeUrl: this.config.localNodeUrl,
|
|
24698
|
-
localLinkName: this.config.localLinkName,
|
|
24699
|
-
expectedNodeDid: this.config.expectedNodeDid,
|
|
24700
|
-
localNodeIdentityStore: this.config.localNodeIdentityStore
|
|
24701
|
-
});
|
|
24702
|
-
return resolved.hosts[0];
|
|
24703
|
-
}
|
|
24704
24866
|
/**
|
|
24705
24867
|
* Resolve the currently-active TinyCloudSession, preferring the auth
|
|
24706
24868
|
* layer's value (wallet mode) and falling back to the node-level
|
|
@@ -25175,10 +25337,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25175
25337
|
* second time. Only a value recorded for this exact host qualifies; older
|
|
25176
25338
|
* nodes that omit `nodeId` fall through to the fetch.
|
|
25177
25339
|
*/
|
|
25178
|
-
async fetchNodeId() {
|
|
25179
|
-
const cached = this.auth?.nodeIdForHost(
|
|
25340
|
+
async fetchNodeId(host = this.config.host) {
|
|
25341
|
+
const cached = this.auth?.nodeIdForHost(host);
|
|
25180
25342
|
if (cached) return cached;
|
|
25181
|
-
const response = await fetch(`${
|
|
25343
|
+
const response = await fetch(`${host}/info`);
|
|
25182
25344
|
if (!response.ok) {
|
|
25183
25345
|
throw await (0, import_sdk_core10.httpResponseError)(response, "Failed to fetch node info");
|
|
25184
25346
|
}
|
|
@@ -25226,10 +25388,24 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25226
25388
|
)
|
|
25227
25389
|
};
|
|
25228
25390
|
}
|
|
25229
|
-
async
|
|
25230
|
-
const
|
|
25231
|
-
|
|
25232
|
-
);
|
|
25391
|
+
async encryptionDestination(networkId) {
|
|
25392
|
+
const destination = (await this.routingDestinations(networkId, "encryption"))[0];
|
|
25393
|
+
if (!destination) throw new Error("No encryption destination is registered");
|
|
25394
|
+
await (0, import_sdk_services5.verifyResourceDestination)(destination, globalThis.fetch.bind(globalThis));
|
|
25395
|
+
return { ...destination, nodeDid: destination.nodeDid ?? await this.fetchNodeId(destination.url) };
|
|
25396
|
+
}
|
|
25397
|
+
async fetchEncryptionNetworkAt(host, networkId, fetchFn, callerSignal) {
|
|
25398
|
+
let response;
|
|
25399
|
+
const timeout = AbortSignal.timeout(5e3);
|
|
25400
|
+
try {
|
|
25401
|
+
response = await fetchFn(`${host}/encryption/networks/${encodeURIComponent(networkId)}`, {
|
|
25402
|
+
signal: callerSignal ? AbortSignal.any([callerSignal, timeout]) : timeout,
|
|
25403
|
+
redirect: "error"
|
|
25404
|
+
});
|
|
25405
|
+
} catch (error) {
|
|
25406
|
+
if ((0, import_sdk_services5.routingFailure)(error) || callerSignal?.aborted || !timeout.aborted && error instanceof Error && error.name === "AbortError") throw error;
|
|
25407
|
+
throw new import_sdk_services5.NodeNotRespondingError([host], error);
|
|
25408
|
+
}
|
|
25233
25409
|
if (response.status === 404) return null;
|
|
25234
25410
|
if (!response.ok) {
|
|
25235
25411
|
throw await (0, import_sdk_core10.httpResponseError)(response, `Failed to fetch encryption network ${networkId}`);
|
|
@@ -25239,18 +25415,18 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25239
25415
|
}
|
|
25240
25416
|
createEncryptionService(binding) {
|
|
25241
25417
|
const graph = binding?.graph ?? this._serviceGraph;
|
|
25242
|
-
const host = binding?.host ?? this.config.host;
|
|
25243
25418
|
const session = binding?.session ?? this.requireEncryptionSession();
|
|
25244
25419
|
const did = binding?.did ?? this.did;
|
|
25245
25420
|
const address = binding?.address ?? this._address;
|
|
25246
25421
|
const chainId = binding?.chainId ?? this._chainId;
|
|
25247
25422
|
const crypto3 = this.createEncryptionCrypto();
|
|
25248
|
-
const descriptorCache = /* @__PURE__ */ new Map();
|
|
25249
25423
|
const transport = {
|
|
25250
|
-
postDecrypt: async ({ networkId, authorization, canonicalBody, signal }) => {
|
|
25424
|
+
postDecrypt: async ({ targetNode, networkId, authorization, canonicalBody, signal }) => {
|
|
25251
25425
|
graph.assertActive();
|
|
25426
|
+
const destination = await this.encryptionDestination(networkId);
|
|
25427
|
+
if (destination.nodeDid !== targetNode) throw new import_sdk_services5.NodeIdentityMismatchError(destination.url, destination.nodeDid, targetNode);
|
|
25252
25428
|
const response = await graph.fetch(
|
|
25253
|
-
`${
|
|
25429
|
+
`${destination.url}/encryption/networks/${encodeURIComponent(networkId)}/decrypt`,
|
|
25254
25430
|
{
|
|
25255
25431
|
method: "POST",
|
|
25256
25432
|
headers: {
|
|
@@ -25263,7 +25439,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25263
25439
|
);
|
|
25264
25440
|
graph.assertActive();
|
|
25265
25441
|
if (!response.ok) {
|
|
25266
|
-
const permissionHint = response.status === 401 || response.status === 403 ? (0,
|
|
25442
|
+
const permissionHint = response.status === 401 || response.status === 403 ? (0, import_sdk_services6.parsePermissionHint)({
|
|
25267
25443
|
service: "tinycloud.encryption",
|
|
25268
25444
|
path: networkId,
|
|
25269
25445
|
actions: ["tinycloud.encryption/decrypt"]
|
|
@@ -25278,6 +25454,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25278
25454
|
}
|
|
25279
25455
|
};
|
|
25280
25456
|
return new import_sdk_core10.EncryptionService({
|
|
25457
|
+
resolveTargetNode: async (networkId) => (await this.encryptionDestination(networkId)).nodeDid,
|
|
25281
25458
|
crypto: crypto3,
|
|
25282
25459
|
signer: {
|
|
25283
25460
|
signDecryptInvocation: async (input) => {
|
|
@@ -25301,18 +25478,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25301
25478
|
node: {
|
|
25302
25479
|
fetchByNetworkId: async (networkId) => {
|
|
25303
25480
|
graph.assertActive();
|
|
25304
|
-
|
|
25305
|
-
|
|
25306
|
-
pending = this.fetchEncryptionNetworkAt(host, networkId, graph.fetch).then((descriptor2) => {
|
|
25307
|
-
if (descriptor2 === null) descriptorCache.delete(networkId);
|
|
25308
|
-
return descriptor2;
|
|
25309
|
-
}).catch((error) => {
|
|
25310
|
-
descriptorCache.delete(networkId);
|
|
25311
|
-
throw error;
|
|
25312
|
-
});
|
|
25313
|
-
descriptorCache.set(networkId, pending);
|
|
25314
|
-
}
|
|
25315
|
-
const descriptor = await pending;
|
|
25481
|
+
const destination = await this.encryptionDestination(networkId);
|
|
25482
|
+
const descriptor = await this.fetchEncryptionNetworkAt(destination.url, networkId, graph.fetch, graph.signal);
|
|
25316
25483
|
graph.assertActive();
|
|
25317
25484
|
return descriptor;
|
|
25318
25485
|
}
|
|
@@ -25320,13 +25487,15 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25320
25487
|
wellKnown: {
|
|
25321
25488
|
fetchWellKnown: async (principal, discoveryKey) => {
|
|
25322
25489
|
graph.assertActive();
|
|
25490
|
+
const destination = await this.encryptionDestination(this.getDefaultEncryptionNetworkId());
|
|
25491
|
+
if (this.auth?.registryRecordPresent) return null;
|
|
25323
25492
|
if (!address || !didPrincipalMatches2(principal, did)) {
|
|
25324
25493
|
return null;
|
|
25325
25494
|
}
|
|
25326
25495
|
const publicSpaceId = (0, import_sdk_core10.makePublicSpaceId)(address, chainId);
|
|
25327
25496
|
const encodedKey = discoveryKey.split("/").map(encodeURIComponent).join("/");
|
|
25328
25497
|
const response = await graph.fetch(
|
|
25329
|
-
`${
|
|
25498
|
+
`${destination.url}/public/${encodeURIComponent(publicSpaceId)}/kv/${encodedKey}`,
|
|
25330
25499
|
{ method: "GET" }
|
|
25331
25500
|
);
|
|
25332
25501
|
graph.assertActive();
|
|
@@ -25696,7 +25865,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25696
25865
|
const signature2 = await this.signer.signMessage(prepared.siwe);
|
|
25697
25866
|
assertOwnerGraphActive();
|
|
25698
25867
|
const delegationSession = this.wasmBindings.completeSessionSetup({ ...prepared, signature: signature2 });
|
|
25699
|
-
const activation = await (
|
|
25868
|
+
const activation = await this.activateSignedSession(delegationSession, prepared.siwe, host);
|
|
25700
25869
|
assertOwnerGraphActive();
|
|
25701
25870
|
if (!activation.success) {
|
|
25702
25871
|
throw Object.assign(
|
|
@@ -26015,15 +26184,16 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26015
26184
|
async getEncryptionNetwork(nameOrNetworkId = this.getDefaultEncryptionNetworkId()) {
|
|
26016
26185
|
const networkId = nameOrNetworkId.startsWith("urn:tinycloud:encryption:") ? nameOrNetworkId : this.getDefaultEncryptionNetworkId(nameOrNetworkId);
|
|
26017
26186
|
return this.fetchEncryptionNetworkAt(
|
|
26018
|
-
this.
|
|
26187
|
+
(await this.encryptionDestination(networkId)).url,
|
|
26019
26188
|
networkId,
|
|
26020
26189
|
globalThis.fetch.bind(globalThis)
|
|
26021
26190
|
);
|
|
26022
26191
|
}
|
|
26023
26192
|
async createEncryptionNetwork(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
|
|
26024
|
-
const targetNode = await this.fetchNodeId();
|
|
26025
26193
|
const ownerDid = this.did;
|
|
26026
26194
|
const networkId = this.getDefaultEncryptionNetworkId(name);
|
|
26195
|
+
const destination = await this.encryptionDestination(networkId);
|
|
26196
|
+
const targetNode = destination.nodeDid;
|
|
26027
26197
|
const body = {
|
|
26028
26198
|
name,
|
|
26029
26199
|
ownerDid,
|
|
@@ -26046,7 +26216,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26046
26216
|
action: NETWORK_CREATE_ACTION2,
|
|
26047
26217
|
facts
|
|
26048
26218
|
});
|
|
26049
|
-
const response = await fetch(`${
|
|
26219
|
+
const response = await this._serviceGraph.fetch(`${destination.url}/encryption/networks`, {
|
|
26050
26220
|
method: "POST",
|
|
26051
26221
|
headers: {
|
|
26052
26222
|
Authorization: signed2.authorization,
|
|
@@ -26139,7 +26309,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26139
26309
|
);
|
|
26140
26310
|
if (result.status !== "ok") {
|
|
26141
26311
|
if (result.status === "permission_required") {
|
|
26142
|
-
const hint = (0,
|
|
26312
|
+
const hint = (0, import_sdk_services6.parsePermissionHint)(result.hint);
|
|
26143
26313
|
if (hint === void 0) return { status: "read_failed" };
|
|
26144
26314
|
return {
|
|
26145
26315
|
status: "permission_required",
|
|
@@ -26386,6 +26556,97 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26386
26556
|
}
|
|
26387
26557
|
return { recap: exactVerifiedRecap(verified), expiresAt };
|
|
26388
26558
|
}
|
|
26559
|
+
async activateResourceSession(delegationHeader, resources, hostHint, signal) {
|
|
26560
|
+
const targets = /* @__PURE__ */ new Map();
|
|
26561
|
+
const routedSpaces = [];
|
|
26562
|
+
let unserved;
|
|
26563
|
+
let requestedResource;
|
|
26564
|
+
for (const resource of new Set(resources)) {
|
|
26565
|
+
requestedResource ?? (requestedResource = resource);
|
|
26566
|
+
signal?.throwIfAborted();
|
|
26567
|
+
try {
|
|
26568
|
+
const encryption = resource.startsWith("urn:tinycloud:encryption:");
|
|
26569
|
+
const destinations = await this.routingDestinations(
|
|
26570
|
+
resource,
|
|
26571
|
+
encryption ? "encryption" : "host",
|
|
26572
|
+
[hostHint ?? this.config.host]
|
|
26573
|
+
);
|
|
26574
|
+
if (!encryption && destinations.some(({ nodeDid }) => nodeDid !== void 0)) routedSpaces.push(resource);
|
|
26575
|
+
for (const destination of destinations) {
|
|
26576
|
+
let target = targets.get(destination.url);
|
|
26577
|
+
if (!target) {
|
|
26578
|
+
target = { destination, spaces: [] };
|
|
26579
|
+
targets.set(destination.url, target);
|
|
26580
|
+
}
|
|
26581
|
+
if (!encryption && destination.nodeDid !== void 0) target.spaces.push(resource);
|
|
26582
|
+
}
|
|
26583
|
+
} catch (error) {
|
|
26584
|
+
if (!(error instanceof import_sdk_core10.NodeNotFoundError)) throw error;
|
|
26585
|
+
unserved ?? (unserved = error);
|
|
26586
|
+
}
|
|
26587
|
+
}
|
|
26588
|
+
if (targets.size === 0) throw unserved ?? new import_sdk_services5.ResourceNotRoutedError(requestedResource ?? "session grant");
|
|
26589
|
+
const succeeded = [];
|
|
26590
|
+
const accepted = [];
|
|
26591
|
+
const failed = [];
|
|
26592
|
+
let refusal;
|
|
26593
|
+
for (const { destination: target, spaces } of targets.values()) {
|
|
26594
|
+
signal?.throwIfAborted();
|
|
26595
|
+
try {
|
|
26596
|
+
await (0, import_sdk_services5.verifyResourceDestination)(target, globalThis.fetch.bind(globalThis));
|
|
26597
|
+
const timeout = AbortSignal.timeout(5e3);
|
|
26598
|
+
const result = await (0, import_sdk_core10.activateSessionWithHost)(
|
|
26599
|
+
target.url,
|
|
26600
|
+
delegationHeader,
|
|
26601
|
+
signal ? AbortSignal.any([signal, timeout]) : timeout
|
|
26602
|
+
);
|
|
26603
|
+
this.throwRegistryRefusal(result.status, result.error ?? "");
|
|
26604
|
+
if (result.success) {
|
|
26605
|
+
const skipped = result.skipped?.find((space) => spaces.some((expected) => this.spaceIdsEqual(space, expected)));
|
|
26606
|
+
if (skipped) {
|
|
26607
|
+
failed.push({ url: target.url, status: result.status, cause: new import_sdk_services5.ResourceNotRoutedError(skipped) });
|
|
26608
|
+
continue;
|
|
26609
|
+
}
|
|
26610
|
+
succeeded.push(target.url);
|
|
26611
|
+
accepted.push(result);
|
|
26612
|
+
} else {
|
|
26613
|
+
refusal = result;
|
|
26614
|
+
failed.push({ url: target.url, status: result.status, cause: result });
|
|
26615
|
+
}
|
|
26616
|
+
} catch (error) {
|
|
26617
|
+
if (signal?.aborted) throw error;
|
|
26618
|
+
failed.push({ url: target.url, cause: (0, import_sdk_services5.routingFailure)(error) ? error : new import_sdk_services5.NodeNotRespondingError([target.url], error) });
|
|
26619
|
+
}
|
|
26620
|
+
}
|
|
26621
|
+
if (failed.length > 0) {
|
|
26622
|
+
if (targets.size > 1) throw new import_sdk_services5.PartialNodeWriteError(succeeded, failed);
|
|
26623
|
+
if (refusal) return refusal;
|
|
26624
|
+
throw failed[0].cause;
|
|
26625
|
+
}
|
|
26626
|
+
const activated = new Set(accepted.flatMap((result) => result.activated ?? []));
|
|
26627
|
+
return {
|
|
26628
|
+
...accepted[0],
|
|
26629
|
+
activated: [...activated],
|
|
26630
|
+
skipped: [...new Set(accepted.flatMap((result) => result.skipped ?? []))].filter((space) => !activated.has(space) && !routedSpaces.some((expected) => this.spaceIdsEqual(space, expected)))
|
|
26631
|
+
};
|
|
26632
|
+
}
|
|
26633
|
+
activateRoutedDelegation(delegation, signal) {
|
|
26634
|
+
const resources = this.operationsFromDelegation(delegation).map(
|
|
26635
|
+
(operation) => operation.resource?.startsWith("urn:tinycloud:encryption:") ? operation.resource.split("/")[0] : operation.spaceId ?? delegation.spaceId
|
|
26636
|
+
);
|
|
26637
|
+
return this.activateResourceSession(delegation.delegationHeader, resources, delegation.host, signal);
|
|
26638
|
+
}
|
|
26639
|
+
activateSignedSession(session, siwe, hostHint, signal) {
|
|
26640
|
+
const resources = this.parseRecapWithCaveats(siwe).map(
|
|
26641
|
+
(entry) => this.isEncryptionNetworkOperation(this.invocationServiceName(entry.service), entry.path, entry.space) ? entry.path.split("/")[0] : entry.space.startsWith("tinycloud:") ? entry.space : session.spaceId
|
|
26642
|
+
);
|
|
26643
|
+
return this.activateResourceSession(
|
|
26644
|
+
session.delegationHeader,
|
|
26645
|
+
resources.length > 0 ? resources : [session.spaceId],
|
|
26646
|
+
hostHint,
|
|
26647
|
+
signal
|
|
26648
|
+
);
|
|
26649
|
+
}
|
|
26389
26650
|
/**
|
|
26390
26651
|
* Install a portable runtime permission delegation into this SDK instance so
|
|
26391
26652
|
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
@@ -26405,16 +26666,12 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26405
26666
|
`Runtime delegation targets ${delegation.delegateDID} but this session key is ${session.verificationMethod}.`
|
|
26406
26667
|
);
|
|
26407
26668
|
}
|
|
26408
|
-
const targetHost = delegation.host ?? this.config.host;
|
|
26409
26669
|
let activateResult;
|
|
26410
26670
|
try {
|
|
26411
|
-
activateResult = await (
|
|
26412
|
-
targetHost,
|
|
26413
|
-
delegation.delegationHeader,
|
|
26414
|
-
graph.signal
|
|
26415
|
-
);
|
|
26671
|
+
activateResult = await this.activateRoutedDelegation(delegation, graph.signal);
|
|
26416
26672
|
} catch (cause) {
|
|
26417
26673
|
graph.assertActive();
|
|
26674
|
+
if ((0, import_sdk_services5.routingFailure)(cause)) throw cause;
|
|
26418
26675
|
throw Object.assign(
|
|
26419
26676
|
new Error("The TinyCloud host could not be reached to activate the delegation."),
|
|
26420
26677
|
{ code: "ACTIVATION_TRANSPORT_FAILED", cause }
|
|
@@ -26570,11 +26827,15 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26570
26827
|
...prepared,
|
|
26571
26828
|
signature: signature2
|
|
26572
26829
|
});
|
|
26573
|
-
const
|
|
26574
|
-
|
|
26575
|
-
|
|
26576
|
-
|
|
26577
|
-
|
|
26830
|
+
const delegation = this.runtimeDelegationFromSession(
|
|
26831
|
+
delegatedSession,
|
|
26832
|
+
delegatedEntries,
|
|
26833
|
+
spaceId,
|
|
26834
|
+
captured,
|
|
26835
|
+
expiresAt,
|
|
26836
|
+
{ siwe: prepared.siwe, signature: signature2 }
|
|
26837
|
+
);
|
|
26838
|
+
const activateResult = await this.activateRoutedDelegation(delegation, graph.signal).catch((error) => {
|
|
26578
26839
|
graph.assertActive();
|
|
26579
26840
|
throw error;
|
|
26580
26841
|
});
|
|
@@ -26590,14 +26851,6 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26590
26851
|
{ cause: activateResult }
|
|
26591
26852
|
);
|
|
26592
26853
|
}
|
|
26593
|
-
const delegation = this.runtimeDelegationFromSession(
|
|
26594
|
-
delegatedSession,
|
|
26595
|
-
delegatedEntries,
|
|
26596
|
-
spaceId,
|
|
26597
|
-
captured,
|
|
26598
|
-
expiresAt,
|
|
26599
|
-
{ siwe: prepared.siwe, signature: signature2 }
|
|
26600
|
-
);
|
|
26601
26854
|
graph.assertActive();
|
|
26602
26855
|
assertCapturedKey?.();
|
|
26603
26856
|
this.runtimePermissionGrants.push({
|
|
@@ -26742,7 +26995,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26742
26995
|
if (!publicSpaceId) {
|
|
26743
26996
|
throw new Error("Public space not enabled. Set enablePublicSpace: true in config.");
|
|
26744
26997
|
}
|
|
26745
|
-
await this.assertMayCreateSpace();
|
|
26998
|
+
await this.assertMayCreateSpace(publicSpaceId);
|
|
26746
26999
|
await this.auth.hostPublicSpace(publicSpaceId);
|
|
26747
27000
|
const kvActions = [import_sdk_core10.KV.PUT, import_sdk_core10.KV.GET, import_sdk_core10.KV.DEL, import_sdk_core10.KV.LIST, import_sdk_core10.KV.METADATA];
|
|
26748
27001
|
const abilities = { kv: { "": kvActions } };
|
|
@@ -26765,10 +27018,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26765
27018
|
...prepared,
|
|
26766
27019
|
signature: signature2
|
|
26767
27020
|
});
|
|
26768
|
-
const activateResult = await (
|
|
26769
|
-
this.config.host,
|
|
26770
|
-
delegationSession.delegationHeader
|
|
26771
|
-
);
|
|
27021
|
+
const activateResult = await this.activateSignedSession(delegationSession, prepared.siwe);
|
|
26772
27022
|
if (!activateResult.success) {
|
|
26773
27023
|
throw Object.assign(
|
|
26774
27024
|
new Error(`Failed to activate public space delegation: ${describeHostFailure2(activateResult)}`),
|
|
@@ -27318,10 +27568,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
27318
27568
|
if (onPrepared) {
|
|
27319
27569
|
await onPrepared(delegation);
|
|
27320
27570
|
}
|
|
27321
|
-
const activateResult = await (
|
|
27322
|
-
this.config.host,
|
|
27323
|
-
delegationHeader
|
|
27324
|
-
);
|
|
27571
|
+
const activateResult = await this.activateRoutedDelegation(delegation);
|
|
27325
27572
|
assertDelegationActivated(activateResult, delegation, requireActivation);
|
|
27326
27573
|
return delegation;
|
|
27327
27574
|
}
|
|
@@ -27355,10 +27602,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
27355
27602
|
if (onPrepared) {
|
|
27356
27603
|
await onPrepared(delegation);
|
|
27357
27604
|
}
|
|
27358
|
-
const activateResult = await (
|
|
27359
|
-
targetHost,
|
|
27360
|
-
delegationHeader
|
|
27361
|
-
);
|
|
27605
|
+
const activateResult = await this.activateRoutedDelegation(delegation);
|
|
27362
27606
|
assertDelegationActivated(activateResult, delegation, requireActivation);
|
|
27363
27607
|
return delegation;
|
|
27364
27608
|
}
|
|
@@ -28010,10 +28254,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
28010
28254
|
...prepared,
|
|
28011
28255
|
signature: signature2
|
|
28012
28256
|
});
|
|
28013
|
-
const activateResult = await (
|
|
28014
|
-
this.config.host,
|
|
28015
|
-
delegationSession.delegationHeader
|
|
28016
|
-
);
|
|
28257
|
+
const activateResult = await this.activateSignedSession(delegationSession, prepared.siwe);
|
|
28017
28258
|
if (!activateResult.success) {
|
|
28018
28259
|
throw Object.assign(
|
|
28019
28260
|
new Error(`Failed to activate delegation: ${describeHostFailure2(activateResult)}`),
|
|
@@ -28058,10 +28299,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
28058
28299
|
...publicPrepared,
|
|
28059
28300
|
signature: publicSignature
|
|
28060
28301
|
});
|
|
28061
|
-
const publicActivateResult = await (
|
|
28062
|
-
this.config.host,
|
|
28063
|
-
publicSession.delegationHeader
|
|
28064
|
-
);
|
|
28302
|
+
const publicActivateResult = await this.activateSignedSession(publicSession, publicPrepared.siwe);
|
|
28065
28303
|
if (publicActivateResult.success) {
|
|
28066
28304
|
result.publicDelegation = {
|
|
28067
28305
|
cid: publicSession.delegationCid,
|
|
@@ -28167,11 +28405,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
28167
28405
|
...prepared,
|
|
28168
28406
|
signature: signature2
|
|
28169
28407
|
});
|
|
28170
|
-
const activateResult = await (
|
|
28171
|
-
targetHost,
|
|
28172
|
-
invokerSession.delegationHeader,
|
|
28173
|
-
graph.signal
|
|
28174
|
-
).catch((error) => {
|
|
28408
|
+
const activateResult = await this.activateSignedSession(invokerSession, prepared.siwe, targetHost, graph.signal).catch((error) => {
|
|
28175
28409
|
graph.assertActive();
|
|
28176
28410
|
throw error;
|
|
28177
28411
|
});
|
|
@@ -28298,10 +28532,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
28298
28532
|
...prepared,
|
|
28299
28533
|
signature: signature2
|
|
28300
28534
|
});
|
|
28301
|
-
const activateResult = await (
|
|
28302
|
-
targetHost,
|
|
28303
|
-
subDelegationSession.delegationHeader
|
|
28304
|
-
);
|
|
28535
|
+
const activateResult = await this.activateSignedSession(subDelegationSession, prepared.siwe, targetHost);
|
|
28305
28536
|
if (!activateResult.success) {
|
|
28306
28537
|
throw Object.assign(
|
|
28307
28538
|
new Error(`Failed to activate sub-delegation: ${describeHostFailure2(activateResult)}`),
|
|
@@ -28365,6 +28596,7 @@ registerReplicationLoaders({
|
|
|
28365
28596
|
|
|
28366
28597
|
// src/index.ts
|
|
28367
28598
|
var import_sdk_core15 = require("@tinycloud/sdk-core");
|
|
28599
|
+
var import_sdk_services8 = require("@tinycloud/sdk-services");
|
|
28368
28600
|
var import_sdk_core16 = require("@tinycloud/sdk-core");
|
|
28369
28601
|
|
|
28370
28602
|
// src/storage/FileSessionStorage.ts
|
|
@@ -28564,7 +28796,7 @@ var import_node_fs = require("fs");
|
|
|
28564
28796
|
var import_promises = require("fs/promises");
|
|
28565
28797
|
var import_node_path = require("path");
|
|
28566
28798
|
var import_sdk_core14 = require("@tinycloud/sdk-core");
|
|
28567
|
-
var
|
|
28799
|
+
var import_sdk_services7 = require("@tinycloud/sdk-services");
|
|
28568
28800
|
var import_contractConstants = require("@tinycloud/sdk-services/kv/replication/contractConstants");
|
|
28569
28801
|
init_authority();
|
|
28570
28802
|
var REPLICA_LOGOUT_PENDING_FILE = "replica-logout-pending.json";
|
|
@@ -29126,7 +29358,7 @@ function sha256Base32(material) {
|
|
|
29126
29358
|
return out.slice(0, 26);
|
|
29127
29359
|
}
|
|
29128
29360
|
function partitionDirOf(root, identity) {
|
|
29129
|
-
return (0, import_node_path.join)(root, sha256Base32((0,
|
|
29361
|
+
return (0, import_node_path.join)(root, sha256Base32((0, import_sdk_services7.replicationIdentityKey)(identity)));
|
|
29130
29362
|
}
|
|
29131
29363
|
function replicaHashOf(prefix, deviceDid) {
|
|
29132
29364
|
return sha256Base32(`${prefix}
|
|
@@ -29200,8 +29432,8 @@ function deviceRecordOf(value) {
|
|
|
29200
29432
|
}
|
|
29201
29433
|
function sameIdentity(a, b) {
|
|
29202
29434
|
try {
|
|
29203
|
-
const left = (0,
|
|
29204
|
-
const right = (0,
|
|
29435
|
+
const left = (0, import_sdk_services7.canonicalReplicationIdentity)(a);
|
|
29436
|
+
const right = (0, import_sdk_services7.canonicalReplicationIdentity)(b);
|
|
29205
29437
|
return left.host === right.host && left.space === right.space && left.principal === right.principal;
|
|
29206
29438
|
} catch {
|
|
29207
29439
|
return false;
|
|
@@ -29217,7 +29449,7 @@ async function ensurePartitionIdentity(dir, identity, dirSync) {
|
|
|
29217
29449
|
const file = (0, import_node_path.join)(dir, "identity.json");
|
|
29218
29450
|
const existing = await readJsonFile(file);
|
|
29219
29451
|
if (existing === void 0) {
|
|
29220
|
-
await writeJsonAtomic(file, { v: 1, ...(0,
|
|
29452
|
+
await writeJsonAtomic(file, { v: 1, ...(0, import_sdk_services7.canonicalReplicationIdentity)(identity) }, dirSync);
|
|
29221
29453
|
return;
|
|
29222
29454
|
}
|
|
29223
29455
|
const record = identityRecordOf(existing);
|
|
@@ -29316,7 +29548,7 @@ withGate_fn = async function(section) {
|
|
|
29316
29548
|
if (snapshot === void 0) throw gated("gate_unavailable");
|
|
29317
29549
|
const [principalHash, identityKeyHash] = await Promise.all([
|
|
29318
29550
|
gate.principalHashOf(snapshot.salt, this.identity.principal),
|
|
29319
|
-
gate.identityHashOf(snapshot.salt, (0,
|
|
29551
|
+
gate.identityHashOf(snapshot.salt, (0, import_sdk_services7.replicationIdentityKey)(this.identity))
|
|
29320
29552
|
]);
|
|
29321
29553
|
const access = {
|
|
29322
29554
|
stamp: __privateGet(this, _stamp),
|
|
@@ -29817,7 +30049,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
29817
30049
|
if (snapshot === void 0) throw gated("gate_unavailable");
|
|
29818
30050
|
const [principalHash, identityKeyHash] = await Promise.all([
|
|
29819
30051
|
gate.principalHashOf(snapshot.salt, principal),
|
|
29820
|
-
identity === void 0 ? Promise.resolve(void 0) : gate.identityHashOf(snapshot.salt, (0,
|
|
30052
|
+
identity === void 0 ? Promise.resolve(void 0) : gate.identityHashOf(snapshot.salt, (0, import_sdk_services7.replicationIdentityKey)(identity))
|
|
29821
30053
|
]);
|
|
29822
30054
|
return profileSection(async () => {
|
|
29823
30055
|
assertLogoutClear(options.logoutPendingFile);
|
|
@@ -29840,7 +30072,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
29840
30072
|
if (snapshot === void 0) throw gated("gate_unavailable");
|
|
29841
30073
|
const [principalHash, identityKeyHash] = await Promise.all([
|
|
29842
30074
|
gate.principalHashOf(snapshot.salt, spec.identity.principal),
|
|
29843
|
-
gate.identityHashOf(snapshot.salt, (0,
|
|
30075
|
+
gate.identityHashOf(snapshot.salt, (0, import_sdk_services7.replicationIdentityKey)(spec.identity))
|
|
29844
30076
|
]);
|
|
29845
30077
|
const access = {
|
|
29846
30078
|
stamp: spec.stamp,
|
|
@@ -29924,7 +30156,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
29924
30156
|
const identity = identityRecordOf(await readJsonFile((0, import_node_path.join)(idDir, "identity.json")));
|
|
29925
30157
|
if (identity === void 0) throw new ReplicaStorageError("STORAGE_ERROR", `Missing identity at ${idDir}.`);
|
|
29926
30158
|
if (await gate.principalHashOf(salt, identity.principal) !== principalHash) continue;
|
|
29927
|
-
const identityKeyHash = await gate.identityHashOf(salt, (0,
|
|
30159
|
+
const identityKeyHash = await gate.identityHashOf(salt, (0, import_sdk_services7.replicationIdentityKey)(identity));
|
|
29928
30160
|
const recordedAt = Date.now();
|
|
29929
30161
|
if (await pathExists((0, import_node_path.join)(idDir, "pending.json")) || await pathExists((0, import_node_path.join)(idDir, "device.jwk"))) {
|
|
29930
30162
|
intents.push({ id: `pending:${identityKeyHash}`, kind: "pending", identityKeyHash, principalHash, recordedAt });
|
|
@@ -30602,7 +30834,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
30602
30834
|
for (const dir of await identityDirs()) {
|
|
30603
30835
|
const identity = identityRecordOf(await readJsonFile((0, import_node_path.join)(dir, "identity.json")));
|
|
30604
30836
|
if (identity === void 0) throw new ReplicaStorageError("STORAGE_ERROR", `Missing identity at ${dir}.`);
|
|
30605
|
-
const hash3 = await gate.identityHashOf(state.salt, (0,
|
|
30837
|
+
const hash3 = await gate.identityHashOf(state.salt, (0, import_sdk_services7.replicationIdentityKey)(identity));
|
|
30606
30838
|
if (intent.kind === "pending" ? hash3 === intent.identityKeyHash : intent.partitionId.startsWith(`${hash3}:`)) {
|
|
30607
30839
|
idDir = dir;
|
|
30608
30840
|
break;
|
|
@@ -30748,7 +30980,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
30748
30980
|
const gate = await fileGate();
|
|
30749
30981
|
const snapshot = await gate.readGateSnapshot(profileDir);
|
|
30750
30982
|
if (snapshot === void 0) throw gated("gate_unavailable");
|
|
30751
|
-
const identityKeyHash = await gate.identityHashOf(snapshot.salt, (0,
|
|
30983
|
+
const identityKeyHash = await gate.identityHashOf(snapshot.salt, (0, import_sdk_services7.replicationIdentityKey)(spec.identity));
|
|
30752
30984
|
const resolvePolicy = async (at, recordFirst) => {
|
|
30753
30985
|
const policy = spec.grantPolicy;
|
|
30754
30986
|
if (!Number.isSafeInteger(policy.grantTtlMs) || policy.grantTtlMs <= 0 || policy.grantTtlMs > import_contractConstants.MAX_GRANT_TTL_MS) {
|
|
@@ -30770,7 +31002,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
30770
31002
|
let partitionId;
|
|
30771
31003
|
const storeGuard = (section) => guarded(spec.identity.principal, spec.stamp, spec.identity, partitionId, section);
|
|
30772
31004
|
return guarded(spec.identity.principal, spec.stamp, spec.identity, void 0, async () => {
|
|
30773
|
-
if ((0,
|
|
31005
|
+
if ((0, import_sdk_services7.requiresSecretsOptIn)(spec.space, spec.prefix) && spec.allowSecrets !== true) {
|
|
30774
31006
|
throw new replica.ReplicaError(
|
|
30775
31007
|
replica.ReplicaErrorCode.SECRETS_OPT_IN_REQUIRED,
|
|
30776
31008
|
`Replicating ${spec.space}/kv/${spec.prefix} copies secret material to disk; pass allowSecrets to opt in.`
|
|
@@ -31016,7 +31248,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
31016
31248
|
const gate = await fileGate();
|
|
31017
31249
|
const currentGate = await gate.readGateSnapshot(profileDir);
|
|
31018
31250
|
if (currentGate !== void 0 && currentGate.principalHash !== null && currentGate.principalHash === await gate.principalHashOf(currentGate.salt, target.identity.principal)) {
|
|
31019
|
-
const identityKeyHash = await gate.identityHashOf(currentGate.salt, (0,
|
|
31251
|
+
const identityKeyHash = await gate.identityHashOf(currentGate.salt, (0, import_sdk_services7.replicationIdentityKey)(target.identity));
|
|
31020
31252
|
await retainGrantInstalls(gate, currentGate, identityKeyHash, store);
|
|
31021
31253
|
}
|
|
31022
31254
|
if (state.revoked === null) {
|
|
@@ -31112,8 +31344,11 @@ function sqliteReplicaStorage(options) {
|
|
|
31112
31344
|
MemorySessionStorage,
|
|
31113
31345
|
NETWORK_NAME_PATTERN,
|
|
31114
31346
|
NetworkIdError,
|
|
31347
|
+
NodeIdentityMismatchError,
|
|
31348
|
+
NodeNotRespondingError,
|
|
31115
31349
|
NodeUserAuthorization,
|
|
31116
31350
|
NodeWasmBindings,
|
|
31351
|
+
PartialNodeWriteError,
|
|
31117
31352
|
PermissionNotInManifestError,
|
|
31118
31353
|
PrefixedKVService,
|
|
31119
31354
|
PrivateKeySigner,
|