@tinycloud/node-sdk 2.2.0-beta.9 → 2.2.1-beta.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 +725 -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 +743 -99
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +819 -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 +822 -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,180 @@ 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
|
+
wellKnown: {
|
|
2307
|
+
fetchWellKnown: async (principal, discoveryKey) => {
|
|
2308
|
+
if (!this._address || principal !== this.did) {
|
|
2309
|
+
return null;
|
|
2310
|
+
}
|
|
2311
|
+
if (!this.config.host) {
|
|
2312
|
+
return null;
|
|
2313
|
+
}
|
|
2314
|
+
const publicSpaceId = makePublicSpaceId(this._address, this._chainId);
|
|
2315
|
+
const result = await TinyCloud.readPublicSpace(this.config.host, publicSpaceId, discoveryKey);
|
|
2316
|
+
if (!result.ok) {
|
|
2317
|
+
return null;
|
|
2318
|
+
}
|
|
2319
|
+
const body = result.data;
|
|
2320
|
+
return "descriptor" in body && body.descriptor ? body.descriptor : body;
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
});
|
|
2324
|
+
}
|
|
2325
|
+
getEncryptionService() {
|
|
2326
|
+
if (!this._serviceContext) {
|
|
2327
|
+
throw new Error("Not signed in. Call signIn() first.");
|
|
2328
|
+
}
|
|
2329
|
+
if (!this._encryption) {
|
|
2330
|
+
this._encryption = this.createEncryptionService();
|
|
2331
|
+
this._encryption.initialize(this._serviceContext);
|
|
2332
|
+
this._serviceContext.registerService("encryption", this._encryption);
|
|
2333
|
+
}
|
|
2334
|
+
return this._encryption;
|
|
2335
|
+
}
|
|
1941
2336
|
createVaultService(spaceId, kv) {
|
|
1942
2337
|
const wasm = this.wasmBindings;
|
|
1943
2338
|
const vaultCrypto = createVaultCrypto({
|
|
@@ -1953,6 +2348,13 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1953
2348
|
return new DataVaultService({
|
|
1954
2349
|
spaceId,
|
|
1955
2350
|
crypto: vaultCrypto,
|
|
2351
|
+
encryption: {
|
|
2352
|
+
networkId: this.getDefaultEncryptionNetworkId(),
|
|
2353
|
+
service: this.getEncryptionService(),
|
|
2354
|
+
decryptCapabilityProof: () => ({
|
|
2355
|
+
proofs: [this.requireServiceSession().delegationCid]
|
|
2356
|
+
})
|
|
2357
|
+
},
|
|
1956
2358
|
tc: {
|
|
1957
2359
|
kv,
|
|
1958
2360
|
ensurePublicSpace: async () => {
|
|
@@ -2133,7 +2535,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2133
2535
|
* @internal
|
|
2134
2536
|
*/
|
|
2135
2537
|
getSessionExpiry() {
|
|
2136
|
-
const expirationMs = this.config.sessionExpirationMs ??
|
|
2538
|
+
const expirationMs = this.config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS;
|
|
2137
2539
|
return new Date(Date.now() + expirationMs);
|
|
2138
2540
|
}
|
|
2139
2541
|
/**
|
|
@@ -2190,7 +2592,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2190
2592
|
if (!this.signer) {
|
|
2191
2593
|
return void 0;
|
|
2192
2594
|
}
|
|
2193
|
-
const session = this.
|
|
2595
|
+
const session = this.currentTinyCloudSession();
|
|
2194
2596
|
if (!session) {
|
|
2195
2597
|
return void 0;
|
|
2196
2598
|
}
|
|
@@ -2290,6 +2692,34 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2290
2692
|
}
|
|
2291
2693
|
return this._sql;
|
|
2292
2694
|
}
|
|
2695
|
+
/**
|
|
2696
|
+
* Get an SQL service scoped to a specific space.
|
|
2697
|
+
*
|
|
2698
|
+
* Mirrors {@link SpaceService}'s per-space KV factory: clones the active
|
|
2699
|
+
* service context and overrides its session's spaceId so that subsequent
|
|
2700
|
+
* `sql/<dbName>/<action>` invocations route to that space. Useful when
|
|
2701
|
+
* the caller already holds a delegation covering the target space (e.g.
|
|
2702
|
+
* via {@link grantRuntimePermissions} or {@link useRuntimeDelegation})
|
|
2703
|
+
* but the SDK's per-space SQL surface isn't otherwise exposed.
|
|
2704
|
+
*
|
|
2705
|
+
* Does NOT auto-create the space.
|
|
2706
|
+
*
|
|
2707
|
+
* @param spaceId - Full space URI (`tinycloud:pkh:eip155:<chain>:<addr>:<name>`).
|
|
2708
|
+
*/
|
|
2709
|
+
sqlForSpace(spaceId) {
|
|
2710
|
+
if (!this._serviceContext || !this._serviceContext.session) {
|
|
2711
|
+
throw new Error("Not signed in. Call signIn() first.");
|
|
2712
|
+
}
|
|
2713
|
+
const sql = new SQLService2({});
|
|
2714
|
+
const spaceScopedContext = new ServiceContext2({
|
|
2715
|
+
invoke: this._serviceContext.invoke,
|
|
2716
|
+
fetch: this._serviceContext.fetch,
|
|
2717
|
+
hosts: this._serviceContext.hosts
|
|
2718
|
+
});
|
|
2719
|
+
spaceScopedContext.setSession({ ...this._serviceContext.session, spaceId });
|
|
2720
|
+
sql.initialize(spaceScopedContext);
|
|
2721
|
+
return sql;
|
|
2722
|
+
}
|
|
2293
2723
|
/**
|
|
2294
2724
|
* DuckDB database operations on this user's space.
|
|
2295
2725
|
*/
|
|
@@ -2313,6 +2743,79 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2313
2743
|
}
|
|
2314
2744
|
return this._vault;
|
|
2315
2745
|
}
|
|
2746
|
+
/**
|
|
2747
|
+
* Network-scoped encryption/decrypt service.
|
|
2748
|
+
*/
|
|
2749
|
+
get encryption() {
|
|
2750
|
+
return this.getEncryptionService();
|
|
2751
|
+
}
|
|
2752
|
+
async getEncryptionNetwork(nameOrNetworkId = this.getDefaultEncryptionNetworkId()) {
|
|
2753
|
+
const networkId = nameOrNetworkId.startsWith("urn:tinycloud:encryption:") ? nameOrNetworkId : this.getDefaultEncryptionNetworkId(nameOrNetworkId);
|
|
2754
|
+
const response = await fetch(
|
|
2755
|
+
`${this.config.host}/encryption/networks/${encodeURIComponent(networkId)}`
|
|
2756
|
+
);
|
|
2757
|
+
if (response.status === 404) {
|
|
2758
|
+
return null;
|
|
2759
|
+
}
|
|
2760
|
+
if (!response.ok) {
|
|
2761
|
+
throw new Error(
|
|
2762
|
+
`Failed to fetch encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
|
|
2763
|
+
);
|
|
2764
|
+
}
|
|
2765
|
+
const body = await response.json();
|
|
2766
|
+
return "descriptor" in body && body.descriptor ? body.descriptor : body;
|
|
2767
|
+
}
|
|
2768
|
+
async createEncryptionNetwork(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
|
|
2769
|
+
const targetNode = await this.fetchNodeId();
|
|
2770
|
+
const principal = this.did;
|
|
2771
|
+
const networkId = this.getDefaultEncryptionNetworkId(name);
|
|
2772
|
+
const body = {
|
|
2773
|
+
name,
|
|
2774
|
+
principal,
|
|
2775
|
+
threshold: { n: 1, t: 1 }
|
|
2776
|
+
};
|
|
2777
|
+
const crypto = this.createEncryptionCrypto();
|
|
2778
|
+
const facts = {
|
|
2779
|
+
type: NETWORK_ADMIN_TYPE,
|
|
2780
|
+
targetNode,
|
|
2781
|
+
networkId,
|
|
2782
|
+
bodyHash: canonicalHashHex(
|
|
2783
|
+
crypto.sha256,
|
|
2784
|
+
body
|
|
2785
|
+
),
|
|
2786
|
+
action: NETWORK_CREATE_ACTION
|
|
2787
|
+
};
|
|
2788
|
+
const signed = await this.signRawNetworkAuthorization({
|
|
2789
|
+
targetNode,
|
|
2790
|
+
networkId,
|
|
2791
|
+
action: NETWORK_CREATE_ACTION,
|
|
2792
|
+
facts
|
|
2793
|
+
});
|
|
2794
|
+
const response = await fetch(`${this.config.host}/encryption/networks`, {
|
|
2795
|
+
method: "POST",
|
|
2796
|
+
headers: {
|
|
2797
|
+
Authorization: signed.authorization,
|
|
2798
|
+
"Content-Type": "application/json"
|
|
2799
|
+
},
|
|
2800
|
+
body: canonicalizeEncryptionJson(
|
|
2801
|
+
body
|
|
2802
|
+
)
|
|
2803
|
+
});
|
|
2804
|
+
if (!response.ok) {
|
|
2805
|
+
throw new Error(
|
|
2806
|
+
`Failed to create encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
|
|
2807
|
+
);
|
|
2808
|
+
}
|
|
2809
|
+
const created = await response.json();
|
|
2810
|
+
return created.descriptor;
|
|
2811
|
+
}
|
|
2812
|
+
async ensureEncryptionNetwork(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
|
|
2813
|
+
const existing = await this.getEncryptionNetwork(name);
|
|
2814
|
+
if (existing) {
|
|
2815
|
+
return existing;
|
|
2816
|
+
}
|
|
2817
|
+
return this.createEncryptionNetwork(name);
|
|
2818
|
+
}
|
|
2316
2819
|
/**
|
|
2317
2820
|
* App-facing secrets API backed by the `secrets` space vault.
|
|
2318
2821
|
*/
|
|
@@ -2324,8 +2827,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2324
2827
|
this._secrets = new NodeSecretsService({
|
|
2325
2828
|
getService: () => this.getBaseSecrets(),
|
|
2326
2829
|
getManifest: () => this.manifest,
|
|
2830
|
+
hasPermissions: (permissions) => this.hasRuntimePermissions(permissions),
|
|
2327
2831
|
grantPermissions: (additional) => this.grantRuntimePermissions(additional),
|
|
2328
2832
|
canEscalate: () => this.signer !== void 0 && this.tc !== void 0,
|
|
2833
|
+
getEncryptionNetworkId: () => this.getDefaultEncryptionNetworkId(),
|
|
2329
2834
|
getUnlockSigner: () => this.signer ?? void 0
|
|
2330
2835
|
});
|
|
2331
2836
|
}
|
|
@@ -2415,7 +2920,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2415
2920
|
* every requested permission.
|
|
2416
2921
|
*/
|
|
2417
2922
|
hasRuntimePermissions(permissions) {
|
|
2418
|
-
const session = this.
|
|
2923
|
+
const session = this.currentTinyCloudSession();
|
|
2419
2924
|
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
2420
2925
|
return false;
|
|
2421
2926
|
}
|
|
@@ -2435,7 +2940,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2435
2940
|
if (permissions === void 0) {
|
|
2436
2941
|
return this.runtimePermissionGrants.map((grant) => grant.delegation);
|
|
2437
2942
|
}
|
|
2438
|
-
const session = this.
|
|
2943
|
+
const session = this.currentTinyCloudSession();
|
|
2439
2944
|
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
2440
2945
|
return [];
|
|
2441
2946
|
}
|
|
@@ -2449,7 +2954,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2449
2954
|
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
2450
2955
|
*/
|
|
2451
2956
|
async useRuntimeDelegation(delegation) {
|
|
2452
|
-
const session = this.
|
|
2957
|
+
const session = this.currentTinyCloudSession();
|
|
2453
2958
|
if (!session) {
|
|
2454
2959
|
throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
2455
2960
|
}
|
|
@@ -2488,7 +2993,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2488
2993
|
if (!Array.isArray(permissions) || permissions.length === 0) {
|
|
2489
2994
|
throw new Error("grantRuntimePermissions requires a non-empty permissions array");
|
|
2490
2995
|
}
|
|
2491
|
-
const session = this.
|
|
2996
|
+
const session = this.currentTinyCloudSession();
|
|
2492
2997
|
if (!session) {
|
|
2493
2998
|
throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
2494
2999
|
}
|
|
@@ -2512,13 +3017,22 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2512
3017
|
"grantRuntimePermissions requires wallet mode with a signer or privateKey."
|
|
2513
3018
|
);
|
|
2514
3019
|
}
|
|
3020
|
+
const rawEntries = expanded.filter(
|
|
3021
|
+
(entry) => this.isEncryptionPermissionEntry(entry)
|
|
3022
|
+
);
|
|
3023
|
+
const spaceEntries = expanded.filter(
|
|
3024
|
+
(entry) => !this.isEncryptionPermissionEntry(entry)
|
|
3025
|
+
);
|
|
2515
3026
|
const bySpace = /* @__PURE__ */ new Map();
|
|
2516
|
-
for (const entry of
|
|
3027
|
+
for (const entry of spaceEntries) {
|
|
2517
3028
|
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
2518
3029
|
const current = bySpace.get(spaceId) ?? [];
|
|
2519
3030
|
current.push(entry);
|
|
2520
3031
|
bySpace.set(spaceId, current);
|
|
2521
3032
|
}
|
|
3033
|
+
if (bySpace.size === 0 && rawEntries.length > 0) {
|
|
3034
|
+
bySpace.set(session.spaceId, []);
|
|
3035
|
+
}
|
|
2522
3036
|
const now = /* @__PURE__ */ new Date();
|
|
2523
3037
|
const requestedExpiryMs = resolveExpiryMs(options?.expiry);
|
|
2524
3038
|
let expiresAt = new Date(now.getTime() + requestedExpiryMs);
|
|
@@ -2526,10 +3040,17 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2526
3040
|
expiresAt = sessionExpiry;
|
|
2527
3041
|
}
|
|
2528
3042
|
const delegations = [];
|
|
3043
|
+
let rawEntriesAttached = false;
|
|
2529
3044
|
for (const [spaceId, entries] of bySpace) {
|
|
3045
|
+
const rawForDelegation = !rawEntriesAttached ? rawEntries : [];
|
|
3046
|
+
if (rawForDelegation.length > 0) {
|
|
3047
|
+
rawEntriesAttached = true;
|
|
3048
|
+
}
|
|
3049
|
+
const delegatedEntries = [...entries, ...rawForDelegation];
|
|
2530
3050
|
const abilities = this.permissionsToAbilities(entries);
|
|
2531
3051
|
const prepared = this.wasmBindings.prepareSession({
|
|
2532
3052
|
abilities,
|
|
3053
|
+
...rawForDelegation.length > 0 ? { rawAbilities: this.permissionsToRawAbilities(rawForDelegation) } : {},
|
|
2533
3054
|
address: this.wasmBindings.ensureEip55(session.address),
|
|
2534
3055
|
chainId: session.chainId,
|
|
2535
3056
|
domain: this.siweDomain,
|
|
@@ -2554,7 +3075,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2554
3075
|
}
|
|
2555
3076
|
const delegation = this.runtimeDelegationFromSession(
|
|
2556
3077
|
delegatedSession,
|
|
2557
|
-
|
|
3078
|
+
delegatedEntries,
|
|
2558
3079
|
spaceId,
|
|
2559
3080
|
session,
|
|
2560
3081
|
expiresAt
|
|
@@ -2568,7 +3089,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2568
3089
|
jwk: session.jwk
|
|
2569
3090
|
},
|
|
2570
3091
|
delegation,
|
|
2571
|
-
operations: this.permissionOperations(
|
|
3092
|
+
operations: this.permissionOperations(delegatedEntries, spaceId),
|
|
2572
3093
|
expiresAt
|
|
2573
3094
|
});
|
|
2574
3095
|
delegations.push(delegation);
|
|
@@ -2716,7 +3237,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2716
3237
|
];
|
|
2717
3238
|
const abilities = { kv: { "": kvActions } };
|
|
2718
3239
|
const now = /* @__PURE__ */ new Date();
|
|
2719
|
-
const expiryMs =
|
|
3240
|
+
const expiryMs = EXPIRY3.EPHEMERAL_MS;
|
|
2720
3241
|
const expirationTime = new Date(now.getTime() + expiryMs);
|
|
2721
3242
|
const prepared = this.wasmBindings.prepareSession({
|
|
2722
3243
|
abilities,
|
|
@@ -2886,7 +3407,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2886
3407
|
* `forceWalletSign` is not set.
|
|
2887
3408
|
*/
|
|
2888
3409
|
async delegateTo(did, permissions, options) {
|
|
2889
|
-
const session = this.
|
|
3410
|
+
const session = this.currentTinyCloudSession();
|
|
2890
3411
|
if (!session) {
|
|
2891
3412
|
throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
2892
3413
|
}
|
|
@@ -3000,11 +3521,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3000
3521
|
* the current session; we build one multi-resource abilities map
|
|
3001
3522
|
* and emit one signed UCAN covering them all.
|
|
3002
3523
|
*
|
|
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.
|
|
3524
|
+
* Non-encryption entries must share the same target space. Encryption
|
|
3525
|
+
* entries are raw network URNs and do not participate in space grouping.
|
|
3008
3526
|
*
|
|
3009
3527
|
* @internal
|
|
3010
3528
|
*/
|
|
@@ -3016,15 +3534,18 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3016
3534
|
}
|
|
3017
3535
|
const resolvedSpaces = /* @__PURE__ */ new Set();
|
|
3018
3536
|
for (const entry of entries) {
|
|
3537
|
+
if (this.isEncryptionPermissionEntry(entry)) {
|
|
3538
|
+
continue;
|
|
3539
|
+
}
|
|
3019
3540
|
const spaceId2 = this.resolvePermissionSpace(entry.space, session);
|
|
3020
3541
|
resolvedSpaces.add(spaceId2);
|
|
3021
3542
|
}
|
|
3022
|
-
if (resolvedSpaces.size
|
|
3543
|
+
if (resolvedSpaces.size > 1) {
|
|
3023
3544
|
throw new Error(
|
|
3024
3545
|
`delegateTo: all permission entries must target the same space, got ${resolvedSpaces.size}: ${JSON.stringify([...resolvedSpaces])}`
|
|
3025
3546
|
);
|
|
3026
3547
|
}
|
|
3027
|
-
const spaceId = [...resolvedSpaces][0];
|
|
3548
|
+
const spaceId = resolvedSpaces.size === 1 ? [...resolvedSpaces][0] : session.spaceId;
|
|
3028
3549
|
const abilities = {};
|
|
3029
3550
|
for (const entry of entries) {
|
|
3030
3551
|
const shortService = SERVICE_LONG_TO_SHORT[entry.service];
|
|
@@ -3171,11 +3692,32 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3171
3692
|
}
|
|
3172
3693
|
return abilities;
|
|
3173
3694
|
}
|
|
3695
|
+
isEncryptionPermissionEntry(entry) {
|
|
3696
|
+
return entry.service === ENCRYPTION_PERMISSION_SERVICE2 && entry.path.startsWith("urn:tinycloud:encryption:");
|
|
3697
|
+
}
|
|
3698
|
+
permissionsToRawAbilities(entries) {
|
|
3699
|
+
const rawAbilities = {};
|
|
3700
|
+
for (const entry of entries) {
|
|
3701
|
+
if (!this.isEncryptionPermissionEntry(entry)) {
|
|
3702
|
+
continue;
|
|
3703
|
+
}
|
|
3704
|
+
const existing = rawAbilities[entry.path] ?? [];
|
|
3705
|
+
const seen = new Set(existing);
|
|
3706
|
+
for (const action of entry.actions) {
|
|
3707
|
+
if (!seen.has(action)) {
|
|
3708
|
+
existing.push(action);
|
|
3709
|
+
seen.add(action);
|
|
3710
|
+
}
|
|
3711
|
+
}
|
|
3712
|
+
rawAbilities[entry.path] = existing;
|
|
3713
|
+
}
|
|
3714
|
+
return rawAbilities;
|
|
3715
|
+
}
|
|
3174
3716
|
permissionOperations(entries, spaceId) {
|
|
3175
3717
|
return entries.flatMap((entry) => {
|
|
3176
3718
|
const service = this.shortServiceName(entry.service);
|
|
3177
3719
|
return entry.actions.map((action) => ({
|
|
3178
|
-
spaceId,
|
|
3720
|
+
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
|
|
3179
3721
|
service,
|
|
3180
3722
|
path: entry.path,
|
|
3181
3723
|
action
|
|
@@ -3198,7 +3740,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3198
3740
|
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
3199
3741
|
const service = this.shortServiceName(entry.service);
|
|
3200
3742
|
return entry.actions.map((action) => ({
|
|
3201
|
-
spaceId,
|
|
3743
|
+
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
|
|
3202
3744
|
service,
|
|
3203
3745
|
path: entry.path,
|
|
3204
3746
|
action
|
|
@@ -3255,24 +3797,40 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3255
3797
|
expiresAt: delegation.expiry
|
|
3256
3798
|
};
|
|
3257
3799
|
}
|
|
3800
|
+
installRuntimeGrantFromServiceSession(delegation, session, expiresAt) {
|
|
3801
|
+
const operations = this.operationsFromDelegation(delegation);
|
|
3802
|
+
if (operations.length === 0) {
|
|
3803
|
+
return;
|
|
3804
|
+
}
|
|
3805
|
+
this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
|
|
3806
|
+
(grant) => grant.delegation.cid !== delegation.cid && grant.session.delegationCid !== session.delegationCid
|
|
3807
|
+
);
|
|
3808
|
+
this.runtimePermissionGrants.push({
|
|
3809
|
+
session,
|
|
3810
|
+
delegation,
|
|
3811
|
+
operations,
|
|
3812
|
+
expiresAt
|
|
3813
|
+
});
|
|
3814
|
+
}
|
|
3258
3815
|
delegatedResourcesForEntries(entries, spaceId) {
|
|
3259
3816
|
return entries.map((entry) => ({
|
|
3260
3817
|
service: this.shortServiceName(entry.service),
|
|
3261
|
-
space: spaceId,
|
|
3818
|
+
space: this.isEncryptionPermissionEntry(entry) ? "encryption" : spaceId,
|
|
3262
3819
|
path: entry.path,
|
|
3263
3820
|
actions: [...entry.actions]
|
|
3264
3821
|
}));
|
|
3265
3822
|
}
|
|
3266
3823
|
operationsFromDelegation(delegation) {
|
|
3267
3824
|
const resources = delegation.resources !== void 0 && delegation.resources.length > 0 ? delegation.resources : this.flatDelegationResources(delegation);
|
|
3268
|
-
return resources.flatMap(
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
service:
|
|
3825
|
+
return resources.flatMap((resource) => {
|
|
3826
|
+
const service = this.invocationServiceName(resource.service);
|
|
3827
|
+
return resource.actions.map((action) => ({
|
|
3828
|
+
...this.isEncryptionNetworkOperation(service, resource.path) ? { resource: resource.path } : { spaceId: resource.space },
|
|
3829
|
+
service,
|
|
3272
3830
|
path: resource.path,
|
|
3273
3831
|
action
|
|
3274
|
-
}))
|
|
3275
|
-
);
|
|
3832
|
+
}));
|
|
3833
|
+
});
|
|
3276
3834
|
}
|
|
3277
3835
|
flatDelegationResources(delegation) {
|
|
3278
3836
|
const byService = /* @__PURE__ */ new Map();
|
|
@@ -3321,7 +3879,13 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3321
3879
|
);
|
|
3322
3880
|
}
|
|
3323
3881
|
operationCovers(granted, requested) {
|
|
3324
|
-
|
|
3882
|
+
if (granted.service !== requested.service || !this.actionContains(granted.action, requested.action)) {
|
|
3883
|
+
return false;
|
|
3884
|
+
}
|
|
3885
|
+
if (granted.resource !== void 0 || requested.resource !== void 0) {
|
|
3886
|
+
return granted.resource !== void 0 && requested.resource !== void 0 && granted.resource === requested.resource && this.pathContains(granted.path, requested.path);
|
|
3887
|
+
}
|
|
3888
|
+
return granted.spaceId !== void 0 && requested.spaceId !== void 0 && granted.spaceId === requested.spaceId && this.pathContains(granted.path, requested.path);
|
|
3325
3889
|
}
|
|
3326
3890
|
actionContains(grantedAction, requestedAction) {
|
|
3327
3891
|
if (grantedAction === requestedAction) {
|
|
@@ -3336,6 +3900,37 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3336
3900
|
invocationServiceName(service) {
|
|
3337
3901
|
return service.startsWith("tinycloud.") ? this.shortServiceName(service) : service;
|
|
3338
3902
|
}
|
|
3903
|
+
isEncryptionNetworkOperation(service, path) {
|
|
3904
|
+
return service === "encryption" && path.startsWith("urn:tinycloud:encryption:");
|
|
3905
|
+
}
|
|
3906
|
+
operationFromInvokeAnyEntry(entry) {
|
|
3907
|
+
const service = this.invocationServiceName(entry.service);
|
|
3908
|
+
if (typeof entry.resource === "string") {
|
|
3909
|
+
return {
|
|
3910
|
+
resource: entry.resource,
|
|
3911
|
+
service,
|
|
3912
|
+
path: entry.path,
|
|
3913
|
+
action: entry.action
|
|
3914
|
+
};
|
|
3915
|
+
}
|
|
3916
|
+
if (this.isEncryptionNetworkOperation(service, entry.path)) {
|
|
3917
|
+
return {
|
|
3918
|
+
resource: entry.path,
|
|
3919
|
+
service,
|
|
3920
|
+
path: entry.path,
|
|
3921
|
+
action: entry.action
|
|
3922
|
+
};
|
|
3923
|
+
}
|
|
3924
|
+
if (typeof entry.spaceId === "string") {
|
|
3925
|
+
return {
|
|
3926
|
+
spaceId: entry.spaceId,
|
|
3927
|
+
service,
|
|
3928
|
+
path: entry.path,
|
|
3929
|
+
action: entry.action
|
|
3930
|
+
};
|
|
3931
|
+
}
|
|
3932
|
+
return void 0;
|
|
3933
|
+
}
|
|
3339
3934
|
pathContains(grantedPath, requestedPath) {
|
|
3340
3935
|
if (grantedPath === "" || grantedPath === "/") {
|
|
3341
3936
|
return true;
|
|
@@ -3574,6 +4169,17 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3574
4169
|
// Not used in session-only mode
|
|
3575
4170
|
};
|
|
3576
4171
|
this.trackReceivedDelegation(delegation, this.sessionKeyJwk);
|
|
4172
|
+
this.installRuntimeGrantFromServiceSession(
|
|
4173
|
+
delegation,
|
|
4174
|
+
{
|
|
4175
|
+
delegationHeader: session2.delegationHeader,
|
|
4176
|
+
delegationCid: session2.delegationCid,
|
|
4177
|
+
spaceId: session2.spaceId,
|
|
4178
|
+
verificationMethod: session2.verificationMethod,
|
|
4179
|
+
jwk: session2.jwk
|
|
4180
|
+
},
|
|
4181
|
+
delegation.expiry
|
|
4182
|
+
);
|
|
3577
4183
|
return new DelegatedAccess(session2, delegation, targetHost, this.wasmBindings.invoke);
|
|
3578
4184
|
}
|
|
3579
4185
|
const mySession = this.auth?.tinyCloudSession;
|
|
@@ -3585,6 +4191,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3585
4191
|
const kvActions = delegation.actions.filter((a) => a.startsWith("tinycloud.kv/"));
|
|
3586
4192
|
const sqlActions = delegation.actions.filter((a) => a.startsWith("tinycloud.sql/"));
|
|
3587
4193
|
const duckdbActions = delegation.actions.filter((a) => a.startsWith("tinycloud.duckdb/"));
|
|
4194
|
+
const encryptionActions = delegation.actions.filter(
|
|
4195
|
+
(a) => a.startsWith("tinycloud.encryption/")
|
|
4196
|
+
);
|
|
4197
|
+
const rawAbilities = {};
|
|
3588
4198
|
if (kvActions.length > 0) {
|
|
3589
4199
|
abilities.kv = { [delegation.path]: kvActions };
|
|
3590
4200
|
}
|
|
@@ -3594,6 +4204,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3594
4204
|
if (duckdbActions.length > 0) {
|
|
3595
4205
|
abilities.duckdb = { [delegation.path]: duckdbActions };
|
|
3596
4206
|
}
|
|
4207
|
+
if (encryptionActions.length > 0 && delegation.path.startsWith("urn:tinycloud:encryption:")) {
|
|
4208
|
+
rawAbilities[delegation.path] = encryptionActions;
|
|
4209
|
+
}
|
|
3597
4210
|
const now = /* @__PURE__ */ new Date();
|
|
3598
4211
|
const maxExpiry = new Date(now.getTime() + 60 * 60 * 1e3);
|
|
3599
4212
|
const expirationTime = delegation.expiry < maxExpiry ? delegation.expiry : maxExpiry;
|
|
@@ -3606,7 +4219,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3606
4219
|
expirationTime: expirationTime.toISOString(),
|
|
3607
4220
|
spaceId: delegation.spaceId,
|
|
3608
4221
|
jwk,
|
|
3609
|
-
parents: [delegation.cid]
|
|
4222
|
+
parents: [delegation.cid],
|
|
4223
|
+
...Object.keys(rawAbilities).length > 0 ? { rawAbilities } : {}
|
|
3610
4224
|
});
|
|
3611
4225
|
const signature = await this.signer.signMessage(prepared.siwe);
|
|
3612
4226
|
const invokerSession = this.wasmBindings.completeSessionSetup({
|
|
@@ -3633,6 +4247,17 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3633
4247
|
signature
|
|
3634
4248
|
};
|
|
3635
4249
|
this.trackReceivedDelegation(delegation, jwk);
|
|
4250
|
+
this.installRuntimeGrantFromServiceSession(
|
|
4251
|
+
delegation,
|
|
4252
|
+
{
|
|
4253
|
+
delegationHeader: session.delegationHeader,
|
|
4254
|
+
delegationCid: session.delegationCid,
|
|
4255
|
+
spaceId: session.spaceId,
|
|
4256
|
+
verificationMethod: session.verificationMethod,
|
|
4257
|
+
jwk: session.jwk
|
|
4258
|
+
},
|
|
4259
|
+
expirationTime
|
|
4260
|
+
);
|
|
3636
4261
|
return new DelegatedAccess(session, delegation, targetHost, this.wasmBindings.invoke);
|
|
3637
4262
|
}
|
|
3638
4263
|
/**
|
|
@@ -3753,7 +4378,7 @@ import {
|
|
|
3753
4378
|
loadManifest,
|
|
3754
4379
|
isCapabilitySubset as isCapabilitySubset2,
|
|
3755
4380
|
expandActionShortNames,
|
|
3756
|
-
expandPermissionEntries,
|
|
4381
|
+
expandPermissionEntries as expandPermissionEntries2,
|
|
3757
4382
|
expandPermissionEntry,
|
|
3758
4383
|
parseExpiry as parseExpiry2,
|
|
3759
4384
|
resourceCapabilitiesToSpaceAbilitiesMap as resourceCapabilitiesToSpaceAbilitiesMap2
|
|
@@ -3776,10 +4401,24 @@ function deserializeDelegation(data) {
|
|
|
3776
4401
|
}
|
|
3777
4402
|
|
|
3778
4403
|
// src/core.ts
|
|
3779
|
-
import {
|
|
4404
|
+
import {
|
|
4405
|
+
DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
|
|
4406
|
+
KVService as KVService3,
|
|
4407
|
+
PrefixedKVService
|
|
4408
|
+
} from "@tinycloud/sdk-core";
|
|
3780
4409
|
import { SQLService as SQLService3, SQLAction, DatabaseHandle } from "@tinycloud/sdk-core";
|
|
3781
4410
|
import { DuckDbService as DuckDbService3, DuckDbDatabaseHandle, DuckDbAction } from "@tinycloud/sdk-core";
|
|
3782
|
-
import {
|
|
4411
|
+
import {
|
|
4412
|
+
DataVaultService as DataVaultService2,
|
|
4413
|
+
VaultHeaders,
|
|
4414
|
+
VaultPublicSpaceKVActions,
|
|
4415
|
+
createVaultCrypto as createVaultCrypto2,
|
|
4416
|
+
SecretsService as SecretsService2,
|
|
4417
|
+
SECRET_NAME_RE,
|
|
4418
|
+
canonicalizeSecretScope,
|
|
4419
|
+
resolveSecretListPrefix as resolveSecretListPrefix2,
|
|
4420
|
+
resolveSecretPath as resolveSecretPath2
|
|
4421
|
+
} from "@tinycloud/sdk-core";
|
|
3783
4422
|
import {
|
|
3784
4423
|
DelegationManager as DelegationManager2,
|
|
3785
4424
|
SharingService as SharingService2,
|
|
@@ -3815,6 +4454,7 @@ export {
|
|
|
3815
4454
|
CapabilityKeyRegistryErrorCodes,
|
|
3816
4455
|
DEFAULT_MANIFEST_SPACE2 as DEFAULT_MANIFEST_SPACE,
|
|
3817
4456
|
DEFAULT_MANIFEST_VERSION,
|
|
4457
|
+
DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
|
|
3818
4458
|
DataVaultService2 as DataVaultService,
|
|
3819
4459
|
DatabaseHandle,
|
|
3820
4460
|
DelegatedAccess,
|
|
@@ -3831,6 +4471,7 @@ export {
|
|
|
3831
4471
|
PermissionNotInManifestError2 as PermissionNotInManifestError,
|
|
3832
4472
|
PrefixedKVService,
|
|
3833
4473
|
ProtocolMismatchError,
|
|
4474
|
+
SECRET_NAME_RE,
|
|
3834
4475
|
SQLAction,
|
|
3835
4476
|
SQLService3 as SQLService,
|
|
3836
4477
|
SecretsService2 as SecretsService,
|
|
@@ -3850,6 +4491,7 @@ export {
|
|
|
3850
4491
|
VersionCheckError,
|
|
3851
4492
|
WasmKeyProvider,
|
|
3852
4493
|
buildSpaceUri,
|
|
4494
|
+
canonicalizeSecretScope,
|
|
3853
4495
|
checkNodeInfo2 as checkNodeInfo,
|
|
3854
4496
|
composeManifestRequest2 as composeManifestRequest,
|
|
3855
4497
|
createCapabilityKeyRegistry,
|
|
@@ -3861,7 +4503,7 @@ export {
|
|
|
3861
4503
|
defaultSpaceCreationHandler,
|
|
3862
4504
|
deserializeDelegation,
|
|
3863
4505
|
expandActionShortNames,
|
|
3864
|
-
expandPermissionEntries,
|
|
4506
|
+
expandPermissionEntries2 as expandPermissionEntries,
|
|
3865
4507
|
expandPermissionEntry,
|
|
3866
4508
|
isCapabilitySubset2 as isCapabilitySubset,
|
|
3867
4509
|
loadManifest,
|
|
@@ -3869,6 +4511,8 @@ export {
|
|
|
3869
4511
|
parseExpiry2 as parseExpiry,
|
|
3870
4512
|
parseSpaceUri,
|
|
3871
4513
|
resolveManifest2 as resolveManifest,
|
|
4514
|
+
resolveSecretListPrefix2 as resolveSecretListPrefix,
|
|
4515
|
+
resolveSecretPath2 as resolveSecretPath,
|
|
3872
4516
|
resourceCapabilitiesToSpaceAbilitiesMap2 as resourceCapabilitiesToSpaceAbilitiesMap,
|
|
3873
4517
|
serializeDelegation,
|
|
3874
4518
|
validateManifest
|