@tinycloud/node-sdk 2.2.0-beta.8 → 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 +717 -105
- 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 +736 -113
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +811 -127
- 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 +815 -114
- 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 };
|
|
@@ -1240,32 +1314,40 @@ function secretsError(code, message, cause) {
|
|
|
1240
1314
|
}
|
|
1241
1315
|
};
|
|
1242
1316
|
}
|
|
1243
|
-
function
|
|
1244
|
-
|
|
1317
|
+
function displayActionUrn(action) {
|
|
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
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
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"],
|
|
1263
1347
|
skipPrefix: true
|
|
1264
|
-
}
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
function isSecretsSpace(space) {
|
|
1268
|
-
return space === SECRETS_SPACE || space.endsWith(`:${SECRETS_SPACE}`);
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
return entries;
|
|
1269
1351
|
}
|
|
1270
1352
|
var NodeSecretsService = class {
|
|
1271
1353
|
constructor(config) {
|
|
@@ -1293,48 +1375,62 @@ var NodeSecretsService = class {
|
|
|
1293
1375
|
this.shouldRestoreUnlock = false;
|
|
1294
1376
|
this.service.lock();
|
|
1295
1377
|
}
|
|
1296
|
-
get(name) {
|
|
1297
|
-
|
|
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);
|
|
1298
1382
|
}
|
|
1299
|
-
async put(name, value) {
|
|
1300
|
-
const permission = await this.
|
|
1383
|
+
async put(name, value, options) {
|
|
1384
|
+
const permission = await this.ensurePermission(name, options, "put");
|
|
1301
1385
|
if (!permission.ok) return permission;
|
|
1302
|
-
return this.service.put(name, value);
|
|
1386
|
+
return options === void 0 ? this.service.put(name, value) : this.service.put(name, value, options);
|
|
1303
1387
|
}
|
|
1304
|
-
async delete(name) {
|
|
1305
|
-
const permission = await this.
|
|
1388
|
+
async delete(name, options) {
|
|
1389
|
+
const permission = await this.ensurePermission(name, options, "del");
|
|
1306
1390
|
if (!permission.ok) return permission;
|
|
1307
|
-
return this.service.delete(name);
|
|
1391
|
+
return options === void 0 ? this.service.delete(name) : this.service.delete(name, options);
|
|
1308
1392
|
}
|
|
1309
|
-
list() {
|
|
1310
|
-
|
|
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);
|
|
1311
1397
|
}
|
|
1312
1398
|
get service() {
|
|
1313
1399
|
return this.config.getService();
|
|
1314
1400
|
}
|
|
1315
|
-
async
|
|
1316
|
-
|
|
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) {
|
|
1317
1412
|
return secretsError(
|
|
1318
1413
|
ErrorCodes.INVALID_INPUT,
|
|
1319
|
-
|
|
1414
|
+
error instanceof Error ? error.message : String(error),
|
|
1415
|
+
error instanceof Error ? error : void 0
|
|
1320
1416
|
);
|
|
1321
1417
|
}
|
|
1322
|
-
if (this.
|
|
1418
|
+
if (this.hasPermission(permissionEntries)) {
|
|
1323
1419
|
return ok();
|
|
1324
1420
|
}
|
|
1325
1421
|
if (!this.config.canEscalate()) {
|
|
1326
1422
|
return secretsError(
|
|
1327
1423
|
ErrorCodes.PERMISSION_DENIED,
|
|
1328
|
-
`Cannot autosign ${
|
|
1424
|
+
`Cannot autosign ${displayActionUrn(action)} for ${target}; TinyCloudNode needs wallet mode with a signer or privateKey.`
|
|
1329
1425
|
);
|
|
1330
1426
|
}
|
|
1331
1427
|
try {
|
|
1332
|
-
await this.config.grantPermissions(
|
|
1428
|
+
await this.config.grantPermissions(permissionEntries);
|
|
1333
1429
|
return this.restoreUnlockAfterEscalation();
|
|
1334
1430
|
} catch (error) {
|
|
1335
1431
|
return secretsError(
|
|
1336
1432
|
ErrorCodes.PERMISSION_DENIED,
|
|
1337
|
-
error instanceof Error ? error.message : `Autosign escalation for ${
|
|
1433
|
+
error instanceof Error ? error.message : `Autosign escalation for ${displayActionUrn(action)} on ${target} failed.`,
|
|
1338
1434
|
error instanceof Error ? error : void 0
|
|
1339
1435
|
);
|
|
1340
1436
|
}
|
|
@@ -1345,26 +1441,117 @@ var NodeSecretsService = class {
|
|
|
1345
1441
|
}
|
|
1346
1442
|
return this.service.unlock(this.unlockSigner);
|
|
1347
1443
|
}
|
|
1348
|
-
|
|
1444
|
+
hasPermission(permissionEntries) {
|
|
1445
|
+
if (this.config.hasPermissions?.(permissionEntries)) {
|
|
1446
|
+
return true;
|
|
1447
|
+
}
|
|
1349
1448
|
const manifest = this.config.getManifest();
|
|
1350
1449
|
if (manifest === void 0) {
|
|
1351
1450
|
return false;
|
|
1352
1451
|
}
|
|
1353
1452
|
const manifests = Array.isArray(manifest) ? manifest : [manifest];
|
|
1354
|
-
const
|
|
1355
|
-
return
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
(resource) => resource.service ===
|
|
1360
|
-
)
|
|
1361
|
-
)
|
|
1362
|
-
|
|
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
|
+
);
|
|
1363
1462
|
}
|
|
1364
1463
|
};
|
|
1365
1464
|
|
|
1366
1465
|
// src/TinyCloudNode.ts
|
|
1367
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
|
+
}
|
|
1368
1555
|
var _TinyCloudNode = class _TinyCloudNode {
|
|
1369
1556
|
/**
|
|
1370
1557
|
* Create a new TinyCloudNode instance.
|
|
@@ -1409,12 +1596,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1409
1596
|
throw new Error("WASM binding does not support invokeAny");
|
|
1410
1597
|
}
|
|
1411
1598
|
const grant = this.findGrantForOperations(
|
|
1412
|
-
entries.
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
action: entry.action
|
|
1417
|
-
}))
|
|
1599
|
+
entries.flatMap((entry) => {
|
|
1600
|
+
const operation = this.operationFromInvokeAnyEntry(entry);
|
|
1601
|
+
return operation ? [operation] : [];
|
|
1602
|
+
})
|
|
1418
1603
|
);
|
|
1419
1604
|
return this.wasmBindings.invokeAny(grant?.session ?? session, entries, facts);
|
|
1420
1605
|
};
|
|
@@ -1507,7 +1692,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1507
1692
|
sessionStorage: config.sessionStorage ?? new MemorySessionStorage(),
|
|
1508
1693
|
domain: this.siweDomain,
|
|
1509
1694
|
spacePrefix: config.prefix,
|
|
1510
|
-
sessionExpirationMs: config.sessionExpirationMs ??
|
|
1695
|
+
sessionExpirationMs: config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS,
|
|
1511
1696
|
tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
|
|
1512
1697
|
tinycloudRegistryUrl: config.tinycloudRegistryUrl,
|
|
1513
1698
|
tinycloudFallbackHosts: config.tinycloudFallbackHosts,
|
|
@@ -1642,6 +1827,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1642
1827
|
this._duckdb = void 0;
|
|
1643
1828
|
this._hooks = void 0;
|
|
1644
1829
|
this._vault = void 0;
|
|
1830
|
+
this._encryption = void 0;
|
|
1645
1831
|
this._baseSecrets = void 0;
|
|
1646
1832
|
this._secrets = void 0;
|
|
1647
1833
|
this._spaceService = void 0;
|
|
@@ -1650,6 +1836,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1650
1836
|
await this.tc.signIn(options);
|
|
1651
1837
|
this.syncResolvedHostFromAuth();
|
|
1652
1838
|
this.initializeServices();
|
|
1839
|
+
if (this.config.manifest === void 0 && this.config.capabilityRequest === void 0) {
|
|
1840
|
+
await this.ensureOwnedSpaceHosted(this.ownedSpaceId("secrets"));
|
|
1841
|
+
}
|
|
1653
1842
|
await this.writeManifestRegistryRecords();
|
|
1654
1843
|
this.notificationHandler.success("Successfully signed in");
|
|
1655
1844
|
}
|
|
@@ -1732,6 +1921,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1732
1921
|
this._duckdb = void 0;
|
|
1733
1922
|
this._hooks = void 0;
|
|
1734
1923
|
this._vault = void 0;
|
|
1924
|
+
this._encryption = void 0;
|
|
1735
1925
|
this._baseSecrets = void 0;
|
|
1736
1926
|
this._secrets = void 0;
|
|
1737
1927
|
this._spaceService = void 0;
|
|
@@ -1773,6 +1963,33 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1773
1963
|
this._vault.initialize(this._serviceContext);
|
|
1774
1964
|
this._serviceContext.registerService("vault", this._vault);
|
|
1775
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;
|
|
1776
1993
|
}
|
|
1777
1994
|
/**
|
|
1778
1995
|
* Connect a wallet to upgrade from session-only mode to wallet mode.
|
|
@@ -1817,7 +2034,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1817
2034
|
sessionStorage: options?.sessionStorage ?? this.config.sessionStorage ?? new MemorySessionStorage(),
|
|
1818
2035
|
domain: this.siweDomain,
|
|
1819
2036
|
spacePrefix: prefix,
|
|
1820
|
-
sessionExpirationMs: this.config.sessionExpirationMs ??
|
|
2037
|
+
sessionExpirationMs: this.config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS,
|
|
1821
2038
|
tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
|
|
1822
2039
|
tinycloudRegistryUrl: this.config.tinycloudRegistryUrl,
|
|
1823
2040
|
tinycloudFallbackHosts: this.config.tinycloudFallbackHosts,
|
|
@@ -1861,7 +2078,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1861
2078
|
sessionStorage: options?.sessionStorage ?? this.config.sessionStorage ?? new MemorySessionStorage(),
|
|
1862
2079
|
domain: this.siweDomain,
|
|
1863
2080
|
spacePrefix: prefix,
|
|
1864
|
-
sessionExpirationMs: this.config.sessionExpirationMs ??
|
|
2081
|
+
sessionExpirationMs: this.config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS,
|
|
1865
2082
|
tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
|
|
1866
2083
|
tinycloudRegistryUrl: this.config.tinycloudRegistryUrl,
|
|
1867
2084
|
tinycloudFallbackHosts: this.config.tinycloudFallbackHosts,
|
|
@@ -1884,7 +2101,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1884
2101
|
* @internal
|
|
1885
2102
|
*/
|
|
1886
2103
|
initializeServices() {
|
|
1887
|
-
const session = this.
|
|
2104
|
+
const session = this.currentTinyCloudSession();
|
|
1888
2105
|
if (!session) {
|
|
1889
2106
|
return;
|
|
1890
2107
|
}
|
|
@@ -1942,6 +2159,163 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1942
2159
|
}
|
|
1943
2160
|
return kvService;
|
|
1944
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
|
+
}
|
|
1945
2319
|
createVaultService(spaceId, kv) {
|
|
1946
2320
|
const wasm = this.wasmBindings;
|
|
1947
2321
|
const vaultCrypto = createVaultCrypto({
|
|
@@ -1957,6 +2331,13 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1957
2331
|
return new DataVaultService({
|
|
1958
2332
|
spaceId,
|
|
1959
2333
|
crypto: vaultCrypto,
|
|
2334
|
+
encryption: {
|
|
2335
|
+
networkId: this.getDefaultEncryptionNetworkId(),
|
|
2336
|
+
service: this.getEncryptionService(),
|
|
2337
|
+
decryptCapabilityProof: () => ({
|
|
2338
|
+
proofs: [this.requireServiceSession().delegationCid]
|
|
2339
|
+
})
|
|
2340
|
+
},
|
|
1960
2341
|
tc: {
|
|
1961
2342
|
kv,
|
|
1962
2343
|
ensurePublicSpace: async () => {
|
|
@@ -2137,7 +2518,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2137
2518
|
* @internal
|
|
2138
2519
|
*/
|
|
2139
2520
|
getSessionExpiry() {
|
|
2140
|
-
const expirationMs = this.config.sessionExpirationMs ??
|
|
2521
|
+
const expirationMs = this.config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS;
|
|
2141
2522
|
return new Date(Date.now() + expirationMs);
|
|
2142
2523
|
}
|
|
2143
2524
|
/**
|
|
@@ -2194,7 +2575,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2194
2575
|
if (!this.signer) {
|
|
2195
2576
|
return void 0;
|
|
2196
2577
|
}
|
|
2197
|
-
const session = this.
|
|
2578
|
+
const session = this.currentTinyCloudSession();
|
|
2198
2579
|
if (!session) {
|
|
2199
2580
|
return void 0;
|
|
2200
2581
|
}
|
|
@@ -2294,6 +2675,34 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2294
2675
|
}
|
|
2295
2676
|
return this._sql;
|
|
2296
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
|
+
}
|
|
2297
2706
|
/**
|
|
2298
2707
|
* DuckDB database operations on this user's space.
|
|
2299
2708
|
*/
|
|
@@ -2317,6 +2726,79 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2317
2726
|
}
|
|
2318
2727
|
return this._vault;
|
|
2319
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
|
+
}
|
|
2320
2802
|
/**
|
|
2321
2803
|
* App-facing secrets API backed by the `secrets` space vault.
|
|
2322
2804
|
*/
|
|
@@ -2328,8 +2810,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2328
2810
|
this._secrets = new NodeSecretsService({
|
|
2329
2811
|
getService: () => this.getBaseSecrets(),
|
|
2330
2812
|
getManifest: () => this.manifest,
|
|
2813
|
+
hasPermissions: (permissions) => this.hasRuntimePermissions(permissions),
|
|
2331
2814
|
grantPermissions: (additional) => this.grantRuntimePermissions(additional),
|
|
2332
2815
|
canEscalate: () => this.signer !== void 0 && this.tc !== void 0,
|
|
2816
|
+
getEncryptionNetworkId: () => this.getDefaultEncryptionNetworkId(),
|
|
2333
2817
|
getUnlockSigner: () => this.signer ?? void 0
|
|
2334
2818
|
});
|
|
2335
2819
|
}
|
|
@@ -2419,7 +2903,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2419
2903
|
* every requested permission.
|
|
2420
2904
|
*/
|
|
2421
2905
|
hasRuntimePermissions(permissions) {
|
|
2422
|
-
const session = this.
|
|
2906
|
+
const session = this.currentTinyCloudSession();
|
|
2423
2907
|
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
2424
2908
|
return false;
|
|
2425
2909
|
}
|
|
@@ -2439,7 +2923,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2439
2923
|
if (permissions === void 0) {
|
|
2440
2924
|
return this.runtimePermissionGrants.map((grant) => grant.delegation);
|
|
2441
2925
|
}
|
|
2442
|
-
const session = this.
|
|
2926
|
+
const session = this.currentTinyCloudSession();
|
|
2443
2927
|
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
2444
2928
|
return [];
|
|
2445
2929
|
}
|
|
@@ -2453,7 +2937,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2453
2937
|
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
2454
2938
|
*/
|
|
2455
2939
|
async useRuntimeDelegation(delegation) {
|
|
2456
|
-
const session = this.
|
|
2940
|
+
const session = this.currentTinyCloudSession();
|
|
2457
2941
|
if (!session) {
|
|
2458
2942
|
throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
2459
2943
|
}
|
|
@@ -2492,7 +2976,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2492
2976
|
if (!Array.isArray(permissions) || permissions.length === 0) {
|
|
2493
2977
|
throw new Error("grantRuntimePermissions requires a non-empty permissions array");
|
|
2494
2978
|
}
|
|
2495
|
-
const session = this.
|
|
2979
|
+
const session = this.currentTinyCloudSession();
|
|
2496
2980
|
if (!session) {
|
|
2497
2981
|
throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
2498
2982
|
}
|
|
@@ -2516,13 +3000,22 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2516
3000
|
"grantRuntimePermissions requires wallet mode with a signer or privateKey."
|
|
2517
3001
|
);
|
|
2518
3002
|
}
|
|
3003
|
+
const rawEntries = expanded.filter(
|
|
3004
|
+
(entry) => this.isEncryptionPermissionEntry(entry)
|
|
3005
|
+
);
|
|
3006
|
+
const spaceEntries = expanded.filter(
|
|
3007
|
+
(entry) => !this.isEncryptionPermissionEntry(entry)
|
|
3008
|
+
);
|
|
2519
3009
|
const bySpace = /* @__PURE__ */ new Map();
|
|
2520
|
-
for (const entry of
|
|
3010
|
+
for (const entry of spaceEntries) {
|
|
2521
3011
|
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
2522
3012
|
const current = bySpace.get(spaceId) ?? [];
|
|
2523
3013
|
current.push(entry);
|
|
2524
3014
|
bySpace.set(spaceId, current);
|
|
2525
3015
|
}
|
|
3016
|
+
if (bySpace.size === 0 && rawEntries.length > 0) {
|
|
3017
|
+
bySpace.set(session.spaceId, []);
|
|
3018
|
+
}
|
|
2526
3019
|
const now = /* @__PURE__ */ new Date();
|
|
2527
3020
|
const requestedExpiryMs = resolveExpiryMs(options?.expiry);
|
|
2528
3021
|
let expiresAt = new Date(now.getTime() + requestedExpiryMs);
|
|
@@ -2530,10 +3023,17 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2530
3023
|
expiresAt = sessionExpiry;
|
|
2531
3024
|
}
|
|
2532
3025
|
const delegations = [];
|
|
3026
|
+
let rawEntriesAttached = false;
|
|
2533
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];
|
|
2534
3033
|
const abilities = this.permissionsToAbilities(entries);
|
|
2535
3034
|
const prepared = this.wasmBindings.prepareSession({
|
|
2536
3035
|
abilities,
|
|
3036
|
+
...rawForDelegation.length > 0 ? { rawAbilities: this.permissionsToRawAbilities(rawForDelegation) } : {},
|
|
2537
3037
|
address: this.wasmBindings.ensureEip55(session.address),
|
|
2538
3038
|
chainId: session.chainId,
|
|
2539
3039
|
domain: this.siweDomain,
|
|
@@ -2558,7 +3058,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2558
3058
|
}
|
|
2559
3059
|
const delegation = this.runtimeDelegationFromSession(
|
|
2560
3060
|
delegatedSession,
|
|
2561
|
-
|
|
3061
|
+
delegatedEntries,
|
|
2562
3062
|
spaceId,
|
|
2563
3063
|
session,
|
|
2564
3064
|
expiresAt
|
|
@@ -2572,7 +3072,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2572
3072
|
jwk: session.jwk
|
|
2573
3073
|
},
|
|
2574
3074
|
delegation,
|
|
2575
|
-
operations: this.permissionOperations(
|
|
3075
|
+
operations: this.permissionOperations(delegatedEntries, spaceId),
|
|
2576
3076
|
expiresAt
|
|
2577
3077
|
});
|
|
2578
3078
|
delegations.push(delegation);
|
|
@@ -2720,7 +3220,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2720
3220
|
];
|
|
2721
3221
|
const abilities = { kv: { "": kvActions } };
|
|
2722
3222
|
const now = /* @__PURE__ */ new Date();
|
|
2723
|
-
const expiryMs =
|
|
3223
|
+
const expiryMs = EXPIRY3.EPHEMERAL_MS;
|
|
2724
3224
|
const expirationTime = new Date(now.getTime() + expiryMs);
|
|
2725
3225
|
const prepared = this.wasmBindings.prepareSession({
|
|
2726
3226
|
abilities,
|
|
@@ -2890,7 +3390,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2890
3390
|
* `forceWalletSign` is not set.
|
|
2891
3391
|
*/
|
|
2892
3392
|
async delegateTo(did, permissions, options) {
|
|
2893
|
-
const session = this.
|
|
3393
|
+
const session = this.currentTinyCloudSession();
|
|
2894
3394
|
if (!session) {
|
|
2895
3395
|
throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
2896
3396
|
}
|
|
@@ -2907,10 +3407,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2907
3407
|
"delegateTo requires a non-empty permissions array"
|
|
2908
3408
|
);
|
|
2909
3409
|
}
|
|
2910
|
-
const expandedEntries =
|
|
2911
|
-
...entry,
|
|
2912
|
-
actions: expandActionShortNames(entry.service, entry.actions)
|
|
2913
|
-
}));
|
|
3410
|
+
const expandedEntries = this.expandPermissionEntries(permissions);
|
|
2914
3411
|
const now = /* @__PURE__ */ new Date();
|
|
2915
3412
|
const expiryMs = resolveExpiryMs(options?.expiry);
|
|
2916
3413
|
const expirationTime = new Date(now.getTime() + expiryMs);
|
|
@@ -3007,11 +3504,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3007
3504
|
* the current session; we build one multi-resource abilities map
|
|
3008
3505
|
* and emit one signed UCAN covering them all.
|
|
3009
3506
|
*
|
|
3010
|
-
*
|
|
3011
|
-
*
|
|
3012
|
-
* spaces in a single delegation is not supported by the underlying
|
|
3013
|
-
* Rust create_delegation call and the resulting UCAN would be
|
|
3014
|
-
* 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.
|
|
3015
3509
|
*
|
|
3016
3510
|
* @internal
|
|
3017
3511
|
*/
|
|
@@ -3023,15 +3517,18 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3023
3517
|
}
|
|
3024
3518
|
const resolvedSpaces = /* @__PURE__ */ new Set();
|
|
3025
3519
|
for (const entry of entries) {
|
|
3520
|
+
if (this.isEncryptionPermissionEntry(entry)) {
|
|
3521
|
+
continue;
|
|
3522
|
+
}
|
|
3026
3523
|
const spaceId2 = this.resolvePermissionSpace(entry.space, session);
|
|
3027
3524
|
resolvedSpaces.add(spaceId2);
|
|
3028
3525
|
}
|
|
3029
|
-
if (resolvedSpaces.size
|
|
3526
|
+
if (resolvedSpaces.size > 1) {
|
|
3030
3527
|
throw new Error(
|
|
3031
3528
|
`delegateTo: all permission entries must target the same space, got ${resolvedSpaces.size}: ${JSON.stringify([...resolvedSpaces])}`
|
|
3032
3529
|
);
|
|
3033
3530
|
}
|
|
3034
|
-
const spaceId = [...resolvedSpaces][0];
|
|
3531
|
+
const spaceId = resolvedSpaces.size === 1 ? [...resolvedSpaces][0] : session.spaceId;
|
|
3035
3532
|
const abilities = {};
|
|
3036
3533
|
for (const entry of entries) {
|
|
3037
3534
|
const shortService = SERVICE_LONG_TO_SHORT[entry.service];
|
|
@@ -3150,10 +3647,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3150
3647
|
return this.wasmBindings.makeSpaceId(session.address, session.chainId, space);
|
|
3151
3648
|
}
|
|
3152
3649
|
expandPermissionEntries(permissions) {
|
|
3153
|
-
return permissions
|
|
3154
|
-
...entry,
|
|
3155
|
-
actions: expandActionShortNames(entry.service, entry.actions)
|
|
3156
|
-
}));
|
|
3650
|
+
return expandPermissionEntriesCore(permissions);
|
|
3157
3651
|
}
|
|
3158
3652
|
shortServiceName(service) {
|
|
3159
3653
|
const short = SERVICE_LONG_TO_SHORT[service];
|
|
@@ -3181,11 +3675,32 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3181
3675
|
}
|
|
3182
3676
|
return abilities;
|
|
3183
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
|
+
}
|
|
3184
3699
|
permissionOperations(entries, spaceId) {
|
|
3185
3700
|
return entries.flatMap((entry) => {
|
|
3186
3701
|
const service = this.shortServiceName(entry.service);
|
|
3187
3702
|
return entry.actions.map((action) => ({
|
|
3188
|
-
spaceId,
|
|
3703
|
+
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
|
|
3189
3704
|
service,
|
|
3190
3705
|
path: entry.path,
|
|
3191
3706
|
action
|
|
@@ -3208,7 +3723,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3208
3723
|
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
3209
3724
|
const service = this.shortServiceName(entry.service);
|
|
3210
3725
|
return entry.actions.map((action) => ({
|
|
3211
|
-
spaceId,
|
|
3726
|
+
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
|
|
3212
3727
|
service,
|
|
3213
3728
|
path: entry.path,
|
|
3214
3729
|
action
|
|
@@ -3265,24 +3780,40 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3265
3780
|
expiresAt: delegation.expiry
|
|
3266
3781
|
};
|
|
3267
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
|
+
}
|
|
3268
3798
|
delegatedResourcesForEntries(entries, spaceId) {
|
|
3269
3799
|
return entries.map((entry) => ({
|
|
3270
3800
|
service: this.shortServiceName(entry.service),
|
|
3271
|
-
space: spaceId,
|
|
3801
|
+
space: this.isEncryptionPermissionEntry(entry) ? "encryption" : spaceId,
|
|
3272
3802
|
path: entry.path,
|
|
3273
3803
|
actions: [...entry.actions]
|
|
3274
3804
|
}));
|
|
3275
3805
|
}
|
|
3276
3806
|
operationsFromDelegation(delegation) {
|
|
3277
3807
|
const resources = delegation.resources !== void 0 && delegation.resources.length > 0 ? delegation.resources : this.flatDelegationResources(delegation);
|
|
3278
|
-
return resources.flatMap(
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
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,
|
|
3282
3813
|
path: resource.path,
|
|
3283
3814
|
action
|
|
3284
|
-
}))
|
|
3285
|
-
);
|
|
3815
|
+
}));
|
|
3816
|
+
});
|
|
3286
3817
|
}
|
|
3287
3818
|
flatDelegationResources(delegation) {
|
|
3288
3819
|
const byService = /* @__PURE__ */ new Map();
|
|
@@ -3331,7 +3862,13 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3331
3862
|
);
|
|
3332
3863
|
}
|
|
3333
3864
|
operationCovers(granted, requested) {
|
|
3334
|
-
|
|
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);
|
|
3335
3872
|
}
|
|
3336
3873
|
actionContains(grantedAction, requestedAction) {
|
|
3337
3874
|
if (grantedAction === requestedAction) {
|
|
@@ -3346,6 +3883,37 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3346
3883
|
invocationServiceName(service) {
|
|
3347
3884
|
return service.startsWith("tinycloud.") ? this.shortServiceName(service) : service;
|
|
3348
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
|
+
}
|
|
3349
3917
|
pathContains(grantedPath, requestedPath) {
|
|
3350
3918
|
if (grantedPath === "" || grantedPath === "/") {
|
|
3351
3919
|
return true;
|
|
@@ -3584,6 +4152,17 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3584
4152
|
// Not used in session-only mode
|
|
3585
4153
|
};
|
|
3586
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
|
+
);
|
|
3587
4166
|
return new DelegatedAccess(session2, delegation, targetHost, this.wasmBindings.invoke);
|
|
3588
4167
|
}
|
|
3589
4168
|
const mySession = this.auth?.tinyCloudSession;
|
|
@@ -3595,6 +4174,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3595
4174
|
const kvActions = delegation.actions.filter((a) => a.startsWith("tinycloud.kv/"));
|
|
3596
4175
|
const sqlActions = delegation.actions.filter((a) => a.startsWith("tinycloud.sql/"));
|
|
3597
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 = {};
|
|
3598
4181
|
if (kvActions.length > 0) {
|
|
3599
4182
|
abilities.kv = { [delegation.path]: kvActions };
|
|
3600
4183
|
}
|
|
@@ -3604,6 +4187,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3604
4187
|
if (duckdbActions.length > 0) {
|
|
3605
4188
|
abilities.duckdb = { [delegation.path]: duckdbActions };
|
|
3606
4189
|
}
|
|
4190
|
+
if (encryptionActions.length > 0 && delegation.path.startsWith("urn:tinycloud:encryption:")) {
|
|
4191
|
+
rawAbilities[delegation.path] = encryptionActions;
|
|
4192
|
+
}
|
|
3607
4193
|
const now = /* @__PURE__ */ new Date();
|
|
3608
4194
|
const maxExpiry = new Date(now.getTime() + 60 * 60 * 1e3);
|
|
3609
4195
|
const expirationTime = delegation.expiry < maxExpiry ? delegation.expiry : maxExpiry;
|
|
@@ -3616,7 +4202,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3616
4202
|
expirationTime: expirationTime.toISOString(),
|
|
3617
4203
|
spaceId: delegation.spaceId,
|
|
3618
4204
|
jwk,
|
|
3619
|
-
parents: [delegation.cid]
|
|
4205
|
+
parents: [delegation.cid],
|
|
4206
|
+
...Object.keys(rawAbilities).length > 0 ? { rawAbilities } : {}
|
|
3620
4207
|
});
|
|
3621
4208
|
const signature = await this.signer.signMessage(prepared.siwe);
|
|
3622
4209
|
const invokerSession = this.wasmBindings.completeSessionSetup({
|
|
@@ -3643,6 +4230,17 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3643
4230
|
signature
|
|
3644
4231
|
};
|
|
3645
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
|
+
);
|
|
3646
4244
|
return new DelegatedAccess(session, delegation, targetHost, this.wasmBindings.invoke);
|
|
3647
4245
|
}
|
|
3648
4246
|
/**
|
|
@@ -3753,6 +4351,7 @@ import {
|
|
|
3753
4351
|
ACCOUNT_REGISTRY_SPACE as ACCOUNT_REGISTRY_SPACE2,
|
|
3754
4352
|
DEFAULT_MANIFEST_SPACE as DEFAULT_MANIFEST_SPACE2,
|
|
3755
4353
|
DEFAULT_MANIFEST_VERSION,
|
|
4354
|
+
VAULT_PERMISSION_SERVICE,
|
|
3756
4355
|
PermissionNotInManifestError as PermissionNotInManifestError2,
|
|
3757
4356
|
SessionExpiredError as SessionExpiredError2,
|
|
3758
4357
|
ManifestValidationError,
|
|
@@ -3761,7 +4360,9 @@ import {
|
|
|
3761
4360
|
validateManifest,
|
|
3762
4361
|
loadManifest,
|
|
3763
4362
|
isCapabilitySubset as isCapabilitySubset2,
|
|
3764
|
-
expandActionShortNames
|
|
4363
|
+
expandActionShortNames,
|
|
4364
|
+
expandPermissionEntries as expandPermissionEntries2,
|
|
4365
|
+
expandPermissionEntry,
|
|
3765
4366
|
parseExpiry as parseExpiry2,
|
|
3766
4367
|
resourceCapabilitiesToSpaceAbilitiesMap as resourceCapabilitiesToSpaceAbilitiesMap2
|
|
3767
4368
|
} from "@tinycloud/sdk-core";
|
|
@@ -3783,10 +4384,24 @@ function deserializeDelegation(data) {
|
|
|
3783
4384
|
}
|
|
3784
4385
|
|
|
3785
4386
|
// src/core.ts
|
|
3786
|
-
import {
|
|
4387
|
+
import {
|
|
4388
|
+
DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
|
|
4389
|
+
KVService as KVService3,
|
|
4390
|
+
PrefixedKVService
|
|
4391
|
+
} from "@tinycloud/sdk-core";
|
|
3787
4392
|
import { SQLService as SQLService3, SQLAction, DatabaseHandle } from "@tinycloud/sdk-core";
|
|
3788
4393
|
import { DuckDbService as DuckDbService3, DuckDbDatabaseHandle, DuckDbAction } from "@tinycloud/sdk-core";
|
|
3789
|
-
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";
|
|
3790
4405
|
import {
|
|
3791
4406
|
DelegationManager as DelegationManager2,
|
|
3792
4407
|
SharingService as SharingService2,
|
|
@@ -3822,6 +4437,7 @@ export {
|
|
|
3822
4437
|
CapabilityKeyRegistryErrorCodes,
|
|
3823
4438
|
DEFAULT_MANIFEST_SPACE2 as DEFAULT_MANIFEST_SPACE,
|
|
3824
4439
|
DEFAULT_MANIFEST_VERSION,
|
|
4440
|
+
DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
|
|
3825
4441
|
DataVaultService2 as DataVaultService,
|
|
3826
4442
|
DatabaseHandle,
|
|
3827
4443
|
DelegatedAccess,
|
|
@@ -3838,6 +4454,7 @@ export {
|
|
|
3838
4454
|
PermissionNotInManifestError2 as PermissionNotInManifestError,
|
|
3839
4455
|
PrefixedKVService,
|
|
3840
4456
|
ProtocolMismatchError,
|
|
4457
|
+
SECRET_NAME_RE,
|
|
3841
4458
|
SQLAction,
|
|
3842
4459
|
SQLService3 as SQLService,
|
|
3843
4460
|
SecretsService2 as SecretsService,
|
|
@@ -3851,11 +4468,13 @@ export {
|
|
|
3851
4468
|
TinyCloud2 as TinyCloud,
|
|
3852
4469
|
TinyCloudNode,
|
|
3853
4470
|
UnsupportedFeatureError2 as UnsupportedFeatureError,
|
|
4471
|
+
VAULT_PERMISSION_SERVICE,
|
|
3854
4472
|
VaultHeaders,
|
|
3855
4473
|
VaultPublicSpaceKVActions,
|
|
3856
4474
|
VersionCheckError,
|
|
3857
4475
|
WasmKeyProvider,
|
|
3858
4476
|
buildSpaceUri,
|
|
4477
|
+
canonicalizeSecretScope,
|
|
3859
4478
|
checkNodeInfo2 as checkNodeInfo,
|
|
3860
4479
|
composeManifestRequest2 as composeManifestRequest,
|
|
3861
4480
|
createCapabilityKeyRegistry,
|
|
@@ -3866,13 +4485,17 @@ export {
|
|
|
3866
4485
|
defaultSignStrategy,
|
|
3867
4486
|
defaultSpaceCreationHandler,
|
|
3868
4487
|
deserializeDelegation,
|
|
3869
|
-
|
|
4488
|
+
expandActionShortNames,
|
|
4489
|
+
expandPermissionEntries2 as expandPermissionEntries,
|
|
4490
|
+
expandPermissionEntry,
|
|
3870
4491
|
isCapabilitySubset2 as isCapabilitySubset,
|
|
3871
4492
|
loadManifest,
|
|
3872
4493
|
makePublicSpaceId2 as makePublicSpaceId,
|
|
3873
4494
|
parseExpiry2 as parseExpiry,
|
|
3874
4495
|
parseSpaceUri,
|
|
3875
4496
|
resolveManifest2 as resolveManifest,
|
|
4497
|
+
resolveSecretListPrefix2 as resolveSecretListPrefix,
|
|
4498
|
+
resolveSecretPath2 as resolveSecretPath,
|
|
3876
4499
|
resourceCapabilitiesToSpaceAbilitiesMap2 as resourceCapabilitiesToSpaceAbilitiesMap,
|
|
3877
4500
|
serializeDelegation,
|
|
3878
4501
|
validateManifest
|