@tinycloud/node-sdk 2.7.0-beta.2 → 2.7.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{core-BfqzTbfY.d.cts → core-D_KNrE_y.d.cts} +153 -8
- package/dist/{core-BfqzTbfY.d.ts → core-D_KNrE_y.d.ts} +153 -8
- package/dist/core.cjs +1152 -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 +1083 -181
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +1470 -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 +1361 -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
|
*/
|
|
@@ -19108,6 +19349,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19108
19349
|
getPrimarySpaceId: () => this.spaceId,
|
|
19109
19350
|
getAccountSpaceId: () => this.accountSpaceId,
|
|
19110
19351
|
getSpaces: () => this.spaces,
|
|
19352
|
+
getDelegationManager: () => this.delegationManager,
|
|
19111
19353
|
getAccountDb: () => this.accountSpaceId ? this.sqlForSpace(this.accountSpaceId).db("account") : void 0,
|
|
19112
19354
|
ensureAccountSpaceHosted: async () => {
|
|
19113
19355
|
if (this.accountSpaceId && this.auth) {
|
|
@@ -19154,6 +19396,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19154
19396
|
this._hooks = void 0;
|
|
19155
19397
|
this._vault = void 0;
|
|
19156
19398
|
this._encryption = void 0;
|
|
19399
|
+
this._baseVaults.clear();
|
|
19157
19400
|
this._baseSecrets.clear();
|
|
19158
19401
|
this._secrets.clear();
|
|
19159
19402
|
this._spaceService = void 0;
|
|
@@ -19161,6 +19404,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19161
19404
|
this.runtimePermissionGrants = [];
|
|
19162
19405
|
await this.tc.signIn(options);
|
|
19163
19406
|
this.syncResolvedHostFromAuth();
|
|
19407
|
+
const oldGraph = this._serviceGraph;
|
|
19408
|
+
this._serviceGraph = this.createServiceGraphLifetime();
|
|
19409
|
+
oldGraph.retire();
|
|
19410
|
+
this.tc.retireServices();
|
|
19164
19411
|
const signedInSession = this.currentTinyCloudSession();
|
|
19165
19412
|
if (signedInSession) {
|
|
19166
19413
|
this.sessionKeyId = signedInSession.sessionKey;
|
|
@@ -19445,7 +19692,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19445
19692
|
}
|
|
19446
19693
|
let operations = [];
|
|
19447
19694
|
try {
|
|
19448
|
-
const entries = this.
|
|
19695
|
+
const entries = this.parseRecapWithCaveats(siwe);
|
|
19449
19696
|
if (Array.isArray(entries)) {
|
|
19450
19697
|
operations = entries.flatMap((entry) => {
|
|
19451
19698
|
const service = this.invocationServiceName(entry.service);
|
|
@@ -19453,7 +19700,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19453
19700
|
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId: entry.space },
|
|
19454
19701
|
service,
|
|
19455
19702
|
path: entry.path,
|
|
19456
|
-
action
|
|
19703
|
+
action,
|
|
19704
|
+
caveats: cloneRecapCaveats(entry.caveats)
|
|
19457
19705
|
}));
|
|
19458
19706
|
});
|
|
19459
19707
|
}
|
|
@@ -19806,86 +20054,441 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19806
20054
|
*/
|
|
19807
20055
|
async restoreSession(sessionData) {
|
|
19808
20056
|
await this.wasmBindings.ensureInitialized?.();
|
|
19809
|
-
|
|
19810
|
-
|
|
19811
|
-
|
|
19812
|
-
|
|
19813
|
-
|
|
19814
|
-
|
|
19815
|
-
this.
|
|
19816
|
-
|
|
19817
|
-
|
|
19818
|
-
|
|
19819
|
-
|
|
19820
|
-
|
|
19821
|
-
|
|
19822
|
-
|
|
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.");
|
|
19823
20090
|
}
|
|
19824
|
-
|
|
19825
|
-
|
|
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);
|
|
19826
20150
|
}
|
|
19827
20151
|
const resolvedHost = await this.resolveRestoredHost(
|
|
19828
20152
|
sessionData.tinycloudHosts,
|
|
19829
20153
|
restoredAddress,
|
|
19830
20154
|
sessionData.chainId
|
|
19831
20155
|
);
|
|
19832
|
-
|
|
19833
|
-
|
|
19834
|
-
|
|
19835
|
-
|
|
19836
|
-
invoke: this.invokeWithRuntimePermissions,
|
|
19837
|
-
invokeAny: this.invokeAnyWithRuntimePermissions,
|
|
19838
|
-
fetch: globalThis.fetch.bind(globalThis),
|
|
19839
|
-
hosts: [this.config.host],
|
|
19840
|
-
telemetry: this.config.telemetry
|
|
19841
|
-
});
|
|
19842
|
-
this._kv = new KVService2({});
|
|
19843
|
-
this._kv.initialize(this._serviceContext);
|
|
19844
|
-
this._serviceContext.registerService("kv", this._kv);
|
|
19845
|
-
this._sql = new SQLService2({});
|
|
19846
|
-
this._sql.initialize(this._serviceContext);
|
|
19847
|
-
this._serviceContext.registerService("sql", this._sql);
|
|
19848
|
-
this._duckdb = new DuckDbService2({});
|
|
19849
|
-
this._duckdb.initialize(this._serviceContext);
|
|
19850
|
-
this._serviceContext.registerService("duckdb", this._duckdb);
|
|
19851
|
-
this._hooks = new HooksService2({});
|
|
19852
|
-
this._hooks.initialize(this._serviceContext);
|
|
19853
|
-
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;
|
|
19854
20160
|
const serviceSession = {
|
|
19855
20161
|
delegationHeader: sessionData.delegationHeader,
|
|
19856
20162
|
delegationCid: sessionData.delegationCid,
|
|
19857
20163
|
spaceId: sessionData.spaceId,
|
|
19858
|
-
verificationMethod:
|
|
19859
|
-
jwk:
|
|
20164
|
+
verificationMethod: restoredVerificationMethod,
|
|
20165
|
+
jwk: stagedJwk
|
|
19860
20166
|
};
|
|
19861
|
-
|
|
19862
|
-
|
|
19863
|
-
|
|
19864
|
-
|
|
19865
|
-
|
|
19866
|
-
|
|
19867
|
-
|
|
19868
|
-
|
|
19869
|
-
|
|
19870
|
-
|
|
19871
|
-
|
|
19872
|
-
|
|
19873
|
-
|
|
19874
|
-
|
|
19875
|
-
|
|
19876
|
-
|
|
19877
|
-
|
|
19878
|
-
|
|
19879
|
-
|
|
19880
|
-
|
|
19881
|
-
|
|
19882
|
-
|
|
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()
|
|
19883
20402
|
);
|
|
19884
|
-
|
|
19885
|
-
|
|
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);
|
|
19886
20488
|
}
|
|
19887
|
-
|
|
19888
|
-
}
|
|
20489
|
+
});
|
|
20490
|
+
spaceService.updateConfig({ sharingService });
|
|
20491
|
+
return { core, graph, serviceContext, kv, sql, duckdb, hooks, vault, encryption, capabilityRegistry, keyProvider, sharingService, delegationManager, spaceService };
|
|
19889
20492
|
}
|
|
19890
20493
|
/**
|
|
19891
20494
|
* Resolve the host a restored session should target.
|
|
@@ -20047,14 +20650,19 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20047
20650
|
if (!session) {
|
|
20048
20651
|
return;
|
|
20049
20652
|
}
|
|
20050
|
-
this.tc.initializeServices(
|
|
20051
|
-
|
|
20052
|
-
|
|
20053
|
-
|
|
20054
|
-
|
|
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,
|
|
20055
20663
|
hosts: [this.config.host],
|
|
20056
20664
|
telemetry: this.config.telemetry
|
|
20057
|
-
});
|
|
20665
|
+
}));
|
|
20058
20666
|
this._kv = new KVService2({});
|
|
20059
20667
|
this._kv.initialize(this._serviceContext);
|
|
20060
20668
|
this._serviceContext.registerService("kv", this._kv);
|
|
@@ -20089,12 +20697,12 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20089
20697
|
createSpaceScopedKVService(spaceId) {
|
|
20090
20698
|
const kvService = new KVService2({});
|
|
20091
20699
|
if (this._serviceContext) {
|
|
20092
|
-
const spaceScopedContext = new ServiceContext2({
|
|
20700
|
+
const spaceScopedContext = this._serviceGraph.track(new ServiceContext2({
|
|
20093
20701
|
invoke: this._serviceContext.invoke,
|
|
20094
20702
|
fetch: this._serviceContext.fetch,
|
|
20095
20703
|
hosts: this._serviceContext.hosts,
|
|
20096
20704
|
telemetry: this.config.telemetry
|
|
20097
|
-
});
|
|
20705
|
+
}));
|
|
20098
20706
|
const session = this._serviceContext.session;
|
|
20099
20707
|
if (session) {
|
|
20100
20708
|
spaceScopedContext.setSession({ ...session, spaceId });
|
|
@@ -20127,6 +20735,18 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20127
20735
|
}
|
|
20128
20736
|
return session;
|
|
20129
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
|
+
}
|
|
20130
20750
|
createEncryptionCrypto() {
|
|
20131
20751
|
const wasm = this.wasmBindings;
|
|
20132
20752
|
const columnEncrypt = (key2, plaintext) => {
|
|
@@ -20182,14 +20802,15 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20182
20802
|
}
|
|
20183
20803
|
return info.nodeId;
|
|
20184
20804
|
}
|
|
20185
|
-
async signRawNetworkAuthorization(input) {
|
|
20805
|
+
async signRawNetworkAuthorization(input, binding) {
|
|
20806
|
+
binding?.graph.assertActive();
|
|
20186
20807
|
if (!this.wasmBindings.invokeAny) {
|
|
20187
20808
|
throw new Error("WASM binding does not support raw-resource invokeAny");
|
|
20188
20809
|
}
|
|
20189
20810
|
if (!this.wasmBindings.computeCid) {
|
|
20190
20811
|
throw new Error("WASM binding does not support invocation CID computation");
|
|
20191
20812
|
}
|
|
20192
|
-
const session = this.
|
|
20813
|
+
const session = binding?.session ?? this.requireEncryptionSession();
|
|
20193
20814
|
const headers = this.invokeAnyWithRuntimePermissions(
|
|
20194
20815
|
session,
|
|
20195
20816
|
[
|
|
@@ -20208,6 +20829,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20208
20829
|
input.targetNode,
|
|
20209
20830
|
session.jwk
|
|
20210
20831
|
);
|
|
20832
|
+
binding?.graph.assertActive();
|
|
20211
20833
|
return {
|
|
20212
20834
|
authorization: audienceBound,
|
|
20213
20835
|
invocationCid: this.wasmBindings.computeCid(
|
|
@@ -20216,25 +20838,55 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20216
20838
|
)
|
|
20217
20839
|
};
|
|
20218
20840
|
}
|
|
20219
|
-
|
|
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;
|
|
20220
20861
|
const crypto2 = this.createEncryptionCrypto();
|
|
20221
20862
|
const transport = {
|
|
20222
|
-
postDecrypt: async ({ networkId, authorization, canonicalBody }) => {
|
|
20223
|
-
|
|
20224
|
-
|
|
20863
|
+
postDecrypt: async ({ networkId, authorization, canonicalBody, signal }) => {
|
|
20864
|
+
graph.assertActive();
|
|
20865
|
+
const response = await graph.fetch(
|
|
20866
|
+
`${host}/encryption/networks/${encodeURIComponent(networkId)}/decrypt`,
|
|
20225
20867
|
{
|
|
20226
20868
|
method: "POST",
|
|
20227
20869
|
headers: {
|
|
20228
20870
|
Authorization: authorization,
|
|
20229
20871
|
"Content-Type": "application/json"
|
|
20230
20872
|
},
|
|
20231
|
-
body: canonicalBody
|
|
20873
|
+
body: canonicalBody,
|
|
20874
|
+
signal
|
|
20232
20875
|
}
|
|
20233
20876
|
);
|
|
20877
|
+
graph.assertActive();
|
|
20234
20878
|
if (!response.ok) {
|
|
20235
|
-
|
|
20236
|
-
|
|
20237
|
-
|
|
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);
|
|
20238
20890
|
}
|
|
20239
20891
|
return await response.json();
|
|
20240
20892
|
}
|
|
@@ -20243,12 +20895,14 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20243
20895
|
crypto: crypto2,
|
|
20244
20896
|
signer: {
|
|
20245
20897
|
signDecryptInvocation: async (input) => {
|
|
20898
|
+
graph.assertActive();
|
|
20246
20899
|
const signed2 = await this.signRawNetworkAuthorization({
|
|
20247
20900
|
targetNode: input.targetNode,
|
|
20248
20901
|
networkId: input.networkId,
|
|
20249
20902
|
action: DECRYPT_ACTION2,
|
|
20250
20903
|
facts: input.facts
|
|
20251
|
-
});
|
|
20904
|
+
}, { graph, session });
|
|
20905
|
+
graph.assertActive();
|
|
20252
20906
|
return {
|
|
20253
20907
|
...signed2,
|
|
20254
20908
|
canonicalBody: canonicalizeEncryptionJson(
|
|
@@ -20259,25 +20913,35 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20259
20913
|
},
|
|
20260
20914
|
transport,
|
|
20261
20915
|
node: {
|
|
20262
|
-
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
|
+
}
|
|
20263
20922
|
},
|
|
20264
20923
|
wellKnown: {
|
|
20265
20924
|
fetchWellKnown: async (principal, discoveryKey) => {
|
|
20266
|
-
|
|
20267
|
-
|
|
20268
|
-
}
|
|
20269
|
-
if (!this.config.host) {
|
|
20925
|
+
graph.assertActive();
|
|
20926
|
+
if (!address || !didPrincipalMatches2(principal, did)) {
|
|
20270
20927
|
return null;
|
|
20271
20928
|
}
|
|
20272
|
-
const publicSpaceId = makePublicSpaceId(
|
|
20273
|
-
const
|
|
20274
|
-
|
|
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) {
|
|
20275
20937
|
return null;
|
|
20276
20938
|
}
|
|
20277
|
-
const body =
|
|
20939
|
+
const body = await response.json();
|
|
20940
|
+
graph.assertActive();
|
|
20278
20941
|
return "descriptor" in body && body.descriptor ? body.descriptor : body;
|
|
20279
20942
|
}
|
|
20280
|
-
}
|
|
20943
|
+
},
|
|
20944
|
+
assertActive: () => graph.assertActive()
|
|
20281
20945
|
});
|
|
20282
20946
|
}
|
|
20283
20947
|
getEncryptionService() {
|
|
@@ -20291,8 +20955,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20291
20955
|
}
|
|
20292
20956
|
return this._encryption;
|
|
20293
20957
|
}
|
|
20294
|
-
createVaultService(spaceId, kv) {
|
|
20958
|
+
createVaultService(spaceId, kv, encryptionService = this.getEncryptionService(), nodeContext) {
|
|
20295
20959
|
const wasm = this.wasmBindings;
|
|
20960
|
+
const serviceGraph = nodeContext?.serviceGraph ?? this._serviceGraph;
|
|
20296
20961
|
const vaultCrypto = createVaultCrypto({
|
|
20297
20962
|
vault_encrypt: wasm.vault_encrypt,
|
|
20298
20963
|
vault_decrypt: wasm.vault_decrypt,
|
|
@@ -20303,12 +20968,17 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20303
20968
|
vault_sha256: wasm.vault_sha256
|
|
20304
20969
|
});
|
|
20305
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;
|
|
20306
20976
|
return new DataVaultService({
|
|
20307
20977
|
spaceId,
|
|
20308
20978
|
crypto: vaultCrypto,
|
|
20309
20979
|
encryption: {
|
|
20310
|
-
networkId:
|
|
20311
|
-
service:
|
|
20980
|
+
networkId: `urn:tinycloud:encryption:${ownerDid}:${DEFAULT_ENCRYPTION_NETWORK_NAME}`,
|
|
20981
|
+
service: encryptionService,
|
|
20312
20982
|
decryptCapabilityProof: () => ({
|
|
20313
20983
|
proofs: [this.requireServiceSession().delegationCid]
|
|
20314
20984
|
})
|
|
@@ -20317,6 +20987,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20317
20987
|
kv,
|
|
20318
20988
|
ensurePublicSpace: async () => {
|
|
20319
20989
|
try {
|
|
20990
|
+
serviceGraph.assertActive();
|
|
20320
20991
|
await self2.ensurePublicSpace();
|
|
20321
20992
|
return { ok: true, data: void 0 };
|
|
20322
20993
|
} catch (error) {
|
|
@@ -20324,14 +20995,15 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20324
20995
|
}
|
|
20325
20996
|
},
|
|
20326
20997
|
get publicKV() {
|
|
20998
|
+
serviceGraph.assertActive();
|
|
20327
20999
|
return self2._publicKV ?? self2.tc.publicKV;
|
|
20328
21000
|
},
|
|
20329
|
-
readPublicSpace: (
|
|
21001
|
+
readPublicSpace: (host2, targetSpaceId, key2) => TinyCloud.readPublicSpace(host2, targetSpaceId, key2),
|
|
20330
21002
|
makePublicSpaceId: TinyCloud.makePublicSpaceId,
|
|
20331
|
-
did
|
|
20332
|
-
address:
|
|
20333
|
-
chainId
|
|
20334
|
-
hosts: [
|
|
21003
|
+
did,
|
|
21004
|
+
address: address ?? "",
|
|
21005
|
+
chainId,
|
|
21006
|
+
hosts: [host]
|
|
20335
21007
|
}
|
|
20336
21008
|
});
|
|
20337
21009
|
}
|
|
@@ -20340,6 +21012,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20340
21012
|
* @internal
|
|
20341
21013
|
*/
|
|
20342
21014
|
initializeV2Services(serviceSession) {
|
|
21015
|
+
const graph = this._serviceGraph;
|
|
20343
21016
|
this._capabilityRegistry = new CapabilityKeyRegistry();
|
|
20344
21017
|
const tcSession = this.auth?.tinyCloudSession;
|
|
20345
21018
|
if (tcSession && this._address) {
|
|
@@ -20382,22 +21055,25 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20382
21055
|
}
|
|
20383
21056
|
this._delegationManager = new DelegationManager({
|
|
20384
21057
|
hosts: [this.config.host],
|
|
21058
|
+
accountSpaceId: this.accountSpaceId,
|
|
20385
21059
|
session: serviceSession,
|
|
20386
|
-
invoke:
|
|
20387
|
-
invokeAny:
|
|
20388
|
-
fetch:
|
|
21060
|
+
invoke: graph.invoke,
|
|
21061
|
+
invokeAny: graph.invokeAny,
|
|
21062
|
+
fetch: graph.fetch
|
|
20389
21063
|
});
|
|
20390
21064
|
this._spaceService = new SpaceService({
|
|
20391
21065
|
hosts: [this.config.host],
|
|
20392
21066
|
session: serviceSession,
|
|
20393
|
-
invoke:
|
|
20394
|
-
fetch:
|
|
21067
|
+
invoke: graph.invoke,
|
|
21068
|
+
fetch: graph.fetch,
|
|
20395
21069
|
capabilityRegistry: this._capabilityRegistry,
|
|
20396
21070
|
userDid: this.did,
|
|
20397
21071
|
createKVService: (spaceId) => {
|
|
21072
|
+
graph.assertActive();
|
|
20398
21073
|
return this.createSpaceScopedKVService(spaceId);
|
|
20399
21074
|
},
|
|
20400
21075
|
createVaultService: (spaceId) => {
|
|
21076
|
+
graph.assertActive();
|
|
20401
21077
|
const kvService = this.createSpaceScopedKVService(spaceId);
|
|
20402
21078
|
const vaultService = this.createVaultService(spaceId, kvService);
|
|
20403
21079
|
if (this._serviceContext) {
|
|
@@ -20406,11 +21082,13 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20406
21082
|
return vaultService;
|
|
20407
21083
|
},
|
|
20408
21084
|
createSecretsService: (spaceId) => {
|
|
21085
|
+
graph.assertActive();
|
|
20409
21086
|
return this.secretsForSpace(spaceId);
|
|
20410
21087
|
},
|
|
20411
21088
|
// Enable space.delegations.create() via SIWE-based delegation
|
|
20412
21089
|
createDelegation: async (params) => {
|
|
20413
21090
|
try {
|
|
21091
|
+
graph.assertActive();
|
|
20414
21092
|
const portableDelegation = await this.createDelegation({
|
|
20415
21093
|
delegateDID: params.delegateDID,
|
|
20416
21094
|
path: params.path,
|
|
@@ -20444,18 +21122,50 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20444
21122
|
}
|
|
20445
21123
|
},
|
|
20446
21124
|
onSpaceRegistered: async (space) => {
|
|
21125
|
+
graph.assertActive();
|
|
20447
21126
|
await this.account.spaces.register(space);
|
|
20448
21127
|
}
|
|
20449
21128
|
});
|
|
20450
|
-
this._sharingService
|
|
21129
|
+
this._sharingService = new SharingService({
|
|
21130
|
+
hosts: [this.config.host],
|
|
20451
21131
|
session: serviceSession,
|
|
21132
|
+
invoke: graph.invoke,
|
|
21133
|
+
fetch: graph.fetch,
|
|
21134
|
+
assertActive: () => graph.assertActive(),
|
|
21135
|
+
keyProvider: this._keyProvider,
|
|
21136
|
+
registry: this._capabilityRegistry,
|
|
20452
21137
|
delegationManager: this._delegationManager,
|
|
20453
21138
|
sessionExpiry: this.getSessionExpiry(),
|
|
20454
|
-
|
|
20455
|
-
|
|
20456
|
-
|
|
20457
|
-
|
|
20458
|
-
|
|
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
|
|
20459
21169
|
});
|
|
20460
21170
|
this._spaceService.updateConfig({
|
|
20461
21171
|
sharingService: this._sharingService
|
|
@@ -20522,7 +21232,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20522
21232
|
* with expiry longer than the current session.
|
|
20523
21233
|
* @internal
|
|
20524
21234
|
*/
|
|
20525
|
-
async createOwnerDelegation(params) {
|
|
21235
|
+
async createOwnerDelegation(params, assertActive) {
|
|
21236
|
+
const assertOwnerGraphActive = assertActive ?? this._serviceGraph.assertActive.bind(this._serviceGraph);
|
|
21237
|
+
assertOwnerGraphActive();
|
|
20526
21238
|
if (!params.delegateDid.startsWith("did:key:") || params.actions.length === 0 || params.path.length === 0) {
|
|
20527
21239
|
throw new Error("Owner delegation requires an external did:key audience and bounded capabilities");
|
|
20528
21240
|
}
|
|
@@ -20547,8 +21259,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20547
21259
|
delegateUri: params.delegateDid
|
|
20548
21260
|
});
|
|
20549
21261
|
const signature2 = await this.signer.signMessage(prepared.siwe);
|
|
21262
|
+
assertOwnerGraphActive();
|
|
20550
21263
|
const delegationSession = this.wasmBindings.completeSessionSetup({ ...prepared, signature: signature2 });
|
|
20551
21264
|
const activation = await activateSessionWithHost2(host, delegationSession.delegationHeader);
|
|
21265
|
+
assertOwnerGraphActive();
|
|
20552
21266
|
if (!activation.success) {
|
|
20553
21267
|
throw new Error(`Owner delegation import failed: ${activation.status} ${activation.error ?? ""}`.trim());
|
|
20554
21268
|
}
|
|
@@ -20576,7 +21290,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20576
21290
|
}
|
|
20577
21291
|
};
|
|
20578
21292
|
}
|
|
20579
|
-
async createRootDelegationForSharing(params) {
|
|
21293
|
+
async createRootDelegationForSharing(params, assertActive) {
|
|
20580
21294
|
try {
|
|
20581
21295
|
return (await this.createOwnerDelegation({
|
|
20582
21296
|
delegateDid: params.shareKeyDID,
|
|
@@ -20584,8 +21298,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20584
21298
|
path: params.path,
|
|
20585
21299
|
actions: params.actions,
|
|
20586
21300
|
expiresAt: params.requestedExpiry
|
|
20587
|
-
})).delegation;
|
|
21301
|
+
}, assertActive)).delegation;
|
|
20588
21302
|
} catch {
|
|
21303
|
+
assertActive?.();
|
|
20589
21304
|
return void 0;
|
|
20590
21305
|
}
|
|
20591
21306
|
}
|
|
@@ -20657,13 +21372,13 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20657
21372
|
throw new Error("Not signed in. Call signIn() first.");
|
|
20658
21373
|
}
|
|
20659
21374
|
const sql = new SQLService2({});
|
|
20660
|
-
const spaceScopedContext = new ServiceContext2({
|
|
21375
|
+
const spaceScopedContext = this._serviceGraph.track(new ServiceContext2({
|
|
20661
21376
|
invoke: this._serviceContext.invoke,
|
|
20662
21377
|
invokeAny: this._serviceContext.invokeAny,
|
|
20663
21378
|
fetch: this._serviceContext.fetch,
|
|
20664
21379
|
hosts: this._serviceContext.hosts,
|
|
20665
21380
|
telemetry: this.config.telemetry
|
|
20666
|
-
});
|
|
21381
|
+
}));
|
|
20667
21382
|
spaceScopedContext.setSession({ ...this._serviceContext.session, spaceId });
|
|
20668
21383
|
sql.initialize(spaceScopedContext);
|
|
20669
21384
|
return sql;
|
|
@@ -20687,12 +21402,12 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20687
21402
|
throw new Error("Not signed in. Call signIn() first.");
|
|
20688
21403
|
}
|
|
20689
21404
|
const kv = new KVService2({});
|
|
20690
|
-
const spaceScopedContext = new ServiceContext2({
|
|
21405
|
+
const spaceScopedContext = this._serviceGraph.track(new ServiceContext2({
|
|
20691
21406
|
invoke: this._serviceContext.invoke,
|
|
20692
21407
|
invokeAny: this._serviceContext.invokeAny,
|
|
20693
21408
|
fetch: this._serviceContext.fetch,
|
|
20694
21409
|
hosts: this._serviceContext.hosts
|
|
20695
|
-
});
|
|
21410
|
+
}));
|
|
20696
21411
|
spaceScopedContext.setSession({ ...this._serviceContext.session, spaceId });
|
|
20697
21412
|
kv.initialize(spaceScopedContext);
|
|
20698
21413
|
return kv;
|
|
@@ -20728,19 +21443,11 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20728
21443
|
}
|
|
20729
21444
|
async getEncryptionNetwork(nameOrNetworkId = this.getDefaultEncryptionNetworkId()) {
|
|
20730
21445
|
const networkId = nameOrNetworkId.startsWith("urn:tinycloud:encryption:") ? nameOrNetworkId : this.getDefaultEncryptionNetworkId(nameOrNetworkId);
|
|
20731
|
-
|
|
20732
|
-
|
|
21446
|
+
return this.fetchEncryptionNetworkAt(
|
|
21447
|
+
this.config.host,
|
|
21448
|
+
networkId,
|
|
21449
|
+
globalThis.fetch.bind(globalThis)
|
|
20733
21450
|
);
|
|
20734
|
-
if (response.status === 404) {
|
|
20735
|
-
return null;
|
|
20736
|
-
}
|
|
20737
|
-
if (!response.ok) {
|
|
20738
|
-
throw new Error(
|
|
20739
|
-
`Failed to fetch encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
|
|
20740
|
-
);
|
|
20741
|
-
}
|
|
20742
|
-
const body = await response.json();
|
|
20743
|
-
return "descriptor" in body && body.descriptor ? body.descriptor : body;
|
|
20744
21451
|
}
|
|
20745
21452
|
async createEncryptionNetwork(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
|
|
20746
21453
|
const targetNode = await this.fetchNodeId();
|
|
@@ -20834,6 +21541,38 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20834
21541
|
}
|
|
20835
21542
|
return secrets;
|
|
20836
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
|
+
}
|
|
20837
21576
|
getBaseSecrets(spaceId) {
|
|
20838
21577
|
if (!this._spaceService) {
|
|
20839
21578
|
throw new Error("Not signed in. Call signIn() first.");
|
|
@@ -20841,16 +21580,29 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20841
21580
|
const resolvedSpace = spaceId.startsWith("tinycloud:") ? spaceId : this.ownedSpaceId(spaceId);
|
|
20842
21581
|
let secrets = this._baseSecrets.get(resolvedSpace);
|
|
20843
21582
|
if (!secrets) {
|
|
20844
|
-
|
|
20845
|
-
const vaultService = this.createVaultService(resolvedSpace, kvService);
|
|
20846
|
-
if (this._serviceContext) {
|
|
20847
|
-
vaultService.initialize(this._serviceContext);
|
|
20848
|
-
}
|
|
20849
|
-
secrets = new SecretsService(() => vaultService);
|
|
21583
|
+
secrets = new SecretsService(() => this.getBaseVault(resolvedSpace));
|
|
20850
21584
|
this._baseSecrets.set(resolvedSpace, secrets);
|
|
20851
21585
|
}
|
|
20852
21586
|
return secrets;
|
|
20853
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
|
+
}
|
|
20854
21606
|
/**
|
|
20855
21607
|
* Hooks write stream subscription API.
|
|
20856
21608
|
*/
|
|
@@ -20955,6 +21707,63 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20955
21707
|
(grant) => grant.delegation
|
|
20956
21708
|
);
|
|
20957
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
|
+
}
|
|
20958
21767
|
/**
|
|
20959
21768
|
* Install a portable runtime permission delegation into this SDK instance so
|
|
20960
21769
|
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
@@ -21023,6 +21832,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21023
21832
|
"grantRuntimePermissions requires wallet mode with a signer or privateKey."
|
|
21024
21833
|
);
|
|
21025
21834
|
}
|
|
21835
|
+
this.assertDelegationCaveatsPreservable(expanded);
|
|
21026
21836
|
const rawEntries = expanded.filter(
|
|
21027
21837
|
(entry) => this.isEncryptionPermissionEntry(entry)
|
|
21028
21838
|
);
|
|
@@ -21284,12 +22094,12 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21284
22094
|
}
|
|
21285
22095
|
if (this._serviceContext) {
|
|
21286
22096
|
const publicKV = new KVService2({ prefix: "" });
|
|
21287
|
-
const publicContext = new ServiceContext2({
|
|
21288
|
-
invoke: this.
|
|
21289
|
-
fetch: this.
|
|
22097
|
+
const publicContext = this._serviceGraph.track(new ServiceContext2({
|
|
22098
|
+
invoke: this._serviceGraph.invoke,
|
|
22099
|
+
fetch: this._serviceGraph.fetch,
|
|
21290
22100
|
hosts: this._serviceContext.hosts,
|
|
21291
22101
|
telemetry: this.config.telemetry
|
|
21292
|
-
});
|
|
22102
|
+
}));
|
|
21293
22103
|
publicContext.setSession({
|
|
21294
22104
|
delegationHeader: delegationSession.delegationHeader,
|
|
21295
22105
|
delegationCid: delegationSession.delegationCid,
|
|
@@ -21356,13 +22166,14 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21356
22166
|
async getDelegationStatus(cid) {
|
|
21357
22167
|
return this.delegationManager.status(cid);
|
|
21358
22168
|
}
|
|
21359
|
-
/** Compute the canonical CID of a compact delegation authorization. */
|
|
22169
|
+
/** Compute the canonical CID of a compact UCAN or DAG-CBOR delegation authorization. */
|
|
21360
22170
|
computeDelegationCid(authorization) {
|
|
21361
22171
|
if (!authorization || !this.wasmBindings.computeCid) {
|
|
21362
22172
|
throw new Error("Delegation CID computation is unavailable");
|
|
21363
22173
|
}
|
|
22174
|
+
const compact = authorization.replace(/^Bearer /i, "");
|
|
21364
22175
|
return this.wasmBindings.computeCid(
|
|
21365
|
-
new TextEncoder().encode(authorization),
|
|
22176
|
+
compact.includes(".") ? new TextEncoder().encode(compact) : decodeAuthorizationBytes(authorization),
|
|
21366
22177
|
0x55n
|
|
21367
22178
|
);
|
|
21368
22179
|
}
|
|
@@ -21454,6 +22265,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21454
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."
|
|
21455
22266
|
);
|
|
21456
22267
|
}
|
|
22268
|
+
this.assertDelegationCaveatsPreservable(expandedEntries);
|
|
21457
22269
|
const delegation2 = await this.createDelegationLegacyWalletPath(
|
|
21458
22270
|
did,
|
|
21459
22271
|
expandedEntries[0],
|
|
@@ -21462,13 +22274,14 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21462
22274
|
return { delegation: delegation2, prompted: true };
|
|
21463
22275
|
}
|
|
21464
22276
|
const granted = parseRecapCapabilities(
|
|
21465
|
-
(siwe) => this.
|
|
22277
|
+
(siwe) => this.parseRecapWithCaveats(siwe),
|
|
21466
22278
|
session.siwe
|
|
21467
22279
|
);
|
|
21468
22280
|
const { subset, missing } = isCapabilitySubset(expandedEntries, granted);
|
|
21469
22281
|
if (!subset) {
|
|
22282
|
+
const runtimeOperations = this.permissionEntriesToOperations(expandedEntries, session);
|
|
21470
22283
|
const runtimeGrant = this.findGrantForOperations(
|
|
21471
|
-
|
|
22284
|
+
runtimeOperations,
|
|
21472
22285
|
{ excludePrimary: true }
|
|
21473
22286
|
);
|
|
21474
22287
|
if (runtimeGrant) {
|
|
@@ -21481,7 +22294,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21481
22294
|
did,
|
|
21482
22295
|
expandedEntries,
|
|
21483
22296
|
runtimeExpiration,
|
|
21484
|
-
runtimeGrant
|
|
22297
|
+
runtimeGrant,
|
|
22298
|
+
runtimeOperations
|
|
21485
22299
|
);
|
|
21486
22300
|
return { delegation: delegation2, prompted: false };
|
|
21487
22301
|
}
|
|
@@ -21551,6 +22365,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21551
22365
|
"createDelegationViaWasmPath requires a non-empty entries array"
|
|
21552
22366
|
);
|
|
21553
22367
|
}
|
|
22368
|
+
this.assertDelegationCaveatsPreservable(entries);
|
|
21554
22369
|
const resolvedSpaces = /* @__PURE__ */ new Set();
|
|
21555
22370
|
for (const entry of entries) {
|
|
21556
22371
|
if (this.isEncryptionPermissionEntry(entry)) {
|
|
@@ -21631,7 +22446,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21631
22446
|
host: this.config.host
|
|
21632
22447
|
};
|
|
21633
22448
|
}
|
|
21634
|
-
async createDelegationViaRuntimeGrant(did, entries, expirationTime, grant) {
|
|
22449
|
+
async createDelegationViaRuntimeGrant(did, entries, expirationTime, grant, requestedOperations) {
|
|
22450
|
+
this.assertRuntimeGrantCaveatsPreservable(entries, requestedOperations, grant);
|
|
22451
|
+
this.assertDelegationCaveatsPreservable(entries);
|
|
21635
22452
|
const result = this.createDelegationWrapper({
|
|
21636
22453
|
session: grant.session,
|
|
21637
22454
|
delegateDID: did,
|
|
@@ -21685,6 +22502,74 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21685
22502
|
expandPermissionEntries(permissions) {
|
|
21686
22503
|
return expandPermissionEntriesCore(permissions);
|
|
21687
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
|
+
}
|
|
21688
22573
|
shortServiceName(service) {
|
|
21689
22574
|
const short = SERVICE_LONG_TO_SHORT[service];
|
|
21690
22575
|
if (short === void 0) {
|
|
@@ -21739,14 +22624,15 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21739
22624
|
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
|
|
21740
22625
|
service,
|
|
21741
22626
|
path: entry.path,
|
|
21742
|
-
action
|
|
22627
|
+
action,
|
|
22628
|
+
...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
|
|
21743
22629
|
}));
|
|
21744
22630
|
});
|
|
21745
22631
|
}
|
|
21746
22632
|
sessionCoversPermissionEntries(session, entries) {
|
|
21747
22633
|
try {
|
|
21748
22634
|
const granted = parseRecapCapabilities(
|
|
21749
|
-
(siwe) => this.
|
|
22635
|
+
(siwe) => this.parseRecapWithCaveats(siwe),
|
|
21750
22636
|
session.siwe
|
|
21751
22637
|
);
|
|
21752
22638
|
return isCapabilitySubset(entries, granted).subset;
|
|
@@ -21762,7 +22648,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21762
22648
|
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
|
|
21763
22649
|
service,
|
|
21764
22650
|
path: entry.path,
|
|
21765
|
-
action
|
|
22651
|
+
action,
|
|
22652
|
+
...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
|
|
21766
22653
|
}));
|
|
21767
22654
|
});
|
|
21768
22655
|
}
|
|
@@ -21838,7 +22725,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21838
22725
|
service: this.shortServiceName(entry.service),
|
|
21839
22726
|
space: this.isEncryptionPermissionEntry(entry) ? "encryption" : spaceId,
|
|
21840
22727
|
path: entry.path,
|
|
21841
|
-
actions: [...entry.actions]
|
|
22728
|
+
actions: [...entry.actions],
|
|
22729
|
+
...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
|
|
21842
22730
|
}));
|
|
21843
22731
|
}
|
|
21844
22732
|
operationsFromDelegation(delegation) {
|
|
@@ -21849,7 +22737,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21849
22737
|
...this.isEncryptionNetworkOperation(service, resource.path) ? { resource: resource.path } : { spaceId: resource.space },
|
|
21850
22738
|
service,
|
|
21851
22739
|
path: resource.path,
|
|
21852
|
-
action
|
|
22740
|
+
action,
|
|
22741
|
+
caveats: cloneRecapCaveats(resource.caveats)
|
|
21853
22742
|
}));
|
|
21854
22743
|
});
|
|
21855
22744
|
}
|
|
@@ -21865,7 +22754,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21865
22754
|
service,
|
|
21866
22755
|
space: delegation.spaceId,
|
|
21867
22756
|
path: delegation.path,
|
|
21868
|
-
actions
|
|
22757
|
+
actions,
|
|
22758
|
+
...delegation.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(delegation.caveats) }
|
|
21869
22759
|
}));
|
|
21870
22760
|
}
|
|
21871
22761
|
/**
|
|
@@ -21966,6 +22856,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21966
22856
|
if (granted.service !== requested.service || !this.actionContains(granted.action, requested.action)) {
|
|
21967
22857
|
return false;
|
|
21968
22858
|
}
|
|
22859
|
+
if (requested.caveats !== void 0 && !recapCaveatsEqual(granted.caveats, requested.caveats)) {
|
|
22860
|
+
return false;
|
|
22861
|
+
}
|
|
21969
22862
|
if (granted.resource !== void 0 || requested.resource !== void 0) {
|
|
21970
22863
|
return granted.resource !== void 0 && requested.resource !== void 0 && granted.resource === requested.resource && this.pathContains(granted.path, requested.path);
|
|
21971
22864
|
}
|
|
@@ -22000,6 +22893,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22000
22893
|
invocationServiceName(service) {
|
|
22001
22894
|
return service.startsWith("tinycloud.") ? this.shortServiceName(service) : service;
|
|
22002
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
|
+
}
|
|
22003
22900
|
isEncryptionNetworkOperation(service, path) {
|
|
22004
22901
|
return service === "encryption" && path.startsWith("urn:tinycloud:encryption:");
|
|
22005
22902
|
}
|
|
@@ -22010,7 +22907,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22010
22907
|
resource: entry.resource,
|
|
22011
22908
|
service,
|
|
22012
22909
|
path: entry.path,
|
|
22013
|
-
action: entry.action
|
|
22910
|
+
action: entry.action,
|
|
22911
|
+
...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
|
|
22014
22912
|
};
|
|
22015
22913
|
}
|
|
22016
22914
|
if (this.isEncryptionNetworkOperation(service, entry.path)) {
|
|
@@ -22018,7 +22916,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22018
22916
|
resource: entry.path,
|
|
22019
22917
|
service,
|
|
22020
22918
|
path: entry.path,
|
|
22021
|
-
action: entry.action
|
|
22919
|
+
action: entry.action,
|
|
22920
|
+
...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
|
|
22022
22921
|
};
|
|
22023
22922
|
}
|
|
22024
22923
|
if (typeof entry.spaceId === "string") {
|
|
@@ -22026,7 +22925,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22026
22925
|
spaceId: entry.spaceId,
|
|
22027
22926
|
service,
|
|
22028
22927
|
path: entry.path,
|
|
22029
|
-
action: entry.action
|
|
22928
|
+
action: entry.action,
|
|
22929
|
+
...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
|
|
22030
22930
|
};
|
|
22031
22931
|
}
|
|
22032
22932
|
return void 0;
|
|
@@ -22366,6 +23266,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22366
23266
|
* @returns A portable delegation for the sub-delegate
|
|
22367
23267
|
*/
|
|
22368
23268
|
async createSubDelegation(parentDelegation, params) {
|
|
23269
|
+
this.assertPortableDelegationCaveatsPreservable(parentDelegation);
|
|
22369
23270
|
if (!this.signer) {
|
|
22370
23271
|
throw new Error("Cannot createSubDelegation() in session-only mode. Requires wallet mode.");
|
|
22371
23272
|
}
|
|
@@ -22482,7 +23383,7 @@ import {
|
|
|
22482
23383
|
parsePkhDid,
|
|
22483
23384
|
pkhDid as pkhDid3,
|
|
22484
23385
|
principalDid,
|
|
22485
|
-
principalDidEquals as
|
|
23386
|
+
principalDidEquals as principalDidEquals4,
|
|
22486
23387
|
parseCanonicalNetworkId,
|
|
22487
23388
|
TinyCloudDebugLogger,
|
|
22488
23389
|
tinyCloudDebugLogger,
|
|
@@ -22620,6 +23521,7 @@ import {
|
|
|
22620
23521
|
DEFAULT_MANIFEST_SPACE as DEFAULT_MANIFEST_SPACE2,
|
|
22621
23522
|
DEFAULT_MANIFEST_VERSION,
|
|
22622
23523
|
VAULT_PERMISSION_SERVICE,
|
|
23524
|
+
CaveatedDelegationUnsupportedError as CaveatedDelegationUnsupportedError2,
|
|
22623
23525
|
PermissionNotInManifestError as PermissionNotInManifestError2,
|
|
22624
23526
|
SessionExpiredError as SessionExpiredError2,
|
|
22625
23527
|
ManifestValidationError,
|
|
@@ -22636,6 +23538,279 @@ import {
|
|
|
22636
23538
|
} from "@tinycloud/sdk-core";
|
|
22637
23539
|
|
|
22638
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
|
+
await node.useRuntimeDelegation(installedCandidate);
|
|
23793
|
+
const installed = node.getRuntimePermissionDelegations().find((candidate) => candidate.cid === cid);
|
|
23794
|
+
if (!installed) {
|
|
23795
|
+
throw new Error("Runtime delegation activation did not install the validated authority.");
|
|
23796
|
+
}
|
|
23797
|
+
if (installed.delegationHeader.Authorization !== authorization || !didPrincipalsMatch(installed.delegateDID, signed2.audience) || installed.expiry.getTime() !== signed2.expiry.getTime() || !resourcesMatch(
|
|
23798
|
+
canonicalResourcesFromPortableDelegation(installed),
|
|
23799
|
+
signed2.resources
|
|
23800
|
+
) || normalizedHost(installed.host ?? "") !== host) {
|
|
23801
|
+
throw new Error(
|
|
23802
|
+
"Runtime delegation activation installed authority that differs from the validated authority."
|
|
23803
|
+
);
|
|
23804
|
+
}
|
|
23805
|
+
return {
|
|
23806
|
+
cid,
|
|
23807
|
+
delegation: installed,
|
|
23808
|
+
effectivePermissions: signed2.permissions,
|
|
23809
|
+
expiry: new Date(signed2.expiry),
|
|
23810
|
+
audience: signed2.audience,
|
|
23811
|
+
host: normalizedHost(installed.host ?? host)
|
|
23812
|
+
};
|
|
23813
|
+
}
|
|
22639
23814
|
async function grantAuthRequest(authority, request, options) {
|
|
22640
23815
|
if (request.kind !== "tinycloud.auth.request") {
|
|
22641
23816
|
throw new Error(
|
|
@@ -22698,10 +23873,11 @@ import {
|
|
|
22698
23873
|
SECRET_NAME_RE,
|
|
22699
23874
|
canonicalizeSecretScope,
|
|
22700
23875
|
resolveSecretListPrefix as resolveSecretListPrefix2,
|
|
22701
|
-
resolveSecretPath as
|
|
23876
|
+
resolveSecretPath as resolveSecretPath3
|
|
22702
23877
|
} from "@tinycloud/sdk-core";
|
|
22703
23878
|
import {
|
|
22704
23879
|
EncryptionService as EncryptionService2,
|
|
23880
|
+
DecryptTransportResponseError as DecryptTransportResponseError2,
|
|
22705
23881
|
parseNetworkId as parseNetworkId3,
|
|
22706
23882
|
buildNetworkId,
|
|
22707
23883
|
isNetworkId,
|
|
@@ -22777,6 +23953,7 @@ export {
|
|
|
22777
23953
|
AutoApproveSpaceCreationHandler2 as AutoApproveSpaceCreationHandler,
|
|
22778
23954
|
CapabilityKeyRegistry2 as CapabilityKeyRegistry,
|
|
22779
23955
|
CapabilityKeyRegistryErrorCodes,
|
|
23956
|
+
CaveatedDelegationUnsupportedError2 as CaveatedDelegationUnsupportedError,
|
|
22780
23957
|
DECRYPT_ACTION3 as DECRYPT_ACTION,
|
|
22781
23958
|
DECRYPT_FACT_TYPE,
|
|
22782
23959
|
DECRYPT_RESULT_TYPE,
|
|
@@ -22787,6 +23964,7 @@ export {
|
|
|
22787
23964
|
DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
|
|
22788
23965
|
DataVaultService2 as DataVaultService,
|
|
22789
23966
|
DatabaseHandle,
|
|
23967
|
+
DecryptTransportResponseError2 as DecryptTransportResponseError,
|
|
22790
23968
|
DelegatedAccess,
|
|
22791
23969
|
DelegationErrorCodes,
|
|
22792
23970
|
DelegationManager2 as DelegationManager,
|
|
@@ -22827,11 +24005,13 @@ export {
|
|
|
22827
24005
|
TinyCloudDebugLogger,
|
|
22828
24006
|
TinyCloudNode,
|
|
22829
24007
|
UnsupportedFeatureError2 as UnsupportedFeatureError,
|
|
24008
|
+
UnsupportedSessionRestoreError,
|
|
22830
24009
|
VAULT_PERMISSION_SERVICE,
|
|
22831
24010
|
VaultHeaders,
|
|
22832
24011
|
VaultPublicSpaceKVActions,
|
|
22833
24012
|
VersionCheckError,
|
|
22834
24013
|
WasmKeyProvider,
|
|
24014
|
+
activateValidatedRuntimeDelegation,
|
|
22835
24015
|
addressStorageKey,
|
|
22836
24016
|
buildCanonicalDecryptRequest,
|
|
22837
24017
|
buildDecryptAttenuation,
|
|
@@ -22898,10 +24078,10 @@ export {
|
|
|
22898
24078
|
parseSpaceUri,
|
|
22899
24079
|
pkhDid3 as pkhDid,
|
|
22900
24080
|
principalDid,
|
|
22901
|
-
|
|
24081
|
+
principalDidEquals4 as principalDidEquals,
|
|
22902
24082
|
resolveManifest2 as resolveManifest,
|
|
22903
24083
|
resolveSecretListPrefix2 as resolveSecretListPrefix,
|
|
22904
|
-
|
|
24084
|
+
resolveSecretPath3 as resolveSecretPath,
|
|
22905
24085
|
resourceCapabilitiesToSpaceAbilitiesMap2 as resourceCapabilitiesToSpaceAbilitiesMap,
|
|
22906
24086
|
serializeDelegation,
|
|
22907
24087
|
tinyCloudDebugLogger,
|