@tinycloud/node-sdk 2.7.0-beta.3 → 2.7.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{core-DGFXEW74.d.cts → core-aXks8jFT.d.cts} +154 -6
- package/dist/{core-DGFXEW74.d.ts → core-aXks8jFT.d.ts} +154 -6
- package/dist/core.cjs +1150 -260
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +2 -2
- package/dist/core.d.ts +2 -2
- package/dist/core.js +1081 -181
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +1472 -301
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -4
- package/dist/index.d.ts +6 -4
- package/dist/index.js +1363 -181
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -17034,10 +17034,12 @@ import {
|
|
|
17034
17034
|
computeCid,
|
|
17035
17035
|
prepareSession,
|
|
17036
17036
|
completeSessionSetup,
|
|
17037
|
+
validatePersistedSession,
|
|
17037
17038
|
ensureEip55,
|
|
17038
17039
|
makeSpaceId,
|
|
17039
17040
|
createDelegation,
|
|
17040
17041
|
parseRecapFromSiwe,
|
|
17042
|
+
parseVerifiedRecapFromSiwe,
|
|
17041
17043
|
generateHostSIWEMessage,
|
|
17042
17044
|
siweToDelegationHeaders,
|
|
17043
17045
|
protocolVersion,
|
|
@@ -17062,6 +17064,7 @@ var _NodeWasmBindings = class _NodeWasmBindings {
|
|
|
17062
17064
|
this.makeSpaceId = makeSpaceId;
|
|
17063
17065
|
this.createDelegation = createDelegation;
|
|
17064
17066
|
this.parseRecapFromSiwe = parseRecapFromSiwe;
|
|
17067
|
+
this.parseVerifiedRecapFromSiwe = parseVerifiedRecapFromSiwe;
|
|
17065
17068
|
this.generateHostSIWEMessage = generateHostSIWEMessage;
|
|
17066
17069
|
this.siweToDelegationHeaders = siweToDelegationHeaders;
|
|
17067
17070
|
this.protocolVersion = protocolVersion;
|
|
@@ -17078,6 +17081,9 @@ var _NodeWasmBindings = class _NodeWasmBindings {
|
|
|
17078
17081
|
_NodeWasmBindings.panicHookInitialized = true;
|
|
17079
17082
|
}
|
|
17080
17083
|
}
|
|
17084
|
+
validatePersistedSession(proof) {
|
|
17085
|
+
return validatePersistedSession(proof);
|
|
17086
|
+
}
|
|
17081
17087
|
createSessionManager() {
|
|
17082
17088
|
return new TCWSessionManager();
|
|
17083
17089
|
}
|
|
@@ -17162,6 +17168,7 @@ import {
|
|
|
17162
17168
|
HooksService as HooksService2,
|
|
17163
17169
|
DataVaultService,
|
|
17164
17170
|
EncryptionService,
|
|
17171
|
+
DecryptTransportResponseError,
|
|
17165
17172
|
SecretsService,
|
|
17166
17173
|
createVaultCrypto,
|
|
17167
17174
|
ServiceContext as ServiceContext2,
|
|
@@ -17179,12 +17186,16 @@ import {
|
|
|
17179
17186
|
TINYCLOUD_SECRETS_BOOTSTRAP_MANIFEST,
|
|
17180
17187
|
bootstrapSteps,
|
|
17181
17188
|
ENCRYPTION_PERMISSION_SERVICE as ENCRYPTION_PERMISSION_SERVICE2,
|
|
17189
|
+
CaveatedDelegationUnsupportedError,
|
|
17182
17190
|
PermissionNotInManifestError,
|
|
17183
17191
|
SessionExpiredError,
|
|
17192
|
+
canonicalizeRecapCaveats,
|
|
17193
|
+
recapCaveatsEqual,
|
|
17184
17194
|
expandPermissionEntries as expandPermissionEntriesCore,
|
|
17185
17195
|
isCapabilitySubset,
|
|
17186
17196
|
parseRecapCapabilities,
|
|
17187
17197
|
SERVICE_LONG_TO_SHORT,
|
|
17198
|
+
SERVICE_SHORT_TO_LONG,
|
|
17188
17199
|
KV as KV2,
|
|
17189
17200
|
SQL as SQL2,
|
|
17190
17201
|
DUCKDB as DUCKDB2,
|
|
@@ -17197,8 +17208,12 @@ import {
|
|
|
17197
17208
|
pkhDid as pkhDid2,
|
|
17198
17209
|
resolveTinyCloudHosts as resolveTinyCloudHosts2,
|
|
17199
17210
|
principalDidEquals as principalDidEquals2,
|
|
17200
|
-
parseNetworkId as parseNetworkId2
|
|
17211
|
+
parseNetworkId as parseNetworkId2,
|
|
17212
|
+
resolveSecretPath as resolveSecretPath2
|
|
17201
17213
|
} from "@tinycloud/sdk-core";
|
|
17214
|
+
import {
|
|
17215
|
+
parsePermissionHint
|
|
17216
|
+
} from "@tinycloud/sdk-services";
|
|
17202
17217
|
|
|
17203
17218
|
// src/authorization/NodeUserAuthorization.ts
|
|
17204
17219
|
import {
|
|
@@ -17443,6 +17458,30 @@ var NodeUserAuthorization = class {
|
|
|
17443
17458
|
this.tinycloudHosts = [...hosts];
|
|
17444
17459
|
}
|
|
17445
17460
|
}
|
|
17461
|
+
/** @internal Atomically adopt the state staged by TinyCloudNode.restoreSession. */
|
|
17462
|
+
installRestoredSession(sessionManager, session, hosts) {
|
|
17463
|
+
this.sessionManager = sessionManager;
|
|
17464
|
+
this.tinycloudHosts = hosts && hosts.length > 0 ? [...hosts] : void 0;
|
|
17465
|
+
this._lastActivationSkippedSpaceIds = [];
|
|
17466
|
+
if (!session) {
|
|
17467
|
+
this._session = void 0;
|
|
17468
|
+
this._tinyCloudSession = void 0;
|
|
17469
|
+
this._address = void 0;
|
|
17470
|
+
this._chainId = void 0;
|
|
17471
|
+
return;
|
|
17472
|
+
}
|
|
17473
|
+
this._tinyCloudSession = { ...session };
|
|
17474
|
+
this._address = session.address;
|
|
17475
|
+
this._chainId = session.chainId;
|
|
17476
|
+
this._session = {
|
|
17477
|
+
address: session.address,
|
|
17478
|
+
walletAddress: session.address,
|
|
17479
|
+
chainId: session.chainId,
|
|
17480
|
+
sessionKey: session.sessionKey,
|
|
17481
|
+
siwe: session.siwe,
|
|
17482
|
+
signature: session.signature
|
|
17483
|
+
};
|
|
17484
|
+
}
|
|
17446
17485
|
/**
|
|
17447
17486
|
* Ensure `tinycloudHosts` are resolved before a host-needing call.
|
|
17448
17487
|
*
|
|
@@ -18650,6 +18689,9 @@ var DEFAULT_ENCRYPTION_NETWORK_NAME = "default";
|
|
|
18650
18689
|
var NETWORK_CREATE_ACTION2 = ENCRYPTION2.NETWORK_CREATE;
|
|
18651
18690
|
var DECRYPT_ACTION2 = ENCRYPTION2.DECRYPT;
|
|
18652
18691
|
var NETWORK_ADMIN_TYPE = "tinycloud.encryption.network-admin/v1";
|
|
18692
|
+
function isSecretPayload(value) {
|
|
18693
|
+
return typeof value === "object" && value !== null && "value" in value && typeof value.value === "string" && "createdAt" in value && typeof value.createdAt === "string" && "updatedAt" in value && typeof value.updatedAt === "string";
|
|
18694
|
+
}
|
|
18653
18695
|
var ROOT_DELEGATION_ACTIONS = [
|
|
18654
18696
|
KV2.PUT,
|
|
18655
18697
|
KV2.GET,
|
|
@@ -18704,6 +18746,141 @@ function didPrincipalMatches2(actual, expected) {
|
|
|
18704
18746
|
return actual === expected;
|
|
18705
18747
|
}
|
|
18706
18748
|
}
|
|
18749
|
+
function clonePersistedSessionJwk(jwk) {
|
|
18750
|
+
if (jwk === null || typeof jwk !== "object" || Array.isArray(jwk)) {
|
|
18751
|
+
throw new Error("Persisted session has an invalid private Ed25519 session key.");
|
|
18752
|
+
}
|
|
18753
|
+
try {
|
|
18754
|
+
const serialized = JSON.stringify(jwk);
|
|
18755
|
+
const cloned = serialized === void 0 ? void 0 : JSON.parse(serialized);
|
|
18756
|
+
if (cloned === null || typeof cloned !== "object" || Array.isArray(cloned)) {
|
|
18757
|
+
throw new Error("invalid JWK object");
|
|
18758
|
+
}
|
|
18759
|
+
return cloned;
|
|
18760
|
+
} catch {
|
|
18761
|
+
throw new Error("Persisted session has an invalid private Ed25519 session key.");
|
|
18762
|
+
}
|
|
18763
|
+
}
|
|
18764
|
+
function cloneRecapCaveats(caveats) {
|
|
18765
|
+
if (!caveats) return [];
|
|
18766
|
+
const cloneJson = (value) => {
|
|
18767
|
+
if (value === null || typeof value === "boolean" || typeof value === "string") {
|
|
18768
|
+
return value;
|
|
18769
|
+
}
|
|
18770
|
+
if (typeof value === "number") {
|
|
18771
|
+
if (Number.isFinite(value)) return value;
|
|
18772
|
+
throw new Error("ReCap caveats must contain only JSON values.");
|
|
18773
|
+
}
|
|
18774
|
+
if (Array.isArray(value)) return value.map(cloneJson);
|
|
18775
|
+
if (typeof value === "object") {
|
|
18776
|
+
const prototype = Object.getPrototypeOf(value);
|
|
18777
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
18778
|
+
throw new Error("ReCap caveats must contain only JSON values.");
|
|
18779
|
+
}
|
|
18780
|
+
const copy = /* @__PURE__ */ Object.create(null);
|
|
18781
|
+
for (const [key2, nested] of Object.entries(value)) {
|
|
18782
|
+
copy[key2] = cloneJson(nested);
|
|
18783
|
+
}
|
|
18784
|
+
return copy;
|
|
18785
|
+
}
|
|
18786
|
+
throw new Error("ReCap caveats must contain only JSON values.");
|
|
18787
|
+
};
|
|
18788
|
+
try {
|
|
18789
|
+
return JSON.parse(JSON.stringify(caveats.map(cloneJson)));
|
|
18790
|
+
} catch {
|
|
18791
|
+
throw new Error("Verified persisted session ReCap contains invalid caveats.");
|
|
18792
|
+
}
|
|
18793
|
+
}
|
|
18794
|
+
var ServiceGraphLifetime = class {
|
|
18795
|
+
constructor(invokeFn, invokeAnyFn, fetchFn) {
|
|
18796
|
+
this.invokeFn = invokeFn;
|
|
18797
|
+
this.invokeAnyFn = invokeAnyFn;
|
|
18798
|
+
this.fetchFn = fetchFn;
|
|
18799
|
+
this.abortController = new AbortController();
|
|
18800
|
+
this.contexts = /* @__PURE__ */ new Set();
|
|
18801
|
+
this.retired = false;
|
|
18802
|
+
this.invoke = (session, service, path, action, facts) => {
|
|
18803
|
+
this.assertActive();
|
|
18804
|
+
return this.invokeFn(session, service, path, action, facts);
|
|
18805
|
+
};
|
|
18806
|
+
this.invokeAny = (session, entries, facts) => {
|
|
18807
|
+
this.assertActive();
|
|
18808
|
+
return this.invokeAnyFn(session, entries, facts);
|
|
18809
|
+
};
|
|
18810
|
+
this.fetch = (url, init2) => {
|
|
18811
|
+
this.assertActive();
|
|
18812
|
+
return this.fetchFn(url, {
|
|
18813
|
+
...init2,
|
|
18814
|
+
signal: this.combineSignals(init2?.signal)
|
|
18815
|
+
});
|
|
18816
|
+
};
|
|
18817
|
+
}
|
|
18818
|
+
track(context) {
|
|
18819
|
+
this.assertActive();
|
|
18820
|
+
this.contexts.add(context);
|
|
18821
|
+
return context;
|
|
18822
|
+
}
|
|
18823
|
+
retire() {
|
|
18824
|
+
if (this.retired) return;
|
|
18825
|
+
this.retired = true;
|
|
18826
|
+
this.abortController.abort();
|
|
18827
|
+
try {
|
|
18828
|
+
for (const context of this.contexts) {
|
|
18829
|
+
try {
|
|
18830
|
+
const retire = context.retire;
|
|
18831
|
+
if (retire) retire.call(context);
|
|
18832
|
+
else context.abort();
|
|
18833
|
+
} catch (error) {
|
|
18834
|
+
console.error("Error retiring service graph context:", error);
|
|
18835
|
+
}
|
|
18836
|
+
}
|
|
18837
|
+
} finally {
|
|
18838
|
+
this.contexts.clear();
|
|
18839
|
+
}
|
|
18840
|
+
}
|
|
18841
|
+
assertActive() {
|
|
18842
|
+
if (this.retired) {
|
|
18843
|
+
throw new Error("Service graph has been retired by session replacement.");
|
|
18844
|
+
}
|
|
18845
|
+
}
|
|
18846
|
+
combineSignals(signal) {
|
|
18847
|
+
if (!signal) return this.abortController.signal;
|
|
18848
|
+
const combined = new AbortController();
|
|
18849
|
+
const abort = () => combined.abort();
|
|
18850
|
+
if (signal.aborted || this.abortController.signal.aborted) {
|
|
18851
|
+
abort();
|
|
18852
|
+
} else {
|
|
18853
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
18854
|
+
this.abortController.signal.addEventListener("abort", abort, { once: true });
|
|
18855
|
+
}
|
|
18856
|
+
return combined.signal;
|
|
18857
|
+
}
|
|
18858
|
+
};
|
|
18859
|
+
var UnsupportedSessionRestoreError = class extends Error {
|
|
18860
|
+
constructor(reason = "it cannot replace and enumerate every live session signer") {
|
|
18861
|
+
super(`Persisted session restore is unsupported by this WASM binding because ${reason}.`);
|
|
18862
|
+
this.code = "RESTORE_SESSION_KEY_REPLACEMENT_UNSUPPORTED";
|
|
18863
|
+
this.name = "UnsupportedSessionRestoreError";
|
|
18864
|
+
}
|
|
18865
|
+
};
|
|
18866
|
+
function canonicalRestoredVerificationMethod(canonicalVerificationMethod, persistedVerificationMethod) {
|
|
18867
|
+
if (typeof persistedVerificationMethod !== "string") return void 0;
|
|
18868
|
+
const principal = canonicalVerificationMethod.split("#", 1)[0];
|
|
18869
|
+
return persistedVerificationMethod === principal || persistedVerificationMethod === canonicalVerificationMethod ? canonicalVerificationMethod : void 0;
|
|
18870
|
+
}
|
|
18871
|
+
function persistedExpiry(value) {
|
|
18872
|
+
if (typeof value !== "string") {
|
|
18873
|
+
throw new Error("Persisted session without a SIWE expiration must include expiresAt.");
|
|
18874
|
+
}
|
|
18875
|
+
const expiry = new Date(value);
|
|
18876
|
+
if (!Number.isFinite(expiry.getTime()) || expiry.getTime() <= Date.now()) {
|
|
18877
|
+
throw new Error("Persisted session expiry is invalid or expired.");
|
|
18878
|
+
}
|
|
18879
|
+
return expiry;
|
|
18880
|
+
}
|
|
18881
|
+
function sameInstant(left, right) {
|
|
18882
|
+
return left.getTime() === right.getTime();
|
|
18883
|
+
}
|
|
18707
18884
|
function sharingActionsToAbilities(path, actions) {
|
|
18708
18885
|
var _a;
|
|
18709
18886
|
const abilities = {};
|
|
@@ -18830,6 +19007,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18830
19007
|
this.auth = null;
|
|
18831
19008
|
this.tc = null;
|
|
18832
19009
|
this._chainId = 1;
|
|
19010
|
+
this._baseVaults = /* @__PURE__ */ new Map();
|
|
18833
19011
|
this._baseSecrets = /* @__PURE__ */ new Map();
|
|
18834
19012
|
this._secrets = /* @__PURE__ */ new Map();
|
|
18835
19013
|
this.runtimePermissionGrants = [];
|
|
@@ -18849,26 +19027,55 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18849
19027
|
skipped: false
|
|
18850
19028
|
};
|
|
18851
19029
|
this.invokeWithRuntimePermissions = (session, service, path, action, facts) => {
|
|
18852
|
-
|
|
18853
|
-
|
|
18854
|
-
service,
|
|
19030
|
+
const operation = {
|
|
19031
|
+
spaceId: session.spaceId,
|
|
19032
|
+
service: this.invocationServiceName(service),
|
|
18855
19033
|
path,
|
|
18856
|
-
action
|
|
18857
|
-
|
|
19034
|
+
action
|
|
19035
|
+
};
|
|
19036
|
+
const grant = this.findGrantForOperation(operation);
|
|
19037
|
+
const grantedOperation = grant?.operations.find(
|
|
19038
|
+
(candidate) => this.operationCovers(candidate, operation)
|
|
18858
19039
|
);
|
|
19040
|
+
const invocationSession = !grant || grant.provenance === "primary" ? session : grant.session;
|
|
19041
|
+
if ((grantedOperation?.caveats?.length ?? 0) > 0) {
|
|
19042
|
+
if (!this.wasmBindings.invokeAny) {
|
|
19043
|
+
throw new Error("WASM binding does not support caveat-preserving invocation");
|
|
19044
|
+
}
|
|
19045
|
+
return this.wasmBindings.invokeAny(invocationSession, [{
|
|
19046
|
+
spaceId: invocationSession.spaceId,
|
|
19047
|
+
service,
|
|
19048
|
+
path,
|
|
19049
|
+
action,
|
|
19050
|
+
caveats: cloneRecapCaveats(grantedOperation.caveats)
|
|
19051
|
+
}], facts);
|
|
19052
|
+
}
|
|
19053
|
+
return this.wasmBindings.invoke(invocationSession, service, path, action, facts);
|
|
18859
19054
|
};
|
|
18860
19055
|
this.invokeAnyWithRuntimePermissions = (session, entries, facts) => {
|
|
18861
19056
|
if (!this.wasmBindings.invokeAny) {
|
|
18862
19057
|
throw new Error("WASM binding does not support invokeAny");
|
|
18863
19058
|
}
|
|
18864
|
-
const
|
|
18865
|
-
|
|
18866
|
-
|
|
18867
|
-
|
|
18868
|
-
|
|
18869
|
-
);
|
|
19059
|
+
const operations = entries.flatMap((entry) => {
|
|
19060
|
+
const operation = this.operationFromInvokeAnyEntry(entry);
|
|
19061
|
+
return operation ? [operation] : [];
|
|
19062
|
+
});
|
|
19063
|
+
const grant = this.findGrantForOperations(operations);
|
|
18870
19064
|
const invocationSession = !grant || grant.provenance === "primary" ? session : grant.session;
|
|
18871
|
-
|
|
19065
|
+
const caveatPreservingEntries = grant ? entries.map((entry) => {
|
|
19066
|
+
const requested = this.operationFromInvokeAnyEntry(entry);
|
|
19067
|
+
const granted = requested && grant.operations.find(
|
|
19068
|
+
(candidate) => this.operationCovers(candidate, requested)
|
|
19069
|
+
);
|
|
19070
|
+
if (!granted?.caveats?.length) {
|
|
19071
|
+
return entry;
|
|
19072
|
+
}
|
|
19073
|
+
if (entry.caveats !== void 0 && !recapCaveatsEqual(entry.caveats, granted.caveats)) {
|
|
19074
|
+
throw new Error("Invocation caveats do not match signed ReCap authority.");
|
|
19075
|
+
}
|
|
19076
|
+
return { ...entry, caveats: cloneRecapCaveats(granted.caveats) };
|
|
19077
|
+
}) : entries;
|
|
19078
|
+
return this.wasmBindings.invokeAny(invocationSession, caveatPreservingEntries, facts);
|
|
18872
19079
|
};
|
|
18873
19080
|
this.explicitHost = config.host;
|
|
18874
19081
|
this.config = {
|
|
@@ -18897,16 +19104,19 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18897
19104
|
throw new Error("Failed to get session key JWK");
|
|
18898
19105
|
}
|
|
18899
19106
|
this.sessionKeyJwk = JSON.parse(jwkStr);
|
|
19107
|
+
this._serviceGraph = this.createServiceGraphLifetime();
|
|
18900
19108
|
this._capabilityRegistry = new CapabilityKeyRegistry();
|
|
18901
19109
|
this._keyProvider = new WasmKeyProvider({
|
|
18902
19110
|
sessionManager: this.sessionManager
|
|
18903
19111
|
});
|
|
18904
19112
|
this.notificationHandler = config.notificationHandler ?? new SilentNotificationHandler();
|
|
19113
|
+
const receiveOnlyGraph = this._serviceGraph;
|
|
18905
19114
|
this._sharingService = new SharingService({
|
|
18906
19115
|
hosts: [this.config.host],
|
|
18907
19116
|
// session: undefined - not needed for receive()
|
|
18908
|
-
invoke:
|
|
18909
|
-
fetch:
|
|
19117
|
+
invoke: receiveOnlyGraph.invoke,
|
|
19118
|
+
fetch: receiveOnlyGraph.fetch,
|
|
19119
|
+
assertActive: () => receiveOnlyGraph.assertActive(),
|
|
18910
19120
|
keyProvider: this._keyProvider,
|
|
18911
19121
|
registry: this._capabilityRegistry,
|
|
18912
19122
|
createDelegationWasm: (params) => this.createDelegationWrapper(params),
|
|
@@ -18918,12 +19128,12 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18918
19128
|
createKVService: (config2) => {
|
|
18919
19129
|
const prefix = config2.pathPrefix?.replace(/\/$/, "");
|
|
18920
19130
|
const kvService = new KVService2({ prefix });
|
|
18921
|
-
const kvContext = new ServiceContext2({
|
|
19131
|
+
const kvContext = receiveOnlyGraph.track(new ServiceContext2({
|
|
18922
19132
|
invoke: config2.invoke,
|
|
18923
19133
|
fetch: config2.fetch ?? globalThis.fetch.bind(globalThis),
|
|
18924
19134
|
hosts: config2.hosts,
|
|
18925
19135
|
telemetry: this.config.telemetry
|
|
18926
|
-
});
|
|
19136
|
+
}));
|
|
18927
19137
|
kvContext.setSession(config2.session);
|
|
18928
19138
|
kvService.initialize(kvContext);
|
|
18929
19139
|
return kvService;
|
|
@@ -18997,6 +19207,16 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18997
19207
|
shouldUseBootstrapSignInRequest(config) {
|
|
18998
19208
|
return config.autoBootstrapAccount !== false && config.manifest === void 0 && config.capabilityRequest === void 0 && (config.prefix ?? "default") === "default" && isOpenKeyAutoSignStrategy(config.signStrategy);
|
|
18999
19209
|
}
|
|
19210
|
+
createServiceGraphLifetime() {
|
|
19211
|
+
return new ServiceGraphLifetime(
|
|
19212
|
+
this.invokeWithRuntimePermissions,
|
|
19213
|
+
this.invokeAnyWithRuntimePermissions,
|
|
19214
|
+
// Resolve fetch at request time. This keeps the graph-owned lifetime
|
|
19215
|
+
// signal while preserving the SDK's existing injectable global fetch
|
|
19216
|
+
// behavior (including adapters that install it after construction).
|
|
19217
|
+
(url, init2) => globalThis.fetch(url, init2)
|
|
19218
|
+
);
|
|
19219
|
+
}
|
|
19000
19220
|
syncResolvedHostFromAuth() {
|
|
19001
19221
|
const host = this.auth?.hosts[0];
|
|
19002
19222
|
if (host) {
|
|
@@ -19066,6 +19286,27 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19066
19286
|
get sessionDid() {
|
|
19067
19287
|
return this.sessionManager.getDID(this.sessionKeyId);
|
|
19068
19288
|
}
|
|
19289
|
+
/**
|
|
19290
|
+
* Return the current session's signed ReCap capabilities after the session
|
|
19291
|
+
* has been authenticated or restored. This is intentionally distinct from
|
|
19292
|
+
* installed runtime delegations: it reports base-session authority only.
|
|
19293
|
+
*
|
|
19294
|
+
* Invalid or unparseable signed ReCap material throws so callers fail closed
|
|
19295
|
+
* rather than treating a malformed session as unrestricted authority.
|
|
19296
|
+
*/
|
|
19297
|
+
getVerifiedSessionCapabilities() {
|
|
19298
|
+
const session = this.currentTinyCloudSession();
|
|
19299
|
+
if (!session || !session.siwe) return [];
|
|
19300
|
+
return parseRecapCapabilities(
|
|
19301
|
+
(siwe) => this.parseRecapWithCaveats(siwe),
|
|
19302
|
+
session.siwe
|
|
19303
|
+
).map((entry) => entry.service === "tinycloud.encryption" ? {
|
|
19304
|
+
service: entry.service,
|
|
19305
|
+
path: entry.path,
|
|
19306
|
+
actions: [...entry.actions],
|
|
19307
|
+
...entry.caveats === void 0 ? {} : { caveats: entry.caveats }
|
|
19308
|
+
} : entry);
|
|
19309
|
+
}
|
|
19069
19310
|
/**
|
|
19070
19311
|
* Get the Ethereum address for this user.
|
|
19071
19312
|
*/
|
|
@@ -19155,6 +19396,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19155
19396
|
this._hooks = void 0;
|
|
19156
19397
|
this._vault = void 0;
|
|
19157
19398
|
this._encryption = void 0;
|
|
19399
|
+
this._baseVaults.clear();
|
|
19158
19400
|
this._baseSecrets.clear();
|
|
19159
19401
|
this._secrets.clear();
|
|
19160
19402
|
this._spaceService = void 0;
|
|
@@ -19162,6 +19404,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19162
19404
|
this.runtimePermissionGrants = [];
|
|
19163
19405
|
await this.tc.signIn(options);
|
|
19164
19406
|
this.syncResolvedHostFromAuth();
|
|
19407
|
+
const oldGraph = this._serviceGraph;
|
|
19408
|
+
this._serviceGraph = this.createServiceGraphLifetime();
|
|
19409
|
+
oldGraph.retire();
|
|
19410
|
+
this.tc.retireServices();
|
|
19165
19411
|
const signedInSession = this.currentTinyCloudSession();
|
|
19166
19412
|
if (signedInSession) {
|
|
19167
19413
|
this.sessionKeyId = signedInSession.sessionKey;
|
|
@@ -19446,7 +19692,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19446
19692
|
}
|
|
19447
19693
|
let operations = [];
|
|
19448
19694
|
try {
|
|
19449
|
-
const entries = this.
|
|
19695
|
+
const entries = this.parseRecapWithCaveats(siwe);
|
|
19450
19696
|
if (Array.isArray(entries)) {
|
|
19451
19697
|
operations = entries.flatMap((entry) => {
|
|
19452
19698
|
const service = this.invocationServiceName(entry.service);
|
|
@@ -19454,7 +19700,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19454
19700
|
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId: entry.space },
|
|
19455
19701
|
service,
|
|
19456
19702
|
path: entry.path,
|
|
19457
|
-
action
|
|
19703
|
+
action,
|
|
19704
|
+
caveats: cloneRecapCaveats(entry.caveats)
|
|
19458
19705
|
}));
|
|
19459
19706
|
});
|
|
19460
19707
|
}
|
|
@@ -19807,86 +20054,441 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19807
20054
|
*/
|
|
19808
20055
|
async restoreSession(sessionData) {
|
|
19809
20056
|
await this.wasmBindings.ensureInitialized?.();
|
|
19810
|
-
|
|
19811
|
-
|
|
19812
|
-
|
|
19813
|
-
|
|
19814
|
-
|
|
19815
|
-
|
|
19816
|
-
this.
|
|
19817
|
-
|
|
19818
|
-
|
|
19819
|
-
|
|
19820
|
-
|
|
19821
|
-
|
|
19822
|
-
|
|
19823
|
-
|
|
20057
|
+
const restoredJwk = clonePersistedSessionJwk(sessionData.jwk);
|
|
20058
|
+
if (sessionData.chainId !== void 0 && (!Number.isSafeInteger(sessionData.chainId) || sessionData.chainId <= 0)) {
|
|
20059
|
+
throw new Error("Persisted session chain ID must be a positive safe integer.");
|
|
20060
|
+
}
|
|
20061
|
+
const stagedManager = this.wasmBindings.createSessionManager();
|
|
20062
|
+
const stagedReplace = stagedManager.replaceSessionKey;
|
|
20063
|
+
const liveKeys = this.sessionManager.listSessionKeys;
|
|
20064
|
+
const stagedKeys = stagedManager.listSessionKeys;
|
|
20065
|
+
if (typeof stagedReplace !== "function" || typeof liveKeys !== "function" || typeof stagedKeys !== "function") {
|
|
20066
|
+
throw new UnsupportedSessionRestoreError();
|
|
20067
|
+
}
|
|
20068
|
+
let stagedKeyId;
|
|
20069
|
+
let stagedJwk;
|
|
20070
|
+
let canonicalVerificationMethod;
|
|
20071
|
+
try {
|
|
20072
|
+
const keyIds = liveKeys.call(this.sessionManager);
|
|
20073
|
+
if (!Array.isArray(keyIds) || !keyIds.every((keyId) => typeof keyId === "string")) {
|
|
20074
|
+
throw new UnsupportedSessionRestoreError("it cannot reliably enumerate every live session signer");
|
|
20075
|
+
}
|
|
20076
|
+
for (const keyId of keyIds) {
|
|
20077
|
+
if (keyId === this.sessionKeyId) continue;
|
|
20078
|
+
const jwk = this.sessionManager.jwk(keyId);
|
|
20079
|
+
if (!jwk) throw new Error("missing live session key");
|
|
20080
|
+
stagedReplace.call(stagedManager, clonePersistedSessionJwk(JSON.parse(jwk)), keyId);
|
|
20081
|
+
}
|
|
20082
|
+
stagedKeyId = stagedReplace.call(stagedManager, restoredJwk, this.sessionKeyId);
|
|
20083
|
+
const stagedJwkJson = stagedManager.jwk(stagedKeyId);
|
|
20084
|
+
if (!stagedJwkJson) throw new Error("missing restored session key");
|
|
20085
|
+
stagedJwk = clonePersistedSessionJwk(JSON.parse(stagedJwkJson));
|
|
20086
|
+
canonicalVerificationMethod = stagedManager.getDID(stagedKeyId);
|
|
20087
|
+
} catch (error) {
|
|
20088
|
+
if (error instanceof UnsupportedSessionRestoreError) throw error;
|
|
20089
|
+
throw new Error("Persisted session has an invalid private Ed25519 session key.");
|
|
19824
20090
|
}
|
|
19825
|
-
|
|
19826
|
-
|
|
20091
|
+
const restoredVerificationMethod = canonicalRestoredVerificationMethod(
|
|
20092
|
+
canonicalVerificationMethod,
|
|
20093
|
+
sessionData.verificationMethod
|
|
20094
|
+
);
|
|
20095
|
+
if (!restoredVerificationMethod) {
|
|
20096
|
+
throw new Error(
|
|
20097
|
+
"Persisted session verification method does not match its private Ed25519 session key."
|
|
20098
|
+
);
|
|
20099
|
+
}
|
|
20100
|
+
const proofValues = [
|
|
20101
|
+
sessionData.siwe,
|
|
20102
|
+
sessionData.signature,
|
|
20103
|
+
sessionData.address,
|
|
20104
|
+
sessionData.chainId
|
|
20105
|
+
];
|
|
20106
|
+
const hasPersistedProof = proofValues.every((value) => value !== void 0);
|
|
20107
|
+
if (!hasPersistedProof && (proofValues.some((value) => value !== void 0) || sessionData.expiresAt !== void 0)) {
|
|
20108
|
+
throw new Error("Persisted session authority metadata is incomplete.");
|
|
20109
|
+
}
|
|
20110
|
+
const restoredAddress = hasPersistedProof ? canonicalizeAddress2(sessionData.address) : void 0;
|
|
20111
|
+
let stagedSessionExpiry = /* @__PURE__ */ new Date(0);
|
|
20112
|
+
let stagedRecap = [];
|
|
20113
|
+
if (hasPersistedProof) {
|
|
20114
|
+
if (typeof this.wasmBindings.validatePersistedSession !== "function") {
|
|
20115
|
+
throw new UnsupportedSessionRestoreError("it cannot verify persisted SIWE authority");
|
|
20116
|
+
}
|
|
20117
|
+
const verified = this.wasmBindings.validatePersistedSession({
|
|
20118
|
+
delegationHeader: sessionData.delegationHeader,
|
|
20119
|
+
delegationCid: sessionData.delegationCid,
|
|
20120
|
+
spaceId: sessionData.spaceId,
|
|
20121
|
+
jwk: stagedJwk,
|
|
20122
|
+
address: restoredAddress,
|
|
20123
|
+
chainId: sessionData.chainId,
|
|
20124
|
+
siwe: sessionData.siwe,
|
|
20125
|
+
signature: sessionData.signature
|
|
20126
|
+
});
|
|
20127
|
+
const exactRecap = verified.verifiedRecap;
|
|
20128
|
+
if (!Array.isArray(exactRecap) || !exactRecap.every(
|
|
20129
|
+
(entry) => entry !== null && typeof entry === "object" && typeof entry.service === "string" && typeof entry.space === "string" && typeof entry.path === "string" && Array.isArray(entry.actions) && entry.actions.every((action) => typeof action === "string") && Array.isArray(entry.caveats) && entry.caveats.every(
|
|
20130
|
+
(caveat) => caveat !== null && typeof caveat === "object" && !Array.isArray(caveat)
|
|
20131
|
+
)
|
|
20132
|
+
)) {
|
|
20133
|
+
throw new UnsupportedSessionRestoreError(
|
|
20134
|
+
"it cannot reconstruct caveat-preserving persisted ReCap authority"
|
|
20135
|
+
);
|
|
20136
|
+
}
|
|
20137
|
+
stagedRecap = exactRecap.map((entry) => ({
|
|
20138
|
+
service: entry.service,
|
|
20139
|
+
space: entry.space,
|
|
20140
|
+
path: entry.path,
|
|
20141
|
+
actions: [...entry.actions],
|
|
20142
|
+
caveats: cloneRecapCaveats(entry.caveats)
|
|
20143
|
+
}));
|
|
20144
|
+
const signedExpiry = verified.expiresAt === void 0 ? void 0 : persistedExpiry(verified.expiresAt);
|
|
20145
|
+
const persistedPolicyExpiry = sessionData.expiresAt === void 0 ? void 0 : persistedExpiry(sessionData.expiresAt);
|
|
20146
|
+
if (signedExpiry && persistedPolicyExpiry && !sameInstant(signedExpiry, persistedPolicyExpiry)) {
|
|
20147
|
+
throw new Error("Persisted session expiry does not match its signed SIWE authority.");
|
|
20148
|
+
}
|
|
20149
|
+
stagedSessionExpiry = signedExpiry ?? persistedPolicyExpiry ?? persistedExpiry(void 0);
|
|
19827
20150
|
}
|
|
19828
20151
|
const resolvedHost = await this.resolveRestoredHost(
|
|
19829
20152
|
sessionData.tinycloudHosts,
|
|
19830
20153
|
restoredAddress,
|
|
19831
20154
|
sessionData.chainId
|
|
19832
20155
|
);
|
|
19833
|
-
|
|
19834
|
-
|
|
19835
|
-
|
|
19836
|
-
|
|
19837
|
-
invoke: this.invokeWithRuntimePermissions,
|
|
19838
|
-
invokeAny: this.invokeAnyWithRuntimePermissions,
|
|
19839
|
-
fetch: globalThis.fetch.bind(globalThis),
|
|
19840
|
-
hosts: [this.config.host],
|
|
19841
|
-
telemetry: this.config.telemetry
|
|
19842
|
-
});
|
|
19843
|
-
this._kv = new KVService2({});
|
|
19844
|
-
this._kv.initialize(this._serviceContext);
|
|
19845
|
-
this._serviceContext.registerService("kv", this._kv);
|
|
19846
|
-
this._sql = new SQLService2({});
|
|
19847
|
-
this._sql.initialize(this._serviceContext);
|
|
19848
|
-
this._serviceContext.registerService("sql", this._sql);
|
|
19849
|
-
this._duckdb = new DuckDbService2({});
|
|
19850
|
-
this._duckdb.initialize(this._serviceContext);
|
|
19851
|
-
this._serviceContext.registerService("duckdb", this._duckdb);
|
|
19852
|
-
this._hooks = new HooksService2({});
|
|
19853
|
-
this._hooks.initialize(this._serviceContext);
|
|
19854
|
-
this._serviceContext.registerService("hooks", this._hooks);
|
|
20156
|
+
const stagedHost = resolvedHost ?? this.config.host;
|
|
20157
|
+
const stagedAddress = restoredAddress;
|
|
20158
|
+
const stagedChainId = sessionData.chainId ?? 1;
|
|
20159
|
+
const stagedNodeDid = stagedAddress ? pkhDid2(stagedAddress, stagedChainId) : canonicalVerificationMethod;
|
|
19855
20160
|
const serviceSession = {
|
|
19856
20161
|
delegationHeader: sessionData.delegationHeader,
|
|
19857
20162
|
delegationCid: sessionData.delegationCid,
|
|
19858
20163
|
spaceId: sessionData.spaceId,
|
|
19859
|
-
verificationMethod:
|
|
19860
|
-
jwk:
|
|
20164
|
+
verificationMethod: restoredVerificationMethod,
|
|
20165
|
+
jwk: stagedJwk
|
|
19861
20166
|
};
|
|
19862
|
-
|
|
19863
|
-
|
|
19864
|
-
|
|
19865
|
-
|
|
19866
|
-
|
|
19867
|
-
|
|
19868
|
-
|
|
19869
|
-
|
|
19870
|
-
|
|
19871
|
-
|
|
19872
|
-
|
|
19873
|
-
|
|
19874
|
-
|
|
19875
|
-
|
|
19876
|
-
|
|
19877
|
-
|
|
19878
|
-
|
|
19879
|
-
|
|
19880
|
-
|
|
19881
|
-
|
|
19882
|
-
|
|
19883
|
-
|
|
20167
|
+
const stagedTcSession = hasPersistedProof ? {
|
|
20168
|
+
address: restoredAddress,
|
|
20169
|
+
chainId: sessionData.chainId,
|
|
20170
|
+
sessionKey: JSON.stringify(stagedJwk),
|
|
20171
|
+
spaceId: sessionData.spaceId,
|
|
20172
|
+
spaces: sessionData.spaces,
|
|
20173
|
+
delegationCid: sessionData.delegationCid,
|
|
20174
|
+
delegationHeader: sessionData.delegationHeader,
|
|
20175
|
+
verificationMethod: restoredVerificationMethod,
|
|
20176
|
+
jwk: stagedJwk,
|
|
20177
|
+
siwe: sessionData.siwe,
|
|
20178
|
+
signature: sessionData.signature
|
|
20179
|
+
} : void 0;
|
|
20180
|
+
const stagedPrimary = this.stagePrimarySessionState(
|
|
20181
|
+
stagedTcSession,
|
|
20182
|
+
stagedNodeDid,
|
|
20183
|
+
stagedHost,
|
|
20184
|
+
stagedSessionExpiry,
|
|
20185
|
+
stagedRecap
|
|
20186
|
+
);
|
|
20187
|
+
const stagedGraph = this.stageRestoredServiceGraph({
|
|
20188
|
+
host: stagedHost,
|
|
20189
|
+
manager: stagedManager,
|
|
20190
|
+
serviceSession,
|
|
20191
|
+
verificationMethod: canonicalVerificationMethod,
|
|
20192
|
+
nodeDid: stagedNodeDid,
|
|
20193
|
+
address: stagedAddress,
|
|
20194
|
+
chainId: stagedChainId,
|
|
20195
|
+
tinyCloudSession: stagedTcSession,
|
|
20196
|
+
sessionExpiry: stagedSessionExpiry,
|
|
20197
|
+
recap: stagedRecap
|
|
20198
|
+
});
|
|
20199
|
+
const oldGraph = this._serviceGraph;
|
|
20200
|
+
const oldCore = this.tc;
|
|
20201
|
+
this.sessionManager = stagedManager;
|
|
20202
|
+
this.sessionKeyId = stagedKeyId;
|
|
20203
|
+
this.sessionKeyJwk = stagedJwk;
|
|
20204
|
+
if (resolvedHost) this.config.host = resolvedHost;
|
|
20205
|
+
this._address = stagedAddress;
|
|
20206
|
+
this._chainId = stagedChainId;
|
|
20207
|
+
this._serviceContext = stagedGraph.serviceContext;
|
|
20208
|
+
this._kv = stagedGraph.kv;
|
|
20209
|
+
this._sql = stagedGraph.sql;
|
|
20210
|
+
this._duckdb = stagedGraph.duckdb;
|
|
20211
|
+
this._hooks = stagedGraph.hooks;
|
|
20212
|
+
this._vault = stagedGraph.vault;
|
|
20213
|
+
this._encryption = stagedGraph.encryption;
|
|
20214
|
+
this._capabilityRegistry = stagedGraph.capabilityRegistry;
|
|
20215
|
+
this._keyProvider = stagedGraph.keyProvider;
|
|
20216
|
+
this._sharingService = stagedGraph.sharingService;
|
|
20217
|
+
this._delegationManager = stagedGraph.delegationManager;
|
|
20218
|
+
this._spaceService = stagedGraph.spaceService;
|
|
20219
|
+
this._serviceGraph = stagedGraph.graph;
|
|
20220
|
+
this._baseSecrets = /* @__PURE__ */ new Map();
|
|
20221
|
+
this._secrets = /* @__PURE__ */ new Map();
|
|
20222
|
+
this._publicKV = void 0;
|
|
20223
|
+
this._account = void 0;
|
|
20224
|
+
this.tc = stagedGraph.core;
|
|
20225
|
+
this._recapOperationsCache = stagedPrimary.cache;
|
|
20226
|
+
this.runtimePermissionGrants = stagedPrimary.grants;
|
|
20227
|
+
if (this.auth) {
|
|
20228
|
+
this.auth.installRestoredSession(stagedManager, stagedTcSession, [stagedHost]);
|
|
20229
|
+
this._restoredTcSession = void 0;
|
|
20230
|
+
} else {
|
|
20231
|
+
this._restoredTcSession = stagedTcSession;
|
|
20232
|
+
}
|
|
20233
|
+
oldGraph.retire();
|
|
20234
|
+
oldCore?.retireServices?.();
|
|
20235
|
+
}
|
|
20236
|
+
stagePrimarySessionState(session, verificationMethod, host, sessionExpiry, recap) {
|
|
20237
|
+
if (!session?.siwe) return { cache: void 0, grants: [] };
|
|
20238
|
+
let operations = [];
|
|
20239
|
+
operations = recap.flatMap((entry) => {
|
|
20240
|
+
const service = this.invocationServiceName(entry.service);
|
|
20241
|
+
return entry.actions.map((action) => ({
|
|
20242
|
+
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId: entry.space },
|
|
20243
|
+
service,
|
|
20244
|
+
path: entry.path,
|
|
20245
|
+
action,
|
|
20246
|
+
caveats: cloneRecapCaveats(entry.caveats)
|
|
20247
|
+
}));
|
|
20248
|
+
});
|
|
20249
|
+
const cache = { siwe: session.siwe, operations };
|
|
20250
|
+
if (operations.length === 0) return { cache, grants: [] };
|
|
20251
|
+
const expiresAt = sessionExpiry;
|
|
20252
|
+
return {
|
|
20253
|
+
cache,
|
|
20254
|
+
grants: [{
|
|
20255
|
+
session: {
|
|
20256
|
+
delegationHeader: session.delegationHeader,
|
|
20257
|
+
delegationCid: session.delegationCid,
|
|
20258
|
+
spaceId: session.spaceId,
|
|
20259
|
+
verificationMethod: session.verificationMethod,
|
|
20260
|
+
jwk: session.jwk
|
|
20261
|
+
},
|
|
20262
|
+
delegation: {
|
|
20263
|
+
cid: session.delegationCid,
|
|
20264
|
+
delegationHeader: session.delegationHeader,
|
|
20265
|
+
delegateDID: session.verificationMethod,
|
|
20266
|
+
delegatorDID: verificationMethod,
|
|
20267
|
+
spaceId: session.spaceId,
|
|
20268
|
+
path: "",
|
|
20269
|
+
actions: [...new Set(operations.map((operation) => operation.action))],
|
|
20270
|
+
expiry: expiresAt,
|
|
20271
|
+
allowSubDelegation: true,
|
|
20272
|
+
ownerAddress: session.address,
|
|
20273
|
+
chainId: session.chainId,
|
|
20274
|
+
host
|
|
20275
|
+
},
|
|
20276
|
+
operations,
|
|
20277
|
+
expiresAt,
|
|
20278
|
+
provenance: "primary"
|
|
20279
|
+
}]
|
|
20280
|
+
};
|
|
20281
|
+
}
|
|
20282
|
+
stageRestoredServiceGraph(input) {
|
|
20283
|
+
const graph = this.createServiceGraphLifetime();
|
|
20284
|
+
const core = this.auth ? new TinyCloud(this.auth, {
|
|
20285
|
+
invokeAny: graph.invokeAny,
|
|
20286
|
+
telemetry: this.config.telemetry
|
|
20287
|
+
}) : null;
|
|
20288
|
+
if (core) {
|
|
20289
|
+
core.initializeServices(graph.invoke, [input.host], graph.fetch);
|
|
20290
|
+
const coreContext = graph.track(core.serviceContext);
|
|
20291
|
+
coreContext.setSession(input.serviceSession);
|
|
20292
|
+
}
|
|
20293
|
+
const serviceContext = graph.track(new ServiceContext2({
|
|
20294
|
+
invoke: graph.invoke,
|
|
20295
|
+
invokeAny: graph.invokeAny,
|
|
20296
|
+
fetch: graph.fetch,
|
|
20297
|
+
hosts: [input.host],
|
|
20298
|
+
telemetry: this.config.telemetry
|
|
20299
|
+
}));
|
|
20300
|
+
const kv = new KVService2({});
|
|
20301
|
+
kv.initialize(serviceContext);
|
|
20302
|
+
serviceContext.registerService("kv", kv);
|
|
20303
|
+
const sql = new SQLService2({});
|
|
20304
|
+
sql.initialize(serviceContext);
|
|
20305
|
+
serviceContext.registerService("sql", sql);
|
|
20306
|
+
const duckdb = new DuckDbService2({});
|
|
20307
|
+
duckdb.initialize(serviceContext);
|
|
20308
|
+
serviceContext.registerService("duckdb", duckdb);
|
|
20309
|
+
const hooks = new HooksService2({});
|
|
20310
|
+
hooks.initialize(serviceContext);
|
|
20311
|
+
serviceContext.registerService("hooks", hooks);
|
|
20312
|
+
serviceContext.setSession(input.serviceSession);
|
|
20313
|
+
const encryption = this.createEncryptionService({
|
|
20314
|
+
graph,
|
|
20315
|
+
host: input.host,
|
|
20316
|
+
session: input.serviceSession,
|
|
20317
|
+
did: input.nodeDid,
|
|
20318
|
+
address: input.address,
|
|
20319
|
+
chainId: input.chainId
|
|
20320
|
+
});
|
|
20321
|
+
encryption.initialize(serviceContext);
|
|
20322
|
+
serviceContext.registerService("encryption", encryption);
|
|
20323
|
+
const vault = this.createVaultService(input.serviceSession.spaceId, kv, encryption, {
|
|
20324
|
+
host: input.host,
|
|
20325
|
+
did: input.nodeDid,
|
|
20326
|
+
address: input.address,
|
|
20327
|
+
chainId: input.chainId,
|
|
20328
|
+
serviceGraph: graph
|
|
20329
|
+
});
|
|
20330
|
+
vault.initialize(serviceContext);
|
|
20331
|
+
serviceContext.registerService("vault", vault);
|
|
20332
|
+
const capabilityRegistry = new CapabilityKeyRegistry();
|
|
20333
|
+
if (input.tinyCloudSession) {
|
|
20334
|
+
const session = input.tinyCloudSession;
|
|
20335
|
+
const delegations = input.recap.map((entry) => ({
|
|
20336
|
+
cid: session.delegationCid,
|
|
20337
|
+
delegateDID: session.verificationMethod,
|
|
20338
|
+
spaceId: entry.space,
|
|
20339
|
+
path: entry.path,
|
|
20340
|
+
actions: [...entry.actions],
|
|
20341
|
+
caveats: cloneRecapCaveats(entry.caveats),
|
|
20342
|
+
expiry: input.sessionExpiry,
|
|
20343
|
+
isRevoked: false,
|
|
20344
|
+
allowSubDelegation: true
|
|
20345
|
+
}));
|
|
20346
|
+
if (delegations.length > 0) {
|
|
20347
|
+
capabilityRegistry.registerKey({
|
|
20348
|
+
id: session.sessionKey,
|
|
20349
|
+
did: session.verificationMethod,
|
|
20350
|
+
type: "session",
|
|
20351
|
+
jwk: session.jwk,
|
|
20352
|
+
priority: 0
|
|
20353
|
+
}, delegations);
|
|
20354
|
+
}
|
|
20355
|
+
}
|
|
20356
|
+
const delegationManager = new DelegationManager({
|
|
20357
|
+
hosts: [input.host],
|
|
20358
|
+
session: input.serviceSession,
|
|
20359
|
+
invoke: graph.invoke,
|
|
20360
|
+
invokeAny: graph.invokeAny,
|
|
20361
|
+
fetch: graph.fetch
|
|
20362
|
+
});
|
|
20363
|
+
const keyProvider = new WasmKeyProvider({ sessionManager: input.manager });
|
|
20364
|
+
const sharingService = new SharingService({
|
|
20365
|
+
hosts: [input.host],
|
|
20366
|
+
invoke: graph.invoke,
|
|
20367
|
+
fetch: graph.fetch,
|
|
20368
|
+
assertActive: () => graph.assertActive(),
|
|
20369
|
+
keyProvider,
|
|
20370
|
+
registry: capabilityRegistry,
|
|
20371
|
+
createDelegationWasm: (params) => this.createDelegationWrapper(params),
|
|
20372
|
+
computeCid: (data, codec) => {
|
|
20373
|
+
if (!this.wasmBindings.computeCid) throw new Error("computeCid is unavailable");
|
|
20374
|
+
return this.wasmBindings.computeCid(data, codec);
|
|
20375
|
+
},
|
|
20376
|
+
createKVService: (config) => {
|
|
20377
|
+
const service = new KVService2({ prefix: config.pathPrefix?.replace(/\/$/, "") });
|
|
20378
|
+
const context = graph.track(new ServiceContext2({
|
|
20379
|
+
invoke: config.invoke,
|
|
20380
|
+
fetch: config.fetch ?? globalThis.fetch.bind(globalThis),
|
|
20381
|
+
hosts: config.hosts,
|
|
20382
|
+
telemetry: this.config.telemetry
|
|
20383
|
+
}));
|
|
20384
|
+
context.setSession(config.session);
|
|
20385
|
+
service.initialize(context);
|
|
20386
|
+
return service;
|
|
20387
|
+
}
|
|
20388
|
+
});
|
|
20389
|
+
sharingService.updateConfig({
|
|
20390
|
+
session: input.serviceSession,
|
|
20391
|
+
delegationManager,
|
|
20392
|
+
sessionExpiry: input.sessionExpiry,
|
|
20393
|
+
createDelegationWasm: (params) => {
|
|
20394
|
+
graph.assertActive();
|
|
20395
|
+
return this.createDelegationWrapper(params);
|
|
20396
|
+
},
|
|
20397
|
+
onRootDelegationNeeded: this.signer ? async (params) => {
|
|
20398
|
+
graph.assertActive();
|
|
20399
|
+
const delegation = await this.createRootDelegationForSharing(
|
|
20400
|
+
params,
|
|
20401
|
+
() => graph.assertActive()
|
|
19884
20402
|
);
|
|
19885
|
-
|
|
19886
|
-
|
|
20403
|
+
graph.assertActive();
|
|
20404
|
+
return delegation;
|
|
20405
|
+
} : void 0
|
|
20406
|
+
});
|
|
20407
|
+
const spaceService = new SpaceService({
|
|
20408
|
+
hosts: [input.host],
|
|
20409
|
+
session: input.serviceSession,
|
|
20410
|
+
invoke: graph.invoke,
|
|
20411
|
+
fetch: graph.fetch,
|
|
20412
|
+
capabilityRegistry,
|
|
20413
|
+
userDid: input.nodeDid,
|
|
20414
|
+
createKVService: (spaceId) => {
|
|
20415
|
+
graph.assertActive();
|
|
20416
|
+
const scopedKv = new KVService2({});
|
|
20417
|
+
const context = graph.track(new ServiceContext2({
|
|
20418
|
+
invoke: graph.invoke,
|
|
20419
|
+
invokeAny: graph.invokeAny,
|
|
20420
|
+
fetch: graph.fetch,
|
|
20421
|
+
hosts: [input.host],
|
|
20422
|
+
telemetry: this.config.telemetry
|
|
20423
|
+
}));
|
|
20424
|
+
context.setSession({ ...input.serviceSession, spaceId });
|
|
20425
|
+
scopedKv.initialize(context);
|
|
20426
|
+
return scopedKv;
|
|
20427
|
+
},
|
|
20428
|
+
createVaultService: (spaceId) => {
|
|
20429
|
+
graph.assertActive();
|
|
20430
|
+
const scopedKv = new KVService2({});
|
|
20431
|
+
const context = graph.track(new ServiceContext2({
|
|
20432
|
+
invoke: graph.invoke,
|
|
20433
|
+
invokeAny: graph.invokeAny,
|
|
20434
|
+
fetch: graph.fetch,
|
|
20435
|
+
hosts: [input.host],
|
|
20436
|
+
telemetry: this.config.telemetry
|
|
20437
|
+
}));
|
|
20438
|
+
context.setSession({ ...input.serviceSession, spaceId });
|
|
20439
|
+
scopedKv.initialize(context);
|
|
20440
|
+
const scopedVault = this.createVaultService(spaceId, scopedKv, encryption, {
|
|
20441
|
+
host: input.host,
|
|
20442
|
+
did: input.nodeDid,
|
|
20443
|
+
address: input.address,
|
|
20444
|
+
chainId: input.chainId,
|
|
20445
|
+
serviceGraph: graph
|
|
20446
|
+
});
|
|
20447
|
+
scopedVault.initialize(context);
|
|
20448
|
+
return scopedVault;
|
|
20449
|
+
},
|
|
20450
|
+
createSecretsService: (spaceId) => {
|
|
20451
|
+
graph.assertActive();
|
|
20452
|
+
return this.secretsForSpace(spaceId);
|
|
20453
|
+
},
|
|
20454
|
+
createDelegation: async (params) => {
|
|
20455
|
+
graph.assertActive();
|
|
20456
|
+
try {
|
|
20457
|
+
const portableDelegation = await this.createDelegation({
|
|
20458
|
+
delegateDID: params.delegateDID,
|
|
20459
|
+
path: params.path,
|
|
20460
|
+
actions: params.actions,
|
|
20461
|
+
disableSubDelegation: params.disableSubDelegation,
|
|
20462
|
+
expiryMs: params.expiry ? params.expiry.getTime() - Date.now() : void 0
|
|
20463
|
+
});
|
|
20464
|
+
return { ok: true, data: {
|
|
20465
|
+
cid: portableDelegation.cid,
|
|
20466
|
+
delegateDID: portableDelegation.delegateDID,
|
|
20467
|
+
delegatorDID: this.did,
|
|
20468
|
+
spaceId: portableDelegation.spaceId,
|
|
20469
|
+
path: portableDelegation.path,
|
|
20470
|
+
actions: portableDelegation.actions,
|
|
20471
|
+
expiry: portableDelegation.expiry,
|
|
20472
|
+
isRevoked: false,
|
|
20473
|
+
allowSubDelegation: !portableDelegation.disableSubDelegation,
|
|
20474
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
20475
|
+
authHeader: portableDelegation.delegationHeader.Authorization
|
|
20476
|
+
} };
|
|
20477
|
+
} catch (error) {
|
|
20478
|
+
return { ok: false, error: {
|
|
20479
|
+
code: "CREATION_FAILED",
|
|
20480
|
+
message: error instanceof Error ? error.message : String(error),
|
|
20481
|
+
service: "delegation"
|
|
20482
|
+
} };
|
|
20483
|
+
}
|
|
20484
|
+
},
|
|
20485
|
+
onSpaceRegistered: async (space) => {
|
|
20486
|
+
graph.assertActive();
|
|
20487
|
+
await this.account.spaces.register(space);
|
|
19887
20488
|
}
|
|
19888
|
-
|
|
19889
|
-
}
|
|
20489
|
+
});
|
|
20490
|
+
spaceService.updateConfig({ sharingService });
|
|
20491
|
+
return { core, graph, serviceContext, kv, sql, duckdb, hooks, vault, encryption, capabilityRegistry, keyProvider, sharingService, delegationManager, spaceService };
|
|
19890
20492
|
}
|
|
19891
20493
|
/**
|
|
19892
20494
|
* Resolve the host a restored session should target.
|
|
@@ -20048,14 +20650,19 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20048
20650
|
if (!session) {
|
|
20049
20651
|
return;
|
|
20050
20652
|
}
|
|
20051
|
-
this.tc.initializeServices(
|
|
20052
|
-
|
|
20053
|
-
|
|
20054
|
-
|
|
20055
|
-
|
|
20653
|
+
this.tc.initializeServices(
|
|
20654
|
+
this._serviceGraph.invoke,
|
|
20655
|
+
[this.config.host],
|
|
20656
|
+
this._serviceGraph.fetch
|
|
20657
|
+
);
|
|
20658
|
+
this._serviceGraph.track(this.tc.serviceContext);
|
|
20659
|
+
this._serviceContext = this._serviceGraph.track(new ServiceContext2({
|
|
20660
|
+
invoke: this._serviceGraph.invoke,
|
|
20661
|
+
invokeAny: this._serviceGraph.invokeAny,
|
|
20662
|
+
fetch: this._serviceGraph.fetch,
|
|
20056
20663
|
hosts: [this.config.host],
|
|
20057
20664
|
telemetry: this.config.telemetry
|
|
20058
|
-
});
|
|
20665
|
+
}));
|
|
20059
20666
|
this._kv = new KVService2({});
|
|
20060
20667
|
this._kv.initialize(this._serviceContext);
|
|
20061
20668
|
this._serviceContext.registerService("kv", this._kv);
|
|
@@ -20090,12 +20697,12 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20090
20697
|
createSpaceScopedKVService(spaceId) {
|
|
20091
20698
|
const kvService = new KVService2({});
|
|
20092
20699
|
if (this._serviceContext) {
|
|
20093
|
-
const spaceScopedContext = new ServiceContext2({
|
|
20700
|
+
const spaceScopedContext = this._serviceGraph.track(new ServiceContext2({
|
|
20094
20701
|
invoke: this._serviceContext.invoke,
|
|
20095
20702
|
fetch: this._serviceContext.fetch,
|
|
20096
20703
|
hosts: this._serviceContext.hosts,
|
|
20097
20704
|
telemetry: this.config.telemetry
|
|
20098
|
-
});
|
|
20705
|
+
}));
|
|
20099
20706
|
const session = this._serviceContext.session;
|
|
20100
20707
|
if (session) {
|
|
20101
20708
|
spaceScopedContext.setSession({ ...session, spaceId });
|
|
@@ -20128,6 +20735,18 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20128
20735
|
}
|
|
20129
20736
|
return session;
|
|
20130
20737
|
}
|
|
20738
|
+
/**
|
|
20739
|
+
* Runtime-permission helpers can be used from an authenticated Node session
|
|
20740
|
+
* before its public ServiceContext is initialized. They still need the
|
|
20741
|
+
* current signed session, never an optional or stale authority object.
|
|
20742
|
+
*/
|
|
20743
|
+
requireEncryptionSession() {
|
|
20744
|
+
const session = this._serviceContext?.session ?? this.auth?.tinyCloudSession;
|
|
20745
|
+
if (!session) {
|
|
20746
|
+
throw new Error("Not signed in. Call signIn() first.");
|
|
20747
|
+
}
|
|
20748
|
+
return session;
|
|
20749
|
+
}
|
|
20131
20750
|
createEncryptionCrypto() {
|
|
20132
20751
|
const wasm = this.wasmBindings;
|
|
20133
20752
|
const columnEncrypt = (key2, plaintext) => {
|
|
@@ -20183,14 +20802,15 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20183
20802
|
}
|
|
20184
20803
|
return info.nodeId;
|
|
20185
20804
|
}
|
|
20186
|
-
async signRawNetworkAuthorization(input) {
|
|
20805
|
+
async signRawNetworkAuthorization(input, binding) {
|
|
20806
|
+
binding?.graph.assertActive();
|
|
20187
20807
|
if (!this.wasmBindings.invokeAny) {
|
|
20188
20808
|
throw new Error("WASM binding does not support raw-resource invokeAny");
|
|
20189
20809
|
}
|
|
20190
20810
|
if (!this.wasmBindings.computeCid) {
|
|
20191
20811
|
throw new Error("WASM binding does not support invocation CID computation");
|
|
20192
20812
|
}
|
|
20193
|
-
const session = this.
|
|
20813
|
+
const session = binding?.session ?? this.requireEncryptionSession();
|
|
20194
20814
|
const headers = this.invokeAnyWithRuntimePermissions(
|
|
20195
20815
|
session,
|
|
20196
20816
|
[
|
|
@@ -20209,6 +20829,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20209
20829
|
input.targetNode,
|
|
20210
20830
|
session.jwk
|
|
20211
20831
|
);
|
|
20832
|
+
binding?.graph.assertActive();
|
|
20212
20833
|
return {
|
|
20213
20834
|
authorization: audienceBound,
|
|
20214
20835
|
invocationCid: this.wasmBindings.computeCid(
|
|
@@ -20217,25 +20838,55 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20217
20838
|
)
|
|
20218
20839
|
};
|
|
20219
20840
|
}
|
|
20220
|
-
|
|
20841
|
+
async fetchEncryptionNetworkAt(host, networkId, fetchFn) {
|
|
20842
|
+
const response = await fetchFn(
|
|
20843
|
+
`${host}/encryption/networks/${encodeURIComponent(networkId)}`
|
|
20844
|
+
);
|
|
20845
|
+
if (response.status === 404) return null;
|
|
20846
|
+
if (!response.ok) {
|
|
20847
|
+
throw new Error(
|
|
20848
|
+
`Failed to fetch encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
|
|
20849
|
+
);
|
|
20850
|
+
}
|
|
20851
|
+
const body = await response.json();
|
|
20852
|
+
return "descriptor" in body && body.descriptor ? body.descriptor : body;
|
|
20853
|
+
}
|
|
20854
|
+
createEncryptionService(binding) {
|
|
20855
|
+
const graph = binding?.graph ?? this._serviceGraph;
|
|
20856
|
+
const host = binding?.host ?? this.config.host;
|
|
20857
|
+
const session = binding?.session ?? this.requireEncryptionSession();
|
|
20858
|
+
const did = binding?.did ?? this.did;
|
|
20859
|
+
const address = binding?.address ?? this._address;
|
|
20860
|
+
const chainId = binding?.chainId ?? this._chainId;
|
|
20221
20861
|
const crypto2 = this.createEncryptionCrypto();
|
|
20222
20862
|
const transport = {
|
|
20223
|
-
postDecrypt: async ({ networkId, authorization, canonicalBody }) => {
|
|
20224
|
-
|
|
20225
|
-
|
|
20863
|
+
postDecrypt: async ({ networkId, authorization, canonicalBody, signal }) => {
|
|
20864
|
+
graph.assertActive();
|
|
20865
|
+
const response = await graph.fetch(
|
|
20866
|
+
`${host}/encryption/networks/${encodeURIComponent(networkId)}/decrypt`,
|
|
20226
20867
|
{
|
|
20227
20868
|
method: "POST",
|
|
20228
20869
|
headers: {
|
|
20229
20870
|
Authorization: authorization,
|
|
20230
20871
|
"Content-Type": "application/json"
|
|
20231
20872
|
},
|
|
20232
|
-
body: canonicalBody
|
|
20873
|
+
body: canonicalBody,
|
|
20874
|
+
signal
|
|
20233
20875
|
}
|
|
20234
20876
|
);
|
|
20877
|
+
graph.assertActive();
|
|
20235
20878
|
if (!response.ok) {
|
|
20236
|
-
|
|
20237
|
-
|
|
20238
|
-
|
|
20879
|
+
let permissionHint;
|
|
20880
|
+
if (response.status === 401 || response.status === 403) {
|
|
20881
|
+
try {
|
|
20882
|
+
const body = await response.json();
|
|
20883
|
+
const record = typeof body === "object" && body !== null ? body : void 0;
|
|
20884
|
+
const nested = typeof record?.error === "object" && record.error !== null ? record.error : void 0;
|
|
20885
|
+
permissionHint = parsePermissionHint(record?.permissionHint) ?? parsePermissionHint(nested?.permissionHint);
|
|
20886
|
+
} catch {
|
|
20887
|
+
}
|
|
20888
|
+
}
|
|
20889
|
+
throw new DecryptTransportResponseError(response.status, permissionHint);
|
|
20239
20890
|
}
|
|
20240
20891
|
return await response.json();
|
|
20241
20892
|
}
|
|
@@ -20244,12 +20895,14 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20244
20895
|
crypto: crypto2,
|
|
20245
20896
|
signer: {
|
|
20246
20897
|
signDecryptInvocation: async (input) => {
|
|
20898
|
+
graph.assertActive();
|
|
20247
20899
|
const signed2 = await this.signRawNetworkAuthorization({
|
|
20248
20900
|
targetNode: input.targetNode,
|
|
20249
20901
|
networkId: input.networkId,
|
|
20250
20902
|
action: DECRYPT_ACTION2,
|
|
20251
20903
|
facts: input.facts
|
|
20252
|
-
});
|
|
20904
|
+
}, { graph, session });
|
|
20905
|
+
graph.assertActive();
|
|
20253
20906
|
return {
|
|
20254
20907
|
...signed2,
|
|
20255
20908
|
canonicalBody: canonicalizeEncryptionJson(
|
|
@@ -20260,25 +20913,35 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20260
20913
|
},
|
|
20261
20914
|
transport,
|
|
20262
20915
|
node: {
|
|
20263
|
-
fetchByNetworkId: (networkId) =>
|
|
20916
|
+
fetchByNetworkId: async (networkId) => {
|
|
20917
|
+
graph.assertActive();
|
|
20918
|
+
const descriptor = await this.fetchEncryptionNetworkAt(host, networkId, graph.fetch);
|
|
20919
|
+
graph.assertActive();
|
|
20920
|
+
return descriptor;
|
|
20921
|
+
}
|
|
20264
20922
|
},
|
|
20265
20923
|
wellKnown: {
|
|
20266
20924
|
fetchWellKnown: async (principal, discoveryKey) => {
|
|
20267
|
-
|
|
20268
|
-
|
|
20269
|
-
}
|
|
20270
|
-
if (!this.config.host) {
|
|
20925
|
+
graph.assertActive();
|
|
20926
|
+
if (!address || !didPrincipalMatches2(principal, did)) {
|
|
20271
20927
|
return null;
|
|
20272
20928
|
}
|
|
20273
|
-
const publicSpaceId = makePublicSpaceId(
|
|
20274
|
-
const
|
|
20275
|
-
|
|
20929
|
+
const publicSpaceId = makePublicSpaceId(address, chainId);
|
|
20930
|
+
const encodedKey = discoveryKey.split("/").map(encodeURIComponent).join("/");
|
|
20931
|
+
const response = await graph.fetch(
|
|
20932
|
+
`${host}/public/${encodeURIComponent(publicSpaceId)}/kv/${encodedKey}`,
|
|
20933
|
+
{ method: "GET" }
|
|
20934
|
+
);
|
|
20935
|
+
graph.assertActive();
|
|
20936
|
+
if (!response.ok) {
|
|
20276
20937
|
return null;
|
|
20277
20938
|
}
|
|
20278
|
-
const body =
|
|
20939
|
+
const body = await response.json();
|
|
20940
|
+
graph.assertActive();
|
|
20279
20941
|
return "descriptor" in body && body.descriptor ? body.descriptor : body;
|
|
20280
20942
|
}
|
|
20281
|
-
}
|
|
20943
|
+
},
|
|
20944
|
+
assertActive: () => graph.assertActive()
|
|
20282
20945
|
});
|
|
20283
20946
|
}
|
|
20284
20947
|
getEncryptionService() {
|
|
@@ -20292,8 +20955,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20292
20955
|
}
|
|
20293
20956
|
return this._encryption;
|
|
20294
20957
|
}
|
|
20295
|
-
createVaultService(spaceId, kv) {
|
|
20958
|
+
createVaultService(spaceId, kv, encryptionService = this.getEncryptionService(), nodeContext) {
|
|
20296
20959
|
const wasm = this.wasmBindings;
|
|
20960
|
+
const serviceGraph = nodeContext?.serviceGraph ?? this._serviceGraph;
|
|
20297
20961
|
const vaultCrypto = createVaultCrypto({
|
|
20298
20962
|
vault_encrypt: wasm.vault_encrypt,
|
|
20299
20963
|
vault_decrypt: wasm.vault_decrypt,
|
|
@@ -20304,12 +20968,17 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20304
20968
|
vault_sha256: wasm.vault_sha256
|
|
20305
20969
|
});
|
|
20306
20970
|
const self2 = this;
|
|
20971
|
+
const did = nodeContext?.did ?? this.did;
|
|
20972
|
+
const address = nodeContext?.address ?? this._address;
|
|
20973
|
+
const chainId = nodeContext?.chainId ?? this._chainId;
|
|
20974
|
+
const host = nodeContext?.host ?? this.config.host;
|
|
20975
|
+
const ownerDid = this.ownerDidFromSpaceId(spaceId) ?? did;
|
|
20307
20976
|
return new DataVaultService({
|
|
20308
20977
|
spaceId,
|
|
20309
20978
|
crypto: vaultCrypto,
|
|
20310
20979
|
encryption: {
|
|
20311
|
-
networkId:
|
|
20312
|
-
service:
|
|
20980
|
+
networkId: `urn:tinycloud:encryption:${ownerDid}:${DEFAULT_ENCRYPTION_NETWORK_NAME}`,
|
|
20981
|
+
service: encryptionService,
|
|
20313
20982
|
decryptCapabilityProof: () => ({
|
|
20314
20983
|
proofs: [this.requireServiceSession().delegationCid]
|
|
20315
20984
|
})
|
|
@@ -20318,6 +20987,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20318
20987
|
kv,
|
|
20319
20988
|
ensurePublicSpace: async () => {
|
|
20320
20989
|
try {
|
|
20990
|
+
serviceGraph.assertActive();
|
|
20321
20991
|
await self2.ensurePublicSpace();
|
|
20322
20992
|
return { ok: true, data: void 0 };
|
|
20323
20993
|
} catch (error) {
|
|
@@ -20325,14 +20995,15 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20325
20995
|
}
|
|
20326
20996
|
},
|
|
20327
20997
|
get publicKV() {
|
|
20998
|
+
serviceGraph.assertActive();
|
|
20328
20999
|
return self2._publicKV ?? self2.tc.publicKV;
|
|
20329
21000
|
},
|
|
20330
|
-
readPublicSpace: (
|
|
21001
|
+
readPublicSpace: (host2, targetSpaceId, key2) => TinyCloud.readPublicSpace(host2, targetSpaceId, key2),
|
|
20331
21002
|
makePublicSpaceId: TinyCloud.makePublicSpaceId,
|
|
20332
|
-
did
|
|
20333
|
-
address:
|
|
20334
|
-
chainId
|
|
20335
|
-
hosts: [
|
|
21003
|
+
did,
|
|
21004
|
+
address: address ?? "",
|
|
21005
|
+
chainId,
|
|
21006
|
+
hosts: [host]
|
|
20336
21007
|
}
|
|
20337
21008
|
});
|
|
20338
21009
|
}
|
|
@@ -20341,6 +21012,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20341
21012
|
* @internal
|
|
20342
21013
|
*/
|
|
20343
21014
|
initializeV2Services(serviceSession) {
|
|
21015
|
+
const graph = this._serviceGraph;
|
|
20344
21016
|
this._capabilityRegistry = new CapabilityKeyRegistry();
|
|
20345
21017
|
const tcSession = this.auth?.tinyCloudSession;
|
|
20346
21018
|
if (tcSession && this._address) {
|
|
@@ -20385,21 +21057,23 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20385
21057
|
hosts: [this.config.host],
|
|
20386
21058
|
accountSpaceId: this.accountSpaceId,
|
|
20387
21059
|
session: serviceSession,
|
|
20388
|
-
invoke:
|
|
20389
|
-
invokeAny:
|
|
20390
|
-
fetch:
|
|
21060
|
+
invoke: graph.invoke,
|
|
21061
|
+
invokeAny: graph.invokeAny,
|
|
21062
|
+
fetch: graph.fetch
|
|
20391
21063
|
});
|
|
20392
21064
|
this._spaceService = new SpaceService({
|
|
20393
21065
|
hosts: [this.config.host],
|
|
20394
21066
|
session: serviceSession,
|
|
20395
|
-
invoke:
|
|
20396
|
-
fetch:
|
|
21067
|
+
invoke: graph.invoke,
|
|
21068
|
+
fetch: graph.fetch,
|
|
20397
21069
|
capabilityRegistry: this._capabilityRegistry,
|
|
20398
21070
|
userDid: this.did,
|
|
20399
21071
|
createKVService: (spaceId) => {
|
|
21072
|
+
graph.assertActive();
|
|
20400
21073
|
return this.createSpaceScopedKVService(spaceId);
|
|
20401
21074
|
},
|
|
20402
21075
|
createVaultService: (spaceId) => {
|
|
21076
|
+
graph.assertActive();
|
|
20403
21077
|
const kvService = this.createSpaceScopedKVService(spaceId);
|
|
20404
21078
|
const vaultService = this.createVaultService(spaceId, kvService);
|
|
20405
21079
|
if (this._serviceContext) {
|
|
@@ -20408,11 +21082,13 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20408
21082
|
return vaultService;
|
|
20409
21083
|
},
|
|
20410
21084
|
createSecretsService: (spaceId) => {
|
|
21085
|
+
graph.assertActive();
|
|
20411
21086
|
return this.secretsForSpace(spaceId);
|
|
20412
21087
|
},
|
|
20413
21088
|
// Enable space.delegations.create() via SIWE-based delegation
|
|
20414
21089
|
createDelegation: async (params) => {
|
|
20415
21090
|
try {
|
|
21091
|
+
graph.assertActive();
|
|
20416
21092
|
const portableDelegation = await this.createDelegation({
|
|
20417
21093
|
delegateDID: params.delegateDID,
|
|
20418
21094
|
path: params.path,
|
|
@@ -20446,18 +21122,50 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20446
21122
|
}
|
|
20447
21123
|
},
|
|
20448
21124
|
onSpaceRegistered: async (space) => {
|
|
21125
|
+
graph.assertActive();
|
|
20449
21126
|
await this.account.spaces.register(space);
|
|
20450
21127
|
}
|
|
20451
21128
|
});
|
|
20452
|
-
this._sharingService
|
|
21129
|
+
this._sharingService = new SharingService({
|
|
21130
|
+
hosts: [this.config.host],
|
|
20453
21131
|
session: serviceSession,
|
|
21132
|
+
invoke: graph.invoke,
|
|
21133
|
+
fetch: graph.fetch,
|
|
21134
|
+
assertActive: () => graph.assertActive(),
|
|
21135
|
+
keyProvider: this._keyProvider,
|
|
21136
|
+
registry: this._capabilityRegistry,
|
|
20454
21137
|
delegationManager: this._delegationManager,
|
|
20455
21138
|
sessionExpiry: this.getSessionExpiry(),
|
|
20456
|
-
|
|
20457
|
-
|
|
20458
|
-
|
|
20459
|
-
|
|
20460
|
-
|
|
21139
|
+
createDelegationWasm: (params) => {
|
|
21140
|
+
graph.assertActive();
|
|
21141
|
+
return this.createDelegationWrapper(params);
|
|
21142
|
+
},
|
|
21143
|
+
computeCid: (data, codec) => {
|
|
21144
|
+
if (!this.wasmBindings.computeCid) throw new Error("computeCid is unavailable");
|
|
21145
|
+
return this.wasmBindings.computeCid(data, codec);
|
|
21146
|
+
},
|
|
21147
|
+
createKVService: (config) => {
|
|
21148
|
+
graph.assertActive();
|
|
21149
|
+
const service = new KVService2({ prefix: config.pathPrefix?.replace(/\/$/, "") });
|
|
21150
|
+
const context = graph.track(new ServiceContext2({
|
|
21151
|
+
invoke: config.invoke,
|
|
21152
|
+
fetch: config.fetch ?? graph.fetch,
|
|
21153
|
+
hosts: config.hosts,
|
|
21154
|
+
telemetry: this.config.telemetry
|
|
21155
|
+
}));
|
|
21156
|
+
context.setSession(config.session);
|
|
21157
|
+
service.initialize(context);
|
|
21158
|
+
return service;
|
|
21159
|
+
},
|
|
21160
|
+
onRootDelegationNeeded: this.signer ? async (params) => {
|
|
21161
|
+
graph.assertActive();
|
|
21162
|
+
const delegation = await this.createRootDelegationForSharing(
|
|
21163
|
+
params,
|
|
21164
|
+
() => graph.assertActive()
|
|
21165
|
+
);
|
|
21166
|
+
graph.assertActive();
|
|
21167
|
+
return delegation;
|
|
21168
|
+
} : void 0
|
|
20461
21169
|
});
|
|
20462
21170
|
this._spaceService.updateConfig({
|
|
20463
21171
|
sharingService: this._sharingService
|
|
@@ -20524,7 +21232,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20524
21232
|
* with expiry longer than the current session.
|
|
20525
21233
|
* @internal
|
|
20526
21234
|
*/
|
|
20527
|
-
async createOwnerDelegation(params) {
|
|
21235
|
+
async createOwnerDelegation(params, assertActive) {
|
|
21236
|
+
const assertOwnerGraphActive = assertActive ?? this._serviceGraph.assertActive.bind(this._serviceGraph);
|
|
21237
|
+
assertOwnerGraphActive();
|
|
20528
21238
|
if (!params.delegateDid.startsWith("did:key:") || params.actions.length === 0 || params.path.length === 0) {
|
|
20529
21239
|
throw new Error("Owner delegation requires an external did:key audience and bounded capabilities");
|
|
20530
21240
|
}
|
|
@@ -20549,8 +21259,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20549
21259
|
delegateUri: params.delegateDid
|
|
20550
21260
|
});
|
|
20551
21261
|
const signature2 = await this.signer.signMessage(prepared.siwe);
|
|
21262
|
+
assertOwnerGraphActive();
|
|
20552
21263
|
const delegationSession = this.wasmBindings.completeSessionSetup({ ...prepared, signature: signature2 });
|
|
20553
21264
|
const activation = await activateSessionWithHost2(host, delegationSession.delegationHeader);
|
|
21265
|
+
assertOwnerGraphActive();
|
|
20554
21266
|
if (!activation.success) {
|
|
20555
21267
|
throw new Error(`Owner delegation import failed: ${activation.status} ${activation.error ?? ""}`.trim());
|
|
20556
21268
|
}
|
|
@@ -20578,7 +21290,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20578
21290
|
}
|
|
20579
21291
|
};
|
|
20580
21292
|
}
|
|
20581
|
-
async createRootDelegationForSharing(params) {
|
|
21293
|
+
async createRootDelegationForSharing(params, assertActive) {
|
|
20582
21294
|
try {
|
|
20583
21295
|
return (await this.createOwnerDelegation({
|
|
20584
21296
|
delegateDid: params.shareKeyDID,
|
|
@@ -20586,8 +21298,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20586
21298
|
path: params.path,
|
|
20587
21299
|
actions: params.actions,
|
|
20588
21300
|
expiresAt: params.requestedExpiry
|
|
20589
|
-
})).delegation;
|
|
21301
|
+
}, assertActive)).delegation;
|
|
20590
21302
|
} catch {
|
|
21303
|
+
assertActive?.();
|
|
20591
21304
|
return void 0;
|
|
20592
21305
|
}
|
|
20593
21306
|
}
|
|
@@ -20659,13 +21372,13 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20659
21372
|
throw new Error("Not signed in. Call signIn() first.");
|
|
20660
21373
|
}
|
|
20661
21374
|
const sql = new SQLService2({});
|
|
20662
|
-
const spaceScopedContext = new ServiceContext2({
|
|
21375
|
+
const spaceScopedContext = this._serviceGraph.track(new ServiceContext2({
|
|
20663
21376
|
invoke: this._serviceContext.invoke,
|
|
20664
21377
|
invokeAny: this._serviceContext.invokeAny,
|
|
20665
21378
|
fetch: this._serviceContext.fetch,
|
|
20666
21379
|
hosts: this._serviceContext.hosts,
|
|
20667
21380
|
telemetry: this.config.telemetry
|
|
20668
|
-
});
|
|
21381
|
+
}));
|
|
20669
21382
|
spaceScopedContext.setSession({ ...this._serviceContext.session, spaceId });
|
|
20670
21383
|
sql.initialize(spaceScopedContext);
|
|
20671
21384
|
return sql;
|
|
@@ -20689,12 +21402,12 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20689
21402
|
throw new Error("Not signed in. Call signIn() first.");
|
|
20690
21403
|
}
|
|
20691
21404
|
const kv = new KVService2({});
|
|
20692
|
-
const spaceScopedContext = new ServiceContext2({
|
|
21405
|
+
const spaceScopedContext = this._serviceGraph.track(new ServiceContext2({
|
|
20693
21406
|
invoke: this._serviceContext.invoke,
|
|
20694
21407
|
invokeAny: this._serviceContext.invokeAny,
|
|
20695
21408
|
fetch: this._serviceContext.fetch,
|
|
20696
21409
|
hosts: this._serviceContext.hosts
|
|
20697
|
-
});
|
|
21410
|
+
}));
|
|
20698
21411
|
spaceScopedContext.setSession({ ...this._serviceContext.session, spaceId });
|
|
20699
21412
|
kv.initialize(spaceScopedContext);
|
|
20700
21413
|
return kv;
|
|
@@ -20730,19 +21443,11 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20730
21443
|
}
|
|
20731
21444
|
async getEncryptionNetwork(nameOrNetworkId = this.getDefaultEncryptionNetworkId()) {
|
|
20732
21445
|
const networkId = nameOrNetworkId.startsWith("urn:tinycloud:encryption:") ? nameOrNetworkId : this.getDefaultEncryptionNetworkId(nameOrNetworkId);
|
|
20733
|
-
|
|
20734
|
-
|
|
21446
|
+
return this.fetchEncryptionNetworkAt(
|
|
21447
|
+
this.config.host,
|
|
21448
|
+
networkId,
|
|
21449
|
+
globalThis.fetch.bind(globalThis)
|
|
20735
21450
|
);
|
|
20736
|
-
if (response.status === 404) {
|
|
20737
|
-
return null;
|
|
20738
|
-
}
|
|
20739
|
-
if (!response.ok) {
|
|
20740
|
-
throw new Error(
|
|
20741
|
-
`Failed to fetch encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
|
|
20742
|
-
);
|
|
20743
|
-
}
|
|
20744
|
-
const body = await response.json();
|
|
20745
|
-
return "descriptor" in body && body.descriptor ? body.descriptor : body;
|
|
20746
21451
|
}
|
|
20747
21452
|
async createEncryptionNetwork(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
|
|
20748
21453
|
const targetNode = await this.fetchNodeId();
|
|
@@ -20836,6 +21541,38 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20836
21541
|
}
|
|
20837
21542
|
return secrets;
|
|
20838
21543
|
}
|
|
21544
|
+
/**
|
|
21545
|
+
* Read a secret from an explicit target space without requesting or
|
|
21546
|
+
* auto-signing authority. Active runtime delegations are selected by the
|
|
21547
|
+
* normal KV and encryption invocation paths.
|
|
21548
|
+
*/
|
|
21549
|
+
async readSecret(input) {
|
|
21550
|
+
const resolved = resolveSecretPath2(input.name, { scope: input.scope });
|
|
21551
|
+
const targetSpace = input.space.startsWith("tinycloud:") ? input.space : this.ownedSpaceId(input.space);
|
|
21552
|
+
const result = await this.getBaseVault(targetSpace).readNetworkEncrypted(
|
|
21553
|
+
resolved.vaultKey
|
|
21554
|
+
);
|
|
21555
|
+
if (result.status !== "ok") {
|
|
21556
|
+
if (result.status === "permission_required") {
|
|
21557
|
+
const hint = parsePermissionHint(result.hint);
|
|
21558
|
+
if (hint === void 0) return { status: "read_failed" };
|
|
21559
|
+
return {
|
|
21560
|
+
status: "permission_required",
|
|
21561
|
+
hint: {
|
|
21562
|
+
service: hint.service,
|
|
21563
|
+
...hint.space === void 0 ? {} : { space: hint.space },
|
|
21564
|
+
path: hint.path,
|
|
21565
|
+
actions: [...hint.actions]
|
|
21566
|
+
}
|
|
21567
|
+
};
|
|
21568
|
+
}
|
|
21569
|
+
return { status: result.status };
|
|
21570
|
+
}
|
|
21571
|
+
if (!isSecretPayload(result.entry.value)) {
|
|
21572
|
+
return { status: "invalid_payload" };
|
|
21573
|
+
}
|
|
21574
|
+
return { status: "ok", value: result.entry.value.value };
|
|
21575
|
+
}
|
|
20839
21576
|
getBaseSecrets(spaceId) {
|
|
20840
21577
|
if (!this._spaceService) {
|
|
20841
21578
|
throw new Error("Not signed in. Call signIn() first.");
|
|
@@ -20843,16 +21580,29 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20843
21580
|
const resolvedSpace = spaceId.startsWith("tinycloud:") ? spaceId : this.ownedSpaceId(spaceId);
|
|
20844
21581
|
let secrets = this._baseSecrets.get(resolvedSpace);
|
|
20845
21582
|
if (!secrets) {
|
|
20846
|
-
|
|
20847
|
-
const vaultService = this.createVaultService(resolvedSpace, kvService);
|
|
20848
|
-
if (this._serviceContext) {
|
|
20849
|
-
vaultService.initialize(this._serviceContext);
|
|
20850
|
-
}
|
|
20851
|
-
secrets = new SecretsService(() => vaultService);
|
|
21583
|
+
secrets = new SecretsService(() => this.getBaseVault(resolvedSpace));
|
|
20852
21584
|
this._baseSecrets.set(resolvedSpace, secrets);
|
|
20853
21585
|
}
|
|
20854
21586
|
return secrets;
|
|
20855
21587
|
}
|
|
21588
|
+
getBaseVault(spaceId) {
|
|
21589
|
+
if (!this._spaceService) {
|
|
21590
|
+
throw new Error("Not signed in. Call signIn() first.");
|
|
21591
|
+
}
|
|
21592
|
+
const resolvedSpace = spaceId.startsWith("tinycloud:") ? spaceId : this.ownedSpaceId(spaceId);
|
|
21593
|
+
let vault = this._baseVaults.get(resolvedSpace);
|
|
21594
|
+
if (!vault) {
|
|
21595
|
+
vault = this.createVaultService(
|
|
21596
|
+
resolvedSpace,
|
|
21597
|
+
this.createSpaceScopedKVService(resolvedSpace)
|
|
21598
|
+
);
|
|
21599
|
+
if (this._serviceContext) {
|
|
21600
|
+
vault.initialize(this._serviceContext);
|
|
21601
|
+
}
|
|
21602
|
+
this._baseVaults.set(resolvedSpace, vault);
|
|
21603
|
+
}
|
|
21604
|
+
return vault;
|
|
21605
|
+
}
|
|
20856
21606
|
/**
|
|
20857
21607
|
* Hooks write stream subscription API.
|
|
20858
21608
|
*/
|
|
@@ -20957,6 +21707,63 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20957
21707
|
(grant) => grant.delegation
|
|
20958
21708
|
);
|
|
20959
21709
|
}
|
|
21710
|
+
/**
|
|
21711
|
+
* Return the effective capabilities of installed runtime grants.
|
|
21712
|
+
*
|
|
21713
|
+
* The result is projected from the SDK's activated, expiry-pruned grant
|
|
21714
|
+
* operations rather than from PortableDelegation transport metadata. Base
|
|
21715
|
+
* session authority is intentionally omitted; callers that need the full
|
|
21716
|
+
* live authority must combine this with {@link getVerifiedSessionCapabilities}.
|
|
21717
|
+
*/
|
|
21718
|
+
getEffectiveRuntimePermissionEntries() {
|
|
21719
|
+
const session = this.currentTinyCloudSession();
|
|
21720
|
+
if (!session) return [];
|
|
21721
|
+
this.pruneExpiredRuntimePermissionGrants();
|
|
21722
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
21723
|
+
for (const grant of this.runtimePermissionGrants) {
|
|
21724
|
+
if (grant.provenance === "primary") continue;
|
|
21725
|
+
for (const operation of grant.operations) {
|
|
21726
|
+
const service = SERVICE_SHORT_TO_LONG[operation.service];
|
|
21727
|
+
if (service === void 0 || typeof operation.path !== "string" || typeof operation.action !== "string") {
|
|
21728
|
+
continue;
|
|
21729
|
+
}
|
|
21730
|
+
let space;
|
|
21731
|
+
if (service !== "tinycloud.encryption") {
|
|
21732
|
+
if (typeof operation.spaceId !== "string") continue;
|
|
21733
|
+
try {
|
|
21734
|
+
space = this.resolvePermissionSpace(operation.spaceId, session);
|
|
21735
|
+
} catch {
|
|
21736
|
+
continue;
|
|
21737
|
+
}
|
|
21738
|
+
}
|
|
21739
|
+
const caveats = operation.caveats === void 0 || operation.caveats.length === 0 ? void 0 : cloneRecapCaveats(operation.caveats);
|
|
21740
|
+
const identity = JSON.stringify({
|
|
21741
|
+
service,
|
|
21742
|
+
...space === void 0 ? {} : { space },
|
|
21743
|
+
path: operation.path,
|
|
21744
|
+
caveats: canonicalizeRecapCaveats(caveats)
|
|
21745
|
+
});
|
|
21746
|
+
const existing = grouped.get(identity);
|
|
21747
|
+
if (existing === void 0) {
|
|
21748
|
+
grouped.set(identity, {
|
|
21749
|
+
service,
|
|
21750
|
+
...space === void 0 ? {} : { space },
|
|
21751
|
+
path: operation.path,
|
|
21752
|
+
actions: [operation.action],
|
|
21753
|
+
...caveats === void 0 ? {} : { caveats }
|
|
21754
|
+
});
|
|
21755
|
+
continue;
|
|
21756
|
+
}
|
|
21757
|
+
if (!existing.actions.includes(operation.action)) {
|
|
21758
|
+
existing.actions.push(operation.action);
|
|
21759
|
+
existing.actions.sort();
|
|
21760
|
+
}
|
|
21761
|
+
}
|
|
21762
|
+
}
|
|
21763
|
+
return [...grouped.values()].sort(
|
|
21764
|
+
(left, right) => left.service.localeCompare(right.service) || (left.space ?? "").localeCompare(right.space ?? "") || left.path.localeCompare(right.path) || left.actions.join("\0").localeCompare(right.actions.join("\0"))
|
|
21765
|
+
);
|
|
21766
|
+
}
|
|
20960
21767
|
/**
|
|
20961
21768
|
* Install a portable runtime permission delegation into this SDK instance so
|
|
20962
21769
|
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
@@ -21025,6 +21832,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21025
21832
|
"grantRuntimePermissions requires wallet mode with a signer or privateKey."
|
|
21026
21833
|
);
|
|
21027
21834
|
}
|
|
21835
|
+
this.assertDelegationCaveatsPreservable(expanded);
|
|
21028
21836
|
const rawEntries = expanded.filter(
|
|
21029
21837
|
(entry) => this.isEncryptionPermissionEntry(entry)
|
|
21030
21838
|
);
|
|
@@ -21286,12 +22094,12 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21286
22094
|
}
|
|
21287
22095
|
if (this._serviceContext) {
|
|
21288
22096
|
const publicKV = new KVService2({ prefix: "" });
|
|
21289
|
-
const publicContext = new ServiceContext2({
|
|
21290
|
-
invoke: this.
|
|
21291
|
-
fetch: this.
|
|
22097
|
+
const publicContext = this._serviceGraph.track(new ServiceContext2({
|
|
22098
|
+
invoke: this._serviceGraph.invoke,
|
|
22099
|
+
fetch: this._serviceGraph.fetch,
|
|
21292
22100
|
hosts: this._serviceContext.hosts,
|
|
21293
22101
|
telemetry: this.config.telemetry
|
|
21294
|
-
});
|
|
22102
|
+
}));
|
|
21295
22103
|
publicContext.setSession({
|
|
21296
22104
|
delegationHeader: delegationSession.delegationHeader,
|
|
21297
22105
|
delegationCid: delegationSession.delegationCid,
|
|
@@ -21358,13 +22166,14 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21358
22166
|
async getDelegationStatus(cid) {
|
|
21359
22167
|
return this.delegationManager.status(cid);
|
|
21360
22168
|
}
|
|
21361
|
-
/** Compute the canonical CID of a compact delegation authorization. */
|
|
22169
|
+
/** Compute the canonical CID of a compact UCAN or DAG-CBOR delegation authorization. */
|
|
21362
22170
|
computeDelegationCid(authorization) {
|
|
21363
22171
|
if (!authorization || !this.wasmBindings.computeCid) {
|
|
21364
22172
|
throw new Error("Delegation CID computation is unavailable");
|
|
21365
22173
|
}
|
|
22174
|
+
const compact = authorization.replace(/^Bearer /i, "");
|
|
21366
22175
|
return this.wasmBindings.computeCid(
|
|
21367
|
-
new TextEncoder().encode(authorization),
|
|
22176
|
+
compact.includes(".") ? new TextEncoder().encode(compact) : decodeAuthorizationBytes(authorization),
|
|
21368
22177
|
0x55n
|
|
21369
22178
|
);
|
|
21370
22179
|
}
|
|
@@ -21456,6 +22265,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21456
22265
|
"delegateTo with forceWalletSign=true supports at most one PermissionEntry. Multi-entry requests must go through the session-key UCAN path (drop forceWalletSign) or the legacy createDelegation method."
|
|
21457
22266
|
);
|
|
21458
22267
|
}
|
|
22268
|
+
this.assertDelegationCaveatsPreservable(expandedEntries);
|
|
21459
22269
|
const delegation2 = await this.createDelegationLegacyWalletPath(
|
|
21460
22270
|
did,
|
|
21461
22271
|
expandedEntries[0],
|
|
@@ -21464,13 +22274,14 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21464
22274
|
return { delegation: delegation2, prompted: true };
|
|
21465
22275
|
}
|
|
21466
22276
|
const granted = parseRecapCapabilities(
|
|
21467
|
-
(siwe) => this.
|
|
22277
|
+
(siwe) => this.parseRecapWithCaveats(siwe),
|
|
21468
22278
|
session.siwe
|
|
21469
22279
|
);
|
|
21470
22280
|
const { subset, missing } = isCapabilitySubset(expandedEntries, granted);
|
|
21471
22281
|
if (!subset) {
|
|
22282
|
+
const runtimeOperations = this.permissionEntriesToOperations(expandedEntries, session);
|
|
21472
22283
|
const runtimeGrant = this.findGrantForOperations(
|
|
21473
|
-
|
|
22284
|
+
runtimeOperations,
|
|
21474
22285
|
{ excludePrimary: true }
|
|
21475
22286
|
);
|
|
21476
22287
|
if (runtimeGrant) {
|
|
@@ -21483,7 +22294,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21483
22294
|
did,
|
|
21484
22295
|
expandedEntries,
|
|
21485
22296
|
runtimeExpiration,
|
|
21486
|
-
runtimeGrant
|
|
22297
|
+
runtimeGrant,
|
|
22298
|
+
runtimeOperations
|
|
21487
22299
|
);
|
|
21488
22300
|
return { delegation: delegation2, prompted: false };
|
|
21489
22301
|
}
|
|
@@ -21553,6 +22365,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21553
22365
|
"createDelegationViaWasmPath requires a non-empty entries array"
|
|
21554
22366
|
);
|
|
21555
22367
|
}
|
|
22368
|
+
this.assertDelegationCaveatsPreservable(entries);
|
|
21556
22369
|
const resolvedSpaces = /* @__PURE__ */ new Set();
|
|
21557
22370
|
for (const entry of entries) {
|
|
21558
22371
|
if (this.isEncryptionPermissionEntry(entry)) {
|
|
@@ -21633,7 +22446,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21633
22446
|
host: this.config.host
|
|
21634
22447
|
};
|
|
21635
22448
|
}
|
|
21636
|
-
async createDelegationViaRuntimeGrant(did, entries, expirationTime, grant) {
|
|
22449
|
+
async createDelegationViaRuntimeGrant(did, entries, expirationTime, grant, requestedOperations) {
|
|
22450
|
+
this.assertRuntimeGrantCaveatsPreservable(entries, requestedOperations, grant);
|
|
22451
|
+
this.assertDelegationCaveatsPreservable(entries);
|
|
21637
22452
|
const result = this.createDelegationWrapper({
|
|
21638
22453
|
session: grant.session,
|
|
21639
22454
|
delegateDID: did,
|
|
@@ -21687,6 +22502,74 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21687
22502
|
expandPermissionEntries(permissions) {
|
|
21688
22503
|
return expandPermissionEntriesCore(permissions);
|
|
21689
22504
|
}
|
|
22505
|
+
/**
|
|
22506
|
+
* The current WASM child-delegation APIs accept action-only maps. Refuse any
|
|
22507
|
+
* constrained branch rather than signing a broader action-only child UCAN.
|
|
22508
|
+
*/
|
|
22509
|
+
assertDelegationCaveatsPreservable(entries) {
|
|
22510
|
+
const caveated = entries.filter(
|
|
22511
|
+
(entry) => !recapCaveatsEqual(entry.caveats, void 0)
|
|
22512
|
+
);
|
|
22513
|
+
if (caveated.length > 0) {
|
|
22514
|
+
throw new CaveatedDelegationUnsupportedError(caveated);
|
|
22515
|
+
}
|
|
22516
|
+
}
|
|
22517
|
+
/**
|
|
22518
|
+
* Runtime grant selection intentionally allows an uncaveated operation to
|
|
22519
|
+
* select a caveated grant so invocation can restore the signed caveat before
|
|
22520
|
+
* crossing the caveat-aware WASM boundary. Child delegation cannot do that:
|
|
22521
|
+
* it only accepts action maps, so reject the selected constrained branch.
|
|
22522
|
+
*/
|
|
22523
|
+
assertRuntimeGrantCaveatsPreservable(entries, requestedOperations, grant) {
|
|
22524
|
+
let operationIndex = 0;
|
|
22525
|
+
const caveated = [];
|
|
22526
|
+
for (const entry of entries) {
|
|
22527
|
+
for (const action of entry.actions) {
|
|
22528
|
+
const requested = requestedOperations[operationIndex++];
|
|
22529
|
+
if (!requested) continue;
|
|
22530
|
+
const constrained = grant.operations.find(
|
|
22531
|
+
(granted) => this.operationCovers(granted, requested) && !recapCaveatsEqual(granted.caveats, void 0)
|
|
22532
|
+
);
|
|
22533
|
+
if (constrained) {
|
|
22534
|
+
caveated.push({
|
|
22535
|
+
...entry,
|
|
22536
|
+
actions: [action],
|
|
22537
|
+
caveats: cloneRecapCaveats(constrained.caveats)
|
|
22538
|
+
});
|
|
22539
|
+
}
|
|
22540
|
+
}
|
|
22541
|
+
}
|
|
22542
|
+
if (caveated.length > 0) {
|
|
22543
|
+
throw new CaveatedDelegationUnsupportedError(caveated);
|
|
22544
|
+
}
|
|
22545
|
+
}
|
|
22546
|
+
/** Reject caveated parent branches before action-only child signing. */
|
|
22547
|
+
assertPortableDelegationCaveatsPreservable(delegation) {
|
|
22548
|
+
const entries = [];
|
|
22549
|
+
const add3 = (service, space, path, actions, caveats) => {
|
|
22550
|
+
if (recapCaveatsEqual(caveats, void 0)) return;
|
|
22551
|
+
entries.push({
|
|
22552
|
+
service: service.startsWith("tinycloud.") ? service : `tinycloud.${service}`,
|
|
22553
|
+
space,
|
|
22554
|
+
path,
|
|
22555
|
+
actions: [...actions],
|
|
22556
|
+
caveats: cloneRecapCaveats(caveats)
|
|
22557
|
+
});
|
|
22558
|
+
};
|
|
22559
|
+
add3(
|
|
22560
|
+
delegation.actions[0]?.split("/", 1)[0] ?? "tinycloud.unknown",
|
|
22561
|
+
delegation.spaceId,
|
|
22562
|
+
delegation.path,
|
|
22563
|
+
delegation.actions,
|
|
22564
|
+
delegation.caveats
|
|
22565
|
+
);
|
|
22566
|
+
for (const resource of delegation.resources ?? []) {
|
|
22567
|
+
add3(resource.service, resource.space, resource.path, resource.actions, resource.caveats);
|
|
22568
|
+
}
|
|
22569
|
+
if (entries.length > 0) {
|
|
22570
|
+
throw new CaveatedDelegationUnsupportedError(entries);
|
|
22571
|
+
}
|
|
22572
|
+
}
|
|
21690
22573
|
shortServiceName(service) {
|
|
21691
22574
|
const short = SERVICE_LONG_TO_SHORT[service];
|
|
21692
22575
|
if (short === void 0) {
|
|
@@ -21741,14 +22624,15 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21741
22624
|
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
|
|
21742
22625
|
service,
|
|
21743
22626
|
path: entry.path,
|
|
21744
|
-
action
|
|
22627
|
+
action,
|
|
22628
|
+
...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
|
|
21745
22629
|
}));
|
|
21746
22630
|
});
|
|
21747
22631
|
}
|
|
21748
22632
|
sessionCoversPermissionEntries(session, entries) {
|
|
21749
22633
|
try {
|
|
21750
22634
|
const granted = parseRecapCapabilities(
|
|
21751
|
-
(siwe) => this.
|
|
22635
|
+
(siwe) => this.parseRecapWithCaveats(siwe),
|
|
21752
22636
|
session.siwe
|
|
21753
22637
|
);
|
|
21754
22638
|
return isCapabilitySubset(entries, granted).subset;
|
|
@@ -21764,7 +22648,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21764
22648
|
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
|
|
21765
22649
|
service,
|
|
21766
22650
|
path: entry.path,
|
|
21767
|
-
action
|
|
22651
|
+
action,
|
|
22652
|
+
...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
|
|
21768
22653
|
}));
|
|
21769
22654
|
});
|
|
21770
22655
|
}
|
|
@@ -21840,7 +22725,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21840
22725
|
service: this.shortServiceName(entry.service),
|
|
21841
22726
|
space: this.isEncryptionPermissionEntry(entry) ? "encryption" : spaceId,
|
|
21842
22727
|
path: entry.path,
|
|
21843
|
-
actions: [...entry.actions]
|
|
22728
|
+
actions: [...entry.actions],
|
|
22729
|
+
...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
|
|
21844
22730
|
}));
|
|
21845
22731
|
}
|
|
21846
22732
|
operationsFromDelegation(delegation) {
|
|
@@ -21851,7 +22737,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21851
22737
|
...this.isEncryptionNetworkOperation(service, resource.path) ? { resource: resource.path } : { spaceId: resource.space },
|
|
21852
22738
|
service,
|
|
21853
22739
|
path: resource.path,
|
|
21854
|
-
action
|
|
22740
|
+
action,
|
|
22741
|
+
caveats: cloneRecapCaveats(resource.caveats)
|
|
21855
22742
|
}));
|
|
21856
22743
|
});
|
|
21857
22744
|
}
|
|
@@ -21867,7 +22754,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21867
22754
|
service,
|
|
21868
22755
|
space: delegation.spaceId,
|
|
21869
22756
|
path: delegation.path,
|
|
21870
|
-
actions
|
|
22757
|
+
actions,
|
|
22758
|
+
...delegation.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(delegation.caveats) }
|
|
21871
22759
|
}));
|
|
21872
22760
|
}
|
|
21873
22761
|
/**
|
|
@@ -21968,6 +22856,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21968
22856
|
if (granted.service !== requested.service || !this.actionContains(granted.action, requested.action)) {
|
|
21969
22857
|
return false;
|
|
21970
22858
|
}
|
|
22859
|
+
if (requested.caveats !== void 0 && !recapCaveatsEqual(granted.caveats, requested.caveats)) {
|
|
22860
|
+
return false;
|
|
22861
|
+
}
|
|
21971
22862
|
if (granted.resource !== void 0 || requested.resource !== void 0) {
|
|
21972
22863
|
return granted.resource !== void 0 && requested.resource !== void 0 && granted.resource === requested.resource && this.pathContains(granted.path, requested.path);
|
|
21973
22864
|
}
|
|
@@ -22002,6 +22893,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22002
22893
|
invocationServiceName(service) {
|
|
22003
22894
|
return service.startsWith("tinycloud.") ? this.shortServiceName(service) : service;
|
|
22004
22895
|
}
|
|
22896
|
+
/** Prefer the v2 caveat-preserving parser while retaining old custom WASM. */
|
|
22897
|
+
parseRecapWithCaveats(siwe) {
|
|
22898
|
+
return this.wasmBindings.parseVerifiedRecapFromSiwe?.(siwe) ?? this.wasmBindings.parseRecapFromSiwe(siwe);
|
|
22899
|
+
}
|
|
22005
22900
|
isEncryptionNetworkOperation(service, path) {
|
|
22006
22901
|
return service === "encryption" && path.startsWith("urn:tinycloud:encryption:");
|
|
22007
22902
|
}
|
|
@@ -22012,7 +22907,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22012
22907
|
resource: entry.resource,
|
|
22013
22908
|
service,
|
|
22014
22909
|
path: entry.path,
|
|
22015
|
-
action: entry.action
|
|
22910
|
+
action: entry.action,
|
|
22911
|
+
...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
|
|
22016
22912
|
};
|
|
22017
22913
|
}
|
|
22018
22914
|
if (this.isEncryptionNetworkOperation(service, entry.path)) {
|
|
@@ -22020,7 +22916,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22020
22916
|
resource: entry.path,
|
|
22021
22917
|
service,
|
|
22022
22918
|
path: entry.path,
|
|
22023
|
-
action: entry.action
|
|
22919
|
+
action: entry.action,
|
|
22920
|
+
...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
|
|
22024
22921
|
};
|
|
22025
22922
|
}
|
|
22026
22923
|
if (typeof entry.spaceId === "string") {
|
|
@@ -22028,7 +22925,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22028
22925
|
spaceId: entry.spaceId,
|
|
22029
22926
|
service,
|
|
22030
22927
|
path: entry.path,
|
|
22031
|
-
action: entry.action
|
|
22928
|
+
action: entry.action,
|
|
22929
|
+
...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
|
|
22032
22930
|
};
|
|
22033
22931
|
}
|
|
22034
22932
|
return void 0;
|
|
@@ -22368,6 +23266,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22368
23266
|
* @returns A portable delegation for the sub-delegate
|
|
22369
23267
|
*/
|
|
22370
23268
|
async createSubDelegation(parentDelegation, params) {
|
|
23269
|
+
this.assertPortableDelegationCaveatsPreservable(parentDelegation);
|
|
22371
23270
|
if (!this.signer) {
|
|
22372
23271
|
throw new Error("Cannot createSubDelegation() in session-only mode. Requires wallet mode.");
|
|
22373
23272
|
}
|
|
@@ -22484,7 +23383,7 @@ import {
|
|
|
22484
23383
|
parsePkhDid,
|
|
22485
23384
|
pkhDid as pkhDid3,
|
|
22486
23385
|
principalDid,
|
|
22487
|
-
principalDidEquals as
|
|
23386
|
+
principalDidEquals as principalDidEquals4,
|
|
22488
23387
|
parseCanonicalNetworkId,
|
|
22489
23388
|
TinyCloudDebugLogger,
|
|
22490
23389
|
tinyCloudDebugLogger,
|
|
@@ -22622,6 +23521,7 @@ import {
|
|
|
22622
23521
|
DEFAULT_MANIFEST_SPACE as DEFAULT_MANIFEST_SPACE2,
|
|
22623
23522
|
DEFAULT_MANIFEST_VERSION,
|
|
22624
23523
|
VAULT_PERMISSION_SERVICE,
|
|
23524
|
+
CaveatedDelegationUnsupportedError as CaveatedDelegationUnsupportedError2,
|
|
22625
23525
|
PermissionNotInManifestError as PermissionNotInManifestError2,
|
|
22626
23526
|
SessionExpiredError as SessionExpiredError2,
|
|
22627
23527
|
ManifestValidationError,
|
|
@@ -22638,6 +23538,283 @@ import {
|
|
|
22638
23538
|
} from "@tinycloud/sdk-core";
|
|
22639
23539
|
|
|
22640
23540
|
// src/delegation.ts
|
|
23541
|
+
import {
|
|
23542
|
+
canonicalizeRecapCaveats as canonicalizeRecapCaveats2,
|
|
23543
|
+
principalDidEquals as principalDidEquals3
|
|
23544
|
+
} from "@tinycloud/sdk-core";
|
|
23545
|
+
var SERVICE_BY_SHORT_NAME = {
|
|
23546
|
+
kv: "tinycloud.kv",
|
|
23547
|
+
sql: "tinycloud.sql",
|
|
23548
|
+
duckdb: "tinycloud.duckdb",
|
|
23549
|
+
hooks: "tinycloud.hooks"
|
|
23550
|
+
};
|
|
23551
|
+
function authorizationWithoutBearer(authorization) {
|
|
23552
|
+
return authorization.replace(/^Bearer /i, "");
|
|
23553
|
+
}
|
|
23554
|
+
function normalizedHost(host) {
|
|
23555
|
+
if (typeof host !== "string" || host.length === 0) {
|
|
23556
|
+
throw new Error("Validated runtime delegation requires a non-empty host.");
|
|
23557
|
+
}
|
|
23558
|
+
return host.replace(/\/+$/, "");
|
|
23559
|
+
}
|
|
23560
|
+
function didPrincipalsMatch(left, right) {
|
|
23561
|
+
try {
|
|
23562
|
+
return principalDidEquals3(left, right);
|
|
23563
|
+
} catch {
|
|
23564
|
+
return left === right;
|
|
23565
|
+
}
|
|
23566
|
+
}
|
|
23567
|
+
function compactUcanPayload(authorization) {
|
|
23568
|
+
const compact = authorizationWithoutBearer(authorization);
|
|
23569
|
+
const parts = compact.split(".");
|
|
23570
|
+
if (parts.length !== 3 || parts.some((part) => part.length === 0)) {
|
|
23571
|
+
throw new Error(
|
|
23572
|
+
"Validated runtime delegation requires a compact UCAN authorization so its signed authority can be derived."
|
|
23573
|
+
);
|
|
23574
|
+
}
|
|
23575
|
+
try {
|
|
23576
|
+
const payload = JSON.parse(
|
|
23577
|
+
Buffer.from(parts[1], "base64url").toString("utf8")
|
|
23578
|
+
);
|
|
23579
|
+
if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
|
|
23580
|
+
throw new Error("payload is not an object");
|
|
23581
|
+
}
|
|
23582
|
+
return payload;
|
|
23583
|
+
} catch {
|
|
23584
|
+
throw new Error(
|
|
23585
|
+
"Validated runtime delegation authorization has an invalid compact UCAN payload."
|
|
23586
|
+
);
|
|
23587
|
+
}
|
|
23588
|
+
}
|
|
23589
|
+
function canonicalPermissionEntries(permissions) {
|
|
23590
|
+
return permissions.map((permission) => ({
|
|
23591
|
+
...permission,
|
|
23592
|
+
actions: [...permission.actions].sort()
|
|
23593
|
+
})).sort(
|
|
23594
|
+
(left, right) => left.service.localeCompare(right.service) || (left.space ?? "").localeCompare(right.space ?? "") || left.path.localeCompare(right.path) || left.actions.join("\0").localeCompare(right.actions.join("\0")) || canonicalizeRecapCaveats2(left.caveats).localeCompare(canonicalizeRecapCaveats2(right.caveats))
|
|
23595
|
+
);
|
|
23596
|
+
}
|
|
23597
|
+
function signedCaveats(value) {
|
|
23598
|
+
if (!Array.isArray(value) || value.some(
|
|
23599
|
+
(caveat) => caveat === null || typeof caveat !== "object" || Array.isArray(caveat)
|
|
23600
|
+
)) {
|
|
23601
|
+
throw new Error("Validated runtime delegation has malformed signed ReCap caveats.");
|
|
23602
|
+
}
|
|
23603
|
+
const cloned = JSON.parse(JSON.stringify(value));
|
|
23604
|
+
return canonicalizeRecapCaveats2(cloned) === canonicalizeRecapCaveats2(void 0) ? void 0 : cloned;
|
|
23605
|
+
}
|
|
23606
|
+
function groupedSignedEntries(service, space, path, abilities) {
|
|
23607
|
+
const groups = /* @__PURE__ */ new Map();
|
|
23608
|
+
for (const [action, rawCaveats] of Object.entries(abilities)) {
|
|
23609
|
+
const caveats = signedCaveats(rawCaveats);
|
|
23610
|
+
const identity = canonicalizeRecapCaveats2(caveats);
|
|
23611
|
+
const group = groups.get(identity) ?? { actions: [], caveats };
|
|
23612
|
+
group.actions.push(action);
|
|
23613
|
+
groups.set(identity, group);
|
|
23614
|
+
}
|
|
23615
|
+
return [...groups.values()].map((group) => ({
|
|
23616
|
+
service,
|
|
23617
|
+
...space === void 0 ? {} : { space },
|
|
23618
|
+
path,
|
|
23619
|
+
actions: group.actions.sort(),
|
|
23620
|
+
...group.caveats === void 0 ? {} : { caveats: group.caveats }
|
|
23621
|
+
}));
|
|
23622
|
+
}
|
|
23623
|
+
function signedAuthorityFromCompactUcan(authorization) {
|
|
23624
|
+
const payload = compactUcanPayload(authorization);
|
|
23625
|
+
if (typeof payload.aud !== "string" || payload.aud.length === 0) {
|
|
23626
|
+
throw new Error("Validated runtime delegation is missing a signed audience.");
|
|
23627
|
+
}
|
|
23628
|
+
if (typeof payload.exp !== "number" || !Number.isFinite(payload.exp)) {
|
|
23629
|
+
throw new Error("Validated runtime delegation is missing a signed expiry.");
|
|
23630
|
+
}
|
|
23631
|
+
const expiry = new Date(payload.exp * 1e3);
|
|
23632
|
+
if (Number.isNaN(expiry.getTime())) {
|
|
23633
|
+
throw new Error("Validated runtime delegation has an invalid signed expiry.");
|
|
23634
|
+
}
|
|
23635
|
+
if (payload.att === null || typeof payload.att !== "object" || Array.isArray(payload.att)) {
|
|
23636
|
+
throw new Error("Validated runtime delegation is missing a signed attenuation.");
|
|
23637
|
+
}
|
|
23638
|
+
const permissions = [];
|
|
23639
|
+
for (const [resource, abilities] of Object.entries(payload.att)) {
|
|
23640
|
+
if (abilities === null || typeof abilities !== "object" || Array.isArray(abilities)) {
|
|
23641
|
+
throw new Error("Validated runtime delegation has an invalid signed attenuation.");
|
|
23642
|
+
}
|
|
23643
|
+
const abilityEntries = Object.entries(abilities);
|
|
23644
|
+
if (abilityEntries.length === 0) {
|
|
23645
|
+
throw new Error("Validated runtime delegation has an empty signed capability.");
|
|
23646
|
+
}
|
|
23647
|
+
if (resource.startsWith("urn:tinycloud:encryption:")) {
|
|
23648
|
+
permissions.push(...groupedSignedEntries(
|
|
23649
|
+
"tinycloud.encryption",
|
|
23650
|
+
void 0,
|
|
23651
|
+
resource,
|
|
23652
|
+
abilities
|
|
23653
|
+
));
|
|
23654
|
+
continue;
|
|
23655
|
+
}
|
|
23656
|
+
const match = /^(tinycloud:[^/]+)\/(kv|sql|duckdb|hooks)\/(.*)$/.exec(resource);
|
|
23657
|
+
if (!match) {
|
|
23658
|
+
throw new Error(
|
|
23659
|
+
`Validated runtime delegation has an unsupported signed resource '${resource}'.`
|
|
23660
|
+
);
|
|
23661
|
+
}
|
|
23662
|
+
const [, space, shortService, path] = match;
|
|
23663
|
+
const service = SERVICE_BY_SHORT_NAME[shortService];
|
|
23664
|
+
if (service === void 0) {
|
|
23665
|
+
throw new Error(
|
|
23666
|
+
`Validated runtime delegation has an unsupported signed service '${shortService}'.`
|
|
23667
|
+
);
|
|
23668
|
+
}
|
|
23669
|
+
permissions.push(...groupedSignedEntries(
|
|
23670
|
+
service,
|
|
23671
|
+
space,
|
|
23672
|
+
path,
|
|
23673
|
+
abilities
|
|
23674
|
+
));
|
|
23675
|
+
}
|
|
23676
|
+
if (permissions.length === 0) {
|
|
23677
|
+
throw new Error("Validated runtime delegation has no signed capabilities.");
|
|
23678
|
+
}
|
|
23679
|
+
const canonicalPermissions = canonicalPermissionEntries(permissions);
|
|
23680
|
+
const canonicalResources = canonicalPermissions.map((permission) => ({
|
|
23681
|
+
service: permission.service === "tinycloud.encryption" ? "encryption" : permission.service.slice("tinycloud.".length),
|
|
23682
|
+
space: permission.service === "tinycloud.encryption" ? "encryption" : permission.space,
|
|
23683
|
+
path: permission.path,
|
|
23684
|
+
actions: [...permission.actions],
|
|
23685
|
+
...permission.caveats === void 0 ? {} : { caveats: permission.caveats }
|
|
23686
|
+
}));
|
|
23687
|
+
return {
|
|
23688
|
+
audience: payload.aud,
|
|
23689
|
+
expiry,
|
|
23690
|
+
permissions: canonicalPermissions,
|
|
23691
|
+
resources: canonicalResources
|
|
23692
|
+
};
|
|
23693
|
+
}
|
|
23694
|
+
function canonicalResourcesFromPortableDelegation(delegation) {
|
|
23695
|
+
if (delegation.resources !== void 0 && delegation.resources.length > 0) {
|
|
23696
|
+
return delegation.resources.map((resource) => ({ ...resource, actions: [...resource.actions].sort() })).sort(
|
|
23697
|
+
(left, right) => left.service.localeCompare(right.service) || left.space.localeCompare(right.space) || left.path.localeCompare(right.path) || left.actions.join("\0").localeCompare(right.actions.join("\0")) || canonicalizeRecapCaveats2(left.caveats).localeCompare(canonicalizeRecapCaveats2(right.caveats))
|
|
23698
|
+
);
|
|
23699
|
+
}
|
|
23700
|
+
const byService = /* @__PURE__ */ new Map();
|
|
23701
|
+
for (const action of delegation.actions) {
|
|
23702
|
+
const [service] = action.split("/", 1);
|
|
23703
|
+
const shortService = service.replace(/^tinycloud\./, "");
|
|
23704
|
+
const values = byService.get(shortService) ?? [];
|
|
23705
|
+
values.push(action);
|
|
23706
|
+
byService.set(shortService, values);
|
|
23707
|
+
}
|
|
23708
|
+
return [...byService.entries()].map(([service, actions]) => ({
|
|
23709
|
+
service,
|
|
23710
|
+
space: service === "encryption" ? "encryption" : delegation.spaceId,
|
|
23711
|
+
path: delegation.path,
|
|
23712
|
+
actions: [...new Set(actions)].sort(),
|
|
23713
|
+
...delegation.caveats === void 0 ? {} : { caveats: delegation.caveats }
|
|
23714
|
+
})).sort(
|
|
23715
|
+
(left, right) => left.service.localeCompare(right.service) || left.space.localeCompare(right.space) || left.path.localeCompare(right.path)
|
|
23716
|
+
);
|
|
23717
|
+
}
|
|
23718
|
+
function resourcesMatch(actual, expected) {
|
|
23719
|
+
const comparable = (resource) => ({
|
|
23720
|
+
...resource,
|
|
23721
|
+
caveats: canonicalizeRecapCaveats2(resource.caveats)
|
|
23722
|
+
});
|
|
23723
|
+
return JSON.stringify(actual.map(comparable)) === JSON.stringify(expected.map(comparable));
|
|
23724
|
+
}
|
|
23725
|
+
async function activateValidatedRuntimeDelegation(node, delegation, options) {
|
|
23726
|
+
const host = normalizedHost(options.host);
|
|
23727
|
+
if (delegation.host !== void 0 && normalizedHost(delegation.host) !== host) {
|
|
23728
|
+
throw new Error(
|
|
23729
|
+
`Runtime delegation host '${delegation.host}' does not match expected host '${options.host}'.`
|
|
23730
|
+
);
|
|
23731
|
+
}
|
|
23732
|
+
if (!(delegation.expiry instanceof Date) || Number.isNaN(delegation.expiry.getTime())) {
|
|
23733
|
+
throw new Error("Runtime delegation has an invalid expiry.");
|
|
23734
|
+
}
|
|
23735
|
+
if (delegation.expiry.getTime() <= Date.now()) {
|
|
23736
|
+
throw new Error("Runtime delegation is expired.");
|
|
23737
|
+
}
|
|
23738
|
+
const authorization = delegation.delegationHeader?.Authorization;
|
|
23739
|
+
if (typeof authorization !== "string" || authorization.length === 0) {
|
|
23740
|
+
throw new Error("Runtime delegation is missing authorization bytes.");
|
|
23741
|
+
}
|
|
23742
|
+
const cid = node.computeDelegationCid(authorization);
|
|
23743
|
+
if (cid !== delegation.cid) {
|
|
23744
|
+
throw new Error("Runtime delegation CID does not match authorization bytes.");
|
|
23745
|
+
}
|
|
23746
|
+
const signed2 = signedAuthorityFromCompactUcan(authorization);
|
|
23747
|
+
if (signed2.expiry.getTime() <= Date.now()) {
|
|
23748
|
+
throw new Error("Runtime delegation is expired.");
|
|
23749
|
+
}
|
|
23750
|
+
if (delegation.expiry.getTime() !== signed2.expiry.getTime()) {
|
|
23751
|
+
throw new Error("Runtime delegation expiry does not match signed authority.");
|
|
23752
|
+
}
|
|
23753
|
+
if (!didPrincipalsMatch(signed2.audience, node.sessionDid)) {
|
|
23754
|
+
throw new Error(
|
|
23755
|
+
`Runtime delegation targets ${signed2.audience} but this session is ${node.sessionDid}.`
|
|
23756
|
+
);
|
|
23757
|
+
}
|
|
23758
|
+
if (!didPrincipalsMatch(delegation.delegateDID, signed2.audience)) {
|
|
23759
|
+
throw new Error("Runtime delegation audience does not match signed authority.");
|
|
23760
|
+
}
|
|
23761
|
+
const declaredResources = canonicalResourcesFromPortableDelegation(delegation);
|
|
23762
|
+
if (!resourcesMatch(declaredResources, signed2.resources)) {
|
|
23763
|
+
throw new Error("Runtime delegation resources do not match signed authority.");
|
|
23764
|
+
}
|
|
23765
|
+
const previouslyInstalled = node.getRuntimePermissionDelegations().find((candidate) => candidate.cid === cid);
|
|
23766
|
+
if (previouslyInstalled !== void 0 && previouslyInstalled.delegationHeader.Authorization !== authorization) {
|
|
23767
|
+
throw new Error(
|
|
23768
|
+
"A different authorization is already installed for this runtime delegation CID."
|
|
23769
|
+
);
|
|
23770
|
+
}
|
|
23771
|
+
const primary = signed2.resources[0];
|
|
23772
|
+
const signedSpace = signed2.permissions.find(
|
|
23773
|
+
(permission) => permission.service !== "tinycloud.encryption"
|
|
23774
|
+
)?.space;
|
|
23775
|
+
const installedCandidate = {
|
|
23776
|
+
cid,
|
|
23777
|
+
delegationHeader: { Authorization: authorization },
|
|
23778
|
+
ownerAddress: delegation.ownerAddress,
|
|
23779
|
+
chainId: delegation.chainId,
|
|
23780
|
+
spaceId: signedSpace ?? delegation.spaceId,
|
|
23781
|
+
path: primary.path,
|
|
23782
|
+
actions: [...primary.actions],
|
|
23783
|
+
...primary.caveats === void 0 ? {} : { caveats: primary.caveats },
|
|
23784
|
+
resources: signed2.resources.map((resource) => ({
|
|
23785
|
+
...resource,
|
|
23786
|
+
actions: [...resource.actions]
|
|
23787
|
+
})),
|
|
23788
|
+
expiry: new Date(signed2.expiry),
|
|
23789
|
+
delegateDID: signed2.audience,
|
|
23790
|
+
host
|
|
23791
|
+
};
|
|
23792
|
+
if (node.isSessionOnly === true && typeof node.did === "string" && principalDidEquals3(node.did, node.sessionDid) && typeof node.useDelegation === "function") {
|
|
23793
|
+
await node.useDelegation(installedCandidate);
|
|
23794
|
+
} else {
|
|
23795
|
+
await node.useRuntimeDelegation(installedCandidate);
|
|
23796
|
+
}
|
|
23797
|
+
const installed = node.getRuntimePermissionDelegations().find((candidate) => candidate.cid === cid);
|
|
23798
|
+
if (!installed) {
|
|
23799
|
+
throw new Error("Runtime delegation activation did not install the validated authority.");
|
|
23800
|
+
}
|
|
23801
|
+
if (installed.delegationHeader.Authorization !== authorization || !didPrincipalsMatch(installed.delegateDID, signed2.audience) || installed.expiry.getTime() !== signed2.expiry.getTime() || !resourcesMatch(
|
|
23802
|
+
canonicalResourcesFromPortableDelegation(installed),
|
|
23803
|
+
signed2.resources
|
|
23804
|
+
) || normalizedHost(installed.host ?? "") !== host) {
|
|
23805
|
+
throw new Error(
|
|
23806
|
+
"Runtime delegation activation installed authority that differs from the validated authority."
|
|
23807
|
+
);
|
|
23808
|
+
}
|
|
23809
|
+
return {
|
|
23810
|
+
cid,
|
|
23811
|
+
delegation: installed,
|
|
23812
|
+
effectivePermissions: signed2.permissions,
|
|
23813
|
+
expiry: new Date(signed2.expiry),
|
|
23814
|
+
audience: signed2.audience,
|
|
23815
|
+
host: normalizedHost(installed.host ?? host)
|
|
23816
|
+
};
|
|
23817
|
+
}
|
|
22641
23818
|
async function grantAuthRequest(authority, request, options) {
|
|
22642
23819
|
if (request.kind !== "tinycloud.auth.request") {
|
|
22643
23820
|
throw new Error(
|
|
@@ -22700,10 +23877,11 @@ import {
|
|
|
22700
23877
|
SECRET_NAME_RE,
|
|
22701
23878
|
canonicalizeSecretScope,
|
|
22702
23879
|
resolveSecretListPrefix as resolveSecretListPrefix2,
|
|
22703
|
-
resolveSecretPath as
|
|
23880
|
+
resolveSecretPath as resolveSecretPath3
|
|
22704
23881
|
} from "@tinycloud/sdk-core";
|
|
22705
23882
|
import {
|
|
22706
23883
|
EncryptionService as EncryptionService2,
|
|
23884
|
+
DecryptTransportResponseError as DecryptTransportResponseError2,
|
|
22707
23885
|
parseNetworkId as parseNetworkId3,
|
|
22708
23886
|
buildNetworkId,
|
|
22709
23887
|
isNetworkId,
|
|
@@ -22779,6 +23957,7 @@ export {
|
|
|
22779
23957
|
AutoApproveSpaceCreationHandler2 as AutoApproveSpaceCreationHandler,
|
|
22780
23958
|
CapabilityKeyRegistry2 as CapabilityKeyRegistry,
|
|
22781
23959
|
CapabilityKeyRegistryErrorCodes,
|
|
23960
|
+
CaveatedDelegationUnsupportedError2 as CaveatedDelegationUnsupportedError,
|
|
22782
23961
|
DECRYPT_ACTION3 as DECRYPT_ACTION,
|
|
22783
23962
|
DECRYPT_FACT_TYPE,
|
|
22784
23963
|
DECRYPT_RESULT_TYPE,
|
|
@@ -22789,6 +23968,7 @@ export {
|
|
|
22789
23968
|
DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
|
|
22790
23969
|
DataVaultService2 as DataVaultService,
|
|
22791
23970
|
DatabaseHandle,
|
|
23971
|
+
DecryptTransportResponseError2 as DecryptTransportResponseError,
|
|
22792
23972
|
DelegatedAccess,
|
|
22793
23973
|
DelegationErrorCodes,
|
|
22794
23974
|
DelegationManager2 as DelegationManager,
|
|
@@ -22829,11 +24009,13 @@ export {
|
|
|
22829
24009
|
TinyCloudDebugLogger,
|
|
22830
24010
|
TinyCloudNode,
|
|
22831
24011
|
UnsupportedFeatureError2 as UnsupportedFeatureError,
|
|
24012
|
+
UnsupportedSessionRestoreError,
|
|
22832
24013
|
VAULT_PERMISSION_SERVICE,
|
|
22833
24014
|
VaultHeaders,
|
|
22834
24015
|
VaultPublicSpaceKVActions,
|
|
22835
24016
|
VersionCheckError,
|
|
22836
24017
|
WasmKeyProvider,
|
|
24018
|
+
activateValidatedRuntimeDelegation,
|
|
22837
24019
|
addressStorageKey,
|
|
22838
24020
|
buildCanonicalDecryptRequest,
|
|
22839
24021
|
buildDecryptAttenuation,
|
|
@@ -22900,10 +24082,10 @@ export {
|
|
|
22900
24082
|
parseSpaceUri,
|
|
22901
24083
|
pkhDid3 as pkhDid,
|
|
22902
24084
|
principalDid,
|
|
22903
|
-
|
|
24085
|
+
principalDidEquals4 as principalDidEquals,
|
|
22904
24086
|
resolveManifest2 as resolveManifest,
|
|
22905
24087
|
resolveSecretListPrefix2 as resolveSecretListPrefix,
|
|
22906
|
-
|
|
24088
|
+
resolveSecretPath3 as resolveSecretPath,
|
|
22907
24089
|
resourceCapabilitiesToSpaceAbilitiesMap2 as resourceCapabilitiesToSpaceAbilitiesMap,
|
|
22908
24090
|
serializeDelegation,
|
|
22909
24091
|
tinyCloudDebugLogger,
|