@tinycloud/node-sdk 2.2.0-beta.9 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/core.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 ?? 60 * 60 * 1e3;
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 primarySpaceName = request.resources.find((entry) => entry.space !== "account")?.space ?? import_sdk_core2.DEFAULT_MANIFEST_SPACE;
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)(request.resources);
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 60 * 60 * 1e3;
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
- return action === "put" ? "tinycloud.vault/write" : "tinycloud.vault/delete";
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 kvActionUrn(action) {
1282
- return `tinycloud.kv/${action}`;
1367
+ function secretActionName(action) {
1368
+ return action;
1283
1369
  }
1284
- function secretPermissionEntries(name, action) {
1285
- return [
1286
- {
1287
- service: "tinycloud.vault",
1288
- space: SECRETS_SPACE,
1289
- path: `${SECRET_PREFIX}${name}`,
1290
- actions: [action === "put" ? "write" : "delete"],
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
- return this.service.get(name);
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.ensureMutationPermission(name, "put");
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.ensureMutationPermission(name, "del");
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
- return this.service.list();
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 ensureMutationPermission(name, action) {
1347
- if (!SECRET_NAME_RE.test(name)) {
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
- `Invalid secret name ${JSON.stringify(name)}. Secret names must match ${SECRET_NAME_RE.source}.`
1452
+ error instanceof Error ? error.message : String(error),
1453
+ error instanceof Error ? error : void 0
1351
1454
  );
1352
1455
  }
1353
- if (this.hasMutationPermission(name, action)) {
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 ${name}; TinyCloudNode needs wallet mode with a signer or privateKey.`
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(secretPermissionEntries(name, action));
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 ${name} failed.`,
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
- hasMutationPermission(name, action) {
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 requiredAction = kvActionUrn(action);
1386
- return manifests.some((entry) => {
1387
- const resolved = (0, import_sdk_core5.resolveManifest)(entry);
1388
- return ["keys", "vault"].every(
1389
- (base) => resolved.resources.some(
1390
- (resource) => resource.service === "tinycloud.kv" && isSecretsSpace(resource.space) && resource.path === secretResourcePath(base, name) && resource.actions.includes(requiredAction)
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.map((entry) => ({
1444
- spaceId: entry.spaceId,
1445
- service: this.invocationServiceName(entry.service),
1446
- path: entry.path,
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 ?? 60 * 60 * 1e3,
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 ?? 60 * 60 * 1e3,
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 ?? 60 * 60 * 1e3,
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.auth?.tinyCloudSession;
2142
+ const session = this.currentTinyCloudSession();
1919
2143
  if (!session) {
1920
2144
  return;
1921
2145
  }
@@ -1973,6 +2197,163 @@ 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
+ });
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
+ }
1976
2357
  createVaultService(spaceId, kv) {
1977
2358
  const wasm = this.wasmBindings;
1978
2359
  const vaultCrypto = (0, import_sdk_core6.createVaultCrypto)({
@@ -1988,6 +2369,13 @@ var _TinyCloudNode = class _TinyCloudNode {
1988
2369
  return new import_sdk_core6.DataVaultService({
1989
2370
  spaceId,
1990
2371
  crypto: vaultCrypto,
2372
+ encryption: {
2373
+ networkId: this.getDefaultEncryptionNetworkId(),
2374
+ service: this.getEncryptionService(),
2375
+ decryptCapabilityProof: () => ({
2376
+ proofs: [this.requireServiceSession().delegationCid]
2377
+ })
2378
+ },
1991
2379
  tc: {
1992
2380
  kv,
1993
2381
  ensurePublicSpace: async () => {
@@ -2168,7 +2556,7 @@ var _TinyCloudNode = class _TinyCloudNode {
2168
2556
  * @internal
2169
2557
  */
2170
2558
  getSessionExpiry() {
2171
- const expirationMs = this.config.sessionExpirationMs ?? 60 * 60 * 1e3;
2559
+ const expirationMs = this.config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS;
2172
2560
  return new Date(Date.now() + expirationMs);
2173
2561
  }
2174
2562
  /**
@@ -2225,7 +2613,7 @@ var _TinyCloudNode = class _TinyCloudNode {
2225
2613
  if (!this.signer) {
2226
2614
  return void 0;
2227
2615
  }
2228
- const session = this.auth?.tinyCloudSession;
2616
+ const session = this.currentTinyCloudSession();
2229
2617
  if (!session) {
2230
2618
  return void 0;
2231
2619
  }
@@ -2325,6 +2713,34 @@ var _TinyCloudNode = class _TinyCloudNode {
2325
2713
  }
2326
2714
  return this._sql;
2327
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
+ }
2328
2744
  /**
2329
2745
  * DuckDB database operations on this user's space.
2330
2746
  */
@@ -2348,6 +2764,79 @@ var _TinyCloudNode = class _TinyCloudNode {
2348
2764
  }
2349
2765
  return this._vault;
2350
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
+ }
2351
2840
  /**
2352
2841
  * App-facing secrets API backed by the `secrets` space vault.
2353
2842
  */
@@ -2359,8 +2848,10 @@ var _TinyCloudNode = class _TinyCloudNode {
2359
2848
  this._secrets = new NodeSecretsService({
2360
2849
  getService: () => this.getBaseSecrets(),
2361
2850
  getManifest: () => this.manifest,
2851
+ hasPermissions: (permissions) => this.hasRuntimePermissions(permissions),
2362
2852
  grantPermissions: (additional) => this.grantRuntimePermissions(additional),
2363
2853
  canEscalate: () => this.signer !== void 0 && this.tc !== void 0,
2854
+ getEncryptionNetworkId: () => this.getDefaultEncryptionNetworkId(),
2364
2855
  getUnlockSigner: () => this.signer ?? void 0
2365
2856
  });
2366
2857
  }
@@ -2450,7 +2941,7 @@ var _TinyCloudNode = class _TinyCloudNode {
2450
2941
  * every requested permission.
2451
2942
  */
2452
2943
  hasRuntimePermissions(permissions) {
2453
- const session = this.auth?.tinyCloudSession;
2944
+ const session = this.currentTinyCloudSession();
2454
2945
  if (!session || !Array.isArray(permissions) || permissions.length === 0) {
2455
2946
  return false;
2456
2947
  }
@@ -2470,7 +2961,7 @@ var _TinyCloudNode = class _TinyCloudNode {
2470
2961
  if (permissions === void 0) {
2471
2962
  return this.runtimePermissionGrants.map((grant) => grant.delegation);
2472
2963
  }
2473
- const session = this.auth?.tinyCloudSession;
2964
+ const session = this.currentTinyCloudSession();
2474
2965
  if (!session || !Array.isArray(permissions) || permissions.length === 0) {
2475
2966
  return [];
2476
2967
  }
@@ -2484,7 +2975,7 @@ var _TinyCloudNode = class _TinyCloudNode {
2484
2975
  * matching service calls and downstream `delegateTo()` calls can use it.
2485
2976
  */
2486
2977
  async useRuntimeDelegation(delegation) {
2487
- const session = this.auth?.tinyCloudSession;
2978
+ const session = this.currentTinyCloudSession();
2488
2979
  if (!session) {
2489
2980
  throw new import_sdk_core6.SessionExpiredError(/* @__PURE__ */ new Date(0));
2490
2981
  }
@@ -2523,7 +3014,7 @@ var _TinyCloudNode = class _TinyCloudNode {
2523
3014
  if (!Array.isArray(permissions) || permissions.length === 0) {
2524
3015
  throw new Error("grantRuntimePermissions requires a non-empty permissions array");
2525
3016
  }
2526
- const session = this.auth?.tinyCloudSession;
3017
+ const session = this.currentTinyCloudSession();
2527
3018
  if (!session) {
2528
3019
  throw new import_sdk_core6.SessionExpiredError(/* @__PURE__ */ new Date(0));
2529
3020
  }
@@ -2547,13 +3038,22 @@ var _TinyCloudNode = class _TinyCloudNode {
2547
3038
  "grantRuntimePermissions requires wallet mode with a signer or privateKey."
2548
3039
  );
2549
3040
  }
3041
+ const rawEntries = expanded.filter(
3042
+ (entry) => this.isEncryptionPermissionEntry(entry)
3043
+ );
3044
+ const spaceEntries = expanded.filter(
3045
+ (entry) => !this.isEncryptionPermissionEntry(entry)
3046
+ );
2550
3047
  const bySpace = /* @__PURE__ */ new Map();
2551
- for (const entry of expanded) {
3048
+ for (const entry of spaceEntries) {
2552
3049
  const spaceId = this.resolvePermissionSpace(entry.space, session);
2553
3050
  const current = bySpace.get(spaceId) ?? [];
2554
3051
  current.push(entry);
2555
3052
  bySpace.set(spaceId, current);
2556
3053
  }
3054
+ if (bySpace.size === 0 && rawEntries.length > 0) {
3055
+ bySpace.set(session.spaceId, []);
3056
+ }
2557
3057
  const now = /* @__PURE__ */ new Date();
2558
3058
  const requestedExpiryMs = resolveExpiryMs(options?.expiry);
2559
3059
  let expiresAt = new Date(now.getTime() + requestedExpiryMs);
@@ -2561,10 +3061,17 @@ var _TinyCloudNode = class _TinyCloudNode {
2561
3061
  expiresAt = sessionExpiry;
2562
3062
  }
2563
3063
  const delegations = [];
3064
+ let rawEntriesAttached = false;
2564
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];
2565
3071
  const abilities = this.permissionsToAbilities(entries);
2566
3072
  const prepared = this.wasmBindings.prepareSession({
2567
3073
  abilities,
3074
+ ...rawForDelegation.length > 0 ? { rawAbilities: this.permissionsToRawAbilities(rawForDelegation) } : {},
2568
3075
  address: this.wasmBindings.ensureEip55(session.address),
2569
3076
  chainId: session.chainId,
2570
3077
  domain: this.siweDomain,
@@ -2589,7 +3096,7 @@ var _TinyCloudNode = class _TinyCloudNode {
2589
3096
  }
2590
3097
  const delegation = this.runtimeDelegationFromSession(
2591
3098
  delegatedSession,
2592
- entries,
3099
+ delegatedEntries,
2593
3100
  spaceId,
2594
3101
  session,
2595
3102
  expiresAt
@@ -2603,7 +3110,7 @@ var _TinyCloudNode = class _TinyCloudNode {
2603
3110
  jwk: session.jwk
2604
3111
  },
2605
3112
  delegation,
2606
- operations: this.permissionOperations(entries, spaceId),
3113
+ operations: this.permissionOperations(delegatedEntries, spaceId),
2607
3114
  expiresAt
2608
3115
  });
2609
3116
  delegations.push(delegation);
@@ -2751,7 +3258,7 @@ var _TinyCloudNode = class _TinyCloudNode {
2751
3258
  ];
2752
3259
  const abilities = { kv: { "": kvActions } };
2753
3260
  const now = /* @__PURE__ */ new Date();
2754
- const expiryMs = 60 * 60 * 1e3;
3261
+ const expiryMs = import_sdk_core6.EXPIRY.EPHEMERAL_MS;
2755
3262
  const expirationTime = new Date(now.getTime() + expiryMs);
2756
3263
  const prepared = this.wasmBindings.prepareSession({
2757
3264
  abilities,
@@ -2921,7 +3428,7 @@ var _TinyCloudNode = class _TinyCloudNode {
2921
3428
  * `forceWalletSign` is not set.
2922
3429
  */
2923
3430
  async delegateTo(did, permissions, options) {
2924
- const session = this.auth?.tinyCloudSession;
3431
+ const session = this.currentTinyCloudSession();
2925
3432
  if (!session) {
2926
3433
  throw new import_sdk_core6.SessionExpiredError(/* @__PURE__ */ new Date(0));
2927
3434
  }
@@ -3035,11 +3542,8 @@ var _TinyCloudNode = class _TinyCloudNode {
3035
3542
  * the current session; we build one multi-resource abilities map
3036
3543
  * and emit one signed UCAN covering them all.
3037
3544
  *
3038
- * All entries must share the same target space (the UCAN is
3039
- * scoped to a single space). If they don't, this throws mixing
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.
3545
+ * Non-encryption entries must share the same target space. Encryption
3546
+ * entries are raw network URNs and do not participate in space grouping.
3043
3547
  *
3044
3548
  * @internal
3045
3549
  */
@@ -3051,15 +3555,18 @@ var _TinyCloudNode = class _TinyCloudNode {
3051
3555
  }
3052
3556
  const resolvedSpaces = /* @__PURE__ */ new Set();
3053
3557
  for (const entry of entries) {
3558
+ if (this.isEncryptionPermissionEntry(entry)) {
3559
+ continue;
3560
+ }
3054
3561
  const spaceId2 = this.resolvePermissionSpace(entry.space, session);
3055
3562
  resolvedSpaces.add(spaceId2);
3056
3563
  }
3057
- if (resolvedSpaces.size !== 1) {
3564
+ if (resolvedSpaces.size > 1) {
3058
3565
  throw new Error(
3059
3566
  `delegateTo: all permission entries must target the same space, got ${resolvedSpaces.size}: ${JSON.stringify([...resolvedSpaces])}`
3060
3567
  );
3061
3568
  }
3062
- const spaceId = [...resolvedSpaces][0];
3569
+ const spaceId = resolvedSpaces.size === 1 ? [...resolvedSpaces][0] : session.spaceId;
3063
3570
  const abilities = {};
3064
3571
  for (const entry of entries) {
3065
3572
  const shortService = import_sdk_core6.SERVICE_LONG_TO_SHORT[entry.service];
@@ -3206,11 +3713,32 @@ var _TinyCloudNode = class _TinyCloudNode {
3206
3713
  }
3207
3714
  return abilities;
3208
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
+ }
3209
3737
  permissionOperations(entries, spaceId) {
3210
3738
  return entries.flatMap((entry) => {
3211
3739
  const service = this.shortServiceName(entry.service);
3212
3740
  return entry.actions.map((action) => ({
3213
- spaceId,
3741
+ ...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
3214
3742
  service,
3215
3743
  path: entry.path,
3216
3744
  action
@@ -3233,7 +3761,7 @@ var _TinyCloudNode = class _TinyCloudNode {
3233
3761
  const spaceId = this.resolvePermissionSpace(entry.space, session);
3234
3762
  const service = this.shortServiceName(entry.service);
3235
3763
  return entry.actions.map((action) => ({
3236
- spaceId,
3764
+ ...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
3237
3765
  service,
3238
3766
  path: entry.path,
3239
3767
  action
@@ -3290,24 +3818,40 @@ var _TinyCloudNode = class _TinyCloudNode {
3290
3818
  expiresAt: delegation.expiry
3291
3819
  };
3292
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
+ }
3293
3836
  delegatedResourcesForEntries(entries, spaceId) {
3294
3837
  return entries.map((entry) => ({
3295
3838
  service: this.shortServiceName(entry.service),
3296
- space: spaceId,
3839
+ space: this.isEncryptionPermissionEntry(entry) ? "encryption" : spaceId,
3297
3840
  path: entry.path,
3298
3841
  actions: [...entry.actions]
3299
3842
  }));
3300
3843
  }
3301
3844
  operationsFromDelegation(delegation) {
3302
3845
  const resources = delegation.resources !== void 0 && delegation.resources.length > 0 ? delegation.resources : this.flatDelegationResources(delegation);
3303
- return resources.flatMap(
3304
- (resource) => resource.actions.map((action) => ({
3305
- spaceId: resource.space,
3306
- service: this.invocationServiceName(resource.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,
3307
3851
  path: resource.path,
3308
3852
  action
3309
- }))
3310
- );
3853
+ }));
3854
+ });
3311
3855
  }
3312
3856
  flatDelegationResources(delegation) {
3313
3857
  const byService = /* @__PURE__ */ new Map();
@@ -3356,7 +3900,13 @@ var _TinyCloudNode = class _TinyCloudNode {
3356
3900
  );
3357
3901
  }
3358
3902
  operationCovers(granted, requested) {
3359
- return granted.spaceId === requested.spaceId && granted.service === requested.service && this.actionContains(granted.action, requested.action) && this.pathContains(granted.path, requested.path);
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);
3360
3910
  }
3361
3911
  actionContains(grantedAction, requestedAction) {
3362
3912
  if (grantedAction === requestedAction) {
@@ -3371,6 +3921,37 @@ var _TinyCloudNode = class _TinyCloudNode {
3371
3921
  invocationServiceName(service) {
3372
3922
  return service.startsWith("tinycloud.") ? this.shortServiceName(service) : service;
3373
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
+ }
3374
3955
  pathContains(grantedPath, requestedPath) {
3375
3956
  if (grantedPath === "" || grantedPath === "/") {
3376
3957
  return true;
@@ -3609,6 +4190,17 @@ var _TinyCloudNode = class _TinyCloudNode {
3609
4190
  // Not used in session-only mode
3610
4191
  };
3611
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
+ );
3612
4204
  return new DelegatedAccess(session2, delegation, targetHost, this.wasmBindings.invoke);
3613
4205
  }
3614
4206
  const mySession = this.auth?.tinyCloudSession;
@@ -3620,6 +4212,10 @@ var _TinyCloudNode = class _TinyCloudNode {
3620
4212
  const kvActions = delegation.actions.filter((a) => a.startsWith("tinycloud.kv/"));
3621
4213
  const sqlActions = delegation.actions.filter((a) => a.startsWith("tinycloud.sql/"));
3622
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 = {};
3623
4219
  if (kvActions.length > 0) {
3624
4220
  abilities.kv = { [delegation.path]: kvActions };
3625
4221
  }
@@ -3629,6 +4225,9 @@ var _TinyCloudNode = class _TinyCloudNode {
3629
4225
  if (duckdbActions.length > 0) {
3630
4226
  abilities.duckdb = { [delegation.path]: duckdbActions };
3631
4227
  }
4228
+ if (encryptionActions.length > 0 && delegation.path.startsWith("urn:tinycloud:encryption:")) {
4229
+ rawAbilities[delegation.path] = encryptionActions;
4230
+ }
3632
4231
  const now = /* @__PURE__ */ new Date();
3633
4232
  const maxExpiry = new Date(now.getTime() + 60 * 60 * 1e3);
3634
4233
  const expirationTime = delegation.expiry < maxExpiry ? delegation.expiry : maxExpiry;
@@ -3641,7 +4240,8 @@ var _TinyCloudNode = class _TinyCloudNode {
3641
4240
  expirationTime: expirationTime.toISOString(),
3642
4241
  spaceId: delegation.spaceId,
3643
4242
  jwk,
3644
- parents: [delegation.cid]
4243
+ parents: [delegation.cid],
4244
+ ...Object.keys(rawAbilities).length > 0 ? { rawAbilities } : {}
3645
4245
  });
3646
4246
  const signature = await this.signer.signMessage(prepared.siwe);
3647
4247
  const invokerSession = this.wasmBindings.completeSessionSetup({
@@ -3668,6 +4268,17 @@ var _TinyCloudNode = class _TinyCloudNode {
3668
4268
  signature
3669
4269
  };
3670
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
+ );
3671
4282
  return new DelegatedAccess(session, delegation, targetHost, this.wasmBindings.invoke);
3672
4283
  }
3673
4284
  /**
@@ -3810,6 +4421,7 @@ var import_sdk_core18 = require("@tinycloud/sdk-core");
3810
4421
  CapabilityKeyRegistryErrorCodes,
3811
4422
  DEFAULT_MANIFEST_SPACE,
3812
4423
  DEFAULT_MANIFEST_VERSION,
4424
+ DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
3813
4425
  DataVaultService,
3814
4426
  DatabaseHandle,
3815
4427
  DelegatedAccess,
@@ -3826,6 +4438,7 @@ var import_sdk_core18 = require("@tinycloud/sdk-core");
3826
4438
  PermissionNotInManifestError,
3827
4439
  PrefixedKVService,
3828
4440
  ProtocolMismatchError,
4441
+ SECRET_NAME_RE,
3829
4442
  SQLAction,
3830
4443
  SQLService,
3831
4444
  SecretsService,
@@ -3845,6 +4458,7 @@ var import_sdk_core18 = require("@tinycloud/sdk-core");
3845
4458
  VersionCheckError,
3846
4459
  WasmKeyProvider,
3847
4460
  buildSpaceUri,
4461
+ canonicalizeSecretScope,
3848
4462
  checkNodeInfo,
3849
4463
  composeManifestRequest,
3850
4464
  createCapabilityKeyRegistry,
@@ -3864,6 +4478,8 @@ var import_sdk_core18 = require("@tinycloud/sdk-core");
3864
4478
  parseExpiry,
3865
4479
  parseSpaceUri,
3866
4480
  resolveManifest,
4481
+ resolveSecretListPrefix,
4482
+ resolveSecretPath,
3867
4483
  resourceCapabilitiesToSpaceAbilitiesMap,
3868
4484
  serializeDelegation,
3869
4485
  validateManifest