@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.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,
|
|
@@ -62,6 +74,7 @@ __export(core_exports, {
|
|
|
62
74
|
VersionCheckError: () => import_sdk_core17.VersionCheckError,
|
|
63
75
|
WasmKeyProvider: () => WasmKeyProvider,
|
|
64
76
|
buildSpaceUri: () => import_sdk_core16.buildSpaceUri,
|
|
77
|
+
canonicalizeSecretScope: () => import_sdk_core13.canonicalizeSecretScope,
|
|
65
78
|
checkNodeInfo: () => import_sdk_core17.checkNodeInfo,
|
|
66
79
|
composeManifestRequest: () => import_sdk_core9.composeManifestRequest,
|
|
67
80
|
createCapabilityKeyRegistry: () => import_sdk_core15.createCapabilityKeyRegistry,
|
|
@@ -81,6 +94,8 @@ __export(core_exports, {
|
|
|
81
94
|
parseExpiry: () => import_sdk_core9.parseExpiry,
|
|
82
95
|
parseSpaceUri: () => import_sdk_core16.parseSpaceUri,
|
|
83
96
|
resolveManifest: () => import_sdk_core9.resolveManifest,
|
|
97
|
+
resolveSecretListPrefix: () => import_sdk_core13.resolveSecretListPrefix,
|
|
98
|
+
resolveSecretPath: () => import_sdk_core13.resolveSecretPath,
|
|
84
99
|
resourceCapabilitiesToSpaceAbilitiesMap: () => import_sdk_core9.resourceCapabilitiesToSpaceAbilitiesMap,
|
|
85
100
|
serializeDelegation: () => serializeDelegation,
|
|
86
101
|
validateManifest: () => import_sdk_core9.validateManifest
|
|
@@ -340,7 +355,7 @@ var NodeUserAuthorization = class {
|
|
|
340
355
|
]
|
|
341
356
|
}
|
|
342
357
|
};
|
|
343
|
-
this.sessionExpirationMs = config.sessionExpirationMs ??
|
|
358
|
+
this.sessionExpirationMs = config.sessionExpirationMs ?? import_sdk_core2.EXPIRY.SESSION_MS;
|
|
344
359
|
this.autoCreateSpace = config.autoCreateSpace ?? false;
|
|
345
360
|
this.spaceCreationHandler = config.spaceCreationHandler;
|
|
346
361
|
this.tinycloudHosts = config.tinycloudHosts;
|
|
@@ -393,6 +408,23 @@ var NodeUserAuthorization = class {
|
|
|
393
408
|
get tinyCloudSession() {
|
|
394
409
|
return this._tinyCloudSession;
|
|
395
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
|
+
}
|
|
396
428
|
async resolveTinyCloudHostsForSignIn(address, chainId) {
|
|
397
429
|
if (this.tinycloudHosts && this.tinycloudHosts.length > 0) {
|
|
398
430
|
return;
|
|
@@ -463,21 +495,64 @@ var NodeUserAuthorization = class {
|
|
|
463
495
|
}
|
|
464
496
|
return this.wasm.makeSpaceId(address, chainId, space);
|
|
465
497
|
}
|
|
498
|
+
defaultEncryptionNetworkId(address, chainId) {
|
|
499
|
+
return `urn:tinycloud:encryption:did:pkh:eip155:${chainId}:${address}:default`;
|
|
500
|
+
}
|
|
466
501
|
resolveSignInCapabilities(address, chainId) {
|
|
467
502
|
const request = this.getCapabilityRequest();
|
|
468
503
|
if (request === void 0) {
|
|
504
|
+
const defaultNetworkId = this.defaultEncryptionNetworkId(address, chainId);
|
|
505
|
+
const secretsSpaceId = this.wasm.makeSpaceId(address, chainId, "secrets");
|
|
469
506
|
return {
|
|
470
507
|
abilities: this.defaultActions,
|
|
471
|
-
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
|
+
}
|
|
472
528
|
};
|
|
473
529
|
}
|
|
474
|
-
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;
|
|
475
550
|
const primarySpaceId = this.resolveSpaceName(
|
|
476
551
|
primarySpaceName,
|
|
477
552
|
address,
|
|
478
553
|
chainId
|
|
479
554
|
);
|
|
480
|
-
const bySpace = (0, import_sdk_core2.resourceCapabilitiesToSpaceAbilitiesMap)(
|
|
555
|
+
const bySpace = (0, import_sdk_core2.resourceCapabilitiesToSpaceAbilitiesMap)(spaceResources);
|
|
481
556
|
const spaceAbilities = {};
|
|
482
557
|
for (const [space, abilities] of Object.entries(bySpace)) {
|
|
483
558
|
spaceAbilities[this.resolveSpaceName(space, address, chainId)] = abilities;
|
|
@@ -485,7 +560,8 @@ var NodeUserAuthorization = class {
|
|
|
485
560
|
return {
|
|
486
561
|
abilities: spaceAbilities[primarySpaceId] ?? (0, import_sdk_core2.resourceCapabilitiesToAbilitiesMap)([]),
|
|
487
562
|
spaceId: primarySpaceId,
|
|
488
|
-
spaceAbilities
|
|
563
|
+
spaceAbilities,
|
|
564
|
+
rawAbilities: Object.keys(rawAbilities).length > 0 ? rawAbilities : void 0
|
|
489
565
|
};
|
|
490
566
|
}
|
|
491
567
|
/**
|
|
@@ -710,6 +786,7 @@ var NodeUserAuthorization = class {
|
|
|
710
786
|
const prepared = this.wasm.prepareSession({
|
|
711
787
|
abilities: capabilityPlan.abilities,
|
|
712
788
|
...capabilityPlan.spaceAbilities !== void 0 ? { spaceAbilities: capabilityPlan.spaceAbilities } : {},
|
|
789
|
+
...capabilityPlan.rawAbilities !== void 0 ? { rawAbilities: capabilityPlan.rawAbilities } : {},
|
|
713
790
|
address,
|
|
714
791
|
chainId,
|
|
715
792
|
domain: this.domain,
|
|
@@ -855,6 +932,7 @@ var NodeUserAuthorization = class {
|
|
|
855
932
|
const prepared = this.wasm.prepareSession({
|
|
856
933
|
abilities: capabilityPlan.abilities,
|
|
857
934
|
...capabilityPlan.spaceAbilities !== void 0 ? { spaceAbilities: capabilityPlan.spaceAbilities } : {},
|
|
935
|
+
...capabilityPlan.rawAbilities !== void 0 ? { rawAbilities: capabilityPlan.rawAbilities } : {},
|
|
858
936
|
address,
|
|
859
937
|
chainId,
|
|
860
938
|
domain: this.domain,
|
|
@@ -1228,9 +1306,10 @@ function legacyParamsToPermissionEntries(actions, path, spaceIdOverride) {
|
|
|
1228
1306
|
}
|
|
1229
1307
|
return entries;
|
|
1230
1308
|
}
|
|
1309
|
+
var DEFAULT_DELEGATION_EXPIRY_MS = import_sdk_core4.EXPIRY.SESSION_MS;
|
|
1231
1310
|
function resolveExpiryMs(expiry) {
|
|
1232
1311
|
if (expiry === void 0) {
|
|
1233
|
-
return
|
|
1312
|
+
return DEFAULT_DELEGATION_EXPIRY_MS;
|
|
1234
1313
|
}
|
|
1235
1314
|
if (typeof expiry === "number") {
|
|
1236
1315
|
if (!Number.isFinite(expiry) || expiry <= 0) {
|
|
@@ -1258,8 +1337,6 @@ function extractSiweExpiration(siwe) {
|
|
|
1258
1337
|
|
|
1259
1338
|
// src/NodeSecretsService.ts
|
|
1260
1339
|
var import_sdk_core5 = require("@tinycloud/sdk-core");
|
|
1261
|
-
var SECRET_NAME_RE = /^[A-Z][A-Z0-9_]*$/;
|
|
1262
|
-
var SECRET_PREFIX = "secrets/";
|
|
1263
1340
|
var SECRETS_SPACE = "secrets";
|
|
1264
1341
|
function ok() {
|
|
1265
1342
|
return { ok: true, data: void 0 };
|
|
@@ -1276,27 +1353,39 @@ function secretsError(code, message, cause) {
|
|
|
1276
1353
|
};
|
|
1277
1354
|
}
|
|
1278
1355
|
function displayActionUrn(action) {
|
|
1279
|
-
|
|
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
|
+
}
|
|
1280
1366
|
}
|
|
1281
|
-
function
|
|
1282
|
-
return
|
|
1367
|
+
function secretActionName(action) {
|
|
1368
|
+
return action;
|
|
1283
1369
|
}
|
|
1284
|
-
function secretPermissionEntries(name, action) {
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
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"],
|
|
1291
1385
|
skipPrefix: true
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
function secretResourcePath(base, name) {
|
|
1296
|
-
return `${base}/${SECRET_PREFIX}${name}`;
|
|
1297
|
-
}
|
|
1298
|
-
function isSecretsSpace(space) {
|
|
1299
|
-
return space === SECRETS_SPACE || space.endsWith(`:${SECRETS_SPACE}`);
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1388
|
+
return entries;
|
|
1300
1389
|
}
|
|
1301
1390
|
var NodeSecretsService = class {
|
|
1302
1391
|
constructor(config) {
|
|
@@ -1324,48 +1413,62 @@ var NodeSecretsService = class {
|
|
|
1324
1413
|
this.shouldRestoreUnlock = false;
|
|
1325
1414
|
this.service.lock();
|
|
1326
1415
|
}
|
|
1327
|
-
get(name) {
|
|
1328
|
-
|
|
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);
|
|
1329
1420
|
}
|
|
1330
|
-
async put(name, value) {
|
|
1331
|
-
const permission = await this.
|
|
1421
|
+
async put(name, value, options) {
|
|
1422
|
+
const permission = await this.ensurePermission(name, options, "put");
|
|
1332
1423
|
if (!permission.ok) return permission;
|
|
1333
|
-
return this.service.put(name, value);
|
|
1424
|
+
return options === void 0 ? this.service.put(name, value) : this.service.put(name, value, options);
|
|
1334
1425
|
}
|
|
1335
|
-
async delete(name) {
|
|
1336
|
-
const permission = await this.
|
|
1426
|
+
async delete(name, options) {
|
|
1427
|
+
const permission = await this.ensurePermission(name, options, "del");
|
|
1337
1428
|
if (!permission.ok) return permission;
|
|
1338
|
-
return this.service.delete(name);
|
|
1429
|
+
return options === void 0 ? this.service.delete(name) : this.service.delete(name, options);
|
|
1339
1430
|
}
|
|
1340
|
-
list() {
|
|
1341
|
-
|
|
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);
|
|
1342
1435
|
}
|
|
1343
1436
|
get service() {
|
|
1344
1437
|
return this.config.getService();
|
|
1345
1438
|
}
|
|
1346
|
-
async
|
|
1347
|
-
|
|
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) {
|
|
1348
1450
|
return secretsError(
|
|
1349
1451
|
import_sdk_core5.ErrorCodes.INVALID_INPUT,
|
|
1350
|
-
|
|
1452
|
+
error instanceof Error ? error.message : String(error),
|
|
1453
|
+
error instanceof Error ? error : void 0
|
|
1351
1454
|
);
|
|
1352
1455
|
}
|
|
1353
|
-
if (this.
|
|
1456
|
+
if (this.hasPermission(permissionEntries)) {
|
|
1354
1457
|
return ok();
|
|
1355
1458
|
}
|
|
1356
1459
|
if (!this.config.canEscalate()) {
|
|
1357
1460
|
return secretsError(
|
|
1358
1461
|
import_sdk_core5.ErrorCodes.PERMISSION_DENIED,
|
|
1359
|
-
`Cannot autosign ${displayActionUrn(action)} for ${
|
|
1462
|
+
`Cannot autosign ${displayActionUrn(action)} for ${target}; TinyCloudNode needs wallet mode with a signer or privateKey.`
|
|
1360
1463
|
);
|
|
1361
1464
|
}
|
|
1362
1465
|
try {
|
|
1363
|
-
await this.config.grantPermissions(
|
|
1466
|
+
await this.config.grantPermissions(permissionEntries);
|
|
1364
1467
|
return this.restoreUnlockAfterEscalation();
|
|
1365
1468
|
} catch (error) {
|
|
1366
1469
|
return secretsError(
|
|
1367
1470
|
import_sdk_core5.ErrorCodes.PERMISSION_DENIED,
|
|
1368
|
-
error instanceof Error ? error.message : `Autosign escalation for ${displayActionUrn(action)} on ${
|
|
1471
|
+
error instanceof Error ? error.message : `Autosign escalation for ${displayActionUrn(action)} on ${target} failed.`,
|
|
1369
1472
|
error instanceof Error ? error : void 0
|
|
1370
1473
|
);
|
|
1371
1474
|
}
|
|
@@ -1376,26 +1479,117 @@ var NodeSecretsService = class {
|
|
|
1376
1479
|
}
|
|
1377
1480
|
return this.service.unlock(this.unlockSigner);
|
|
1378
1481
|
}
|
|
1379
|
-
|
|
1482
|
+
hasPermission(permissionEntries) {
|
|
1483
|
+
if (this.config.hasPermissions?.(permissionEntries)) {
|
|
1484
|
+
return true;
|
|
1485
|
+
}
|
|
1380
1486
|
const manifest = this.config.getManifest();
|
|
1381
1487
|
if (manifest === void 0) {
|
|
1382
1488
|
return false;
|
|
1383
1489
|
}
|
|
1384
1490
|
const manifests = Array.isArray(manifest) ? manifest : [manifest];
|
|
1385
|
-
const
|
|
1386
|
-
return
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
(resource) => resource.service ===
|
|
1391
|
-
)
|
|
1392
|
-
)
|
|
1393
|
-
|
|
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
|
+
);
|
|
1394
1500
|
}
|
|
1395
1501
|
};
|
|
1396
1502
|
|
|
1397
1503
|
// src/TinyCloudNode.ts
|
|
1398
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
|
+
}
|
|
1399
1593
|
var _TinyCloudNode = class _TinyCloudNode {
|
|
1400
1594
|
/**
|
|
1401
1595
|
* Create a new TinyCloudNode instance.
|
|
@@ -1440,12 +1634,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1440
1634
|
throw new Error("WASM binding does not support invokeAny");
|
|
1441
1635
|
}
|
|
1442
1636
|
const grant = this.findGrantForOperations(
|
|
1443
|
-
entries.
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
action: entry.action
|
|
1448
|
-
}))
|
|
1637
|
+
entries.flatMap((entry) => {
|
|
1638
|
+
const operation = this.operationFromInvokeAnyEntry(entry);
|
|
1639
|
+
return operation ? [operation] : [];
|
|
1640
|
+
})
|
|
1449
1641
|
);
|
|
1450
1642
|
return this.wasmBindings.invokeAny(grant?.session ?? session, entries, facts);
|
|
1451
1643
|
};
|
|
@@ -1538,7 +1730,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1538
1730
|
sessionStorage: config.sessionStorage ?? new MemorySessionStorage(),
|
|
1539
1731
|
domain: this.siweDomain,
|
|
1540
1732
|
spacePrefix: config.prefix,
|
|
1541
|
-
sessionExpirationMs: config.sessionExpirationMs ??
|
|
1733
|
+
sessionExpirationMs: config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS,
|
|
1542
1734
|
tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
|
|
1543
1735
|
tinycloudRegistryUrl: config.tinycloudRegistryUrl,
|
|
1544
1736
|
tinycloudFallbackHosts: config.tinycloudFallbackHosts,
|
|
@@ -1673,6 +1865,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1673
1865
|
this._duckdb = void 0;
|
|
1674
1866
|
this._hooks = void 0;
|
|
1675
1867
|
this._vault = void 0;
|
|
1868
|
+
this._encryption = void 0;
|
|
1676
1869
|
this._baseSecrets = void 0;
|
|
1677
1870
|
this._secrets = void 0;
|
|
1678
1871
|
this._spaceService = void 0;
|
|
@@ -1681,6 +1874,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1681
1874
|
await this.tc.signIn(options);
|
|
1682
1875
|
this.syncResolvedHostFromAuth();
|
|
1683
1876
|
this.initializeServices();
|
|
1877
|
+
if (this.config.manifest === void 0 && this.config.capabilityRequest === void 0) {
|
|
1878
|
+
await this.ensureOwnedSpaceHosted(this.ownedSpaceId("secrets"));
|
|
1879
|
+
}
|
|
1684
1880
|
await this.writeManifestRegistryRecords();
|
|
1685
1881
|
this.notificationHandler.success("Successfully signed in");
|
|
1686
1882
|
}
|
|
@@ -1763,6 +1959,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1763
1959
|
this._duckdb = void 0;
|
|
1764
1960
|
this._hooks = void 0;
|
|
1765
1961
|
this._vault = void 0;
|
|
1962
|
+
this._encryption = void 0;
|
|
1766
1963
|
this._baseSecrets = void 0;
|
|
1767
1964
|
this._secrets = void 0;
|
|
1768
1965
|
this._spaceService = void 0;
|
|
@@ -1804,6 +2001,33 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1804
2001
|
this._vault.initialize(this._serviceContext);
|
|
1805
2002
|
this._serviceContext.registerService("vault", this._vault);
|
|
1806
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;
|
|
1807
2031
|
}
|
|
1808
2032
|
/**
|
|
1809
2033
|
* Connect a wallet to upgrade from session-only mode to wallet mode.
|
|
@@ -1848,7 +2072,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1848
2072
|
sessionStorage: options?.sessionStorage ?? this.config.sessionStorage ?? new MemorySessionStorage(),
|
|
1849
2073
|
domain: this.siweDomain,
|
|
1850
2074
|
spacePrefix: prefix,
|
|
1851
|
-
sessionExpirationMs: this.config.sessionExpirationMs ??
|
|
2075
|
+
sessionExpirationMs: this.config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS,
|
|
1852
2076
|
tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
|
|
1853
2077
|
tinycloudRegistryUrl: this.config.tinycloudRegistryUrl,
|
|
1854
2078
|
tinycloudFallbackHosts: this.config.tinycloudFallbackHosts,
|
|
@@ -1892,7 +2116,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1892
2116
|
sessionStorage: options?.sessionStorage ?? this.config.sessionStorage ?? new MemorySessionStorage(),
|
|
1893
2117
|
domain: this.siweDomain,
|
|
1894
2118
|
spacePrefix: prefix,
|
|
1895
|
-
sessionExpirationMs: this.config.sessionExpirationMs ??
|
|
2119
|
+
sessionExpirationMs: this.config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS,
|
|
1896
2120
|
tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
|
|
1897
2121
|
tinycloudRegistryUrl: this.config.tinycloudRegistryUrl,
|
|
1898
2122
|
tinycloudFallbackHosts: this.config.tinycloudFallbackHosts,
|
|
@@ -1915,7 +2139,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1915
2139
|
* @internal
|
|
1916
2140
|
*/
|
|
1917
2141
|
initializeServices() {
|
|
1918
|
-
const session = this.
|
|
2142
|
+
const session = this.currentTinyCloudSession();
|
|
1919
2143
|
if (!session) {
|
|
1920
2144
|
return;
|
|
1921
2145
|
}
|
|
@@ -1973,6 +2197,180 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1973
2197
|
}
|
|
1974
2198
|
return kvService;
|
|
1975
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
|
+
wellKnown: {
|
|
2345
|
+
fetchWellKnown: async (principal, discoveryKey) => {
|
|
2346
|
+
if (!this._address || principal !== this.did) {
|
|
2347
|
+
return null;
|
|
2348
|
+
}
|
|
2349
|
+
if (!this.config.host) {
|
|
2350
|
+
return null;
|
|
2351
|
+
}
|
|
2352
|
+
const publicSpaceId = (0, import_sdk_core6.makePublicSpaceId)(this._address, this._chainId);
|
|
2353
|
+
const result = await import_sdk_core6.TinyCloud.readPublicSpace(this.config.host, publicSpaceId, discoveryKey);
|
|
2354
|
+
if (!result.ok) {
|
|
2355
|
+
return null;
|
|
2356
|
+
}
|
|
2357
|
+
const body = result.data;
|
|
2358
|
+
return "descriptor" in body && body.descriptor ? body.descriptor : body;
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
});
|
|
2362
|
+
}
|
|
2363
|
+
getEncryptionService() {
|
|
2364
|
+
if (!this._serviceContext) {
|
|
2365
|
+
throw new Error("Not signed in. Call signIn() first.");
|
|
2366
|
+
}
|
|
2367
|
+
if (!this._encryption) {
|
|
2368
|
+
this._encryption = this.createEncryptionService();
|
|
2369
|
+
this._encryption.initialize(this._serviceContext);
|
|
2370
|
+
this._serviceContext.registerService("encryption", this._encryption);
|
|
2371
|
+
}
|
|
2372
|
+
return this._encryption;
|
|
2373
|
+
}
|
|
1976
2374
|
createVaultService(spaceId, kv) {
|
|
1977
2375
|
const wasm = this.wasmBindings;
|
|
1978
2376
|
const vaultCrypto = (0, import_sdk_core6.createVaultCrypto)({
|
|
@@ -1988,6 +2386,13 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1988
2386
|
return new import_sdk_core6.DataVaultService({
|
|
1989
2387
|
spaceId,
|
|
1990
2388
|
crypto: vaultCrypto,
|
|
2389
|
+
encryption: {
|
|
2390
|
+
networkId: this.getDefaultEncryptionNetworkId(),
|
|
2391
|
+
service: this.getEncryptionService(),
|
|
2392
|
+
decryptCapabilityProof: () => ({
|
|
2393
|
+
proofs: [this.requireServiceSession().delegationCid]
|
|
2394
|
+
})
|
|
2395
|
+
},
|
|
1991
2396
|
tc: {
|
|
1992
2397
|
kv,
|
|
1993
2398
|
ensurePublicSpace: async () => {
|
|
@@ -2168,7 +2573,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2168
2573
|
* @internal
|
|
2169
2574
|
*/
|
|
2170
2575
|
getSessionExpiry() {
|
|
2171
|
-
const expirationMs = this.config.sessionExpirationMs ??
|
|
2576
|
+
const expirationMs = this.config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS;
|
|
2172
2577
|
return new Date(Date.now() + expirationMs);
|
|
2173
2578
|
}
|
|
2174
2579
|
/**
|
|
@@ -2225,7 +2630,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2225
2630
|
if (!this.signer) {
|
|
2226
2631
|
return void 0;
|
|
2227
2632
|
}
|
|
2228
|
-
const session = this.
|
|
2633
|
+
const session = this.currentTinyCloudSession();
|
|
2229
2634
|
if (!session) {
|
|
2230
2635
|
return void 0;
|
|
2231
2636
|
}
|
|
@@ -2325,6 +2730,34 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2325
2730
|
}
|
|
2326
2731
|
return this._sql;
|
|
2327
2732
|
}
|
|
2733
|
+
/**
|
|
2734
|
+
* Get an SQL service scoped to a specific space.
|
|
2735
|
+
*
|
|
2736
|
+
* Mirrors {@link SpaceService}'s per-space KV factory: clones the active
|
|
2737
|
+
* service context and overrides its session's spaceId so that subsequent
|
|
2738
|
+
* `sql/<dbName>/<action>` invocations route to that space. Useful when
|
|
2739
|
+
* the caller already holds a delegation covering the target space (e.g.
|
|
2740
|
+
* via {@link grantRuntimePermissions} or {@link useRuntimeDelegation})
|
|
2741
|
+
* but the SDK's per-space SQL surface isn't otherwise exposed.
|
|
2742
|
+
*
|
|
2743
|
+
* Does NOT auto-create the space.
|
|
2744
|
+
*
|
|
2745
|
+
* @param spaceId - Full space URI (`tinycloud:pkh:eip155:<chain>:<addr>:<name>`).
|
|
2746
|
+
*/
|
|
2747
|
+
sqlForSpace(spaceId) {
|
|
2748
|
+
if (!this._serviceContext || !this._serviceContext.session) {
|
|
2749
|
+
throw new Error("Not signed in. Call signIn() first.");
|
|
2750
|
+
}
|
|
2751
|
+
const sql = new import_sdk_core6.SQLService({});
|
|
2752
|
+
const spaceScopedContext = new import_sdk_core6.ServiceContext({
|
|
2753
|
+
invoke: this._serviceContext.invoke,
|
|
2754
|
+
fetch: this._serviceContext.fetch,
|
|
2755
|
+
hosts: this._serviceContext.hosts
|
|
2756
|
+
});
|
|
2757
|
+
spaceScopedContext.setSession({ ...this._serviceContext.session, spaceId });
|
|
2758
|
+
sql.initialize(spaceScopedContext);
|
|
2759
|
+
return sql;
|
|
2760
|
+
}
|
|
2328
2761
|
/**
|
|
2329
2762
|
* DuckDB database operations on this user's space.
|
|
2330
2763
|
*/
|
|
@@ -2348,6 +2781,79 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2348
2781
|
}
|
|
2349
2782
|
return this._vault;
|
|
2350
2783
|
}
|
|
2784
|
+
/**
|
|
2785
|
+
* Network-scoped encryption/decrypt service.
|
|
2786
|
+
*/
|
|
2787
|
+
get encryption() {
|
|
2788
|
+
return this.getEncryptionService();
|
|
2789
|
+
}
|
|
2790
|
+
async getEncryptionNetwork(nameOrNetworkId = this.getDefaultEncryptionNetworkId()) {
|
|
2791
|
+
const networkId = nameOrNetworkId.startsWith("urn:tinycloud:encryption:") ? nameOrNetworkId : this.getDefaultEncryptionNetworkId(nameOrNetworkId);
|
|
2792
|
+
const response = await fetch(
|
|
2793
|
+
`${this.config.host}/encryption/networks/${encodeURIComponent(networkId)}`
|
|
2794
|
+
);
|
|
2795
|
+
if (response.status === 404) {
|
|
2796
|
+
return null;
|
|
2797
|
+
}
|
|
2798
|
+
if (!response.ok) {
|
|
2799
|
+
throw new Error(
|
|
2800
|
+
`Failed to fetch encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
|
|
2801
|
+
);
|
|
2802
|
+
}
|
|
2803
|
+
const body = await response.json();
|
|
2804
|
+
return "descriptor" in body && body.descriptor ? body.descriptor : body;
|
|
2805
|
+
}
|
|
2806
|
+
async createEncryptionNetwork(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
|
|
2807
|
+
const targetNode = await this.fetchNodeId();
|
|
2808
|
+
const principal = this.did;
|
|
2809
|
+
const networkId = this.getDefaultEncryptionNetworkId(name);
|
|
2810
|
+
const body = {
|
|
2811
|
+
name,
|
|
2812
|
+
principal,
|
|
2813
|
+
threshold: { n: 1, t: 1 }
|
|
2814
|
+
};
|
|
2815
|
+
const crypto = this.createEncryptionCrypto();
|
|
2816
|
+
const facts = {
|
|
2817
|
+
type: NETWORK_ADMIN_TYPE,
|
|
2818
|
+
targetNode,
|
|
2819
|
+
networkId,
|
|
2820
|
+
bodyHash: (0, import_sdk_core6.canonicalHashHex)(
|
|
2821
|
+
crypto.sha256,
|
|
2822
|
+
body
|
|
2823
|
+
),
|
|
2824
|
+
action: NETWORK_CREATE_ACTION
|
|
2825
|
+
};
|
|
2826
|
+
const signed = await this.signRawNetworkAuthorization({
|
|
2827
|
+
targetNode,
|
|
2828
|
+
networkId,
|
|
2829
|
+
action: NETWORK_CREATE_ACTION,
|
|
2830
|
+
facts
|
|
2831
|
+
});
|
|
2832
|
+
const response = await fetch(`${this.config.host}/encryption/networks`, {
|
|
2833
|
+
method: "POST",
|
|
2834
|
+
headers: {
|
|
2835
|
+
Authorization: signed.authorization,
|
|
2836
|
+
"Content-Type": "application/json"
|
|
2837
|
+
},
|
|
2838
|
+
body: (0, import_sdk_core6.canonicalizeEncryptionJson)(
|
|
2839
|
+
body
|
|
2840
|
+
)
|
|
2841
|
+
});
|
|
2842
|
+
if (!response.ok) {
|
|
2843
|
+
throw new Error(
|
|
2844
|
+
`Failed to create encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
|
|
2845
|
+
);
|
|
2846
|
+
}
|
|
2847
|
+
const created = await response.json();
|
|
2848
|
+
return created.descriptor;
|
|
2849
|
+
}
|
|
2850
|
+
async ensureEncryptionNetwork(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
|
|
2851
|
+
const existing = await this.getEncryptionNetwork(name);
|
|
2852
|
+
if (existing) {
|
|
2853
|
+
return existing;
|
|
2854
|
+
}
|
|
2855
|
+
return this.createEncryptionNetwork(name);
|
|
2856
|
+
}
|
|
2351
2857
|
/**
|
|
2352
2858
|
* App-facing secrets API backed by the `secrets` space vault.
|
|
2353
2859
|
*/
|
|
@@ -2359,8 +2865,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2359
2865
|
this._secrets = new NodeSecretsService({
|
|
2360
2866
|
getService: () => this.getBaseSecrets(),
|
|
2361
2867
|
getManifest: () => this.manifest,
|
|
2868
|
+
hasPermissions: (permissions) => this.hasRuntimePermissions(permissions),
|
|
2362
2869
|
grantPermissions: (additional) => this.grantRuntimePermissions(additional),
|
|
2363
2870
|
canEscalate: () => this.signer !== void 0 && this.tc !== void 0,
|
|
2871
|
+
getEncryptionNetworkId: () => this.getDefaultEncryptionNetworkId(),
|
|
2364
2872
|
getUnlockSigner: () => this.signer ?? void 0
|
|
2365
2873
|
});
|
|
2366
2874
|
}
|
|
@@ -2450,7 +2958,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2450
2958
|
* every requested permission.
|
|
2451
2959
|
*/
|
|
2452
2960
|
hasRuntimePermissions(permissions) {
|
|
2453
|
-
const session = this.
|
|
2961
|
+
const session = this.currentTinyCloudSession();
|
|
2454
2962
|
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
2455
2963
|
return false;
|
|
2456
2964
|
}
|
|
@@ -2470,7 +2978,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2470
2978
|
if (permissions === void 0) {
|
|
2471
2979
|
return this.runtimePermissionGrants.map((grant) => grant.delegation);
|
|
2472
2980
|
}
|
|
2473
|
-
const session = this.
|
|
2981
|
+
const session = this.currentTinyCloudSession();
|
|
2474
2982
|
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
2475
2983
|
return [];
|
|
2476
2984
|
}
|
|
@@ -2484,7 +2992,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2484
2992
|
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
2485
2993
|
*/
|
|
2486
2994
|
async useRuntimeDelegation(delegation) {
|
|
2487
|
-
const session = this.
|
|
2995
|
+
const session = this.currentTinyCloudSession();
|
|
2488
2996
|
if (!session) {
|
|
2489
2997
|
throw new import_sdk_core6.SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
2490
2998
|
}
|
|
@@ -2523,7 +3031,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2523
3031
|
if (!Array.isArray(permissions) || permissions.length === 0) {
|
|
2524
3032
|
throw new Error("grantRuntimePermissions requires a non-empty permissions array");
|
|
2525
3033
|
}
|
|
2526
|
-
const session = this.
|
|
3034
|
+
const session = this.currentTinyCloudSession();
|
|
2527
3035
|
if (!session) {
|
|
2528
3036
|
throw new import_sdk_core6.SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
2529
3037
|
}
|
|
@@ -2547,13 +3055,22 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2547
3055
|
"grantRuntimePermissions requires wallet mode with a signer or privateKey."
|
|
2548
3056
|
);
|
|
2549
3057
|
}
|
|
3058
|
+
const rawEntries = expanded.filter(
|
|
3059
|
+
(entry) => this.isEncryptionPermissionEntry(entry)
|
|
3060
|
+
);
|
|
3061
|
+
const spaceEntries = expanded.filter(
|
|
3062
|
+
(entry) => !this.isEncryptionPermissionEntry(entry)
|
|
3063
|
+
);
|
|
2550
3064
|
const bySpace = /* @__PURE__ */ new Map();
|
|
2551
|
-
for (const entry of
|
|
3065
|
+
for (const entry of spaceEntries) {
|
|
2552
3066
|
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
2553
3067
|
const current = bySpace.get(spaceId) ?? [];
|
|
2554
3068
|
current.push(entry);
|
|
2555
3069
|
bySpace.set(spaceId, current);
|
|
2556
3070
|
}
|
|
3071
|
+
if (bySpace.size === 0 && rawEntries.length > 0) {
|
|
3072
|
+
bySpace.set(session.spaceId, []);
|
|
3073
|
+
}
|
|
2557
3074
|
const now = /* @__PURE__ */ new Date();
|
|
2558
3075
|
const requestedExpiryMs = resolveExpiryMs(options?.expiry);
|
|
2559
3076
|
let expiresAt = new Date(now.getTime() + requestedExpiryMs);
|
|
@@ -2561,10 +3078,17 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2561
3078
|
expiresAt = sessionExpiry;
|
|
2562
3079
|
}
|
|
2563
3080
|
const delegations = [];
|
|
3081
|
+
let rawEntriesAttached = false;
|
|
2564
3082
|
for (const [spaceId, entries] of bySpace) {
|
|
3083
|
+
const rawForDelegation = !rawEntriesAttached ? rawEntries : [];
|
|
3084
|
+
if (rawForDelegation.length > 0) {
|
|
3085
|
+
rawEntriesAttached = true;
|
|
3086
|
+
}
|
|
3087
|
+
const delegatedEntries = [...entries, ...rawForDelegation];
|
|
2565
3088
|
const abilities = this.permissionsToAbilities(entries);
|
|
2566
3089
|
const prepared = this.wasmBindings.prepareSession({
|
|
2567
3090
|
abilities,
|
|
3091
|
+
...rawForDelegation.length > 0 ? { rawAbilities: this.permissionsToRawAbilities(rawForDelegation) } : {},
|
|
2568
3092
|
address: this.wasmBindings.ensureEip55(session.address),
|
|
2569
3093
|
chainId: session.chainId,
|
|
2570
3094
|
domain: this.siweDomain,
|
|
@@ -2589,7 +3113,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2589
3113
|
}
|
|
2590
3114
|
const delegation = this.runtimeDelegationFromSession(
|
|
2591
3115
|
delegatedSession,
|
|
2592
|
-
|
|
3116
|
+
delegatedEntries,
|
|
2593
3117
|
spaceId,
|
|
2594
3118
|
session,
|
|
2595
3119
|
expiresAt
|
|
@@ -2603,7 +3127,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2603
3127
|
jwk: session.jwk
|
|
2604
3128
|
},
|
|
2605
3129
|
delegation,
|
|
2606
|
-
operations: this.permissionOperations(
|
|
3130
|
+
operations: this.permissionOperations(delegatedEntries, spaceId),
|
|
2607
3131
|
expiresAt
|
|
2608
3132
|
});
|
|
2609
3133
|
delegations.push(delegation);
|
|
@@ -2751,7 +3275,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2751
3275
|
];
|
|
2752
3276
|
const abilities = { kv: { "": kvActions } };
|
|
2753
3277
|
const now = /* @__PURE__ */ new Date();
|
|
2754
|
-
const expiryMs =
|
|
3278
|
+
const expiryMs = import_sdk_core6.EXPIRY.EPHEMERAL_MS;
|
|
2755
3279
|
const expirationTime = new Date(now.getTime() + expiryMs);
|
|
2756
3280
|
const prepared = this.wasmBindings.prepareSession({
|
|
2757
3281
|
abilities,
|
|
@@ -2921,7 +3445,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2921
3445
|
* `forceWalletSign` is not set.
|
|
2922
3446
|
*/
|
|
2923
3447
|
async delegateTo(did, permissions, options) {
|
|
2924
|
-
const session = this.
|
|
3448
|
+
const session = this.currentTinyCloudSession();
|
|
2925
3449
|
if (!session) {
|
|
2926
3450
|
throw new import_sdk_core6.SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
2927
3451
|
}
|
|
@@ -3035,11 +3559,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3035
3559
|
* the current session; we build one multi-resource abilities map
|
|
3036
3560
|
* and emit one signed UCAN covering them all.
|
|
3037
3561
|
*
|
|
3038
|
-
*
|
|
3039
|
-
*
|
|
3040
|
-
* spaces in a single delegation is not supported by the underlying
|
|
3041
|
-
* Rust create_delegation call and the resulting UCAN would be
|
|
3042
|
-
* under-specified.
|
|
3562
|
+
* Non-encryption entries must share the same target space. Encryption
|
|
3563
|
+
* entries are raw network URNs and do not participate in space grouping.
|
|
3043
3564
|
*
|
|
3044
3565
|
* @internal
|
|
3045
3566
|
*/
|
|
@@ -3051,15 +3572,18 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3051
3572
|
}
|
|
3052
3573
|
const resolvedSpaces = /* @__PURE__ */ new Set();
|
|
3053
3574
|
for (const entry of entries) {
|
|
3575
|
+
if (this.isEncryptionPermissionEntry(entry)) {
|
|
3576
|
+
continue;
|
|
3577
|
+
}
|
|
3054
3578
|
const spaceId2 = this.resolvePermissionSpace(entry.space, session);
|
|
3055
3579
|
resolvedSpaces.add(spaceId2);
|
|
3056
3580
|
}
|
|
3057
|
-
if (resolvedSpaces.size
|
|
3581
|
+
if (resolvedSpaces.size > 1) {
|
|
3058
3582
|
throw new Error(
|
|
3059
3583
|
`delegateTo: all permission entries must target the same space, got ${resolvedSpaces.size}: ${JSON.stringify([...resolvedSpaces])}`
|
|
3060
3584
|
);
|
|
3061
3585
|
}
|
|
3062
|
-
const spaceId = [...resolvedSpaces][0];
|
|
3586
|
+
const spaceId = resolvedSpaces.size === 1 ? [...resolvedSpaces][0] : session.spaceId;
|
|
3063
3587
|
const abilities = {};
|
|
3064
3588
|
for (const entry of entries) {
|
|
3065
3589
|
const shortService = import_sdk_core6.SERVICE_LONG_TO_SHORT[entry.service];
|
|
@@ -3206,11 +3730,32 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3206
3730
|
}
|
|
3207
3731
|
return abilities;
|
|
3208
3732
|
}
|
|
3733
|
+
isEncryptionPermissionEntry(entry) {
|
|
3734
|
+
return entry.service === import_sdk_core6.ENCRYPTION_PERMISSION_SERVICE && entry.path.startsWith("urn:tinycloud:encryption:");
|
|
3735
|
+
}
|
|
3736
|
+
permissionsToRawAbilities(entries) {
|
|
3737
|
+
const rawAbilities = {};
|
|
3738
|
+
for (const entry of entries) {
|
|
3739
|
+
if (!this.isEncryptionPermissionEntry(entry)) {
|
|
3740
|
+
continue;
|
|
3741
|
+
}
|
|
3742
|
+
const existing = rawAbilities[entry.path] ?? [];
|
|
3743
|
+
const seen = new Set(existing);
|
|
3744
|
+
for (const action of entry.actions) {
|
|
3745
|
+
if (!seen.has(action)) {
|
|
3746
|
+
existing.push(action);
|
|
3747
|
+
seen.add(action);
|
|
3748
|
+
}
|
|
3749
|
+
}
|
|
3750
|
+
rawAbilities[entry.path] = existing;
|
|
3751
|
+
}
|
|
3752
|
+
return rawAbilities;
|
|
3753
|
+
}
|
|
3209
3754
|
permissionOperations(entries, spaceId) {
|
|
3210
3755
|
return entries.flatMap((entry) => {
|
|
3211
3756
|
const service = this.shortServiceName(entry.service);
|
|
3212
3757
|
return entry.actions.map((action) => ({
|
|
3213
|
-
spaceId,
|
|
3758
|
+
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
|
|
3214
3759
|
service,
|
|
3215
3760
|
path: entry.path,
|
|
3216
3761
|
action
|
|
@@ -3233,7 +3778,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3233
3778
|
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
3234
3779
|
const service = this.shortServiceName(entry.service);
|
|
3235
3780
|
return entry.actions.map((action) => ({
|
|
3236
|
-
spaceId,
|
|
3781
|
+
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
|
|
3237
3782
|
service,
|
|
3238
3783
|
path: entry.path,
|
|
3239
3784
|
action
|
|
@@ -3290,24 +3835,40 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3290
3835
|
expiresAt: delegation.expiry
|
|
3291
3836
|
};
|
|
3292
3837
|
}
|
|
3838
|
+
installRuntimeGrantFromServiceSession(delegation, session, expiresAt) {
|
|
3839
|
+
const operations = this.operationsFromDelegation(delegation);
|
|
3840
|
+
if (operations.length === 0) {
|
|
3841
|
+
return;
|
|
3842
|
+
}
|
|
3843
|
+
this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
|
|
3844
|
+
(grant) => grant.delegation.cid !== delegation.cid && grant.session.delegationCid !== session.delegationCid
|
|
3845
|
+
);
|
|
3846
|
+
this.runtimePermissionGrants.push({
|
|
3847
|
+
session,
|
|
3848
|
+
delegation,
|
|
3849
|
+
operations,
|
|
3850
|
+
expiresAt
|
|
3851
|
+
});
|
|
3852
|
+
}
|
|
3293
3853
|
delegatedResourcesForEntries(entries, spaceId) {
|
|
3294
3854
|
return entries.map((entry) => ({
|
|
3295
3855
|
service: this.shortServiceName(entry.service),
|
|
3296
|
-
space: spaceId,
|
|
3856
|
+
space: this.isEncryptionPermissionEntry(entry) ? "encryption" : spaceId,
|
|
3297
3857
|
path: entry.path,
|
|
3298
3858
|
actions: [...entry.actions]
|
|
3299
3859
|
}));
|
|
3300
3860
|
}
|
|
3301
3861
|
operationsFromDelegation(delegation) {
|
|
3302
3862
|
const resources = delegation.resources !== void 0 && delegation.resources.length > 0 ? delegation.resources : this.flatDelegationResources(delegation);
|
|
3303
|
-
return resources.flatMap(
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
service:
|
|
3863
|
+
return resources.flatMap((resource) => {
|
|
3864
|
+
const service = this.invocationServiceName(resource.service);
|
|
3865
|
+
return resource.actions.map((action) => ({
|
|
3866
|
+
...this.isEncryptionNetworkOperation(service, resource.path) ? { resource: resource.path } : { spaceId: resource.space },
|
|
3867
|
+
service,
|
|
3307
3868
|
path: resource.path,
|
|
3308
3869
|
action
|
|
3309
|
-
}))
|
|
3310
|
-
);
|
|
3870
|
+
}));
|
|
3871
|
+
});
|
|
3311
3872
|
}
|
|
3312
3873
|
flatDelegationResources(delegation) {
|
|
3313
3874
|
const byService = /* @__PURE__ */ new Map();
|
|
@@ -3356,7 +3917,13 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3356
3917
|
);
|
|
3357
3918
|
}
|
|
3358
3919
|
operationCovers(granted, requested) {
|
|
3359
|
-
|
|
3920
|
+
if (granted.service !== requested.service || !this.actionContains(granted.action, requested.action)) {
|
|
3921
|
+
return false;
|
|
3922
|
+
}
|
|
3923
|
+
if (granted.resource !== void 0 || requested.resource !== void 0) {
|
|
3924
|
+
return granted.resource !== void 0 && requested.resource !== void 0 && granted.resource === requested.resource && this.pathContains(granted.path, requested.path);
|
|
3925
|
+
}
|
|
3926
|
+
return granted.spaceId !== void 0 && requested.spaceId !== void 0 && granted.spaceId === requested.spaceId && this.pathContains(granted.path, requested.path);
|
|
3360
3927
|
}
|
|
3361
3928
|
actionContains(grantedAction, requestedAction) {
|
|
3362
3929
|
if (grantedAction === requestedAction) {
|
|
@@ -3371,6 +3938,37 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3371
3938
|
invocationServiceName(service) {
|
|
3372
3939
|
return service.startsWith("tinycloud.") ? this.shortServiceName(service) : service;
|
|
3373
3940
|
}
|
|
3941
|
+
isEncryptionNetworkOperation(service, path) {
|
|
3942
|
+
return service === "encryption" && path.startsWith("urn:tinycloud:encryption:");
|
|
3943
|
+
}
|
|
3944
|
+
operationFromInvokeAnyEntry(entry) {
|
|
3945
|
+
const service = this.invocationServiceName(entry.service);
|
|
3946
|
+
if (typeof entry.resource === "string") {
|
|
3947
|
+
return {
|
|
3948
|
+
resource: entry.resource,
|
|
3949
|
+
service,
|
|
3950
|
+
path: entry.path,
|
|
3951
|
+
action: entry.action
|
|
3952
|
+
};
|
|
3953
|
+
}
|
|
3954
|
+
if (this.isEncryptionNetworkOperation(service, entry.path)) {
|
|
3955
|
+
return {
|
|
3956
|
+
resource: entry.path,
|
|
3957
|
+
service,
|
|
3958
|
+
path: entry.path,
|
|
3959
|
+
action: entry.action
|
|
3960
|
+
};
|
|
3961
|
+
}
|
|
3962
|
+
if (typeof entry.spaceId === "string") {
|
|
3963
|
+
return {
|
|
3964
|
+
spaceId: entry.spaceId,
|
|
3965
|
+
service,
|
|
3966
|
+
path: entry.path,
|
|
3967
|
+
action: entry.action
|
|
3968
|
+
};
|
|
3969
|
+
}
|
|
3970
|
+
return void 0;
|
|
3971
|
+
}
|
|
3374
3972
|
pathContains(grantedPath, requestedPath) {
|
|
3375
3973
|
if (grantedPath === "" || grantedPath === "/") {
|
|
3376
3974
|
return true;
|
|
@@ -3609,6 +4207,17 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3609
4207
|
// Not used in session-only mode
|
|
3610
4208
|
};
|
|
3611
4209
|
this.trackReceivedDelegation(delegation, this.sessionKeyJwk);
|
|
4210
|
+
this.installRuntimeGrantFromServiceSession(
|
|
4211
|
+
delegation,
|
|
4212
|
+
{
|
|
4213
|
+
delegationHeader: session2.delegationHeader,
|
|
4214
|
+
delegationCid: session2.delegationCid,
|
|
4215
|
+
spaceId: session2.spaceId,
|
|
4216
|
+
verificationMethod: session2.verificationMethod,
|
|
4217
|
+
jwk: session2.jwk
|
|
4218
|
+
},
|
|
4219
|
+
delegation.expiry
|
|
4220
|
+
);
|
|
3612
4221
|
return new DelegatedAccess(session2, delegation, targetHost, this.wasmBindings.invoke);
|
|
3613
4222
|
}
|
|
3614
4223
|
const mySession = this.auth?.tinyCloudSession;
|
|
@@ -3620,6 +4229,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3620
4229
|
const kvActions = delegation.actions.filter((a) => a.startsWith("tinycloud.kv/"));
|
|
3621
4230
|
const sqlActions = delegation.actions.filter((a) => a.startsWith("tinycloud.sql/"));
|
|
3622
4231
|
const duckdbActions = delegation.actions.filter((a) => a.startsWith("tinycloud.duckdb/"));
|
|
4232
|
+
const encryptionActions = delegation.actions.filter(
|
|
4233
|
+
(a) => a.startsWith("tinycloud.encryption/")
|
|
4234
|
+
);
|
|
4235
|
+
const rawAbilities = {};
|
|
3623
4236
|
if (kvActions.length > 0) {
|
|
3624
4237
|
abilities.kv = { [delegation.path]: kvActions };
|
|
3625
4238
|
}
|
|
@@ -3629,6 +4242,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3629
4242
|
if (duckdbActions.length > 0) {
|
|
3630
4243
|
abilities.duckdb = { [delegation.path]: duckdbActions };
|
|
3631
4244
|
}
|
|
4245
|
+
if (encryptionActions.length > 0 && delegation.path.startsWith("urn:tinycloud:encryption:")) {
|
|
4246
|
+
rawAbilities[delegation.path] = encryptionActions;
|
|
4247
|
+
}
|
|
3632
4248
|
const now = /* @__PURE__ */ new Date();
|
|
3633
4249
|
const maxExpiry = new Date(now.getTime() + 60 * 60 * 1e3);
|
|
3634
4250
|
const expirationTime = delegation.expiry < maxExpiry ? delegation.expiry : maxExpiry;
|
|
@@ -3641,7 +4257,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3641
4257
|
expirationTime: expirationTime.toISOString(),
|
|
3642
4258
|
spaceId: delegation.spaceId,
|
|
3643
4259
|
jwk,
|
|
3644
|
-
parents: [delegation.cid]
|
|
4260
|
+
parents: [delegation.cid],
|
|
4261
|
+
...Object.keys(rawAbilities).length > 0 ? { rawAbilities } : {}
|
|
3645
4262
|
});
|
|
3646
4263
|
const signature = await this.signer.signMessage(prepared.siwe);
|
|
3647
4264
|
const invokerSession = this.wasmBindings.completeSessionSetup({
|
|
@@ -3668,6 +4285,17 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3668
4285
|
signature
|
|
3669
4286
|
};
|
|
3670
4287
|
this.trackReceivedDelegation(delegation, jwk);
|
|
4288
|
+
this.installRuntimeGrantFromServiceSession(
|
|
4289
|
+
delegation,
|
|
4290
|
+
{
|
|
4291
|
+
delegationHeader: session.delegationHeader,
|
|
4292
|
+
delegationCid: session.delegationCid,
|
|
4293
|
+
spaceId: session.spaceId,
|
|
4294
|
+
verificationMethod: session.verificationMethod,
|
|
4295
|
+
jwk: session.jwk
|
|
4296
|
+
},
|
|
4297
|
+
expirationTime
|
|
4298
|
+
);
|
|
3671
4299
|
return new DelegatedAccess(session, delegation, targetHost, this.wasmBindings.invoke);
|
|
3672
4300
|
}
|
|
3673
4301
|
/**
|
|
@@ -3810,6 +4438,7 @@ var import_sdk_core18 = require("@tinycloud/sdk-core");
|
|
|
3810
4438
|
CapabilityKeyRegistryErrorCodes,
|
|
3811
4439
|
DEFAULT_MANIFEST_SPACE,
|
|
3812
4440
|
DEFAULT_MANIFEST_VERSION,
|
|
4441
|
+
DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
|
|
3813
4442
|
DataVaultService,
|
|
3814
4443
|
DatabaseHandle,
|
|
3815
4444
|
DelegatedAccess,
|
|
@@ -3826,6 +4455,7 @@ var import_sdk_core18 = require("@tinycloud/sdk-core");
|
|
|
3826
4455
|
PermissionNotInManifestError,
|
|
3827
4456
|
PrefixedKVService,
|
|
3828
4457
|
ProtocolMismatchError,
|
|
4458
|
+
SECRET_NAME_RE,
|
|
3829
4459
|
SQLAction,
|
|
3830
4460
|
SQLService,
|
|
3831
4461
|
SecretsService,
|
|
@@ -3845,6 +4475,7 @@ var import_sdk_core18 = require("@tinycloud/sdk-core");
|
|
|
3845
4475
|
VersionCheckError,
|
|
3846
4476
|
WasmKeyProvider,
|
|
3847
4477
|
buildSpaceUri,
|
|
4478
|
+
canonicalizeSecretScope,
|
|
3848
4479
|
checkNodeInfo,
|
|
3849
4480
|
composeManifestRequest,
|
|
3850
4481
|
createCapabilityKeyRegistry,
|
|
@@ -3864,6 +4495,8 @@ var import_sdk_core18 = require("@tinycloud/sdk-core");
|
|
|
3864
4495
|
parseExpiry,
|
|
3865
4496
|
parseSpaceUri,
|
|
3866
4497
|
resolveManifest,
|
|
4498
|
+
resolveSecretListPrefix,
|
|
4499
|
+
resolveSecretPath,
|
|
3867
4500
|
resourceCapabilitiesToSpaceAbilitiesMap,
|
|
3868
4501
|
serializeDelegation,
|
|
3869
4502
|
validateManifest
|