@tinycloud/node-sdk 2.2.0-beta.9 → 2.2.0
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-DcJ27GsA.d.cts → core-CNyXnUx9.d.cts} +80 -6
- package/dist/{core-DcJ27GsA.d.ts → core-CNyXnUx9.d.ts} +80 -6
- package/dist/core.cjs +708 -92
- 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 +726 -99
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +802 -114
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +805 -100
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/core.js
CHANGED
|
@@ -205,10 +205,12 @@ import {
|
|
|
205
205
|
checkNodeInfo,
|
|
206
206
|
AutoApproveSpaceCreationHandler,
|
|
207
207
|
DEFAULT_MANIFEST_SPACE,
|
|
208
|
+
ENCRYPTION_PERMISSION_SERVICE,
|
|
208
209
|
composeManifestRequest,
|
|
209
210
|
resourceCapabilitiesToAbilitiesMap,
|
|
210
211
|
resourceCapabilitiesToSpaceAbilitiesMap,
|
|
211
|
-
resolveTinyCloudHosts
|
|
212
|
+
resolveTinyCloudHosts,
|
|
213
|
+
EXPIRY
|
|
212
214
|
} from "@tinycloud/sdk-core";
|
|
213
215
|
|
|
214
216
|
// src/authorization/strategies.ts
|
|
@@ -268,7 +270,7 @@ var NodeUserAuthorization = class {
|
|
|
268
270
|
]
|
|
269
271
|
}
|
|
270
272
|
};
|
|
271
|
-
this.sessionExpirationMs = config.sessionExpirationMs ??
|
|
273
|
+
this.sessionExpirationMs = config.sessionExpirationMs ?? EXPIRY.SESSION_MS;
|
|
272
274
|
this.autoCreateSpace = config.autoCreateSpace ?? false;
|
|
273
275
|
this.spaceCreationHandler = config.spaceCreationHandler;
|
|
274
276
|
this.tinycloudHosts = config.tinycloudHosts;
|
|
@@ -321,6 +323,23 @@ var NodeUserAuthorization = class {
|
|
|
321
323
|
get tinyCloudSession() {
|
|
322
324
|
return this._tinyCloudSession;
|
|
323
325
|
}
|
|
326
|
+
/**
|
|
327
|
+
* Rehydrate the auth-layer session from previously-persisted delegation
|
|
328
|
+
* data. Used by {@link TinyCloudNode.restoreSession} so that downstream
|
|
329
|
+
* surfaces that read from `tinyCloudSession` (notably
|
|
330
|
+
* `grantRuntimePermissions`, which extracts the SIWE expiry from it) work
|
|
331
|
+
* without re-running the full sign-in flow.
|
|
332
|
+
*
|
|
333
|
+
* Caller must supply the same fields that `signIn` would have written —
|
|
334
|
+
* `siwe` is the load-bearing one because `extractSiweExpiration` returns
|
|
335
|
+
* undefined for missing SIWEs and the SDK then treats the session as
|
|
336
|
+
* expired-at-epoch-zero.
|
|
337
|
+
*/
|
|
338
|
+
setRestoredTinyCloudSession(session) {
|
|
339
|
+
this._tinyCloudSession = session;
|
|
340
|
+
this._address = session.address;
|
|
341
|
+
this._chainId = session.chainId;
|
|
342
|
+
}
|
|
324
343
|
async resolveTinyCloudHostsForSignIn(address, chainId) {
|
|
325
344
|
if (this.tinycloudHosts && this.tinycloudHosts.length > 0) {
|
|
326
345
|
return;
|
|
@@ -391,21 +410,64 @@ var NodeUserAuthorization = class {
|
|
|
391
410
|
}
|
|
392
411
|
return this.wasm.makeSpaceId(address, chainId, space);
|
|
393
412
|
}
|
|
413
|
+
defaultEncryptionNetworkId(address, chainId) {
|
|
414
|
+
return `urn:tinycloud:encryption:did:pkh:eip155:${chainId}:${address}:default`;
|
|
415
|
+
}
|
|
394
416
|
resolveSignInCapabilities(address, chainId) {
|
|
395
417
|
const request = this.getCapabilityRequest();
|
|
396
418
|
if (request === void 0) {
|
|
419
|
+
const defaultNetworkId = this.defaultEncryptionNetworkId(address, chainId);
|
|
420
|
+
const secretsSpaceId = this.wasm.makeSpaceId(address, chainId, "secrets");
|
|
397
421
|
return {
|
|
398
422
|
abilities: this.defaultActions,
|
|
399
|
-
spaceId: this.wasm.makeSpaceId(address, chainId, this.spacePrefix)
|
|
423
|
+
spaceId: this.wasm.makeSpaceId(address, chainId, this.spacePrefix),
|
|
424
|
+
spaceAbilities: {
|
|
425
|
+
[secretsSpaceId]: {
|
|
426
|
+
kv: {
|
|
427
|
+
"vault/secrets/": [
|
|
428
|
+
"tinycloud.kv/get",
|
|
429
|
+
"tinycloud.kv/put",
|
|
430
|
+
"tinycloud.kv/del",
|
|
431
|
+
"tinycloud.kv/list",
|
|
432
|
+
"tinycloud.kv/metadata"
|
|
433
|
+
]
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
},
|
|
437
|
+
rawAbilities: {
|
|
438
|
+
[defaultNetworkId]: [
|
|
439
|
+
"tinycloud.encryption/decrypt",
|
|
440
|
+
"tinycloud.encryption/network.create"
|
|
441
|
+
]
|
|
442
|
+
}
|
|
400
443
|
};
|
|
401
444
|
}
|
|
402
|
-
const
|
|
445
|
+
const rawAbilities = {};
|
|
446
|
+
const spaceResources = request.resources.filter((entry) => {
|
|
447
|
+
if (entry.service !== ENCRYPTION_PERMISSION_SERVICE) {
|
|
448
|
+
return true;
|
|
449
|
+
}
|
|
450
|
+
const existing = rawAbilities[entry.path];
|
|
451
|
+
if (existing === void 0) {
|
|
452
|
+
rawAbilities[entry.path] = [...entry.actions];
|
|
453
|
+
} else {
|
|
454
|
+
const seen = new Set(existing);
|
|
455
|
+
for (const action of entry.actions) {
|
|
456
|
+
if (!seen.has(action)) {
|
|
457
|
+
existing.push(action);
|
|
458
|
+
seen.add(action);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return false;
|
|
463
|
+
});
|
|
464
|
+
const primarySpaceName = spaceResources.find((entry) => entry.space !== "account")?.space ?? DEFAULT_MANIFEST_SPACE;
|
|
403
465
|
const primarySpaceId = this.resolveSpaceName(
|
|
404
466
|
primarySpaceName,
|
|
405
467
|
address,
|
|
406
468
|
chainId
|
|
407
469
|
);
|
|
408
|
-
const bySpace = resourceCapabilitiesToSpaceAbilitiesMap(
|
|
470
|
+
const bySpace = resourceCapabilitiesToSpaceAbilitiesMap(spaceResources);
|
|
409
471
|
const spaceAbilities = {};
|
|
410
472
|
for (const [space, abilities] of Object.entries(bySpace)) {
|
|
411
473
|
spaceAbilities[this.resolveSpaceName(space, address, chainId)] = abilities;
|
|
@@ -413,7 +475,8 @@ var NodeUserAuthorization = class {
|
|
|
413
475
|
return {
|
|
414
476
|
abilities: spaceAbilities[primarySpaceId] ?? resourceCapabilitiesToAbilitiesMap([]),
|
|
415
477
|
spaceId: primarySpaceId,
|
|
416
|
-
spaceAbilities
|
|
478
|
+
spaceAbilities,
|
|
479
|
+
rawAbilities: Object.keys(rawAbilities).length > 0 ? rawAbilities : void 0
|
|
417
480
|
};
|
|
418
481
|
}
|
|
419
482
|
/**
|
|
@@ -638,6 +701,7 @@ var NodeUserAuthorization = class {
|
|
|
638
701
|
const prepared = this.wasm.prepareSession({
|
|
639
702
|
abilities: capabilityPlan.abilities,
|
|
640
703
|
...capabilityPlan.spaceAbilities !== void 0 ? { spaceAbilities: capabilityPlan.spaceAbilities } : {},
|
|
704
|
+
...capabilityPlan.rawAbilities !== void 0 ? { rawAbilities: capabilityPlan.rawAbilities } : {},
|
|
641
705
|
address,
|
|
642
706
|
chainId,
|
|
643
707
|
domain: this.domain,
|
|
@@ -783,6 +847,7 @@ var NodeUserAuthorization = class {
|
|
|
783
847
|
const prepared = this.wasm.prepareSession({
|
|
784
848
|
abilities: capabilityPlan.abilities,
|
|
785
849
|
...capabilityPlan.spaceAbilities !== void 0 ? { spaceAbilities: capabilityPlan.spaceAbilities } : {},
|
|
850
|
+
...capabilityPlan.rawAbilities !== void 0 ? { rawAbilities: capabilityPlan.rawAbilities } : {},
|
|
786
851
|
address,
|
|
787
852
|
chainId,
|
|
788
853
|
domain: this.domain,
|
|
@@ -959,6 +1024,7 @@ import {
|
|
|
959
1024
|
DuckDbService as DuckDbService2,
|
|
960
1025
|
HooksService as HooksService2,
|
|
961
1026
|
DataVaultService,
|
|
1027
|
+
EncryptionService,
|
|
962
1028
|
SecretsService,
|
|
963
1029
|
createVaultCrypto,
|
|
964
1030
|
ServiceContext as ServiceContext2,
|
|
@@ -970,12 +1036,17 @@ import {
|
|
|
970
1036
|
UnsupportedFeatureError,
|
|
971
1037
|
makePublicSpaceId,
|
|
972
1038
|
ACCOUNT_REGISTRY_SPACE,
|
|
1039
|
+
ENCRYPTION_PERMISSION_SERVICE as ENCRYPTION_PERMISSION_SERVICE2,
|
|
973
1040
|
PermissionNotInManifestError,
|
|
974
1041
|
SessionExpiredError,
|
|
975
1042
|
expandPermissionEntries as expandPermissionEntriesCore,
|
|
976
1043
|
isCapabilitySubset,
|
|
977
1044
|
parseRecapCapabilities,
|
|
978
|
-
SERVICE_LONG_TO_SHORT
|
|
1045
|
+
SERVICE_LONG_TO_SHORT,
|
|
1046
|
+
EXPIRY as EXPIRY3,
|
|
1047
|
+
canonicalHashHex,
|
|
1048
|
+
canonicalizeEncryptionJson,
|
|
1049
|
+
verifyDidKeyEd25519Signature
|
|
979
1050
|
} from "@tinycloud/sdk-core";
|
|
980
1051
|
|
|
981
1052
|
// src/DelegatedAccess.ts
|
|
@@ -1158,7 +1229,8 @@ function createWasmKeyProvider(sessionManager) {
|
|
|
1158
1229
|
// src/delegateToHelpers.ts
|
|
1159
1230
|
import {
|
|
1160
1231
|
parseExpiry,
|
|
1161
|
-
SiweMessage
|
|
1232
|
+
SiweMessage,
|
|
1233
|
+
EXPIRY as EXPIRY2
|
|
1162
1234
|
} from "@tinycloud/sdk-core";
|
|
1163
1235
|
function legacyParamsToPermissionEntries(actions, path, spaceIdOverride) {
|
|
1164
1236
|
const byService = /* @__PURE__ */ new Map();
|
|
@@ -1190,9 +1262,10 @@ function legacyParamsToPermissionEntries(actions, path, spaceIdOverride) {
|
|
|
1190
1262
|
}
|
|
1191
1263
|
return entries;
|
|
1192
1264
|
}
|
|
1265
|
+
var DEFAULT_DELEGATION_EXPIRY_MS = EXPIRY2.SESSION_MS;
|
|
1193
1266
|
function resolveExpiryMs(expiry) {
|
|
1194
1267
|
if (expiry === void 0) {
|
|
1195
|
-
return
|
|
1268
|
+
return DEFAULT_DELEGATION_EXPIRY_MS;
|
|
1196
1269
|
}
|
|
1197
1270
|
if (typeof expiry === "number") {
|
|
1198
1271
|
if (!Number.isFinite(expiry) || expiry <= 0) {
|
|
@@ -1221,10 +1294,11 @@ function extractSiweExpiration(siwe) {
|
|
|
1221
1294
|
// src/NodeSecretsService.ts
|
|
1222
1295
|
import {
|
|
1223
1296
|
ErrorCodes,
|
|
1297
|
+
resolveSecretListPrefix,
|
|
1298
|
+
resolveSecretPath,
|
|
1299
|
+
expandPermissionEntries,
|
|
1224
1300
|
resolveManifest
|
|
1225
1301
|
} from "@tinycloud/sdk-core";
|
|
1226
|
-
var SECRET_NAME_RE = /^[A-Z][A-Z0-9_]*$/;
|
|
1227
|
-
var SECRET_PREFIX = "secrets/";
|
|
1228
1302
|
var SECRETS_SPACE = "secrets";
|
|
1229
1303
|
function ok() {
|
|
1230
1304
|
return { ok: true, data: void 0 };
|
|
@@ -1241,27 +1315,39 @@ function secretsError(code, message, cause) {
|
|
|
1241
1315
|
};
|
|
1242
1316
|
}
|
|
1243
1317
|
function displayActionUrn(action) {
|
|
1244
|
-
|
|
1318
|
+
switch (action) {
|
|
1319
|
+
case "get":
|
|
1320
|
+
return "tinycloud.kv/get";
|
|
1321
|
+
case "put":
|
|
1322
|
+
return "tinycloud.kv/put";
|
|
1323
|
+
case "del":
|
|
1324
|
+
return "tinycloud.kv/del";
|
|
1325
|
+
case "list":
|
|
1326
|
+
return "tinycloud.kv/list";
|
|
1327
|
+
}
|
|
1245
1328
|
}
|
|
1246
|
-
function
|
|
1247
|
-
return
|
|
1329
|
+
function secretActionName(action) {
|
|
1330
|
+
return action;
|
|
1248
1331
|
}
|
|
1249
|
-
function secretPermissionEntries(name, action) {
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1332
|
+
function secretPermissionEntries(name, options, action, encryptionNetworkId) {
|
|
1333
|
+
const entries = [];
|
|
1334
|
+
const path = action === "list" ? resolveSecretListPrefix(options) : resolveSecretPath(name, options).permissionPaths.vault;
|
|
1335
|
+
entries.push({
|
|
1336
|
+
service: "tinycloud.kv",
|
|
1337
|
+
space: SECRETS_SPACE,
|
|
1338
|
+
path,
|
|
1339
|
+
actions: [secretActionName(action)],
|
|
1340
|
+
skipPrefix: true
|
|
1341
|
+
});
|
|
1342
|
+
if (action === "get" && encryptionNetworkId !== void 0) {
|
|
1343
|
+
entries.push({
|
|
1344
|
+
service: "tinycloud.encryption",
|
|
1345
|
+
path: encryptionNetworkId,
|
|
1346
|
+
actions: ["decrypt"],
|
|
1256
1347
|
skipPrefix: true
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
function secretResourcePath(base, name) {
|
|
1261
|
-
return `${base}/${SECRET_PREFIX}${name}`;
|
|
1262
|
-
}
|
|
1263
|
-
function isSecretsSpace(space) {
|
|
1264
|
-
return space === SECRETS_SPACE || space.endsWith(`:${SECRETS_SPACE}`);
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
return entries;
|
|
1265
1351
|
}
|
|
1266
1352
|
var NodeSecretsService = class {
|
|
1267
1353
|
constructor(config) {
|
|
@@ -1289,48 +1375,62 @@ var NodeSecretsService = class {
|
|
|
1289
1375
|
this.shouldRestoreUnlock = false;
|
|
1290
1376
|
this.service.lock();
|
|
1291
1377
|
}
|
|
1292
|
-
get(name) {
|
|
1293
|
-
|
|
1378
|
+
async get(name, options) {
|
|
1379
|
+
const permission = await this.ensurePermission(name, options, "get");
|
|
1380
|
+
if (!permission.ok) return permission;
|
|
1381
|
+
return options === void 0 ? this.service.get(name) : this.service.get(name, options);
|
|
1294
1382
|
}
|
|
1295
|
-
async put(name, value) {
|
|
1296
|
-
const permission = await this.
|
|
1383
|
+
async put(name, value, options) {
|
|
1384
|
+
const permission = await this.ensurePermission(name, options, "put");
|
|
1297
1385
|
if (!permission.ok) return permission;
|
|
1298
|
-
return this.service.put(name, value);
|
|
1386
|
+
return options === void 0 ? this.service.put(name, value) : this.service.put(name, value, options);
|
|
1299
1387
|
}
|
|
1300
|
-
async delete(name) {
|
|
1301
|
-
const permission = await this.
|
|
1388
|
+
async delete(name, options) {
|
|
1389
|
+
const permission = await this.ensurePermission(name, options, "del");
|
|
1302
1390
|
if (!permission.ok) return permission;
|
|
1303
|
-
return this.service.delete(name);
|
|
1391
|
+
return options === void 0 ? this.service.delete(name) : this.service.delete(name, options);
|
|
1304
1392
|
}
|
|
1305
|
-
list() {
|
|
1306
|
-
|
|
1393
|
+
async list(options) {
|
|
1394
|
+
const permission = await this.ensurePermission("", options, "list");
|
|
1395
|
+
if (!permission.ok) return permission;
|
|
1396
|
+
return options === void 0 ? this.service.list() : this.service.list(options);
|
|
1307
1397
|
}
|
|
1308
1398
|
get service() {
|
|
1309
1399
|
return this.config.getService();
|
|
1310
1400
|
}
|
|
1311
|
-
async
|
|
1312
|
-
|
|
1401
|
+
async ensurePermission(name, options, action) {
|
|
1402
|
+
const target = name || "secrets";
|
|
1403
|
+
let permissionEntries;
|
|
1404
|
+
try {
|
|
1405
|
+
permissionEntries = secretPermissionEntries(
|
|
1406
|
+
name,
|
|
1407
|
+
options,
|
|
1408
|
+
action,
|
|
1409
|
+
action === "get" ? this.config.getEncryptionNetworkId?.() : void 0
|
|
1410
|
+
);
|
|
1411
|
+
} catch (error) {
|
|
1313
1412
|
return secretsError(
|
|
1314
1413
|
ErrorCodes.INVALID_INPUT,
|
|
1315
|
-
|
|
1414
|
+
error instanceof Error ? error.message : String(error),
|
|
1415
|
+
error instanceof Error ? error : void 0
|
|
1316
1416
|
);
|
|
1317
1417
|
}
|
|
1318
|
-
if (this.
|
|
1418
|
+
if (this.hasPermission(permissionEntries)) {
|
|
1319
1419
|
return ok();
|
|
1320
1420
|
}
|
|
1321
1421
|
if (!this.config.canEscalate()) {
|
|
1322
1422
|
return secretsError(
|
|
1323
1423
|
ErrorCodes.PERMISSION_DENIED,
|
|
1324
|
-
`Cannot autosign ${displayActionUrn(action)} for ${
|
|
1424
|
+
`Cannot autosign ${displayActionUrn(action)} for ${target}; TinyCloudNode needs wallet mode with a signer or privateKey.`
|
|
1325
1425
|
);
|
|
1326
1426
|
}
|
|
1327
1427
|
try {
|
|
1328
|
-
await this.config.grantPermissions(
|
|
1428
|
+
await this.config.grantPermissions(permissionEntries);
|
|
1329
1429
|
return this.restoreUnlockAfterEscalation();
|
|
1330
1430
|
} catch (error) {
|
|
1331
1431
|
return secretsError(
|
|
1332
1432
|
ErrorCodes.PERMISSION_DENIED,
|
|
1333
|
-
error instanceof Error ? error.message : `Autosign escalation for ${displayActionUrn(action)} on ${
|
|
1433
|
+
error instanceof Error ? error.message : `Autosign escalation for ${displayActionUrn(action)} on ${target} failed.`,
|
|
1334
1434
|
error instanceof Error ? error : void 0
|
|
1335
1435
|
);
|
|
1336
1436
|
}
|
|
@@ -1341,26 +1441,117 @@ var NodeSecretsService = class {
|
|
|
1341
1441
|
}
|
|
1342
1442
|
return this.service.unlock(this.unlockSigner);
|
|
1343
1443
|
}
|
|
1344
|
-
|
|
1444
|
+
hasPermission(permissionEntries) {
|
|
1445
|
+
if (this.config.hasPermissions?.(permissionEntries)) {
|
|
1446
|
+
return true;
|
|
1447
|
+
}
|
|
1345
1448
|
const manifest = this.config.getManifest();
|
|
1346
1449
|
if (manifest === void 0) {
|
|
1347
1450
|
return false;
|
|
1348
1451
|
}
|
|
1349
1452
|
const manifests = Array.isArray(manifest) ? manifest : [manifest];
|
|
1350
|
-
const
|
|
1351
|
-
return
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
(resource) => resource.service ===
|
|
1356
|
-
)
|
|
1357
|
-
)
|
|
1358
|
-
|
|
1453
|
+
const requestedEntries = expandPermissionEntries(permissionEntries);
|
|
1454
|
+
return requestedEntries.every(
|
|
1455
|
+
(entry) => manifests.some((candidate) => {
|
|
1456
|
+
const resolved = resolveManifest(candidate);
|
|
1457
|
+
return resolved.resources.some(
|
|
1458
|
+
(resource) => resource.service === entry.service && resource.space === entry.space && resource.path === entry.path && entry.actions.every((action) => resource.actions.includes(action))
|
|
1459
|
+
);
|
|
1460
|
+
})
|
|
1461
|
+
);
|
|
1359
1462
|
}
|
|
1360
1463
|
};
|
|
1361
1464
|
|
|
1362
1465
|
// src/TinyCloudNode.ts
|
|
1363
1466
|
var DEFAULT_HOST = "https://node.tinycloud.xyz";
|
|
1467
|
+
var DEFAULT_ENCRYPTION_NETWORK_NAME = "default";
|
|
1468
|
+
var NETWORK_CREATE_ACTION = "tinycloud.encryption/network.create";
|
|
1469
|
+
var DECRYPT_ACTION = "tinycloud.encryption/decrypt";
|
|
1470
|
+
var NETWORK_ADMIN_TYPE = "tinycloud.encryption.network-admin/v1";
|
|
1471
|
+
var DEFAULT_SESSION_EXPIRATION_MS = EXPIRY3.SESSION_MS;
|
|
1472
|
+
function base64UrlEncode(bytes) {
|
|
1473
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
1474
|
+
let output = "";
|
|
1475
|
+
for (let i = 0; i < bytes.length; i += 3) {
|
|
1476
|
+
const a = bytes[i];
|
|
1477
|
+
const b = bytes[i + 1];
|
|
1478
|
+
const c = bytes[i + 2];
|
|
1479
|
+
const triplet = a << 16 | (b ?? 0) << 8 | (c ?? 0);
|
|
1480
|
+
output += alphabet[triplet >> 18 & 63];
|
|
1481
|
+
output += alphabet[triplet >> 12 & 63];
|
|
1482
|
+
if (i + 1 < bytes.length) output += alphabet[triplet >> 6 & 63];
|
|
1483
|
+
if (i + 2 < bytes.length) output += alphabet[triplet & 63];
|
|
1484
|
+
}
|
|
1485
|
+
return output;
|
|
1486
|
+
}
|
|
1487
|
+
function base64UrlDecode(value) {
|
|
1488
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
1489
|
+
const bytes = [];
|
|
1490
|
+
let buffer = 0;
|
|
1491
|
+
let bits = 0;
|
|
1492
|
+
for (const char of value) {
|
|
1493
|
+
const index = alphabet.indexOf(char);
|
|
1494
|
+
if (index < 0) {
|
|
1495
|
+
throw new Error("invalid base64url input");
|
|
1496
|
+
}
|
|
1497
|
+
buffer = buffer << 6 | index;
|
|
1498
|
+
bits += 6;
|
|
1499
|
+
if (bits >= 8) {
|
|
1500
|
+
bits -= 8;
|
|
1501
|
+
bytes.push(buffer >> bits & 255);
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
return new Uint8Array(bytes);
|
|
1505
|
+
}
|
|
1506
|
+
async function signJwtInputWithJwk(signingInput, jwk) {
|
|
1507
|
+
const bytes = new TextEncoder().encode(signingInput);
|
|
1508
|
+
try {
|
|
1509
|
+
const subtle = globalThis.crypto?.subtle;
|
|
1510
|
+
if (!subtle) {
|
|
1511
|
+
throw new Error("WebCrypto subtle API is unavailable");
|
|
1512
|
+
}
|
|
1513
|
+
const key = await subtle.importKey(
|
|
1514
|
+
"jwk",
|
|
1515
|
+
jwk,
|
|
1516
|
+
{ name: "Ed25519" },
|
|
1517
|
+
false,
|
|
1518
|
+
["sign"]
|
|
1519
|
+
);
|
|
1520
|
+
return new Uint8Array(await subtle.sign({ name: "Ed25519" }, key, bytes));
|
|
1521
|
+
} catch {
|
|
1522
|
+
const nodeCrypto = await import("crypto");
|
|
1523
|
+
const key = nodeCrypto.createPrivateKey({ key: jwk, format: "jwk" });
|
|
1524
|
+
return new Uint8Array(nodeCrypto.sign(null, Buffer.from(bytes), key));
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
async function rewriteInvocationAudience(authorization, audience, jwk) {
|
|
1528
|
+
const [headerPart, payloadPart] = authorization.split(".");
|
|
1529
|
+
if (!headerPart || !payloadPart) {
|
|
1530
|
+
throw new Error("invalid invocation authorization");
|
|
1531
|
+
}
|
|
1532
|
+
const header = JSON.parse(new TextDecoder().decode(base64UrlDecode(headerPart)));
|
|
1533
|
+
const payload = JSON.parse(new TextDecoder().decode(base64UrlDecode(payloadPart)));
|
|
1534
|
+
payload.aud = audience;
|
|
1535
|
+
const signingInput = `${base64UrlEncode(
|
|
1536
|
+
new TextEncoder().encode(JSON.stringify(header))
|
|
1537
|
+
)}.${base64UrlEncode(new TextEncoder().encode(JSON.stringify(payload)))}`;
|
|
1538
|
+
const signature = await signJwtInputWithJwk(signingInput, jwk);
|
|
1539
|
+
return `${signingInput}.${base64UrlEncode(signature)}`;
|
|
1540
|
+
}
|
|
1541
|
+
function authorizationHeader(headers) {
|
|
1542
|
+
if (Array.isArray(headers)) {
|
|
1543
|
+
const entry = headers.find(([name]) => name.toLowerCase() === "authorization");
|
|
1544
|
+
if (!entry) {
|
|
1545
|
+
throw new Error("network invocation did not include an Authorization header");
|
|
1546
|
+
}
|
|
1547
|
+
return entry[1];
|
|
1548
|
+
}
|
|
1549
|
+
const value = headers.Authorization ?? headers.authorization;
|
|
1550
|
+
if (!value) {
|
|
1551
|
+
throw new Error("network invocation did not include an Authorization header");
|
|
1552
|
+
}
|
|
1553
|
+
return value;
|
|
1554
|
+
}
|
|
1364
1555
|
var _TinyCloudNode = class _TinyCloudNode {
|
|
1365
1556
|
/**
|
|
1366
1557
|
* Create a new TinyCloudNode instance.
|
|
@@ -1405,12 +1596,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1405
1596
|
throw new Error("WASM binding does not support invokeAny");
|
|
1406
1597
|
}
|
|
1407
1598
|
const grant = this.findGrantForOperations(
|
|
1408
|
-
entries.
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
action: entry.action
|
|
1413
|
-
}))
|
|
1599
|
+
entries.flatMap((entry) => {
|
|
1600
|
+
const operation = this.operationFromInvokeAnyEntry(entry);
|
|
1601
|
+
return operation ? [operation] : [];
|
|
1602
|
+
})
|
|
1414
1603
|
);
|
|
1415
1604
|
return this.wasmBindings.invokeAny(grant?.session ?? session, entries, facts);
|
|
1416
1605
|
};
|
|
@@ -1503,7 +1692,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1503
1692
|
sessionStorage: config.sessionStorage ?? new MemorySessionStorage(),
|
|
1504
1693
|
domain: this.siweDomain,
|
|
1505
1694
|
spacePrefix: config.prefix,
|
|
1506
|
-
sessionExpirationMs: config.sessionExpirationMs ??
|
|
1695
|
+
sessionExpirationMs: config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS,
|
|
1507
1696
|
tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
|
|
1508
1697
|
tinycloudRegistryUrl: config.tinycloudRegistryUrl,
|
|
1509
1698
|
tinycloudFallbackHosts: config.tinycloudFallbackHosts,
|
|
@@ -1638,6 +1827,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1638
1827
|
this._duckdb = void 0;
|
|
1639
1828
|
this._hooks = void 0;
|
|
1640
1829
|
this._vault = void 0;
|
|
1830
|
+
this._encryption = void 0;
|
|
1641
1831
|
this._baseSecrets = void 0;
|
|
1642
1832
|
this._secrets = void 0;
|
|
1643
1833
|
this._spaceService = void 0;
|
|
@@ -1646,6 +1836,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1646
1836
|
await this.tc.signIn(options);
|
|
1647
1837
|
this.syncResolvedHostFromAuth();
|
|
1648
1838
|
this.initializeServices();
|
|
1839
|
+
if (this.config.manifest === void 0 && this.config.capabilityRequest === void 0) {
|
|
1840
|
+
await this.ensureOwnedSpaceHosted(this.ownedSpaceId("secrets"));
|
|
1841
|
+
}
|
|
1649
1842
|
await this.writeManifestRegistryRecords();
|
|
1650
1843
|
this.notificationHandler.success("Successfully signed in");
|
|
1651
1844
|
}
|
|
@@ -1728,6 +1921,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1728
1921
|
this._duckdb = void 0;
|
|
1729
1922
|
this._hooks = void 0;
|
|
1730
1923
|
this._vault = void 0;
|
|
1924
|
+
this._encryption = void 0;
|
|
1731
1925
|
this._baseSecrets = void 0;
|
|
1732
1926
|
this._secrets = void 0;
|
|
1733
1927
|
this._spaceService = void 0;
|
|
@@ -1769,6 +1963,33 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1769
1963
|
this._vault.initialize(this._serviceContext);
|
|
1770
1964
|
this._serviceContext.registerService("vault", this._vault);
|
|
1771
1965
|
this.initializeV2Services(serviceSession);
|
|
1966
|
+
if (sessionData.siwe && sessionData.address && sessionData.chainId) {
|
|
1967
|
+
const tcSession = {
|
|
1968
|
+
address: sessionData.address,
|
|
1969
|
+
chainId: sessionData.chainId,
|
|
1970
|
+
sessionKey: JSON.stringify(sessionData.jwk),
|
|
1971
|
+
spaceId: sessionData.spaceId,
|
|
1972
|
+
delegationCid: sessionData.delegationCid,
|
|
1973
|
+
delegationHeader: sessionData.delegationHeader,
|
|
1974
|
+
verificationMethod: sessionData.verificationMethod,
|
|
1975
|
+
jwk: sessionData.jwk,
|
|
1976
|
+
siwe: sessionData.siwe,
|
|
1977
|
+
signature: sessionData.signature ?? ""
|
|
1978
|
+
};
|
|
1979
|
+
if (this.auth) {
|
|
1980
|
+
this.auth.setRestoredTinyCloudSession(tcSession);
|
|
1981
|
+
} else {
|
|
1982
|
+
this._restoredTcSession = tcSession;
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
/**
|
|
1987
|
+
* Resolve the currently-active TinyCloudSession, preferring the auth
|
|
1988
|
+
* layer's value (wallet mode) and falling back to the node-level
|
|
1989
|
+
* rehydration set by {@link restoreSession} (session-only mode).
|
|
1990
|
+
*/
|
|
1991
|
+
currentTinyCloudSession() {
|
|
1992
|
+
return this.auth?.tinyCloudSession ?? this._restoredTcSession;
|
|
1772
1993
|
}
|
|
1773
1994
|
/**
|
|
1774
1995
|
* Connect a wallet to upgrade from session-only mode to wallet mode.
|
|
@@ -1813,7 +2034,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1813
2034
|
sessionStorage: options?.sessionStorage ?? this.config.sessionStorage ?? new MemorySessionStorage(),
|
|
1814
2035
|
domain: this.siweDomain,
|
|
1815
2036
|
spacePrefix: prefix,
|
|
1816
|
-
sessionExpirationMs: this.config.sessionExpirationMs ??
|
|
2037
|
+
sessionExpirationMs: this.config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS,
|
|
1817
2038
|
tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
|
|
1818
2039
|
tinycloudRegistryUrl: this.config.tinycloudRegistryUrl,
|
|
1819
2040
|
tinycloudFallbackHosts: this.config.tinycloudFallbackHosts,
|
|
@@ -1857,7 +2078,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1857
2078
|
sessionStorage: options?.sessionStorage ?? this.config.sessionStorage ?? new MemorySessionStorage(),
|
|
1858
2079
|
domain: this.siweDomain,
|
|
1859
2080
|
spacePrefix: prefix,
|
|
1860
|
-
sessionExpirationMs: this.config.sessionExpirationMs ??
|
|
2081
|
+
sessionExpirationMs: this.config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS,
|
|
1861
2082
|
tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
|
|
1862
2083
|
tinycloudRegistryUrl: this.config.tinycloudRegistryUrl,
|
|
1863
2084
|
tinycloudFallbackHosts: this.config.tinycloudFallbackHosts,
|
|
@@ -1880,7 +2101,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1880
2101
|
* @internal
|
|
1881
2102
|
*/
|
|
1882
2103
|
initializeServices() {
|
|
1883
|
-
const session = this.
|
|
2104
|
+
const session = this.currentTinyCloudSession();
|
|
1884
2105
|
if (!session) {
|
|
1885
2106
|
return;
|
|
1886
2107
|
}
|
|
@@ -1938,6 +2159,163 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1938
2159
|
}
|
|
1939
2160
|
return kvService;
|
|
1940
2161
|
}
|
|
2162
|
+
getDefaultEncryptionNetworkId(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
|
|
2163
|
+
return `urn:tinycloud:encryption:${this.did}:${name}`;
|
|
2164
|
+
}
|
|
2165
|
+
requireServiceSession() {
|
|
2166
|
+
const session = this._serviceContext?.session;
|
|
2167
|
+
if (!session) {
|
|
2168
|
+
throw new Error("Not signed in. Call signIn() first.");
|
|
2169
|
+
}
|
|
2170
|
+
return session;
|
|
2171
|
+
}
|
|
2172
|
+
createEncryptionCrypto() {
|
|
2173
|
+
const wasm = this.wasmBindings;
|
|
2174
|
+
const columnEncrypt = (key, plaintext) => {
|
|
2175
|
+
const encrypted = wasm.vault_encrypt(key, plaintext);
|
|
2176
|
+
const out = new Uint8Array(1 + encrypted.length);
|
|
2177
|
+
out[0] = 1;
|
|
2178
|
+
out.set(encrypted, 1);
|
|
2179
|
+
return out;
|
|
2180
|
+
};
|
|
2181
|
+
const columnDecrypt = (key, blob) => {
|
|
2182
|
+
if (blob[0] !== 1) {
|
|
2183
|
+
return blob;
|
|
2184
|
+
}
|
|
2185
|
+
return wasm.vault_decrypt(key, blob.slice(1));
|
|
2186
|
+
};
|
|
2187
|
+
return {
|
|
2188
|
+
sha256: (data) => wasm.vault_sha256(data),
|
|
2189
|
+
randomBytes: (length) => wasm.vault_random_bytes(length),
|
|
2190
|
+
x25519FromSeed: (seed) => wasm.vault_x25519_from_seed(seed),
|
|
2191
|
+
x25519Dh: (privateKey, publicKey) => wasm.vault_x25519_dh(privateKey, publicKey),
|
|
2192
|
+
authEncrypt: (key, plaintext) => wasm.vault_encrypt(key, plaintext),
|
|
2193
|
+
authDecrypt: (key, ciphertext) => wasm.vault_decrypt(key, ciphertext),
|
|
2194
|
+
sealToNetworkKey: (networkPublicKey, symmetricKey) => {
|
|
2195
|
+
const seed = wasm.vault_random_bytes(32);
|
|
2196
|
+
const ephemeral = wasm.vault_x25519_from_seed(seed);
|
|
2197
|
+
const shared = wasm.vault_x25519_dh(
|
|
2198
|
+
ephemeral.privateKey,
|
|
2199
|
+
networkPublicKey
|
|
2200
|
+
);
|
|
2201
|
+
const encrypted = columnEncrypt(shared, symmetricKey);
|
|
2202
|
+
const out = new Uint8Array(ephemeral.publicKey.length + encrypted.length);
|
|
2203
|
+
out.set(ephemeral.publicKey, 0);
|
|
2204
|
+
out.set(encrypted, ephemeral.publicKey.length);
|
|
2205
|
+
return out;
|
|
2206
|
+
},
|
|
2207
|
+
openWithReceiverKey: (receiverPrivateKey, wrappedKey) => {
|
|
2208
|
+
const peerPublic = wrappedKey.slice(0, 32);
|
|
2209
|
+
const ciphertext = wrappedKey.slice(32);
|
|
2210
|
+
const shared = wasm.vault_x25519_dh(receiverPrivateKey, peerPublic);
|
|
2211
|
+
return columnDecrypt(shared, ciphertext);
|
|
2212
|
+
},
|
|
2213
|
+
verifyNodeSignature: (nodeId, message, signature) => verifyDidKeyEd25519Signature(nodeId, message, signature)
|
|
2214
|
+
};
|
|
2215
|
+
}
|
|
2216
|
+
async fetchNodeId() {
|
|
2217
|
+
const response = await fetch(`${this.config.host}/info`);
|
|
2218
|
+
if (!response.ok) {
|
|
2219
|
+
throw new Error(`Failed to fetch node info: HTTP ${response.status}`);
|
|
2220
|
+
}
|
|
2221
|
+
const info = await response.json();
|
|
2222
|
+
if (typeof info.nodeId !== "string" || info.nodeId.length === 0) {
|
|
2223
|
+
throw new Error("Node /info response did not include nodeId");
|
|
2224
|
+
}
|
|
2225
|
+
return info.nodeId;
|
|
2226
|
+
}
|
|
2227
|
+
async signRawNetworkAuthorization(input) {
|
|
2228
|
+
if (!this.wasmBindings.invokeAny) {
|
|
2229
|
+
throw new Error("WASM binding does not support raw-resource invokeAny");
|
|
2230
|
+
}
|
|
2231
|
+
if (!this.wasmBindings.computeCid) {
|
|
2232
|
+
throw new Error("WASM binding does not support invocation CID computation");
|
|
2233
|
+
}
|
|
2234
|
+
const session = this.requireServiceSession();
|
|
2235
|
+
const headers = this.invokeAnyWithRuntimePermissions(
|
|
2236
|
+
session,
|
|
2237
|
+
[
|
|
2238
|
+
{
|
|
2239
|
+
resource: input.networkId,
|
|
2240
|
+
service: "encryption",
|
|
2241
|
+
path: input.networkId,
|
|
2242
|
+
action: input.action
|
|
2243
|
+
}
|
|
2244
|
+
],
|
|
2245
|
+
[input.facts]
|
|
2246
|
+
);
|
|
2247
|
+
const authorization = authorizationHeader(headers);
|
|
2248
|
+
const audienceBound = await rewriteInvocationAudience(
|
|
2249
|
+
authorization,
|
|
2250
|
+
input.targetNode,
|
|
2251
|
+
session.jwk
|
|
2252
|
+
);
|
|
2253
|
+
return {
|
|
2254
|
+
authorization: audienceBound,
|
|
2255
|
+
invocationCid: this.wasmBindings.computeCid(
|
|
2256
|
+
new TextEncoder().encode(audienceBound),
|
|
2257
|
+
0x55n
|
|
2258
|
+
)
|
|
2259
|
+
};
|
|
2260
|
+
}
|
|
2261
|
+
createEncryptionService() {
|
|
2262
|
+
const crypto = this.createEncryptionCrypto();
|
|
2263
|
+
const transport = {
|
|
2264
|
+
postDecrypt: async ({ networkId, authorization, canonicalBody }) => {
|
|
2265
|
+
const response = await fetch(
|
|
2266
|
+
`${this.config.host}/encryption/networks/${encodeURIComponent(networkId)}/decrypt`,
|
|
2267
|
+
{
|
|
2268
|
+
method: "POST",
|
|
2269
|
+
headers: {
|
|
2270
|
+
Authorization: authorization,
|
|
2271
|
+
"Content-Type": "application/json"
|
|
2272
|
+
},
|
|
2273
|
+
body: canonicalBody
|
|
2274
|
+
}
|
|
2275
|
+
);
|
|
2276
|
+
if (!response.ok) {
|
|
2277
|
+
throw new Error(
|
|
2278
|
+
`decrypt failed ${response.status}: ${await response.text()}`
|
|
2279
|
+
);
|
|
2280
|
+
}
|
|
2281
|
+
return await response.json();
|
|
2282
|
+
}
|
|
2283
|
+
};
|
|
2284
|
+
return new EncryptionService({
|
|
2285
|
+
crypto,
|
|
2286
|
+
signer: {
|
|
2287
|
+
signDecryptInvocation: async (input) => {
|
|
2288
|
+
const signed = await this.signRawNetworkAuthorization({
|
|
2289
|
+
targetNode: input.targetNode,
|
|
2290
|
+
networkId: input.networkId,
|
|
2291
|
+
action: DECRYPT_ACTION,
|
|
2292
|
+
facts: input.facts
|
|
2293
|
+
});
|
|
2294
|
+
return {
|
|
2295
|
+
...signed,
|
|
2296
|
+
canonicalBody: canonicalizeEncryptionJson(
|
|
2297
|
+
input.body
|
|
2298
|
+
)
|
|
2299
|
+
};
|
|
2300
|
+
}
|
|
2301
|
+
},
|
|
2302
|
+
transport,
|
|
2303
|
+
node: {
|
|
2304
|
+
fetchByNetworkId: (networkId) => this.getEncryptionNetwork(networkId)
|
|
2305
|
+
}
|
|
2306
|
+
});
|
|
2307
|
+
}
|
|
2308
|
+
getEncryptionService() {
|
|
2309
|
+
if (!this._serviceContext) {
|
|
2310
|
+
throw new Error("Not signed in. Call signIn() first.");
|
|
2311
|
+
}
|
|
2312
|
+
if (!this._encryption) {
|
|
2313
|
+
this._encryption = this.createEncryptionService();
|
|
2314
|
+
this._encryption.initialize(this._serviceContext);
|
|
2315
|
+
this._serviceContext.registerService("encryption", this._encryption);
|
|
2316
|
+
}
|
|
2317
|
+
return this._encryption;
|
|
2318
|
+
}
|
|
1941
2319
|
createVaultService(spaceId, kv) {
|
|
1942
2320
|
const wasm = this.wasmBindings;
|
|
1943
2321
|
const vaultCrypto = createVaultCrypto({
|
|
@@ -1953,6 +2331,13 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1953
2331
|
return new DataVaultService({
|
|
1954
2332
|
spaceId,
|
|
1955
2333
|
crypto: vaultCrypto,
|
|
2334
|
+
encryption: {
|
|
2335
|
+
networkId: this.getDefaultEncryptionNetworkId(),
|
|
2336
|
+
service: this.getEncryptionService(),
|
|
2337
|
+
decryptCapabilityProof: () => ({
|
|
2338
|
+
proofs: [this.requireServiceSession().delegationCid]
|
|
2339
|
+
})
|
|
2340
|
+
},
|
|
1956
2341
|
tc: {
|
|
1957
2342
|
kv,
|
|
1958
2343
|
ensurePublicSpace: async () => {
|
|
@@ -2133,7 +2518,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2133
2518
|
* @internal
|
|
2134
2519
|
*/
|
|
2135
2520
|
getSessionExpiry() {
|
|
2136
|
-
const expirationMs = this.config.sessionExpirationMs ??
|
|
2521
|
+
const expirationMs = this.config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS;
|
|
2137
2522
|
return new Date(Date.now() + expirationMs);
|
|
2138
2523
|
}
|
|
2139
2524
|
/**
|
|
@@ -2190,7 +2575,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2190
2575
|
if (!this.signer) {
|
|
2191
2576
|
return void 0;
|
|
2192
2577
|
}
|
|
2193
|
-
const session = this.
|
|
2578
|
+
const session = this.currentTinyCloudSession();
|
|
2194
2579
|
if (!session) {
|
|
2195
2580
|
return void 0;
|
|
2196
2581
|
}
|
|
@@ -2290,6 +2675,34 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2290
2675
|
}
|
|
2291
2676
|
return this._sql;
|
|
2292
2677
|
}
|
|
2678
|
+
/**
|
|
2679
|
+
* Get an SQL service scoped to a specific space.
|
|
2680
|
+
*
|
|
2681
|
+
* Mirrors {@link SpaceService}'s per-space KV factory: clones the active
|
|
2682
|
+
* service context and overrides its session's spaceId so that subsequent
|
|
2683
|
+
* `sql/<dbName>/<action>` invocations route to that space. Useful when
|
|
2684
|
+
* the caller already holds a delegation covering the target space (e.g.
|
|
2685
|
+
* via {@link grantRuntimePermissions} or {@link useRuntimeDelegation})
|
|
2686
|
+
* but the SDK's per-space SQL surface isn't otherwise exposed.
|
|
2687
|
+
*
|
|
2688
|
+
* Does NOT auto-create the space.
|
|
2689
|
+
*
|
|
2690
|
+
* @param spaceId - Full space URI (`tinycloud:pkh:eip155:<chain>:<addr>:<name>`).
|
|
2691
|
+
*/
|
|
2692
|
+
sqlForSpace(spaceId) {
|
|
2693
|
+
if (!this._serviceContext || !this._serviceContext.session) {
|
|
2694
|
+
throw new Error("Not signed in. Call signIn() first.");
|
|
2695
|
+
}
|
|
2696
|
+
const sql = new SQLService2({});
|
|
2697
|
+
const spaceScopedContext = new ServiceContext2({
|
|
2698
|
+
invoke: this._serviceContext.invoke,
|
|
2699
|
+
fetch: this._serviceContext.fetch,
|
|
2700
|
+
hosts: this._serviceContext.hosts
|
|
2701
|
+
});
|
|
2702
|
+
spaceScopedContext.setSession({ ...this._serviceContext.session, spaceId });
|
|
2703
|
+
sql.initialize(spaceScopedContext);
|
|
2704
|
+
return sql;
|
|
2705
|
+
}
|
|
2293
2706
|
/**
|
|
2294
2707
|
* DuckDB database operations on this user's space.
|
|
2295
2708
|
*/
|
|
@@ -2313,6 +2726,79 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2313
2726
|
}
|
|
2314
2727
|
return this._vault;
|
|
2315
2728
|
}
|
|
2729
|
+
/**
|
|
2730
|
+
* Network-scoped encryption/decrypt service.
|
|
2731
|
+
*/
|
|
2732
|
+
get encryption() {
|
|
2733
|
+
return this.getEncryptionService();
|
|
2734
|
+
}
|
|
2735
|
+
async getEncryptionNetwork(nameOrNetworkId = this.getDefaultEncryptionNetworkId()) {
|
|
2736
|
+
const networkId = nameOrNetworkId.startsWith("urn:tinycloud:encryption:") ? nameOrNetworkId : this.getDefaultEncryptionNetworkId(nameOrNetworkId);
|
|
2737
|
+
const response = await fetch(
|
|
2738
|
+
`${this.config.host}/encryption/networks/${encodeURIComponent(networkId)}`
|
|
2739
|
+
);
|
|
2740
|
+
if (response.status === 404) {
|
|
2741
|
+
return null;
|
|
2742
|
+
}
|
|
2743
|
+
if (!response.ok) {
|
|
2744
|
+
throw new Error(
|
|
2745
|
+
`Failed to fetch encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
|
|
2746
|
+
);
|
|
2747
|
+
}
|
|
2748
|
+
const body = await response.json();
|
|
2749
|
+
return "descriptor" in body && body.descriptor ? body.descriptor : body;
|
|
2750
|
+
}
|
|
2751
|
+
async createEncryptionNetwork(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
|
|
2752
|
+
const targetNode = await this.fetchNodeId();
|
|
2753
|
+
const principal = this.did;
|
|
2754
|
+
const networkId = this.getDefaultEncryptionNetworkId(name);
|
|
2755
|
+
const body = {
|
|
2756
|
+
name,
|
|
2757
|
+
principal,
|
|
2758
|
+
threshold: { n: 1, t: 1 }
|
|
2759
|
+
};
|
|
2760
|
+
const crypto = this.createEncryptionCrypto();
|
|
2761
|
+
const facts = {
|
|
2762
|
+
type: NETWORK_ADMIN_TYPE,
|
|
2763
|
+
targetNode,
|
|
2764
|
+
networkId,
|
|
2765
|
+
bodyHash: canonicalHashHex(
|
|
2766
|
+
crypto.sha256,
|
|
2767
|
+
body
|
|
2768
|
+
),
|
|
2769
|
+
action: NETWORK_CREATE_ACTION
|
|
2770
|
+
};
|
|
2771
|
+
const signed = await this.signRawNetworkAuthorization({
|
|
2772
|
+
targetNode,
|
|
2773
|
+
networkId,
|
|
2774
|
+
action: NETWORK_CREATE_ACTION,
|
|
2775
|
+
facts
|
|
2776
|
+
});
|
|
2777
|
+
const response = await fetch(`${this.config.host}/encryption/networks`, {
|
|
2778
|
+
method: "POST",
|
|
2779
|
+
headers: {
|
|
2780
|
+
Authorization: signed.authorization,
|
|
2781
|
+
"Content-Type": "application/json"
|
|
2782
|
+
},
|
|
2783
|
+
body: canonicalizeEncryptionJson(
|
|
2784
|
+
body
|
|
2785
|
+
)
|
|
2786
|
+
});
|
|
2787
|
+
if (!response.ok) {
|
|
2788
|
+
throw new Error(
|
|
2789
|
+
`Failed to create encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
|
|
2790
|
+
);
|
|
2791
|
+
}
|
|
2792
|
+
const created = await response.json();
|
|
2793
|
+
return created.descriptor;
|
|
2794
|
+
}
|
|
2795
|
+
async ensureEncryptionNetwork(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
|
|
2796
|
+
const existing = await this.getEncryptionNetwork(name);
|
|
2797
|
+
if (existing) {
|
|
2798
|
+
return existing;
|
|
2799
|
+
}
|
|
2800
|
+
return this.createEncryptionNetwork(name);
|
|
2801
|
+
}
|
|
2316
2802
|
/**
|
|
2317
2803
|
* App-facing secrets API backed by the `secrets` space vault.
|
|
2318
2804
|
*/
|
|
@@ -2324,8 +2810,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2324
2810
|
this._secrets = new NodeSecretsService({
|
|
2325
2811
|
getService: () => this.getBaseSecrets(),
|
|
2326
2812
|
getManifest: () => this.manifest,
|
|
2813
|
+
hasPermissions: (permissions) => this.hasRuntimePermissions(permissions),
|
|
2327
2814
|
grantPermissions: (additional) => this.grantRuntimePermissions(additional),
|
|
2328
2815
|
canEscalate: () => this.signer !== void 0 && this.tc !== void 0,
|
|
2816
|
+
getEncryptionNetworkId: () => this.getDefaultEncryptionNetworkId(),
|
|
2329
2817
|
getUnlockSigner: () => this.signer ?? void 0
|
|
2330
2818
|
});
|
|
2331
2819
|
}
|
|
@@ -2415,7 +2903,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2415
2903
|
* every requested permission.
|
|
2416
2904
|
*/
|
|
2417
2905
|
hasRuntimePermissions(permissions) {
|
|
2418
|
-
const session = this.
|
|
2906
|
+
const session = this.currentTinyCloudSession();
|
|
2419
2907
|
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
2420
2908
|
return false;
|
|
2421
2909
|
}
|
|
@@ -2435,7 +2923,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2435
2923
|
if (permissions === void 0) {
|
|
2436
2924
|
return this.runtimePermissionGrants.map((grant) => grant.delegation);
|
|
2437
2925
|
}
|
|
2438
|
-
const session = this.
|
|
2926
|
+
const session = this.currentTinyCloudSession();
|
|
2439
2927
|
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
2440
2928
|
return [];
|
|
2441
2929
|
}
|
|
@@ -2449,7 +2937,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2449
2937
|
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
2450
2938
|
*/
|
|
2451
2939
|
async useRuntimeDelegation(delegation) {
|
|
2452
|
-
const session = this.
|
|
2940
|
+
const session = this.currentTinyCloudSession();
|
|
2453
2941
|
if (!session) {
|
|
2454
2942
|
throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
2455
2943
|
}
|
|
@@ -2488,7 +2976,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2488
2976
|
if (!Array.isArray(permissions) || permissions.length === 0) {
|
|
2489
2977
|
throw new Error("grantRuntimePermissions requires a non-empty permissions array");
|
|
2490
2978
|
}
|
|
2491
|
-
const session = this.
|
|
2979
|
+
const session = this.currentTinyCloudSession();
|
|
2492
2980
|
if (!session) {
|
|
2493
2981
|
throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
2494
2982
|
}
|
|
@@ -2512,13 +3000,22 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2512
3000
|
"grantRuntimePermissions requires wallet mode with a signer or privateKey."
|
|
2513
3001
|
);
|
|
2514
3002
|
}
|
|
3003
|
+
const rawEntries = expanded.filter(
|
|
3004
|
+
(entry) => this.isEncryptionPermissionEntry(entry)
|
|
3005
|
+
);
|
|
3006
|
+
const spaceEntries = expanded.filter(
|
|
3007
|
+
(entry) => !this.isEncryptionPermissionEntry(entry)
|
|
3008
|
+
);
|
|
2515
3009
|
const bySpace = /* @__PURE__ */ new Map();
|
|
2516
|
-
for (const entry of
|
|
3010
|
+
for (const entry of spaceEntries) {
|
|
2517
3011
|
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
2518
3012
|
const current = bySpace.get(spaceId) ?? [];
|
|
2519
3013
|
current.push(entry);
|
|
2520
3014
|
bySpace.set(spaceId, current);
|
|
2521
3015
|
}
|
|
3016
|
+
if (bySpace.size === 0 && rawEntries.length > 0) {
|
|
3017
|
+
bySpace.set(session.spaceId, []);
|
|
3018
|
+
}
|
|
2522
3019
|
const now = /* @__PURE__ */ new Date();
|
|
2523
3020
|
const requestedExpiryMs = resolveExpiryMs(options?.expiry);
|
|
2524
3021
|
let expiresAt = new Date(now.getTime() + requestedExpiryMs);
|
|
@@ -2526,10 +3023,17 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2526
3023
|
expiresAt = sessionExpiry;
|
|
2527
3024
|
}
|
|
2528
3025
|
const delegations = [];
|
|
3026
|
+
let rawEntriesAttached = false;
|
|
2529
3027
|
for (const [spaceId, entries] of bySpace) {
|
|
3028
|
+
const rawForDelegation = !rawEntriesAttached ? rawEntries : [];
|
|
3029
|
+
if (rawForDelegation.length > 0) {
|
|
3030
|
+
rawEntriesAttached = true;
|
|
3031
|
+
}
|
|
3032
|
+
const delegatedEntries = [...entries, ...rawForDelegation];
|
|
2530
3033
|
const abilities = this.permissionsToAbilities(entries);
|
|
2531
3034
|
const prepared = this.wasmBindings.prepareSession({
|
|
2532
3035
|
abilities,
|
|
3036
|
+
...rawForDelegation.length > 0 ? { rawAbilities: this.permissionsToRawAbilities(rawForDelegation) } : {},
|
|
2533
3037
|
address: this.wasmBindings.ensureEip55(session.address),
|
|
2534
3038
|
chainId: session.chainId,
|
|
2535
3039
|
domain: this.siweDomain,
|
|
@@ -2554,7 +3058,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2554
3058
|
}
|
|
2555
3059
|
const delegation = this.runtimeDelegationFromSession(
|
|
2556
3060
|
delegatedSession,
|
|
2557
|
-
|
|
3061
|
+
delegatedEntries,
|
|
2558
3062
|
spaceId,
|
|
2559
3063
|
session,
|
|
2560
3064
|
expiresAt
|
|
@@ -2568,7 +3072,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2568
3072
|
jwk: session.jwk
|
|
2569
3073
|
},
|
|
2570
3074
|
delegation,
|
|
2571
|
-
operations: this.permissionOperations(
|
|
3075
|
+
operations: this.permissionOperations(delegatedEntries, spaceId),
|
|
2572
3076
|
expiresAt
|
|
2573
3077
|
});
|
|
2574
3078
|
delegations.push(delegation);
|
|
@@ -2716,7 +3220,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2716
3220
|
];
|
|
2717
3221
|
const abilities = { kv: { "": kvActions } };
|
|
2718
3222
|
const now = /* @__PURE__ */ new Date();
|
|
2719
|
-
const expiryMs =
|
|
3223
|
+
const expiryMs = EXPIRY3.EPHEMERAL_MS;
|
|
2720
3224
|
const expirationTime = new Date(now.getTime() + expiryMs);
|
|
2721
3225
|
const prepared = this.wasmBindings.prepareSession({
|
|
2722
3226
|
abilities,
|
|
@@ -2886,7 +3390,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2886
3390
|
* `forceWalletSign` is not set.
|
|
2887
3391
|
*/
|
|
2888
3392
|
async delegateTo(did, permissions, options) {
|
|
2889
|
-
const session = this.
|
|
3393
|
+
const session = this.currentTinyCloudSession();
|
|
2890
3394
|
if (!session) {
|
|
2891
3395
|
throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
2892
3396
|
}
|
|
@@ -3000,11 +3504,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3000
3504
|
* the current session; we build one multi-resource abilities map
|
|
3001
3505
|
* and emit one signed UCAN covering them all.
|
|
3002
3506
|
*
|
|
3003
|
-
*
|
|
3004
|
-
*
|
|
3005
|
-
* spaces in a single delegation is not supported by the underlying
|
|
3006
|
-
* Rust create_delegation call and the resulting UCAN would be
|
|
3007
|
-
* under-specified.
|
|
3507
|
+
* Non-encryption entries must share the same target space. Encryption
|
|
3508
|
+
* entries are raw network URNs and do not participate in space grouping.
|
|
3008
3509
|
*
|
|
3009
3510
|
* @internal
|
|
3010
3511
|
*/
|
|
@@ -3016,15 +3517,18 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3016
3517
|
}
|
|
3017
3518
|
const resolvedSpaces = /* @__PURE__ */ new Set();
|
|
3018
3519
|
for (const entry of entries) {
|
|
3520
|
+
if (this.isEncryptionPermissionEntry(entry)) {
|
|
3521
|
+
continue;
|
|
3522
|
+
}
|
|
3019
3523
|
const spaceId2 = this.resolvePermissionSpace(entry.space, session);
|
|
3020
3524
|
resolvedSpaces.add(spaceId2);
|
|
3021
3525
|
}
|
|
3022
|
-
if (resolvedSpaces.size
|
|
3526
|
+
if (resolvedSpaces.size > 1) {
|
|
3023
3527
|
throw new Error(
|
|
3024
3528
|
`delegateTo: all permission entries must target the same space, got ${resolvedSpaces.size}: ${JSON.stringify([...resolvedSpaces])}`
|
|
3025
3529
|
);
|
|
3026
3530
|
}
|
|
3027
|
-
const spaceId = [...resolvedSpaces][0];
|
|
3531
|
+
const spaceId = resolvedSpaces.size === 1 ? [...resolvedSpaces][0] : session.spaceId;
|
|
3028
3532
|
const abilities = {};
|
|
3029
3533
|
for (const entry of entries) {
|
|
3030
3534
|
const shortService = SERVICE_LONG_TO_SHORT[entry.service];
|
|
@@ -3171,11 +3675,32 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3171
3675
|
}
|
|
3172
3676
|
return abilities;
|
|
3173
3677
|
}
|
|
3678
|
+
isEncryptionPermissionEntry(entry) {
|
|
3679
|
+
return entry.service === ENCRYPTION_PERMISSION_SERVICE2 && entry.path.startsWith("urn:tinycloud:encryption:");
|
|
3680
|
+
}
|
|
3681
|
+
permissionsToRawAbilities(entries) {
|
|
3682
|
+
const rawAbilities = {};
|
|
3683
|
+
for (const entry of entries) {
|
|
3684
|
+
if (!this.isEncryptionPermissionEntry(entry)) {
|
|
3685
|
+
continue;
|
|
3686
|
+
}
|
|
3687
|
+
const existing = rawAbilities[entry.path] ?? [];
|
|
3688
|
+
const seen = new Set(existing);
|
|
3689
|
+
for (const action of entry.actions) {
|
|
3690
|
+
if (!seen.has(action)) {
|
|
3691
|
+
existing.push(action);
|
|
3692
|
+
seen.add(action);
|
|
3693
|
+
}
|
|
3694
|
+
}
|
|
3695
|
+
rawAbilities[entry.path] = existing;
|
|
3696
|
+
}
|
|
3697
|
+
return rawAbilities;
|
|
3698
|
+
}
|
|
3174
3699
|
permissionOperations(entries, spaceId) {
|
|
3175
3700
|
return entries.flatMap((entry) => {
|
|
3176
3701
|
const service = this.shortServiceName(entry.service);
|
|
3177
3702
|
return entry.actions.map((action) => ({
|
|
3178
|
-
spaceId,
|
|
3703
|
+
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
|
|
3179
3704
|
service,
|
|
3180
3705
|
path: entry.path,
|
|
3181
3706
|
action
|
|
@@ -3198,7 +3723,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3198
3723
|
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
3199
3724
|
const service = this.shortServiceName(entry.service);
|
|
3200
3725
|
return entry.actions.map((action) => ({
|
|
3201
|
-
spaceId,
|
|
3726
|
+
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
|
|
3202
3727
|
service,
|
|
3203
3728
|
path: entry.path,
|
|
3204
3729
|
action
|
|
@@ -3255,24 +3780,40 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3255
3780
|
expiresAt: delegation.expiry
|
|
3256
3781
|
};
|
|
3257
3782
|
}
|
|
3783
|
+
installRuntimeGrantFromServiceSession(delegation, session, expiresAt) {
|
|
3784
|
+
const operations = this.operationsFromDelegation(delegation);
|
|
3785
|
+
if (operations.length === 0) {
|
|
3786
|
+
return;
|
|
3787
|
+
}
|
|
3788
|
+
this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
|
|
3789
|
+
(grant) => grant.delegation.cid !== delegation.cid && grant.session.delegationCid !== session.delegationCid
|
|
3790
|
+
);
|
|
3791
|
+
this.runtimePermissionGrants.push({
|
|
3792
|
+
session,
|
|
3793
|
+
delegation,
|
|
3794
|
+
operations,
|
|
3795
|
+
expiresAt
|
|
3796
|
+
});
|
|
3797
|
+
}
|
|
3258
3798
|
delegatedResourcesForEntries(entries, spaceId) {
|
|
3259
3799
|
return entries.map((entry) => ({
|
|
3260
3800
|
service: this.shortServiceName(entry.service),
|
|
3261
|
-
space: spaceId,
|
|
3801
|
+
space: this.isEncryptionPermissionEntry(entry) ? "encryption" : spaceId,
|
|
3262
3802
|
path: entry.path,
|
|
3263
3803
|
actions: [...entry.actions]
|
|
3264
3804
|
}));
|
|
3265
3805
|
}
|
|
3266
3806
|
operationsFromDelegation(delegation) {
|
|
3267
3807
|
const resources = delegation.resources !== void 0 && delegation.resources.length > 0 ? delegation.resources : this.flatDelegationResources(delegation);
|
|
3268
|
-
return resources.flatMap(
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
service:
|
|
3808
|
+
return resources.flatMap((resource) => {
|
|
3809
|
+
const service = this.invocationServiceName(resource.service);
|
|
3810
|
+
return resource.actions.map((action) => ({
|
|
3811
|
+
...this.isEncryptionNetworkOperation(service, resource.path) ? { resource: resource.path } : { spaceId: resource.space },
|
|
3812
|
+
service,
|
|
3272
3813
|
path: resource.path,
|
|
3273
3814
|
action
|
|
3274
|
-
}))
|
|
3275
|
-
);
|
|
3815
|
+
}));
|
|
3816
|
+
});
|
|
3276
3817
|
}
|
|
3277
3818
|
flatDelegationResources(delegation) {
|
|
3278
3819
|
const byService = /* @__PURE__ */ new Map();
|
|
@@ -3321,7 +3862,13 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3321
3862
|
);
|
|
3322
3863
|
}
|
|
3323
3864
|
operationCovers(granted, requested) {
|
|
3324
|
-
|
|
3865
|
+
if (granted.service !== requested.service || !this.actionContains(granted.action, requested.action)) {
|
|
3866
|
+
return false;
|
|
3867
|
+
}
|
|
3868
|
+
if (granted.resource !== void 0 || requested.resource !== void 0) {
|
|
3869
|
+
return granted.resource !== void 0 && requested.resource !== void 0 && granted.resource === requested.resource && this.pathContains(granted.path, requested.path);
|
|
3870
|
+
}
|
|
3871
|
+
return granted.spaceId !== void 0 && requested.spaceId !== void 0 && granted.spaceId === requested.spaceId && this.pathContains(granted.path, requested.path);
|
|
3325
3872
|
}
|
|
3326
3873
|
actionContains(grantedAction, requestedAction) {
|
|
3327
3874
|
if (grantedAction === requestedAction) {
|
|
@@ -3336,6 +3883,37 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3336
3883
|
invocationServiceName(service) {
|
|
3337
3884
|
return service.startsWith("tinycloud.") ? this.shortServiceName(service) : service;
|
|
3338
3885
|
}
|
|
3886
|
+
isEncryptionNetworkOperation(service, path) {
|
|
3887
|
+
return service === "encryption" && path.startsWith("urn:tinycloud:encryption:");
|
|
3888
|
+
}
|
|
3889
|
+
operationFromInvokeAnyEntry(entry) {
|
|
3890
|
+
const service = this.invocationServiceName(entry.service);
|
|
3891
|
+
if (typeof entry.resource === "string") {
|
|
3892
|
+
return {
|
|
3893
|
+
resource: entry.resource,
|
|
3894
|
+
service,
|
|
3895
|
+
path: entry.path,
|
|
3896
|
+
action: entry.action
|
|
3897
|
+
};
|
|
3898
|
+
}
|
|
3899
|
+
if (this.isEncryptionNetworkOperation(service, entry.path)) {
|
|
3900
|
+
return {
|
|
3901
|
+
resource: entry.path,
|
|
3902
|
+
service,
|
|
3903
|
+
path: entry.path,
|
|
3904
|
+
action: entry.action
|
|
3905
|
+
};
|
|
3906
|
+
}
|
|
3907
|
+
if (typeof entry.spaceId === "string") {
|
|
3908
|
+
return {
|
|
3909
|
+
spaceId: entry.spaceId,
|
|
3910
|
+
service,
|
|
3911
|
+
path: entry.path,
|
|
3912
|
+
action: entry.action
|
|
3913
|
+
};
|
|
3914
|
+
}
|
|
3915
|
+
return void 0;
|
|
3916
|
+
}
|
|
3339
3917
|
pathContains(grantedPath, requestedPath) {
|
|
3340
3918
|
if (grantedPath === "" || grantedPath === "/") {
|
|
3341
3919
|
return true;
|
|
@@ -3574,6 +4152,17 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3574
4152
|
// Not used in session-only mode
|
|
3575
4153
|
};
|
|
3576
4154
|
this.trackReceivedDelegation(delegation, this.sessionKeyJwk);
|
|
4155
|
+
this.installRuntimeGrantFromServiceSession(
|
|
4156
|
+
delegation,
|
|
4157
|
+
{
|
|
4158
|
+
delegationHeader: session2.delegationHeader,
|
|
4159
|
+
delegationCid: session2.delegationCid,
|
|
4160
|
+
spaceId: session2.spaceId,
|
|
4161
|
+
verificationMethod: session2.verificationMethod,
|
|
4162
|
+
jwk: session2.jwk
|
|
4163
|
+
},
|
|
4164
|
+
delegation.expiry
|
|
4165
|
+
);
|
|
3577
4166
|
return new DelegatedAccess(session2, delegation, targetHost, this.wasmBindings.invoke);
|
|
3578
4167
|
}
|
|
3579
4168
|
const mySession = this.auth?.tinyCloudSession;
|
|
@@ -3585,6 +4174,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3585
4174
|
const kvActions = delegation.actions.filter((a) => a.startsWith("tinycloud.kv/"));
|
|
3586
4175
|
const sqlActions = delegation.actions.filter((a) => a.startsWith("tinycloud.sql/"));
|
|
3587
4176
|
const duckdbActions = delegation.actions.filter((a) => a.startsWith("tinycloud.duckdb/"));
|
|
4177
|
+
const encryptionActions = delegation.actions.filter(
|
|
4178
|
+
(a) => a.startsWith("tinycloud.encryption/")
|
|
4179
|
+
);
|
|
4180
|
+
const rawAbilities = {};
|
|
3588
4181
|
if (kvActions.length > 0) {
|
|
3589
4182
|
abilities.kv = { [delegation.path]: kvActions };
|
|
3590
4183
|
}
|
|
@@ -3594,6 +4187,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3594
4187
|
if (duckdbActions.length > 0) {
|
|
3595
4188
|
abilities.duckdb = { [delegation.path]: duckdbActions };
|
|
3596
4189
|
}
|
|
4190
|
+
if (encryptionActions.length > 0 && delegation.path.startsWith("urn:tinycloud:encryption:")) {
|
|
4191
|
+
rawAbilities[delegation.path] = encryptionActions;
|
|
4192
|
+
}
|
|
3597
4193
|
const now = /* @__PURE__ */ new Date();
|
|
3598
4194
|
const maxExpiry = new Date(now.getTime() + 60 * 60 * 1e3);
|
|
3599
4195
|
const expirationTime = delegation.expiry < maxExpiry ? delegation.expiry : maxExpiry;
|
|
@@ -3606,7 +4202,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3606
4202
|
expirationTime: expirationTime.toISOString(),
|
|
3607
4203
|
spaceId: delegation.spaceId,
|
|
3608
4204
|
jwk,
|
|
3609
|
-
parents: [delegation.cid]
|
|
4205
|
+
parents: [delegation.cid],
|
|
4206
|
+
...Object.keys(rawAbilities).length > 0 ? { rawAbilities } : {}
|
|
3610
4207
|
});
|
|
3611
4208
|
const signature = await this.signer.signMessage(prepared.siwe);
|
|
3612
4209
|
const invokerSession = this.wasmBindings.completeSessionSetup({
|
|
@@ -3633,6 +4230,17 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3633
4230
|
signature
|
|
3634
4231
|
};
|
|
3635
4232
|
this.trackReceivedDelegation(delegation, jwk);
|
|
4233
|
+
this.installRuntimeGrantFromServiceSession(
|
|
4234
|
+
delegation,
|
|
4235
|
+
{
|
|
4236
|
+
delegationHeader: session.delegationHeader,
|
|
4237
|
+
delegationCid: session.delegationCid,
|
|
4238
|
+
spaceId: session.spaceId,
|
|
4239
|
+
verificationMethod: session.verificationMethod,
|
|
4240
|
+
jwk: session.jwk
|
|
4241
|
+
},
|
|
4242
|
+
expirationTime
|
|
4243
|
+
);
|
|
3636
4244
|
return new DelegatedAccess(session, delegation, targetHost, this.wasmBindings.invoke);
|
|
3637
4245
|
}
|
|
3638
4246
|
/**
|
|
@@ -3753,7 +4361,7 @@ import {
|
|
|
3753
4361
|
loadManifest,
|
|
3754
4362
|
isCapabilitySubset as isCapabilitySubset2,
|
|
3755
4363
|
expandActionShortNames,
|
|
3756
|
-
expandPermissionEntries,
|
|
4364
|
+
expandPermissionEntries as expandPermissionEntries2,
|
|
3757
4365
|
expandPermissionEntry,
|
|
3758
4366
|
parseExpiry as parseExpiry2,
|
|
3759
4367
|
resourceCapabilitiesToSpaceAbilitiesMap as resourceCapabilitiesToSpaceAbilitiesMap2
|
|
@@ -3776,10 +4384,24 @@ function deserializeDelegation(data) {
|
|
|
3776
4384
|
}
|
|
3777
4385
|
|
|
3778
4386
|
// src/core.ts
|
|
3779
|
-
import {
|
|
4387
|
+
import {
|
|
4388
|
+
DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
|
|
4389
|
+
KVService as KVService3,
|
|
4390
|
+
PrefixedKVService
|
|
4391
|
+
} from "@tinycloud/sdk-core";
|
|
3780
4392
|
import { SQLService as SQLService3, SQLAction, DatabaseHandle } from "@tinycloud/sdk-core";
|
|
3781
4393
|
import { DuckDbService as DuckDbService3, DuckDbDatabaseHandle, DuckDbAction } from "@tinycloud/sdk-core";
|
|
3782
|
-
import {
|
|
4394
|
+
import {
|
|
4395
|
+
DataVaultService as DataVaultService2,
|
|
4396
|
+
VaultHeaders,
|
|
4397
|
+
VaultPublicSpaceKVActions,
|
|
4398
|
+
createVaultCrypto as createVaultCrypto2,
|
|
4399
|
+
SecretsService as SecretsService2,
|
|
4400
|
+
SECRET_NAME_RE,
|
|
4401
|
+
canonicalizeSecretScope,
|
|
4402
|
+
resolveSecretListPrefix as resolveSecretListPrefix2,
|
|
4403
|
+
resolveSecretPath as resolveSecretPath2
|
|
4404
|
+
} from "@tinycloud/sdk-core";
|
|
3783
4405
|
import {
|
|
3784
4406
|
DelegationManager as DelegationManager2,
|
|
3785
4407
|
SharingService as SharingService2,
|
|
@@ -3815,6 +4437,7 @@ export {
|
|
|
3815
4437
|
CapabilityKeyRegistryErrorCodes,
|
|
3816
4438
|
DEFAULT_MANIFEST_SPACE2 as DEFAULT_MANIFEST_SPACE,
|
|
3817
4439
|
DEFAULT_MANIFEST_VERSION,
|
|
4440
|
+
DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
|
|
3818
4441
|
DataVaultService2 as DataVaultService,
|
|
3819
4442
|
DatabaseHandle,
|
|
3820
4443
|
DelegatedAccess,
|
|
@@ -3831,6 +4454,7 @@ export {
|
|
|
3831
4454
|
PermissionNotInManifestError2 as PermissionNotInManifestError,
|
|
3832
4455
|
PrefixedKVService,
|
|
3833
4456
|
ProtocolMismatchError,
|
|
4457
|
+
SECRET_NAME_RE,
|
|
3834
4458
|
SQLAction,
|
|
3835
4459
|
SQLService3 as SQLService,
|
|
3836
4460
|
SecretsService2 as SecretsService,
|
|
@@ -3850,6 +4474,7 @@ export {
|
|
|
3850
4474
|
VersionCheckError,
|
|
3851
4475
|
WasmKeyProvider,
|
|
3852
4476
|
buildSpaceUri,
|
|
4477
|
+
canonicalizeSecretScope,
|
|
3853
4478
|
checkNodeInfo2 as checkNodeInfo,
|
|
3854
4479
|
composeManifestRequest2 as composeManifestRequest,
|
|
3855
4480
|
createCapabilityKeyRegistry,
|
|
@@ -3861,7 +4486,7 @@ export {
|
|
|
3861
4486
|
defaultSpaceCreationHandler,
|
|
3862
4487
|
deserializeDelegation,
|
|
3863
4488
|
expandActionShortNames,
|
|
3864
|
-
expandPermissionEntries,
|
|
4489
|
+
expandPermissionEntries2 as expandPermissionEntries,
|
|
3865
4490
|
expandPermissionEntry,
|
|
3866
4491
|
isCapabilitySubset2 as isCapabilitySubset,
|
|
3867
4492
|
loadManifest,
|
|
@@ -3869,6 +4494,8 @@ export {
|
|
|
3869
4494
|
parseExpiry2 as parseExpiry,
|
|
3870
4495
|
parseSpaceUri,
|
|
3871
4496
|
resolveManifest2 as resolveManifest,
|
|
4497
|
+
resolveSecretListPrefix2 as resolveSecretListPrefix,
|
|
4498
|
+
resolveSecretPath2 as resolveSecretPath,
|
|
3872
4499
|
resourceCapabilitiesToSpaceAbilitiesMap2 as resourceCapabilitiesToSpaceAbilitiesMap,
|
|
3873
4500
|
serializeDelegation,
|
|
3874
4501
|
validateManifest
|