@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.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/core.ts
|
|
@@ -27,6 +37,7 @@ __export(core_exports, {
|
|
|
27
37
|
CapabilityKeyRegistryErrorCodes: () => import_sdk_core15.CapabilityKeyRegistryErrorCodes,
|
|
28
38
|
DEFAULT_MANIFEST_SPACE: () => import_sdk_core9.DEFAULT_MANIFEST_SPACE,
|
|
29
39
|
DEFAULT_MANIFEST_VERSION: () => import_sdk_core9.DEFAULT_MANIFEST_VERSION,
|
|
40
|
+
DEFAULT_SIGNED_READ_URL_EXPIRY_MS: () => import_sdk_core10.DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
|
|
30
41
|
DataVaultService: () => import_sdk_core13.DataVaultService,
|
|
31
42
|
DatabaseHandle: () => import_sdk_core11.DatabaseHandle,
|
|
32
43
|
DelegatedAccess: () => DelegatedAccess,
|
|
@@ -43,6 +54,7 @@ __export(core_exports, {
|
|
|
43
54
|
PermissionNotInManifestError: () => import_sdk_core9.PermissionNotInManifestError,
|
|
44
55
|
PrefixedKVService: () => import_sdk_core10.PrefixedKVService,
|
|
45
56
|
ProtocolMismatchError: () => import_sdk_core17.ProtocolMismatchError,
|
|
57
|
+
SECRET_NAME_RE: () => import_sdk_core13.SECRET_NAME_RE,
|
|
46
58
|
SQLAction: () => import_sdk_core11.SQLAction,
|
|
47
59
|
SQLService: () => import_sdk_core11.SQLService,
|
|
48
60
|
SecretsService: () => import_sdk_core13.SecretsService,
|
|
@@ -56,11 +68,13 @@ __export(core_exports, {
|
|
|
56
68
|
TinyCloud: () => import_sdk_core7.TinyCloud,
|
|
57
69
|
TinyCloudNode: () => TinyCloudNode,
|
|
58
70
|
UnsupportedFeatureError: () => import_sdk_core17.UnsupportedFeatureError,
|
|
71
|
+
VAULT_PERMISSION_SERVICE: () => import_sdk_core9.VAULT_PERMISSION_SERVICE,
|
|
59
72
|
VaultHeaders: () => import_sdk_core13.VaultHeaders,
|
|
60
73
|
VaultPublicSpaceKVActions: () => import_sdk_core13.VaultPublicSpaceKVActions,
|
|
61
74
|
VersionCheckError: () => import_sdk_core17.VersionCheckError,
|
|
62
75
|
WasmKeyProvider: () => WasmKeyProvider,
|
|
63
76
|
buildSpaceUri: () => import_sdk_core16.buildSpaceUri,
|
|
77
|
+
canonicalizeSecretScope: () => import_sdk_core13.canonicalizeSecretScope,
|
|
64
78
|
checkNodeInfo: () => import_sdk_core17.checkNodeInfo,
|
|
65
79
|
composeManifestRequest: () => import_sdk_core9.composeManifestRequest,
|
|
66
80
|
createCapabilityKeyRegistry: () => import_sdk_core15.createCapabilityKeyRegistry,
|
|
@@ -72,12 +86,16 @@ __export(core_exports, {
|
|
|
72
86
|
defaultSpaceCreationHandler: () => import_sdk_core8.defaultSpaceCreationHandler,
|
|
73
87
|
deserializeDelegation: () => deserializeDelegation,
|
|
74
88
|
expandActionShortNames: () => import_sdk_core9.expandActionShortNames,
|
|
89
|
+
expandPermissionEntries: () => import_sdk_core9.expandPermissionEntries,
|
|
90
|
+
expandPermissionEntry: () => import_sdk_core9.expandPermissionEntry,
|
|
75
91
|
isCapabilitySubset: () => import_sdk_core9.isCapabilitySubset,
|
|
76
92
|
loadManifest: () => import_sdk_core9.loadManifest,
|
|
77
93
|
makePublicSpaceId: () => import_sdk_core16.makePublicSpaceId,
|
|
78
94
|
parseExpiry: () => import_sdk_core9.parseExpiry,
|
|
79
95
|
parseSpaceUri: () => import_sdk_core16.parseSpaceUri,
|
|
80
96
|
resolveManifest: () => import_sdk_core9.resolveManifest,
|
|
97
|
+
resolveSecretListPrefix: () => import_sdk_core13.resolveSecretListPrefix,
|
|
98
|
+
resolveSecretPath: () => import_sdk_core13.resolveSecretPath,
|
|
81
99
|
resourceCapabilitiesToSpaceAbilitiesMap: () => import_sdk_core9.resourceCapabilitiesToSpaceAbilitiesMap,
|
|
82
100
|
serializeDelegation: () => serializeDelegation,
|
|
83
101
|
validateManifest: () => import_sdk_core9.validateManifest
|
|
@@ -337,7 +355,7 @@ var NodeUserAuthorization = class {
|
|
|
337
355
|
]
|
|
338
356
|
}
|
|
339
357
|
};
|
|
340
|
-
this.sessionExpirationMs = config.sessionExpirationMs ??
|
|
358
|
+
this.sessionExpirationMs = config.sessionExpirationMs ?? import_sdk_core2.EXPIRY.SESSION_MS;
|
|
341
359
|
this.autoCreateSpace = config.autoCreateSpace ?? false;
|
|
342
360
|
this.spaceCreationHandler = config.spaceCreationHandler;
|
|
343
361
|
this.tinycloudHosts = config.tinycloudHosts;
|
|
@@ -390,6 +408,23 @@ var NodeUserAuthorization = class {
|
|
|
390
408
|
get tinyCloudSession() {
|
|
391
409
|
return this._tinyCloudSession;
|
|
392
410
|
}
|
|
411
|
+
/**
|
|
412
|
+
* Rehydrate the auth-layer session from previously-persisted delegation
|
|
413
|
+
* data. Used by {@link TinyCloudNode.restoreSession} so that downstream
|
|
414
|
+
* surfaces that read from `tinyCloudSession` (notably
|
|
415
|
+
* `grantRuntimePermissions`, which extracts the SIWE expiry from it) work
|
|
416
|
+
* without re-running the full sign-in flow.
|
|
417
|
+
*
|
|
418
|
+
* Caller must supply the same fields that `signIn` would have written —
|
|
419
|
+
* `siwe` is the load-bearing one because `extractSiweExpiration` returns
|
|
420
|
+
* undefined for missing SIWEs and the SDK then treats the session as
|
|
421
|
+
* expired-at-epoch-zero.
|
|
422
|
+
*/
|
|
423
|
+
setRestoredTinyCloudSession(session) {
|
|
424
|
+
this._tinyCloudSession = session;
|
|
425
|
+
this._address = session.address;
|
|
426
|
+
this._chainId = session.chainId;
|
|
427
|
+
}
|
|
393
428
|
async resolveTinyCloudHostsForSignIn(address, chainId) {
|
|
394
429
|
if (this.tinycloudHosts && this.tinycloudHosts.length > 0) {
|
|
395
430
|
return;
|
|
@@ -460,21 +495,64 @@ var NodeUserAuthorization = class {
|
|
|
460
495
|
}
|
|
461
496
|
return this.wasm.makeSpaceId(address, chainId, space);
|
|
462
497
|
}
|
|
498
|
+
defaultEncryptionNetworkId(address, chainId) {
|
|
499
|
+
return `urn:tinycloud:encryption:did:pkh:eip155:${chainId}:${address}:default`;
|
|
500
|
+
}
|
|
463
501
|
resolveSignInCapabilities(address, chainId) {
|
|
464
502
|
const request = this.getCapabilityRequest();
|
|
465
503
|
if (request === void 0) {
|
|
504
|
+
const defaultNetworkId = this.defaultEncryptionNetworkId(address, chainId);
|
|
505
|
+
const secretsSpaceId = this.wasm.makeSpaceId(address, chainId, "secrets");
|
|
466
506
|
return {
|
|
467
507
|
abilities: this.defaultActions,
|
|
468
|
-
spaceId: this.wasm.makeSpaceId(address, chainId, this.spacePrefix)
|
|
508
|
+
spaceId: this.wasm.makeSpaceId(address, chainId, this.spacePrefix),
|
|
509
|
+
spaceAbilities: {
|
|
510
|
+
[secretsSpaceId]: {
|
|
511
|
+
kv: {
|
|
512
|
+
"vault/secrets/": [
|
|
513
|
+
"tinycloud.kv/get",
|
|
514
|
+
"tinycloud.kv/put",
|
|
515
|
+
"tinycloud.kv/del",
|
|
516
|
+
"tinycloud.kv/list",
|
|
517
|
+
"tinycloud.kv/metadata"
|
|
518
|
+
]
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
},
|
|
522
|
+
rawAbilities: {
|
|
523
|
+
[defaultNetworkId]: [
|
|
524
|
+
"tinycloud.encryption/decrypt",
|
|
525
|
+
"tinycloud.encryption/network.create"
|
|
526
|
+
]
|
|
527
|
+
}
|
|
469
528
|
};
|
|
470
529
|
}
|
|
471
|
-
const
|
|
530
|
+
const rawAbilities = {};
|
|
531
|
+
const spaceResources = request.resources.filter((entry) => {
|
|
532
|
+
if (entry.service !== import_sdk_core2.ENCRYPTION_PERMISSION_SERVICE) {
|
|
533
|
+
return true;
|
|
534
|
+
}
|
|
535
|
+
const existing = rawAbilities[entry.path];
|
|
536
|
+
if (existing === void 0) {
|
|
537
|
+
rawAbilities[entry.path] = [...entry.actions];
|
|
538
|
+
} else {
|
|
539
|
+
const seen = new Set(existing);
|
|
540
|
+
for (const action of entry.actions) {
|
|
541
|
+
if (!seen.has(action)) {
|
|
542
|
+
existing.push(action);
|
|
543
|
+
seen.add(action);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
return false;
|
|
548
|
+
});
|
|
549
|
+
const primarySpaceName = spaceResources.find((entry) => entry.space !== "account")?.space ?? import_sdk_core2.DEFAULT_MANIFEST_SPACE;
|
|
472
550
|
const primarySpaceId = this.resolveSpaceName(
|
|
473
551
|
primarySpaceName,
|
|
474
552
|
address,
|
|
475
553
|
chainId
|
|
476
554
|
);
|
|
477
|
-
const bySpace = (0, import_sdk_core2.resourceCapabilitiesToSpaceAbilitiesMap)(
|
|
555
|
+
const bySpace = (0, import_sdk_core2.resourceCapabilitiesToSpaceAbilitiesMap)(spaceResources);
|
|
478
556
|
const spaceAbilities = {};
|
|
479
557
|
for (const [space, abilities] of Object.entries(bySpace)) {
|
|
480
558
|
spaceAbilities[this.resolveSpaceName(space, address, chainId)] = abilities;
|
|
@@ -482,7 +560,8 @@ var NodeUserAuthorization = class {
|
|
|
482
560
|
return {
|
|
483
561
|
abilities: spaceAbilities[primarySpaceId] ?? (0, import_sdk_core2.resourceCapabilitiesToAbilitiesMap)([]),
|
|
484
562
|
spaceId: primarySpaceId,
|
|
485
|
-
spaceAbilities
|
|
563
|
+
spaceAbilities,
|
|
564
|
+
rawAbilities: Object.keys(rawAbilities).length > 0 ? rawAbilities : void 0
|
|
486
565
|
};
|
|
487
566
|
}
|
|
488
567
|
/**
|
|
@@ -707,6 +786,7 @@ var NodeUserAuthorization = class {
|
|
|
707
786
|
const prepared = this.wasm.prepareSession({
|
|
708
787
|
abilities: capabilityPlan.abilities,
|
|
709
788
|
...capabilityPlan.spaceAbilities !== void 0 ? { spaceAbilities: capabilityPlan.spaceAbilities } : {},
|
|
789
|
+
...capabilityPlan.rawAbilities !== void 0 ? { rawAbilities: capabilityPlan.rawAbilities } : {},
|
|
710
790
|
address,
|
|
711
791
|
chainId,
|
|
712
792
|
domain: this.domain,
|
|
@@ -852,6 +932,7 @@ var NodeUserAuthorization = class {
|
|
|
852
932
|
const prepared = this.wasm.prepareSession({
|
|
853
933
|
abilities: capabilityPlan.abilities,
|
|
854
934
|
...capabilityPlan.spaceAbilities !== void 0 ? { spaceAbilities: capabilityPlan.spaceAbilities } : {},
|
|
935
|
+
...capabilityPlan.rawAbilities !== void 0 ? { rawAbilities: capabilityPlan.rawAbilities } : {},
|
|
855
936
|
address,
|
|
856
937
|
chainId,
|
|
857
938
|
domain: this.domain,
|
|
@@ -1225,9 +1306,10 @@ function legacyParamsToPermissionEntries(actions, path, spaceIdOverride) {
|
|
|
1225
1306
|
}
|
|
1226
1307
|
return entries;
|
|
1227
1308
|
}
|
|
1309
|
+
var DEFAULT_DELEGATION_EXPIRY_MS = import_sdk_core4.EXPIRY.SESSION_MS;
|
|
1228
1310
|
function resolveExpiryMs(expiry) {
|
|
1229
1311
|
if (expiry === void 0) {
|
|
1230
|
-
return
|
|
1312
|
+
return DEFAULT_DELEGATION_EXPIRY_MS;
|
|
1231
1313
|
}
|
|
1232
1314
|
if (typeof expiry === "number") {
|
|
1233
1315
|
if (!Number.isFinite(expiry) || expiry <= 0) {
|
|
@@ -1255,8 +1337,6 @@ function extractSiweExpiration(siwe) {
|
|
|
1255
1337
|
|
|
1256
1338
|
// src/NodeSecretsService.ts
|
|
1257
1339
|
var import_sdk_core5 = require("@tinycloud/sdk-core");
|
|
1258
|
-
var SECRET_NAME_RE = /^[A-Z][A-Z0-9_]*$/;
|
|
1259
|
-
var SECRET_PREFIX = "secrets/";
|
|
1260
1340
|
var SECRETS_SPACE = "secrets";
|
|
1261
1341
|
function ok() {
|
|
1262
1342
|
return { ok: true, data: void 0 };
|
|
@@ -1272,32 +1352,40 @@ function secretsError(code, message, cause) {
|
|
|
1272
1352
|
}
|
|
1273
1353
|
};
|
|
1274
1354
|
}
|
|
1275
|
-
function
|
|
1276
|
-
|
|
1355
|
+
function displayActionUrn(action) {
|
|
1356
|
+
switch (action) {
|
|
1357
|
+
case "get":
|
|
1358
|
+
return "tinycloud.kv/get";
|
|
1359
|
+
case "put":
|
|
1360
|
+
return "tinycloud.kv/put";
|
|
1361
|
+
case "del":
|
|
1362
|
+
return "tinycloud.kv/del";
|
|
1363
|
+
case "list":
|
|
1364
|
+
return "tinycloud.kv/list";
|
|
1365
|
+
}
|
|
1277
1366
|
}
|
|
1278
|
-
function
|
|
1279
|
-
return
|
|
1367
|
+
function secretActionName(action) {
|
|
1368
|
+
return action;
|
|
1280
1369
|
}
|
|
1281
|
-
function secretPermissionEntries(name, action) {
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1370
|
+
function secretPermissionEntries(name, options, action, encryptionNetworkId) {
|
|
1371
|
+
const entries = [];
|
|
1372
|
+
const path = action === "list" ? (0, import_sdk_core5.resolveSecretListPrefix)(options) : (0, import_sdk_core5.resolveSecretPath)(name, options).permissionPaths.vault;
|
|
1373
|
+
entries.push({
|
|
1374
|
+
service: "tinycloud.kv",
|
|
1375
|
+
space: SECRETS_SPACE,
|
|
1376
|
+
path,
|
|
1377
|
+
actions: [secretActionName(action)],
|
|
1378
|
+
skipPrefix: true
|
|
1379
|
+
});
|
|
1380
|
+
if (action === "get" && encryptionNetworkId !== void 0) {
|
|
1381
|
+
entries.push({
|
|
1382
|
+
service: "tinycloud.encryption",
|
|
1383
|
+
path: encryptionNetworkId,
|
|
1384
|
+
actions: ["decrypt"],
|
|
1295
1385
|
skipPrefix: true
|
|
1296
|
-
}
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
function isSecretsSpace(space) {
|
|
1300
|
-
return space === SECRETS_SPACE || space.endsWith(`:${SECRETS_SPACE}`);
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1388
|
+
return entries;
|
|
1301
1389
|
}
|
|
1302
1390
|
var NodeSecretsService = class {
|
|
1303
1391
|
constructor(config) {
|
|
@@ -1325,48 +1413,62 @@ var NodeSecretsService = class {
|
|
|
1325
1413
|
this.shouldRestoreUnlock = false;
|
|
1326
1414
|
this.service.lock();
|
|
1327
1415
|
}
|
|
1328
|
-
get(name) {
|
|
1329
|
-
|
|
1416
|
+
async get(name, options) {
|
|
1417
|
+
const permission = await this.ensurePermission(name, options, "get");
|
|
1418
|
+
if (!permission.ok) return permission;
|
|
1419
|
+
return options === void 0 ? this.service.get(name) : this.service.get(name, options);
|
|
1330
1420
|
}
|
|
1331
|
-
async put(name, value) {
|
|
1332
|
-
const permission = await this.
|
|
1421
|
+
async put(name, value, options) {
|
|
1422
|
+
const permission = await this.ensurePermission(name, options, "put");
|
|
1333
1423
|
if (!permission.ok) return permission;
|
|
1334
|
-
return this.service.put(name, value);
|
|
1424
|
+
return options === void 0 ? this.service.put(name, value) : this.service.put(name, value, options);
|
|
1335
1425
|
}
|
|
1336
|
-
async delete(name) {
|
|
1337
|
-
const permission = await this.
|
|
1426
|
+
async delete(name, options) {
|
|
1427
|
+
const permission = await this.ensurePermission(name, options, "del");
|
|
1338
1428
|
if (!permission.ok) return permission;
|
|
1339
|
-
return this.service.delete(name);
|
|
1429
|
+
return options === void 0 ? this.service.delete(name) : this.service.delete(name, options);
|
|
1340
1430
|
}
|
|
1341
|
-
list() {
|
|
1342
|
-
|
|
1431
|
+
async list(options) {
|
|
1432
|
+
const permission = await this.ensurePermission("", options, "list");
|
|
1433
|
+
if (!permission.ok) return permission;
|
|
1434
|
+
return options === void 0 ? this.service.list() : this.service.list(options);
|
|
1343
1435
|
}
|
|
1344
1436
|
get service() {
|
|
1345
1437
|
return this.config.getService();
|
|
1346
1438
|
}
|
|
1347
|
-
async
|
|
1348
|
-
|
|
1439
|
+
async ensurePermission(name, options, action) {
|
|
1440
|
+
const target = name || "secrets";
|
|
1441
|
+
let permissionEntries;
|
|
1442
|
+
try {
|
|
1443
|
+
permissionEntries = secretPermissionEntries(
|
|
1444
|
+
name,
|
|
1445
|
+
options,
|
|
1446
|
+
action,
|
|
1447
|
+
action === "get" ? this.config.getEncryptionNetworkId?.() : void 0
|
|
1448
|
+
);
|
|
1449
|
+
} catch (error) {
|
|
1349
1450
|
return secretsError(
|
|
1350
1451
|
import_sdk_core5.ErrorCodes.INVALID_INPUT,
|
|
1351
|
-
|
|
1452
|
+
error instanceof Error ? error.message : String(error),
|
|
1453
|
+
error instanceof Error ? error : void 0
|
|
1352
1454
|
);
|
|
1353
1455
|
}
|
|
1354
|
-
if (this.
|
|
1456
|
+
if (this.hasPermission(permissionEntries)) {
|
|
1355
1457
|
return ok();
|
|
1356
1458
|
}
|
|
1357
1459
|
if (!this.config.canEscalate()) {
|
|
1358
1460
|
return secretsError(
|
|
1359
1461
|
import_sdk_core5.ErrorCodes.PERMISSION_DENIED,
|
|
1360
|
-
`Cannot autosign ${
|
|
1462
|
+
`Cannot autosign ${displayActionUrn(action)} for ${target}; TinyCloudNode needs wallet mode with a signer or privateKey.`
|
|
1361
1463
|
);
|
|
1362
1464
|
}
|
|
1363
1465
|
try {
|
|
1364
|
-
await this.config.grantPermissions(
|
|
1466
|
+
await this.config.grantPermissions(permissionEntries);
|
|
1365
1467
|
return this.restoreUnlockAfterEscalation();
|
|
1366
1468
|
} catch (error) {
|
|
1367
1469
|
return secretsError(
|
|
1368
1470
|
import_sdk_core5.ErrorCodes.PERMISSION_DENIED,
|
|
1369
|
-
error instanceof Error ? error.message : `Autosign escalation for ${
|
|
1471
|
+
error instanceof Error ? error.message : `Autosign escalation for ${displayActionUrn(action)} on ${target} failed.`,
|
|
1370
1472
|
error instanceof Error ? error : void 0
|
|
1371
1473
|
);
|
|
1372
1474
|
}
|
|
@@ -1377,26 +1479,117 @@ var NodeSecretsService = class {
|
|
|
1377
1479
|
}
|
|
1378
1480
|
return this.service.unlock(this.unlockSigner);
|
|
1379
1481
|
}
|
|
1380
|
-
|
|
1482
|
+
hasPermission(permissionEntries) {
|
|
1483
|
+
if (this.config.hasPermissions?.(permissionEntries)) {
|
|
1484
|
+
return true;
|
|
1485
|
+
}
|
|
1381
1486
|
const manifest = this.config.getManifest();
|
|
1382
1487
|
if (manifest === void 0) {
|
|
1383
1488
|
return false;
|
|
1384
1489
|
}
|
|
1385
1490
|
const manifests = Array.isArray(manifest) ? manifest : [manifest];
|
|
1386
|
-
const
|
|
1387
|
-
return
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
(resource) => resource.service ===
|
|
1392
|
-
)
|
|
1393
|
-
)
|
|
1394
|
-
|
|
1491
|
+
const requestedEntries = (0, import_sdk_core5.expandPermissionEntries)(permissionEntries);
|
|
1492
|
+
return requestedEntries.every(
|
|
1493
|
+
(entry) => manifests.some((candidate) => {
|
|
1494
|
+
const resolved = (0, import_sdk_core5.resolveManifest)(candidate);
|
|
1495
|
+
return resolved.resources.some(
|
|
1496
|
+
(resource) => resource.service === entry.service && resource.space === entry.space && resource.path === entry.path && entry.actions.every((action) => resource.actions.includes(action))
|
|
1497
|
+
);
|
|
1498
|
+
})
|
|
1499
|
+
);
|
|
1395
1500
|
}
|
|
1396
1501
|
};
|
|
1397
1502
|
|
|
1398
1503
|
// src/TinyCloudNode.ts
|
|
1399
1504
|
var DEFAULT_HOST = "https://node.tinycloud.xyz";
|
|
1505
|
+
var DEFAULT_ENCRYPTION_NETWORK_NAME = "default";
|
|
1506
|
+
var NETWORK_CREATE_ACTION = "tinycloud.encryption/network.create";
|
|
1507
|
+
var DECRYPT_ACTION = "tinycloud.encryption/decrypt";
|
|
1508
|
+
var NETWORK_ADMIN_TYPE = "tinycloud.encryption.network-admin/v1";
|
|
1509
|
+
var DEFAULT_SESSION_EXPIRATION_MS = import_sdk_core6.EXPIRY.SESSION_MS;
|
|
1510
|
+
function base64UrlEncode(bytes) {
|
|
1511
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
1512
|
+
let output = "";
|
|
1513
|
+
for (let i = 0; i < bytes.length; i += 3) {
|
|
1514
|
+
const a = bytes[i];
|
|
1515
|
+
const b = bytes[i + 1];
|
|
1516
|
+
const c = bytes[i + 2];
|
|
1517
|
+
const triplet = a << 16 | (b ?? 0) << 8 | (c ?? 0);
|
|
1518
|
+
output += alphabet[triplet >> 18 & 63];
|
|
1519
|
+
output += alphabet[triplet >> 12 & 63];
|
|
1520
|
+
if (i + 1 < bytes.length) output += alphabet[triplet >> 6 & 63];
|
|
1521
|
+
if (i + 2 < bytes.length) output += alphabet[triplet & 63];
|
|
1522
|
+
}
|
|
1523
|
+
return output;
|
|
1524
|
+
}
|
|
1525
|
+
function base64UrlDecode(value) {
|
|
1526
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
1527
|
+
const bytes = [];
|
|
1528
|
+
let buffer = 0;
|
|
1529
|
+
let bits = 0;
|
|
1530
|
+
for (const char of value) {
|
|
1531
|
+
const index = alphabet.indexOf(char);
|
|
1532
|
+
if (index < 0) {
|
|
1533
|
+
throw new Error("invalid base64url input");
|
|
1534
|
+
}
|
|
1535
|
+
buffer = buffer << 6 | index;
|
|
1536
|
+
bits += 6;
|
|
1537
|
+
if (bits >= 8) {
|
|
1538
|
+
bits -= 8;
|
|
1539
|
+
bytes.push(buffer >> bits & 255);
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
return new Uint8Array(bytes);
|
|
1543
|
+
}
|
|
1544
|
+
async function signJwtInputWithJwk(signingInput, jwk) {
|
|
1545
|
+
const bytes = new TextEncoder().encode(signingInput);
|
|
1546
|
+
try {
|
|
1547
|
+
const subtle = globalThis.crypto?.subtle;
|
|
1548
|
+
if (!subtle) {
|
|
1549
|
+
throw new Error("WebCrypto subtle API is unavailable");
|
|
1550
|
+
}
|
|
1551
|
+
const key = await subtle.importKey(
|
|
1552
|
+
"jwk",
|
|
1553
|
+
jwk,
|
|
1554
|
+
{ name: "Ed25519" },
|
|
1555
|
+
false,
|
|
1556
|
+
["sign"]
|
|
1557
|
+
);
|
|
1558
|
+
return new Uint8Array(await subtle.sign({ name: "Ed25519" }, key, bytes));
|
|
1559
|
+
} catch {
|
|
1560
|
+
const nodeCrypto = await import("crypto");
|
|
1561
|
+
const key = nodeCrypto.createPrivateKey({ key: jwk, format: "jwk" });
|
|
1562
|
+
return new Uint8Array(nodeCrypto.sign(null, Buffer.from(bytes), key));
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
async function rewriteInvocationAudience(authorization, audience, jwk) {
|
|
1566
|
+
const [headerPart, payloadPart] = authorization.split(".");
|
|
1567
|
+
if (!headerPart || !payloadPart) {
|
|
1568
|
+
throw new Error("invalid invocation authorization");
|
|
1569
|
+
}
|
|
1570
|
+
const header = JSON.parse(new TextDecoder().decode(base64UrlDecode(headerPart)));
|
|
1571
|
+
const payload = JSON.parse(new TextDecoder().decode(base64UrlDecode(payloadPart)));
|
|
1572
|
+
payload.aud = audience;
|
|
1573
|
+
const signingInput = `${base64UrlEncode(
|
|
1574
|
+
new TextEncoder().encode(JSON.stringify(header))
|
|
1575
|
+
)}.${base64UrlEncode(new TextEncoder().encode(JSON.stringify(payload)))}`;
|
|
1576
|
+
const signature = await signJwtInputWithJwk(signingInput, jwk);
|
|
1577
|
+
return `${signingInput}.${base64UrlEncode(signature)}`;
|
|
1578
|
+
}
|
|
1579
|
+
function authorizationHeader(headers) {
|
|
1580
|
+
if (Array.isArray(headers)) {
|
|
1581
|
+
const entry = headers.find(([name]) => name.toLowerCase() === "authorization");
|
|
1582
|
+
if (!entry) {
|
|
1583
|
+
throw new Error("network invocation did not include an Authorization header");
|
|
1584
|
+
}
|
|
1585
|
+
return entry[1];
|
|
1586
|
+
}
|
|
1587
|
+
const value = headers.Authorization ?? headers.authorization;
|
|
1588
|
+
if (!value) {
|
|
1589
|
+
throw new Error("network invocation did not include an Authorization header");
|
|
1590
|
+
}
|
|
1591
|
+
return value;
|
|
1592
|
+
}
|
|
1400
1593
|
var _TinyCloudNode = class _TinyCloudNode {
|
|
1401
1594
|
/**
|
|
1402
1595
|
* Create a new TinyCloudNode instance.
|
|
@@ -1441,12 +1634,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1441
1634
|
throw new Error("WASM binding does not support invokeAny");
|
|
1442
1635
|
}
|
|
1443
1636
|
const grant = this.findGrantForOperations(
|
|
1444
|
-
entries.
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
action: entry.action
|
|
1449
|
-
}))
|
|
1637
|
+
entries.flatMap((entry) => {
|
|
1638
|
+
const operation = this.operationFromInvokeAnyEntry(entry);
|
|
1639
|
+
return operation ? [operation] : [];
|
|
1640
|
+
})
|
|
1450
1641
|
);
|
|
1451
1642
|
return this.wasmBindings.invokeAny(grant?.session ?? session, entries, facts);
|
|
1452
1643
|
};
|
|
@@ -1539,7 +1730,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1539
1730
|
sessionStorage: config.sessionStorage ?? new MemorySessionStorage(),
|
|
1540
1731
|
domain: this.siweDomain,
|
|
1541
1732
|
spacePrefix: config.prefix,
|
|
1542
|
-
sessionExpirationMs: config.sessionExpirationMs ??
|
|
1733
|
+
sessionExpirationMs: config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS,
|
|
1543
1734
|
tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
|
|
1544
1735
|
tinycloudRegistryUrl: config.tinycloudRegistryUrl,
|
|
1545
1736
|
tinycloudFallbackHosts: config.tinycloudFallbackHosts,
|
|
@@ -1674,6 +1865,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1674
1865
|
this._duckdb = void 0;
|
|
1675
1866
|
this._hooks = void 0;
|
|
1676
1867
|
this._vault = void 0;
|
|
1868
|
+
this._encryption = void 0;
|
|
1677
1869
|
this._baseSecrets = void 0;
|
|
1678
1870
|
this._secrets = void 0;
|
|
1679
1871
|
this._spaceService = void 0;
|
|
@@ -1682,6 +1874,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1682
1874
|
await this.tc.signIn(options);
|
|
1683
1875
|
this.syncResolvedHostFromAuth();
|
|
1684
1876
|
this.initializeServices();
|
|
1877
|
+
if (this.config.manifest === void 0 && this.config.capabilityRequest === void 0) {
|
|
1878
|
+
await this.ensureOwnedSpaceHosted(this.ownedSpaceId("secrets"));
|
|
1879
|
+
}
|
|
1685
1880
|
await this.writeManifestRegistryRecords();
|
|
1686
1881
|
this.notificationHandler.success("Successfully signed in");
|
|
1687
1882
|
}
|
|
@@ -1764,6 +1959,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1764
1959
|
this._duckdb = void 0;
|
|
1765
1960
|
this._hooks = void 0;
|
|
1766
1961
|
this._vault = void 0;
|
|
1962
|
+
this._encryption = void 0;
|
|
1767
1963
|
this._baseSecrets = void 0;
|
|
1768
1964
|
this._secrets = void 0;
|
|
1769
1965
|
this._spaceService = void 0;
|
|
@@ -1805,6 +2001,33 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1805
2001
|
this._vault.initialize(this._serviceContext);
|
|
1806
2002
|
this._serviceContext.registerService("vault", this._vault);
|
|
1807
2003
|
this.initializeV2Services(serviceSession);
|
|
2004
|
+
if (sessionData.siwe && sessionData.address && sessionData.chainId) {
|
|
2005
|
+
const tcSession = {
|
|
2006
|
+
address: sessionData.address,
|
|
2007
|
+
chainId: sessionData.chainId,
|
|
2008
|
+
sessionKey: JSON.stringify(sessionData.jwk),
|
|
2009
|
+
spaceId: sessionData.spaceId,
|
|
2010
|
+
delegationCid: sessionData.delegationCid,
|
|
2011
|
+
delegationHeader: sessionData.delegationHeader,
|
|
2012
|
+
verificationMethod: sessionData.verificationMethod,
|
|
2013
|
+
jwk: sessionData.jwk,
|
|
2014
|
+
siwe: sessionData.siwe,
|
|
2015
|
+
signature: sessionData.signature ?? ""
|
|
2016
|
+
};
|
|
2017
|
+
if (this.auth) {
|
|
2018
|
+
this.auth.setRestoredTinyCloudSession(tcSession);
|
|
2019
|
+
} else {
|
|
2020
|
+
this._restoredTcSession = tcSession;
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
/**
|
|
2025
|
+
* Resolve the currently-active TinyCloudSession, preferring the auth
|
|
2026
|
+
* layer's value (wallet mode) and falling back to the node-level
|
|
2027
|
+
* rehydration set by {@link restoreSession} (session-only mode).
|
|
2028
|
+
*/
|
|
2029
|
+
currentTinyCloudSession() {
|
|
2030
|
+
return this.auth?.tinyCloudSession ?? this._restoredTcSession;
|
|
1808
2031
|
}
|
|
1809
2032
|
/**
|
|
1810
2033
|
* Connect a wallet to upgrade from session-only mode to wallet mode.
|
|
@@ -1849,7 +2072,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1849
2072
|
sessionStorage: options?.sessionStorage ?? this.config.sessionStorage ?? new MemorySessionStorage(),
|
|
1850
2073
|
domain: this.siweDomain,
|
|
1851
2074
|
spacePrefix: prefix,
|
|
1852
|
-
sessionExpirationMs: this.config.sessionExpirationMs ??
|
|
2075
|
+
sessionExpirationMs: this.config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS,
|
|
1853
2076
|
tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
|
|
1854
2077
|
tinycloudRegistryUrl: this.config.tinycloudRegistryUrl,
|
|
1855
2078
|
tinycloudFallbackHosts: this.config.tinycloudFallbackHosts,
|
|
@@ -1893,7 +2116,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1893
2116
|
sessionStorage: options?.sessionStorage ?? this.config.sessionStorage ?? new MemorySessionStorage(),
|
|
1894
2117
|
domain: this.siweDomain,
|
|
1895
2118
|
spacePrefix: prefix,
|
|
1896
|
-
sessionExpirationMs: this.config.sessionExpirationMs ??
|
|
2119
|
+
sessionExpirationMs: this.config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS,
|
|
1897
2120
|
tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
|
|
1898
2121
|
tinycloudRegistryUrl: this.config.tinycloudRegistryUrl,
|
|
1899
2122
|
tinycloudFallbackHosts: this.config.tinycloudFallbackHosts,
|
|
@@ -1916,7 +2139,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1916
2139
|
* @internal
|
|
1917
2140
|
*/
|
|
1918
2141
|
initializeServices() {
|
|
1919
|
-
const session = this.
|
|
2142
|
+
const session = this.currentTinyCloudSession();
|
|
1920
2143
|
if (!session) {
|
|
1921
2144
|
return;
|
|
1922
2145
|
}
|
|
@@ -1974,6 +2197,163 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1974
2197
|
}
|
|
1975
2198
|
return kvService;
|
|
1976
2199
|
}
|
|
2200
|
+
getDefaultEncryptionNetworkId(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
|
|
2201
|
+
return `urn:tinycloud:encryption:${this.did}:${name}`;
|
|
2202
|
+
}
|
|
2203
|
+
requireServiceSession() {
|
|
2204
|
+
const session = this._serviceContext?.session;
|
|
2205
|
+
if (!session) {
|
|
2206
|
+
throw new Error("Not signed in. Call signIn() first.");
|
|
2207
|
+
}
|
|
2208
|
+
return session;
|
|
2209
|
+
}
|
|
2210
|
+
createEncryptionCrypto() {
|
|
2211
|
+
const wasm = this.wasmBindings;
|
|
2212
|
+
const columnEncrypt = (key, plaintext) => {
|
|
2213
|
+
const encrypted = wasm.vault_encrypt(key, plaintext);
|
|
2214
|
+
const out = new Uint8Array(1 + encrypted.length);
|
|
2215
|
+
out[0] = 1;
|
|
2216
|
+
out.set(encrypted, 1);
|
|
2217
|
+
return out;
|
|
2218
|
+
};
|
|
2219
|
+
const columnDecrypt = (key, blob) => {
|
|
2220
|
+
if (blob[0] !== 1) {
|
|
2221
|
+
return blob;
|
|
2222
|
+
}
|
|
2223
|
+
return wasm.vault_decrypt(key, blob.slice(1));
|
|
2224
|
+
};
|
|
2225
|
+
return {
|
|
2226
|
+
sha256: (data) => wasm.vault_sha256(data),
|
|
2227
|
+
randomBytes: (length) => wasm.vault_random_bytes(length),
|
|
2228
|
+
x25519FromSeed: (seed) => wasm.vault_x25519_from_seed(seed),
|
|
2229
|
+
x25519Dh: (privateKey, publicKey) => wasm.vault_x25519_dh(privateKey, publicKey),
|
|
2230
|
+
authEncrypt: (key, plaintext) => wasm.vault_encrypt(key, plaintext),
|
|
2231
|
+
authDecrypt: (key, ciphertext) => wasm.vault_decrypt(key, ciphertext),
|
|
2232
|
+
sealToNetworkKey: (networkPublicKey, symmetricKey) => {
|
|
2233
|
+
const seed = wasm.vault_random_bytes(32);
|
|
2234
|
+
const ephemeral = wasm.vault_x25519_from_seed(seed);
|
|
2235
|
+
const shared = wasm.vault_x25519_dh(
|
|
2236
|
+
ephemeral.privateKey,
|
|
2237
|
+
networkPublicKey
|
|
2238
|
+
);
|
|
2239
|
+
const encrypted = columnEncrypt(shared, symmetricKey);
|
|
2240
|
+
const out = new Uint8Array(ephemeral.publicKey.length + encrypted.length);
|
|
2241
|
+
out.set(ephemeral.publicKey, 0);
|
|
2242
|
+
out.set(encrypted, ephemeral.publicKey.length);
|
|
2243
|
+
return out;
|
|
2244
|
+
},
|
|
2245
|
+
openWithReceiverKey: (receiverPrivateKey, wrappedKey) => {
|
|
2246
|
+
const peerPublic = wrappedKey.slice(0, 32);
|
|
2247
|
+
const ciphertext = wrappedKey.slice(32);
|
|
2248
|
+
const shared = wasm.vault_x25519_dh(receiverPrivateKey, peerPublic);
|
|
2249
|
+
return columnDecrypt(shared, ciphertext);
|
|
2250
|
+
},
|
|
2251
|
+
verifyNodeSignature: (nodeId, message, signature) => (0, import_sdk_core6.verifyDidKeyEd25519Signature)(nodeId, message, signature)
|
|
2252
|
+
};
|
|
2253
|
+
}
|
|
2254
|
+
async fetchNodeId() {
|
|
2255
|
+
const response = await fetch(`${this.config.host}/info`);
|
|
2256
|
+
if (!response.ok) {
|
|
2257
|
+
throw new Error(`Failed to fetch node info: HTTP ${response.status}`);
|
|
2258
|
+
}
|
|
2259
|
+
const info = await response.json();
|
|
2260
|
+
if (typeof info.nodeId !== "string" || info.nodeId.length === 0) {
|
|
2261
|
+
throw new Error("Node /info response did not include nodeId");
|
|
2262
|
+
}
|
|
2263
|
+
return info.nodeId;
|
|
2264
|
+
}
|
|
2265
|
+
async signRawNetworkAuthorization(input) {
|
|
2266
|
+
if (!this.wasmBindings.invokeAny) {
|
|
2267
|
+
throw new Error("WASM binding does not support raw-resource invokeAny");
|
|
2268
|
+
}
|
|
2269
|
+
if (!this.wasmBindings.computeCid) {
|
|
2270
|
+
throw new Error("WASM binding does not support invocation CID computation");
|
|
2271
|
+
}
|
|
2272
|
+
const session = this.requireServiceSession();
|
|
2273
|
+
const headers = this.invokeAnyWithRuntimePermissions(
|
|
2274
|
+
session,
|
|
2275
|
+
[
|
|
2276
|
+
{
|
|
2277
|
+
resource: input.networkId,
|
|
2278
|
+
service: "encryption",
|
|
2279
|
+
path: input.networkId,
|
|
2280
|
+
action: input.action
|
|
2281
|
+
}
|
|
2282
|
+
],
|
|
2283
|
+
[input.facts]
|
|
2284
|
+
);
|
|
2285
|
+
const authorization = authorizationHeader(headers);
|
|
2286
|
+
const audienceBound = await rewriteInvocationAudience(
|
|
2287
|
+
authorization,
|
|
2288
|
+
input.targetNode,
|
|
2289
|
+
session.jwk
|
|
2290
|
+
);
|
|
2291
|
+
return {
|
|
2292
|
+
authorization: audienceBound,
|
|
2293
|
+
invocationCid: this.wasmBindings.computeCid(
|
|
2294
|
+
new TextEncoder().encode(audienceBound),
|
|
2295
|
+
0x55n
|
|
2296
|
+
)
|
|
2297
|
+
};
|
|
2298
|
+
}
|
|
2299
|
+
createEncryptionService() {
|
|
2300
|
+
const crypto = this.createEncryptionCrypto();
|
|
2301
|
+
const transport = {
|
|
2302
|
+
postDecrypt: async ({ networkId, authorization, canonicalBody }) => {
|
|
2303
|
+
const response = await fetch(
|
|
2304
|
+
`${this.config.host}/encryption/networks/${encodeURIComponent(networkId)}/decrypt`,
|
|
2305
|
+
{
|
|
2306
|
+
method: "POST",
|
|
2307
|
+
headers: {
|
|
2308
|
+
Authorization: authorization,
|
|
2309
|
+
"Content-Type": "application/json"
|
|
2310
|
+
},
|
|
2311
|
+
body: canonicalBody
|
|
2312
|
+
}
|
|
2313
|
+
);
|
|
2314
|
+
if (!response.ok) {
|
|
2315
|
+
throw new Error(
|
|
2316
|
+
`decrypt failed ${response.status}: ${await response.text()}`
|
|
2317
|
+
);
|
|
2318
|
+
}
|
|
2319
|
+
return await response.json();
|
|
2320
|
+
}
|
|
2321
|
+
};
|
|
2322
|
+
return new import_sdk_core6.EncryptionService({
|
|
2323
|
+
crypto,
|
|
2324
|
+
signer: {
|
|
2325
|
+
signDecryptInvocation: async (input) => {
|
|
2326
|
+
const signed = await this.signRawNetworkAuthorization({
|
|
2327
|
+
targetNode: input.targetNode,
|
|
2328
|
+
networkId: input.networkId,
|
|
2329
|
+
action: DECRYPT_ACTION,
|
|
2330
|
+
facts: input.facts
|
|
2331
|
+
});
|
|
2332
|
+
return {
|
|
2333
|
+
...signed,
|
|
2334
|
+
canonicalBody: (0, import_sdk_core6.canonicalizeEncryptionJson)(
|
|
2335
|
+
input.body
|
|
2336
|
+
)
|
|
2337
|
+
};
|
|
2338
|
+
}
|
|
2339
|
+
},
|
|
2340
|
+
transport,
|
|
2341
|
+
node: {
|
|
2342
|
+
fetchByNetworkId: (networkId) => this.getEncryptionNetwork(networkId)
|
|
2343
|
+
}
|
|
2344
|
+
});
|
|
2345
|
+
}
|
|
2346
|
+
getEncryptionService() {
|
|
2347
|
+
if (!this._serviceContext) {
|
|
2348
|
+
throw new Error("Not signed in. Call signIn() first.");
|
|
2349
|
+
}
|
|
2350
|
+
if (!this._encryption) {
|
|
2351
|
+
this._encryption = this.createEncryptionService();
|
|
2352
|
+
this._encryption.initialize(this._serviceContext);
|
|
2353
|
+
this._serviceContext.registerService("encryption", this._encryption);
|
|
2354
|
+
}
|
|
2355
|
+
return this._encryption;
|
|
2356
|
+
}
|
|
1977
2357
|
createVaultService(spaceId, kv) {
|
|
1978
2358
|
const wasm = this.wasmBindings;
|
|
1979
2359
|
const vaultCrypto = (0, import_sdk_core6.createVaultCrypto)({
|
|
@@ -1989,6 +2369,13 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1989
2369
|
return new import_sdk_core6.DataVaultService({
|
|
1990
2370
|
spaceId,
|
|
1991
2371
|
crypto: vaultCrypto,
|
|
2372
|
+
encryption: {
|
|
2373
|
+
networkId: this.getDefaultEncryptionNetworkId(),
|
|
2374
|
+
service: this.getEncryptionService(),
|
|
2375
|
+
decryptCapabilityProof: () => ({
|
|
2376
|
+
proofs: [this.requireServiceSession().delegationCid]
|
|
2377
|
+
})
|
|
2378
|
+
},
|
|
1992
2379
|
tc: {
|
|
1993
2380
|
kv,
|
|
1994
2381
|
ensurePublicSpace: async () => {
|
|
@@ -2169,7 +2556,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2169
2556
|
* @internal
|
|
2170
2557
|
*/
|
|
2171
2558
|
getSessionExpiry() {
|
|
2172
|
-
const expirationMs = this.config.sessionExpirationMs ??
|
|
2559
|
+
const expirationMs = this.config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS;
|
|
2173
2560
|
return new Date(Date.now() + expirationMs);
|
|
2174
2561
|
}
|
|
2175
2562
|
/**
|
|
@@ -2226,7 +2613,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2226
2613
|
if (!this.signer) {
|
|
2227
2614
|
return void 0;
|
|
2228
2615
|
}
|
|
2229
|
-
const session = this.
|
|
2616
|
+
const session = this.currentTinyCloudSession();
|
|
2230
2617
|
if (!session) {
|
|
2231
2618
|
return void 0;
|
|
2232
2619
|
}
|
|
@@ -2326,6 +2713,34 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2326
2713
|
}
|
|
2327
2714
|
return this._sql;
|
|
2328
2715
|
}
|
|
2716
|
+
/**
|
|
2717
|
+
* Get an SQL service scoped to a specific space.
|
|
2718
|
+
*
|
|
2719
|
+
* Mirrors {@link SpaceService}'s per-space KV factory: clones the active
|
|
2720
|
+
* service context and overrides its session's spaceId so that subsequent
|
|
2721
|
+
* `sql/<dbName>/<action>` invocations route to that space. Useful when
|
|
2722
|
+
* the caller already holds a delegation covering the target space (e.g.
|
|
2723
|
+
* via {@link grantRuntimePermissions} or {@link useRuntimeDelegation})
|
|
2724
|
+
* but the SDK's per-space SQL surface isn't otherwise exposed.
|
|
2725
|
+
*
|
|
2726
|
+
* Does NOT auto-create the space.
|
|
2727
|
+
*
|
|
2728
|
+
* @param spaceId - Full space URI (`tinycloud:pkh:eip155:<chain>:<addr>:<name>`).
|
|
2729
|
+
*/
|
|
2730
|
+
sqlForSpace(spaceId) {
|
|
2731
|
+
if (!this._serviceContext || !this._serviceContext.session) {
|
|
2732
|
+
throw new Error("Not signed in. Call signIn() first.");
|
|
2733
|
+
}
|
|
2734
|
+
const sql = new import_sdk_core6.SQLService({});
|
|
2735
|
+
const spaceScopedContext = new import_sdk_core6.ServiceContext({
|
|
2736
|
+
invoke: this._serviceContext.invoke,
|
|
2737
|
+
fetch: this._serviceContext.fetch,
|
|
2738
|
+
hosts: this._serviceContext.hosts
|
|
2739
|
+
});
|
|
2740
|
+
spaceScopedContext.setSession({ ...this._serviceContext.session, spaceId });
|
|
2741
|
+
sql.initialize(spaceScopedContext);
|
|
2742
|
+
return sql;
|
|
2743
|
+
}
|
|
2329
2744
|
/**
|
|
2330
2745
|
* DuckDB database operations on this user's space.
|
|
2331
2746
|
*/
|
|
@@ -2349,6 +2764,79 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2349
2764
|
}
|
|
2350
2765
|
return this._vault;
|
|
2351
2766
|
}
|
|
2767
|
+
/**
|
|
2768
|
+
* Network-scoped encryption/decrypt service.
|
|
2769
|
+
*/
|
|
2770
|
+
get encryption() {
|
|
2771
|
+
return this.getEncryptionService();
|
|
2772
|
+
}
|
|
2773
|
+
async getEncryptionNetwork(nameOrNetworkId = this.getDefaultEncryptionNetworkId()) {
|
|
2774
|
+
const networkId = nameOrNetworkId.startsWith("urn:tinycloud:encryption:") ? nameOrNetworkId : this.getDefaultEncryptionNetworkId(nameOrNetworkId);
|
|
2775
|
+
const response = await fetch(
|
|
2776
|
+
`${this.config.host}/encryption/networks/${encodeURIComponent(networkId)}`
|
|
2777
|
+
);
|
|
2778
|
+
if (response.status === 404) {
|
|
2779
|
+
return null;
|
|
2780
|
+
}
|
|
2781
|
+
if (!response.ok) {
|
|
2782
|
+
throw new Error(
|
|
2783
|
+
`Failed to fetch encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
|
|
2784
|
+
);
|
|
2785
|
+
}
|
|
2786
|
+
const body = await response.json();
|
|
2787
|
+
return "descriptor" in body && body.descriptor ? body.descriptor : body;
|
|
2788
|
+
}
|
|
2789
|
+
async createEncryptionNetwork(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
|
|
2790
|
+
const targetNode = await this.fetchNodeId();
|
|
2791
|
+
const principal = this.did;
|
|
2792
|
+
const networkId = this.getDefaultEncryptionNetworkId(name);
|
|
2793
|
+
const body = {
|
|
2794
|
+
name,
|
|
2795
|
+
principal,
|
|
2796
|
+
threshold: { n: 1, t: 1 }
|
|
2797
|
+
};
|
|
2798
|
+
const crypto = this.createEncryptionCrypto();
|
|
2799
|
+
const facts = {
|
|
2800
|
+
type: NETWORK_ADMIN_TYPE,
|
|
2801
|
+
targetNode,
|
|
2802
|
+
networkId,
|
|
2803
|
+
bodyHash: (0, import_sdk_core6.canonicalHashHex)(
|
|
2804
|
+
crypto.sha256,
|
|
2805
|
+
body
|
|
2806
|
+
),
|
|
2807
|
+
action: NETWORK_CREATE_ACTION
|
|
2808
|
+
};
|
|
2809
|
+
const signed = await this.signRawNetworkAuthorization({
|
|
2810
|
+
targetNode,
|
|
2811
|
+
networkId,
|
|
2812
|
+
action: NETWORK_CREATE_ACTION,
|
|
2813
|
+
facts
|
|
2814
|
+
});
|
|
2815
|
+
const response = await fetch(`${this.config.host}/encryption/networks`, {
|
|
2816
|
+
method: "POST",
|
|
2817
|
+
headers: {
|
|
2818
|
+
Authorization: signed.authorization,
|
|
2819
|
+
"Content-Type": "application/json"
|
|
2820
|
+
},
|
|
2821
|
+
body: (0, import_sdk_core6.canonicalizeEncryptionJson)(
|
|
2822
|
+
body
|
|
2823
|
+
)
|
|
2824
|
+
});
|
|
2825
|
+
if (!response.ok) {
|
|
2826
|
+
throw new Error(
|
|
2827
|
+
`Failed to create encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
|
|
2828
|
+
);
|
|
2829
|
+
}
|
|
2830
|
+
const created = await response.json();
|
|
2831
|
+
return created.descriptor;
|
|
2832
|
+
}
|
|
2833
|
+
async ensureEncryptionNetwork(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
|
|
2834
|
+
const existing = await this.getEncryptionNetwork(name);
|
|
2835
|
+
if (existing) {
|
|
2836
|
+
return existing;
|
|
2837
|
+
}
|
|
2838
|
+
return this.createEncryptionNetwork(name);
|
|
2839
|
+
}
|
|
2352
2840
|
/**
|
|
2353
2841
|
* App-facing secrets API backed by the `secrets` space vault.
|
|
2354
2842
|
*/
|
|
@@ -2360,8 +2848,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2360
2848
|
this._secrets = new NodeSecretsService({
|
|
2361
2849
|
getService: () => this.getBaseSecrets(),
|
|
2362
2850
|
getManifest: () => this.manifest,
|
|
2851
|
+
hasPermissions: (permissions) => this.hasRuntimePermissions(permissions),
|
|
2363
2852
|
grantPermissions: (additional) => this.grantRuntimePermissions(additional),
|
|
2364
2853
|
canEscalate: () => this.signer !== void 0 && this.tc !== void 0,
|
|
2854
|
+
getEncryptionNetworkId: () => this.getDefaultEncryptionNetworkId(),
|
|
2365
2855
|
getUnlockSigner: () => this.signer ?? void 0
|
|
2366
2856
|
});
|
|
2367
2857
|
}
|
|
@@ -2451,7 +2941,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2451
2941
|
* every requested permission.
|
|
2452
2942
|
*/
|
|
2453
2943
|
hasRuntimePermissions(permissions) {
|
|
2454
|
-
const session = this.
|
|
2944
|
+
const session = this.currentTinyCloudSession();
|
|
2455
2945
|
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
2456
2946
|
return false;
|
|
2457
2947
|
}
|
|
@@ -2471,7 +2961,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2471
2961
|
if (permissions === void 0) {
|
|
2472
2962
|
return this.runtimePermissionGrants.map((grant) => grant.delegation);
|
|
2473
2963
|
}
|
|
2474
|
-
const session = this.
|
|
2964
|
+
const session = this.currentTinyCloudSession();
|
|
2475
2965
|
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
2476
2966
|
return [];
|
|
2477
2967
|
}
|
|
@@ -2485,7 +2975,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2485
2975
|
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
2486
2976
|
*/
|
|
2487
2977
|
async useRuntimeDelegation(delegation) {
|
|
2488
|
-
const session = this.
|
|
2978
|
+
const session = this.currentTinyCloudSession();
|
|
2489
2979
|
if (!session) {
|
|
2490
2980
|
throw new import_sdk_core6.SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
2491
2981
|
}
|
|
@@ -2524,7 +3014,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2524
3014
|
if (!Array.isArray(permissions) || permissions.length === 0) {
|
|
2525
3015
|
throw new Error("grantRuntimePermissions requires a non-empty permissions array");
|
|
2526
3016
|
}
|
|
2527
|
-
const session = this.
|
|
3017
|
+
const session = this.currentTinyCloudSession();
|
|
2528
3018
|
if (!session) {
|
|
2529
3019
|
throw new import_sdk_core6.SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
2530
3020
|
}
|
|
@@ -2548,13 +3038,22 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2548
3038
|
"grantRuntimePermissions requires wallet mode with a signer or privateKey."
|
|
2549
3039
|
);
|
|
2550
3040
|
}
|
|
3041
|
+
const rawEntries = expanded.filter(
|
|
3042
|
+
(entry) => this.isEncryptionPermissionEntry(entry)
|
|
3043
|
+
);
|
|
3044
|
+
const spaceEntries = expanded.filter(
|
|
3045
|
+
(entry) => !this.isEncryptionPermissionEntry(entry)
|
|
3046
|
+
);
|
|
2551
3047
|
const bySpace = /* @__PURE__ */ new Map();
|
|
2552
|
-
for (const entry of
|
|
3048
|
+
for (const entry of spaceEntries) {
|
|
2553
3049
|
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
2554
3050
|
const current = bySpace.get(spaceId) ?? [];
|
|
2555
3051
|
current.push(entry);
|
|
2556
3052
|
bySpace.set(spaceId, current);
|
|
2557
3053
|
}
|
|
3054
|
+
if (bySpace.size === 0 && rawEntries.length > 0) {
|
|
3055
|
+
bySpace.set(session.spaceId, []);
|
|
3056
|
+
}
|
|
2558
3057
|
const now = /* @__PURE__ */ new Date();
|
|
2559
3058
|
const requestedExpiryMs = resolveExpiryMs(options?.expiry);
|
|
2560
3059
|
let expiresAt = new Date(now.getTime() + requestedExpiryMs);
|
|
@@ -2562,10 +3061,17 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2562
3061
|
expiresAt = sessionExpiry;
|
|
2563
3062
|
}
|
|
2564
3063
|
const delegations = [];
|
|
3064
|
+
let rawEntriesAttached = false;
|
|
2565
3065
|
for (const [spaceId, entries] of bySpace) {
|
|
3066
|
+
const rawForDelegation = !rawEntriesAttached ? rawEntries : [];
|
|
3067
|
+
if (rawForDelegation.length > 0) {
|
|
3068
|
+
rawEntriesAttached = true;
|
|
3069
|
+
}
|
|
3070
|
+
const delegatedEntries = [...entries, ...rawForDelegation];
|
|
2566
3071
|
const abilities = this.permissionsToAbilities(entries);
|
|
2567
3072
|
const prepared = this.wasmBindings.prepareSession({
|
|
2568
3073
|
abilities,
|
|
3074
|
+
...rawForDelegation.length > 0 ? { rawAbilities: this.permissionsToRawAbilities(rawForDelegation) } : {},
|
|
2569
3075
|
address: this.wasmBindings.ensureEip55(session.address),
|
|
2570
3076
|
chainId: session.chainId,
|
|
2571
3077
|
domain: this.siweDomain,
|
|
@@ -2590,7 +3096,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2590
3096
|
}
|
|
2591
3097
|
const delegation = this.runtimeDelegationFromSession(
|
|
2592
3098
|
delegatedSession,
|
|
2593
|
-
|
|
3099
|
+
delegatedEntries,
|
|
2594
3100
|
spaceId,
|
|
2595
3101
|
session,
|
|
2596
3102
|
expiresAt
|
|
@@ -2604,7 +3110,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2604
3110
|
jwk: session.jwk
|
|
2605
3111
|
},
|
|
2606
3112
|
delegation,
|
|
2607
|
-
operations: this.permissionOperations(
|
|
3113
|
+
operations: this.permissionOperations(delegatedEntries, spaceId),
|
|
2608
3114
|
expiresAt
|
|
2609
3115
|
});
|
|
2610
3116
|
delegations.push(delegation);
|
|
@@ -2752,7 +3258,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2752
3258
|
];
|
|
2753
3259
|
const abilities = { kv: { "": kvActions } };
|
|
2754
3260
|
const now = /* @__PURE__ */ new Date();
|
|
2755
|
-
const expiryMs =
|
|
3261
|
+
const expiryMs = import_sdk_core6.EXPIRY.EPHEMERAL_MS;
|
|
2756
3262
|
const expirationTime = new Date(now.getTime() + expiryMs);
|
|
2757
3263
|
const prepared = this.wasmBindings.prepareSession({
|
|
2758
3264
|
abilities,
|
|
@@ -2922,7 +3428,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2922
3428
|
* `forceWalletSign` is not set.
|
|
2923
3429
|
*/
|
|
2924
3430
|
async delegateTo(did, permissions, options) {
|
|
2925
|
-
const session = this.
|
|
3431
|
+
const session = this.currentTinyCloudSession();
|
|
2926
3432
|
if (!session) {
|
|
2927
3433
|
throw new import_sdk_core6.SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
2928
3434
|
}
|
|
@@ -2939,10 +3445,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2939
3445
|
"delegateTo requires a non-empty permissions array"
|
|
2940
3446
|
);
|
|
2941
3447
|
}
|
|
2942
|
-
const expandedEntries =
|
|
2943
|
-
...entry,
|
|
2944
|
-
actions: (0, import_sdk_core6.expandActionShortNames)(entry.service, entry.actions)
|
|
2945
|
-
}));
|
|
3448
|
+
const expandedEntries = this.expandPermissionEntries(permissions);
|
|
2946
3449
|
const now = /* @__PURE__ */ new Date();
|
|
2947
3450
|
const expiryMs = resolveExpiryMs(options?.expiry);
|
|
2948
3451
|
const expirationTime = new Date(now.getTime() + expiryMs);
|
|
@@ -3039,11 +3542,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3039
3542
|
* the current session; we build one multi-resource abilities map
|
|
3040
3543
|
* and emit one signed UCAN covering them all.
|
|
3041
3544
|
*
|
|
3042
|
-
*
|
|
3043
|
-
*
|
|
3044
|
-
* spaces in a single delegation is not supported by the underlying
|
|
3045
|
-
* Rust create_delegation call and the resulting UCAN would be
|
|
3046
|
-
* under-specified.
|
|
3545
|
+
* Non-encryption entries must share the same target space. Encryption
|
|
3546
|
+
* entries are raw network URNs and do not participate in space grouping.
|
|
3047
3547
|
*
|
|
3048
3548
|
* @internal
|
|
3049
3549
|
*/
|
|
@@ -3055,15 +3555,18 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3055
3555
|
}
|
|
3056
3556
|
const resolvedSpaces = /* @__PURE__ */ new Set();
|
|
3057
3557
|
for (const entry of entries) {
|
|
3558
|
+
if (this.isEncryptionPermissionEntry(entry)) {
|
|
3559
|
+
continue;
|
|
3560
|
+
}
|
|
3058
3561
|
const spaceId2 = this.resolvePermissionSpace(entry.space, session);
|
|
3059
3562
|
resolvedSpaces.add(spaceId2);
|
|
3060
3563
|
}
|
|
3061
|
-
if (resolvedSpaces.size
|
|
3564
|
+
if (resolvedSpaces.size > 1) {
|
|
3062
3565
|
throw new Error(
|
|
3063
3566
|
`delegateTo: all permission entries must target the same space, got ${resolvedSpaces.size}: ${JSON.stringify([...resolvedSpaces])}`
|
|
3064
3567
|
);
|
|
3065
3568
|
}
|
|
3066
|
-
const spaceId = [...resolvedSpaces][0];
|
|
3569
|
+
const spaceId = resolvedSpaces.size === 1 ? [...resolvedSpaces][0] : session.spaceId;
|
|
3067
3570
|
const abilities = {};
|
|
3068
3571
|
for (const entry of entries) {
|
|
3069
3572
|
const shortService = import_sdk_core6.SERVICE_LONG_TO_SHORT[entry.service];
|
|
@@ -3182,10 +3685,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3182
3685
|
return this.wasmBindings.makeSpaceId(session.address, session.chainId, space);
|
|
3183
3686
|
}
|
|
3184
3687
|
expandPermissionEntries(permissions) {
|
|
3185
|
-
return
|
|
3186
|
-
...entry,
|
|
3187
|
-
actions: (0, import_sdk_core6.expandActionShortNames)(entry.service, entry.actions)
|
|
3188
|
-
}));
|
|
3688
|
+
return (0, import_sdk_core6.expandPermissionEntries)(permissions);
|
|
3189
3689
|
}
|
|
3190
3690
|
shortServiceName(service) {
|
|
3191
3691
|
const short = import_sdk_core6.SERVICE_LONG_TO_SHORT[service];
|
|
@@ -3213,11 +3713,32 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3213
3713
|
}
|
|
3214
3714
|
return abilities;
|
|
3215
3715
|
}
|
|
3716
|
+
isEncryptionPermissionEntry(entry) {
|
|
3717
|
+
return entry.service === import_sdk_core6.ENCRYPTION_PERMISSION_SERVICE && entry.path.startsWith("urn:tinycloud:encryption:");
|
|
3718
|
+
}
|
|
3719
|
+
permissionsToRawAbilities(entries) {
|
|
3720
|
+
const rawAbilities = {};
|
|
3721
|
+
for (const entry of entries) {
|
|
3722
|
+
if (!this.isEncryptionPermissionEntry(entry)) {
|
|
3723
|
+
continue;
|
|
3724
|
+
}
|
|
3725
|
+
const existing = rawAbilities[entry.path] ?? [];
|
|
3726
|
+
const seen = new Set(existing);
|
|
3727
|
+
for (const action of entry.actions) {
|
|
3728
|
+
if (!seen.has(action)) {
|
|
3729
|
+
existing.push(action);
|
|
3730
|
+
seen.add(action);
|
|
3731
|
+
}
|
|
3732
|
+
}
|
|
3733
|
+
rawAbilities[entry.path] = existing;
|
|
3734
|
+
}
|
|
3735
|
+
return rawAbilities;
|
|
3736
|
+
}
|
|
3216
3737
|
permissionOperations(entries, spaceId) {
|
|
3217
3738
|
return entries.flatMap((entry) => {
|
|
3218
3739
|
const service = this.shortServiceName(entry.service);
|
|
3219
3740
|
return entry.actions.map((action) => ({
|
|
3220
|
-
spaceId,
|
|
3741
|
+
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
|
|
3221
3742
|
service,
|
|
3222
3743
|
path: entry.path,
|
|
3223
3744
|
action
|
|
@@ -3240,7 +3761,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3240
3761
|
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
3241
3762
|
const service = this.shortServiceName(entry.service);
|
|
3242
3763
|
return entry.actions.map((action) => ({
|
|
3243
|
-
spaceId,
|
|
3764
|
+
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
|
|
3244
3765
|
service,
|
|
3245
3766
|
path: entry.path,
|
|
3246
3767
|
action
|
|
@@ -3297,24 +3818,40 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3297
3818
|
expiresAt: delegation.expiry
|
|
3298
3819
|
};
|
|
3299
3820
|
}
|
|
3821
|
+
installRuntimeGrantFromServiceSession(delegation, session, expiresAt) {
|
|
3822
|
+
const operations = this.operationsFromDelegation(delegation);
|
|
3823
|
+
if (operations.length === 0) {
|
|
3824
|
+
return;
|
|
3825
|
+
}
|
|
3826
|
+
this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
|
|
3827
|
+
(grant) => grant.delegation.cid !== delegation.cid && grant.session.delegationCid !== session.delegationCid
|
|
3828
|
+
);
|
|
3829
|
+
this.runtimePermissionGrants.push({
|
|
3830
|
+
session,
|
|
3831
|
+
delegation,
|
|
3832
|
+
operations,
|
|
3833
|
+
expiresAt
|
|
3834
|
+
});
|
|
3835
|
+
}
|
|
3300
3836
|
delegatedResourcesForEntries(entries, spaceId) {
|
|
3301
3837
|
return entries.map((entry) => ({
|
|
3302
3838
|
service: this.shortServiceName(entry.service),
|
|
3303
|
-
space: spaceId,
|
|
3839
|
+
space: this.isEncryptionPermissionEntry(entry) ? "encryption" : spaceId,
|
|
3304
3840
|
path: entry.path,
|
|
3305
3841
|
actions: [...entry.actions]
|
|
3306
3842
|
}));
|
|
3307
3843
|
}
|
|
3308
3844
|
operationsFromDelegation(delegation) {
|
|
3309
3845
|
const resources = delegation.resources !== void 0 && delegation.resources.length > 0 ? delegation.resources : this.flatDelegationResources(delegation);
|
|
3310
|
-
return resources.flatMap(
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
service:
|
|
3846
|
+
return resources.flatMap((resource) => {
|
|
3847
|
+
const service = this.invocationServiceName(resource.service);
|
|
3848
|
+
return resource.actions.map((action) => ({
|
|
3849
|
+
...this.isEncryptionNetworkOperation(service, resource.path) ? { resource: resource.path } : { spaceId: resource.space },
|
|
3850
|
+
service,
|
|
3314
3851
|
path: resource.path,
|
|
3315
3852
|
action
|
|
3316
|
-
}))
|
|
3317
|
-
);
|
|
3853
|
+
}));
|
|
3854
|
+
});
|
|
3318
3855
|
}
|
|
3319
3856
|
flatDelegationResources(delegation) {
|
|
3320
3857
|
const byService = /* @__PURE__ */ new Map();
|
|
@@ -3363,7 +3900,13 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3363
3900
|
);
|
|
3364
3901
|
}
|
|
3365
3902
|
operationCovers(granted, requested) {
|
|
3366
|
-
|
|
3903
|
+
if (granted.service !== requested.service || !this.actionContains(granted.action, requested.action)) {
|
|
3904
|
+
return false;
|
|
3905
|
+
}
|
|
3906
|
+
if (granted.resource !== void 0 || requested.resource !== void 0) {
|
|
3907
|
+
return granted.resource !== void 0 && requested.resource !== void 0 && granted.resource === requested.resource && this.pathContains(granted.path, requested.path);
|
|
3908
|
+
}
|
|
3909
|
+
return granted.spaceId !== void 0 && requested.spaceId !== void 0 && granted.spaceId === requested.spaceId && this.pathContains(granted.path, requested.path);
|
|
3367
3910
|
}
|
|
3368
3911
|
actionContains(grantedAction, requestedAction) {
|
|
3369
3912
|
if (grantedAction === requestedAction) {
|
|
@@ -3378,6 +3921,37 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3378
3921
|
invocationServiceName(service) {
|
|
3379
3922
|
return service.startsWith("tinycloud.") ? this.shortServiceName(service) : service;
|
|
3380
3923
|
}
|
|
3924
|
+
isEncryptionNetworkOperation(service, path) {
|
|
3925
|
+
return service === "encryption" && path.startsWith("urn:tinycloud:encryption:");
|
|
3926
|
+
}
|
|
3927
|
+
operationFromInvokeAnyEntry(entry) {
|
|
3928
|
+
const service = this.invocationServiceName(entry.service);
|
|
3929
|
+
if (typeof entry.resource === "string") {
|
|
3930
|
+
return {
|
|
3931
|
+
resource: entry.resource,
|
|
3932
|
+
service,
|
|
3933
|
+
path: entry.path,
|
|
3934
|
+
action: entry.action
|
|
3935
|
+
};
|
|
3936
|
+
}
|
|
3937
|
+
if (this.isEncryptionNetworkOperation(service, entry.path)) {
|
|
3938
|
+
return {
|
|
3939
|
+
resource: entry.path,
|
|
3940
|
+
service,
|
|
3941
|
+
path: entry.path,
|
|
3942
|
+
action: entry.action
|
|
3943
|
+
};
|
|
3944
|
+
}
|
|
3945
|
+
if (typeof entry.spaceId === "string") {
|
|
3946
|
+
return {
|
|
3947
|
+
spaceId: entry.spaceId,
|
|
3948
|
+
service,
|
|
3949
|
+
path: entry.path,
|
|
3950
|
+
action: entry.action
|
|
3951
|
+
};
|
|
3952
|
+
}
|
|
3953
|
+
return void 0;
|
|
3954
|
+
}
|
|
3381
3955
|
pathContains(grantedPath, requestedPath) {
|
|
3382
3956
|
if (grantedPath === "" || grantedPath === "/") {
|
|
3383
3957
|
return true;
|
|
@@ -3616,6 +4190,17 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3616
4190
|
// Not used in session-only mode
|
|
3617
4191
|
};
|
|
3618
4192
|
this.trackReceivedDelegation(delegation, this.sessionKeyJwk);
|
|
4193
|
+
this.installRuntimeGrantFromServiceSession(
|
|
4194
|
+
delegation,
|
|
4195
|
+
{
|
|
4196
|
+
delegationHeader: session2.delegationHeader,
|
|
4197
|
+
delegationCid: session2.delegationCid,
|
|
4198
|
+
spaceId: session2.spaceId,
|
|
4199
|
+
verificationMethod: session2.verificationMethod,
|
|
4200
|
+
jwk: session2.jwk
|
|
4201
|
+
},
|
|
4202
|
+
delegation.expiry
|
|
4203
|
+
);
|
|
3619
4204
|
return new DelegatedAccess(session2, delegation, targetHost, this.wasmBindings.invoke);
|
|
3620
4205
|
}
|
|
3621
4206
|
const mySession = this.auth?.tinyCloudSession;
|
|
@@ -3627,6 +4212,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3627
4212
|
const kvActions = delegation.actions.filter((a) => a.startsWith("tinycloud.kv/"));
|
|
3628
4213
|
const sqlActions = delegation.actions.filter((a) => a.startsWith("tinycloud.sql/"));
|
|
3629
4214
|
const duckdbActions = delegation.actions.filter((a) => a.startsWith("tinycloud.duckdb/"));
|
|
4215
|
+
const encryptionActions = delegation.actions.filter(
|
|
4216
|
+
(a) => a.startsWith("tinycloud.encryption/")
|
|
4217
|
+
);
|
|
4218
|
+
const rawAbilities = {};
|
|
3630
4219
|
if (kvActions.length > 0) {
|
|
3631
4220
|
abilities.kv = { [delegation.path]: kvActions };
|
|
3632
4221
|
}
|
|
@@ -3636,6 +4225,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3636
4225
|
if (duckdbActions.length > 0) {
|
|
3637
4226
|
abilities.duckdb = { [delegation.path]: duckdbActions };
|
|
3638
4227
|
}
|
|
4228
|
+
if (encryptionActions.length > 0 && delegation.path.startsWith("urn:tinycloud:encryption:")) {
|
|
4229
|
+
rawAbilities[delegation.path] = encryptionActions;
|
|
4230
|
+
}
|
|
3639
4231
|
const now = /* @__PURE__ */ new Date();
|
|
3640
4232
|
const maxExpiry = new Date(now.getTime() + 60 * 60 * 1e3);
|
|
3641
4233
|
const expirationTime = delegation.expiry < maxExpiry ? delegation.expiry : maxExpiry;
|
|
@@ -3648,7 +4240,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3648
4240
|
expirationTime: expirationTime.toISOString(),
|
|
3649
4241
|
spaceId: delegation.spaceId,
|
|
3650
4242
|
jwk,
|
|
3651
|
-
parents: [delegation.cid]
|
|
4243
|
+
parents: [delegation.cid],
|
|
4244
|
+
...Object.keys(rawAbilities).length > 0 ? { rawAbilities } : {}
|
|
3652
4245
|
});
|
|
3653
4246
|
const signature = await this.signer.signMessage(prepared.siwe);
|
|
3654
4247
|
const invokerSession = this.wasmBindings.completeSessionSetup({
|
|
@@ -3675,6 +4268,17 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3675
4268
|
signature
|
|
3676
4269
|
};
|
|
3677
4270
|
this.trackReceivedDelegation(delegation, jwk);
|
|
4271
|
+
this.installRuntimeGrantFromServiceSession(
|
|
4272
|
+
delegation,
|
|
4273
|
+
{
|
|
4274
|
+
delegationHeader: session.delegationHeader,
|
|
4275
|
+
delegationCid: session.delegationCid,
|
|
4276
|
+
spaceId: session.spaceId,
|
|
4277
|
+
verificationMethod: session.verificationMethod,
|
|
4278
|
+
jwk: session.jwk
|
|
4279
|
+
},
|
|
4280
|
+
expirationTime
|
|
4281
|
+
);
|
|
3678
4282
|
return new DelegatedAccess(session, delegation, targetHost, this.wasmBindings.invoke);
|
|
3679
4283
|
}
|
|
3680
4284
|
/**
|
|
@@ -3817,6 +4421,7 @@ var import_sdk_core18 = require("@tinycloud/sdk-core");
|
|
|
3817
4421
|
CapabilityKeyRegistryErrorCodes,
|
|
3818
4422
|
DEFAULT_MANIFEST_SPACE,
|
|
3819
4423
|
DEFAULT_MANIFEST_VERSION,
|
|
4424
|
+
DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
|
|
3820
4425
|
DataVaultService,
|
|
3821
4426
|
DatabaseHandle,
|
|
3822
4427
|
DelegatedAccess,
|
|
@@ -3833,6 +4438,7 @@ var import_sdk_core18 = require("@tinycloud/sdk-core");
|
|
|
3833
4438
|
PermissionNotInManifestError,
|
|
3834
4439
|
PrefixedKVService,
|
|
3835
4440
|
ProtocolMismatchError,
|
|
4441
|
+
SECRET_NAME_RE,
|
|
3836
4442
|
SQLAction,
|
|
3837
4443
|
SQLService,
|
|
3838
4444
|
SecretsService,
|
|
@@ -3846,11 +4452,13 @@ var import_sdk_core18 = require("@tinycloud/sdk-core");
|
|
|
3846
4452
|
TinyCloud,
|
|
3847
4453
|
TinyCloudNode,
|
|
3848
4454
|
UnsupportedFeatureError,
|
|
4455
|
+
VAULT_PERMISSION_SERVICE,
|
|
3849
4456
|
VaultHeaders,
|
|
3850
4457
|
VaultPublicSpaceKVActions,
|
|
3851
4458
|
VersionCheckError,
|
|
3852
4459
|
WasmKeyProvider,
|
|
3853
4460
|
buildSpaceUri,
|
|
4461
|
+
canonicalizeSecretScope,
|
|
3854
4462
|
checkNodeInfo,
|
|
3855
4463
|
composeManifestRequest,
|
|
3856
4464
|
createCapabilityKeyRegistry,
|
|
@@ -3862,12 +4470,16 @@ var import_sdk_core18 = require("@tinycloud/sdk-core");
|
|
|
3862
4470
|
defaultSpaceCreationHandler,
|
|
3863
4471
|
deserializeDelegation,
|
|
3864
4472
|
expandActionShortNames,
|
|
4473
|
+
expandPermissionEntries,
|
|
4474
|
+
expandPermissionEntry,
|
|
3865
4475
|
isCapabilitySubset,
|
|
3866
4476
|
loadManifest,
|
|
3867
4477
|
makePublicSpaceId,
|
|
3868
4478
|
parseExpiry,
|
|
3869
4479
|
parseSpaceUri,
|
|
3870
4480
|
resolveManifest,
|
|
4481
|
+
resolveSecretListPrefix,
|
|
4482
|
+
resolveSecretPath,
|
|
3871
4483
|
resourceCapabilitiesToSpaceAbilitiesMap,
|
|
3872
4484
|
serializeDelegation,
|
|
3873
4485
|
validateManifest
|