@tinycloud/node-sdk 3.8.0-beta.2 → 3.8.0-beta.21

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.
Files changed (44) hide show
  1. package/README.md +6 -0
  2. package/dist/{TinyCloudNode-B6VOBrCb.d.ts → TinyCloudNode-DaD5vJhL.d.cts} +49 -6
  3. package/dist/{TinyCloudNode-CVO41O12.d.cts → TinyCloudNode-k3TbBwwj.d.ts} +49 -6
  4. package/dist/{WasmKeyProvider-5Ee7fcAw.d.cts → WasmKeyProvider-BKCPRC-c.d.cts} +21 -1
  5. package/dist/{WasmKeyProvider-5Ee7fcAw.d.ts → WasmKeyProvider-BKCPRC-c.d.ts} +21 -1
  6. package/dist/{authority-DGi1hIMK.d.ts → authority-C_fWaxAu.d.ts} +1 -1
  7. package/dist/{authority-DgVV80bI.d.cts → authority-DJdbZM6k.d.cts} +1 -1
  8. package/dist/core.cjs +375 -40
  9. package/dist/core.cjs.map +1 -1
  10. package/dist/core.d.cts +5 -4
  11. package/dist/core.d.ts +5 -4
  12. package/dist/core.js +370 -35
  13. package/dist/core.js.map +1 -1
  14. package/dist/credentials-core.d.cts +1 -1
  15. package/dist/credentials-core.d.ts +1 -1
  16. package/dist/credentials.d.cts +1 -1
  17. package/dist/credentials.d.ts +1 -1
  18. package/dist/{delegation-0LKZkmP2.d.cts → delegation-Ckc3UETl.d.cts} +1 -1
  19. package/dist/{delegation-0LKZkmP2.d.ts → delegation-Ckc3UETl.d.ts} +1 -1
  20. package/dist/encryption-keys.d.cts +2 -2
  21. package/dist/encryption-keys.d.ts +2 -2
  22. package/dist/index.cjs +343 -51
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +4 -3
  25. package/dist/index.d.ts +4 -3
  26. package/dist/index.js +333 -38
  27. package/dist/index.js.map +1 -1
  28. package/dist/replication/authority.cjs +3 -0
  29. package/dist/replication/authority.cjs.map +1 -1
  30. package/dist/replication/authority.d.cts +4 -3
  31. package/dist/replication/authority.d.ts +4 -3
  32. package/dist/replication/authority.js +3 -0
  33. package/dist/replication/authority.js.map +1 -1
  34. package/dist/replication/runtime.cjs +1 -0
  35. package/dist/replication/runtime.cjs.map +1 -1
  36. package/dist/replication/runtime.js +1 -0
  37. package/dist/replication/runtime.js.map +1 -1
  38. package/dist/share-receive.cjs +775 -0
  39. package/dist/share-receive.cjs.map +1 -0
  40. package/dist/share-receive.d.cts +32 -0
  41. package/dist/share-receive.d.ts +32 -0
  42. package/dist/share-receive.js +777 -0
  43. package/dist/share-receive.js.map +1 -0
  44. package/package.json +11 -4
package/dist/core.cjs CHANGED
@@ -58,6 +58,7 @@ __export(core_exports, {
58
58
  PermissionNotInManifestError: () => import_sdk_core14.PermissionNotInManifestError,
59
59
  PrefixedKVService: () => import_sdk_core15.PrefixedKVService,
60
60
  ProtocolMismatchError: () => import_sdk_core22.ProtocolMismatchError,
61
+ RuntimeDelegationRejectedError: () => RuntimeDelegationRejectedError,
61
62
  SECRET_NAME_RE: () => import_sdk_core18.SECRET_NAME_RE,
62
63
  SQLAction: () => import_sdk_core16.SQLAction,
63
64
  SQLService: () => import_sdk_core16.SQLService,
@@ -80,6 +81,7 @@ __export(core_exports, {
80
81
  VersionCheckError: () => import_sdk_core22.VersionCheckError,
81
82
  WasmKeyProvider: () => WasmKeyProvider,
82
83
  activateCompactRuntimeDelegation: () => activateCompactRuntimeDelegation,
84
+ activateValidatedRuntimeDelegation: () => activateValidatedRuntimeDelegation,
83
85
  addressStorageKey: () => import_sdk_core13.addressStorageKey,
84
86
  buildSpaceUri: () => import_sdk_core21.buildSpaceUri,
85
87
  canonicalizeAddress: () => import_sdk_core13.canonicalizeAddress,
@@ -109,6 +111,7 @@ __export(core_exports, {
109
111
  getNodeReplicationLoaders: () => getNodeReplicationLoaders,
110
112
  getTinyCloudDebugLogs: () => import_sdk_core13.getTinyCloudDebugLogs,
111
113
  installTinyCloudDebugGlobals: () => import_sdk_core13.installTinyCloudDebugGlobals,
114
+ isAppKeyHostBound: () => isAppKeyHostBound,
112
115
  isCapabilitySubset: () => import_sdk_core14.isCapabilitySubset,
113
116
  isEvmAddress: () => import_sdk_core13.isEvmAddress,
114
117
  loadManifest: () => import_sdk_core14.loadManifest,
@@ -130,7 +133,8 @@ __export(core_exports, {
130
133
  resourceCapabilitiesToSpaceAbilitiesMap: () => import_sdk_core14.resourceCapabilitiesToSpaceAbilitiesMap,
131
134
  serializeDelegation: () => serializeDelegation,
132
135
  tinyCloudDebugLogger: () => import_sdk_core13.tinyCloudDebugLogger,
133
- validateManifest: () => import_sdk_core14.validateManifest
136
+ validateManifest: () => import_sdk_core14.validateManifest,
137
+ wireOpenKeyAuthorize: () => wireOpenKeyAuthorize
134
138
  });
135
139
  module.exports = __toCommonJS(core_exports);
136
140
  var import_sdk_core12 = require("@tinycloud/sdk-core");
@@ -345,6 +349,44 @@ var FileSessionStorage = class {
345
349
  var import_node_crypto = require("crypto");
346
350
  var import_siwe = require("siwe");
347
351
  var import_sdk_core5 = require("@tinycloud/sdk-core");
352
+ var import_resource = require("@tinycloud/sdk-services/encryption/resource");
353
+ var import_sdk_services2 = require("@tinycloud/sdk-services");
354
+
355
+ // src/authorization/appKeyHostBinding.ts
356
+ var DOMAIN_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
357
+ var IPV4 = /^\d+\.\d+\.\d+\.\d+$/;
358
+ var APP_ID = /^[a-z0-9][a-z0-9._-]{0,63}$/;
359
+ function appKeyHostBinding(domain, appId) {
360
+ if (!APP_ID.test(appId)) return false;
361
+ if (domain.endsWith(":")) return false;
362
+ let url;
363
+ try {
364
+ url = new URL(`https://${domain}`);
365
+ } catch {
366
+ return false;
367
+ }
368
+ if (url.protocol !== "https:" || url.port !== "" || url.username !== "" || url.password !== "") return false;
369
+ if (url.pathname !== "/" || url.search !== "" || url.hash !== "") return false;
370
+ const host = url.hostname;
371
+ if (host.startsWith("[") || host.endsWith(".") || IPV4.test(host)) return false;
372
+ const labels = host.split(".");
373
+ if (labels.length < 2 || labels[labels.length - 1] === "localhost") return false;
374
+ if (!labels.every((label) => DOMAIN_LABEL.test(label))) return false;
375
+ const reversed = labels.reverse().join(".");
376
+ return appId === reversed || appId.startsWith(`${reversed}.`);
377
+ }
378
+ function isAppKeyHostBound(keysAppId, origin) {
379
+ if (typeof origin === "string" && !origin.includes("://")) {
380
+ return appKeyHostBinding(origin, keysAppId);
381
+ }
382
+ let url;
383
+ try {
384
+ url = typeof origin === "string" ? new URL(origin) : origin;
385
+ } catch {
386
+ return false;
387
+ }
388
+ return appKeyHostBinding(url.host, keysAppId);
389
+ }
348
390
 
349
391
  // src/replication/authority-sign-in.ts
350
392
  var import_sdk_core2 = require("@tinycloud/sdk-core");
@@ -476,6 +518,7 @@ function cloneAbilitiesMap(actions) {
476
518
  var NodeUserAuthorization = class {
477
519
  constructor(config) {
478
520
  this.activeSessionKeyId = "default";
521
+ this.preparedSessionCounter = 0;
479
522
  /** Incremented by resetSession; a sign-in started earlier publishes nothing. */
480
523
  this.sessionGeneration = 0;
481
524
  /**
@@ -832,6 +875,23 @@ var NodeUserAuthorization = class {
832
875
  );
833
876
  return this._capabilityRequest;
834
877
  }
878
+ async refreshNamedKeyFeatureForSignIn(address, chainId) {
879
+ const request = this.getCapabilityRequest();
880
+ const siweDomain = this.siweConfig?.domain ?? this.domain;
881
+ const hasNamedKeys = request?.manifests.some((manifest) => manifest.keys?.length && appKeyHostBinding(siweDomain, manifest.keys_app_id ?? manifest.app_id));
882
+ if (hasNamedKeys) {
883
+ await this.resolveTinyCloudHostsForSignIn(address, chainId);
884
+ await this.refreshNodeInfo();
885
+ }
886
+ }
887
+ /** Manifest key paths that this sign-in cannot grant. */
888
+ get unavailableKeyPaths() {
889
+ const request = this.getCapabilityRequest();
890
+ const siweDomain = this.siweConfig?.domain ?? this.domain;
891
+ return (request?.manifests ?? []).flatMap(
892
+ (manifest) => this._nodeFeatures.includes("encryption-named-keys-v1") && appKeyHostBinding(siweDomain, manifest.keys_app_id ?? manifest.app_id) ? [] : (manifest.keys ?? []).map((entry) => "path" in entry ? entry.path : entry.prefix)
893
+ );
894
+ }
835
895
  resolveSpaceName(space, address, chainId) {
836
896
  if (space.startsWith("tinycloud:")) {
837
897
  return space;
@@ -1023,15 +1083,32 @@ var NodeUserAuthorization = class {
1023
1083
  }
1024
1084
  const rawAbilities = {};
1025
1085
  const currentDid = (0, import_sdk_core5.pkhDid)(address, chainId);
1086
+ if (this._nodeFeatures.includes("encryption-named-keys-v1")) {
1087
+ const siweDomain = this.siweConfig?.domain ?? this.domain;
1088
+ for (const manifest of request.manifests) {
1089
+ if (!appKeyHostBinding(siweDomain, manifest.keys_app_id ?? manifest.app_id)) continue;
1090
+ for (const declaration of manifest.keys ?? []) {
1091
+ const path = "path" in declaration ? declaration.path : declaration.prefix;
1092
+ const resource = `urn:tinycloud:encryption:${currentDid}:default/${path}`;
1093
+ for (const ability of declaration.abilities) {
1094
+ addRawAbility(rawAbilities, resource, `tinycloud.encryption/${ability}`);
1095
+ }
1096
+ }
1097
+ }
1098
+ }
1026
1099
  const spaceResources = request.resources.filter((entry) => {
1027
1100
  if (entry.service !== import_sdk_core5.ENCRYPTION_PERMISSION_SERVICE) {
1028
1101
  return true;
1029
1102
  }
1103
+ const parsed = entry.path.includes("/") ? (0, import_resource.parseEncryptionResource)(entry.path) : { ...(0, import_sdk_services2.parseNetworkId)(entry.path), kind: "network" };
1104
+ const sharePrefix = parsed.kind === "prefix" && parsed.network === "default" && parsed.prefix === "share" && entry.actions.length === 1 && entry.actions[0] === DECRYPT_ACTION && didPrincipalMatches(parsed.ownerDid, currentDid);
1105
+ if (parsed.kind !== "network" && !sharePrefix) {
1106
+ throw new import_sdk_core5.ManifestValidationError("named keys go in manifest.keys");
1107
+ }
1030
1108
  for (const action of entry.actions) {
1031
1109
  addRawAbility(rawAbilities, entry.path, action);
1032
1110
  }
1033
- if (entry.actions.includes(DECRYPT_ACTION)) {
1034
- const parsed = (0, import_sdk_core5.parseNetworkId)(entry.path);
1111
+ if (entry.actions.includes(DECRYPT_ACTION) && parsed.kind === "network") {
1035
1112
  if (didPrincipalMatches(parsed.ownerDid, currentDid)) {
1036
1113
  addRawAbility(rawAbilities, entry.path, NETWORK_CREATE_ACTION);
1037
1114
  }
@@ -1068,8 +1145,7 @@ var NodeUserAuthorization = class {
1068
1145
  /**
1069
1146
  * Build SIWE overrides from the top-level nonce and siweConfig.
1070
1147
  * - Top-level `nonce` is seeded first so `siweConfig.nonce` wins if both are set.
1071
- * - statement is prepended to the default statement
1072
- * - resources are appended to the default resources
1148
+ * - resources are inserted after WASM preparation, before the final ReCap
1073
1149
  * - uri triggers a warning (overwriting delegation target)
1074
1150
  * - all other fields override directly
1075
1151
  * - per-call nonce overrides siweConfig.nonce when provided
@@ -1087,9 +1163,6 @@ var NodeUserAuthorization = class {
1087
1163
  if (statement) {
1088
1164
  overrides.statement = this.statement ? `${statement} ${this.statement}` : statement;
1089
1165
  }
1090
- if (resources && resources.length > 0) {
1091
- overrides.resources = resources;
1092
- }
1093
1166
  if (uri) {
1094
1167
  console.warn(
1095
1168
  "[tinycloud] siweConfig.uri is overwriting the delegation target URI. This may break delegation chain validation if the URI does not match the session key DID."
@@ -1101,6 +1174,29 @@ var NodeUserAuthorization = class {
1101
1174
  }
1102
1175
  return overrides;
1103
1176
  }
1177
+ prepareSessionWithResources(params) {
1178
+ const resources = this.siweConfig?.resources;
1179
+ if (resources?.some((resource) => typeof resource !== "string" || !/^urn:tinycloud:recipients:[A-Za-z0-9_-]+$/.test(resource))) {
1180
+ throw new Error("SIWE metadata resources must be base64url recipients URIs");
1181
+ }
1182
+ const prepared = this.wasm.prepareSession(params);
1183
+ if (!resources?.length) return prepared;
1184
+ const recapLines = /^- urn:recap:[^\r\n]+$/gim;
1185
+ const recaps = prepared.siwe.match(recapLines);
1186
+ const recap = recaps?.[0];
1187
+ if (recaps?.length !== 1 || !prepared.siwe.endsWith(recap)) {
1188
+ throw new Error("Prepared SIWE must end with exactly one ReCap");
1189
+ }
1190
+ const insertedLines = resources.map((resource) => `- ${resource}`);
1191
+ const siwe = prepared.siwe.slice(0, -recap.length) + insertedLines.join("\n") + "\n" + recap;
1192
+ const expectedFields = (0, import_sdk_core5.extractImmutableSiweFields)(prepared.siwe);
1193
+ expectedFields.nonRecapResources = [expectedFields.nonRecapResources, ...insertedLines].filter(Boolean).join("\n");
1194
+ const actualRecaps = siwe.match(recapLines);
1195
+ if (actualRecaps?.length !== 1 || actualRecaps[0] !== recap || (0, import_sdk_core5.diffImmutableSiweFields)(expectedFields, (0, import_sdk_core5.extractImmutableSiweFields)(siwe)).length) {
1196
+ throw new Error("SIWE metadata insertion changed the prepared message");
1197
+ }
1198
+ return { ...prepared, siwe };
1199
+ }
1104
1200
  /**
1105
1201
  * Add an extension to the authorization flow.
1106
1202
  */
@@ -1348,7 +1444,7 @@ var NodeUserAuthorization = class {
1348
1444
  spaceAbilities[spaceId] = (0, import_sdk_core5.resourceCapabilitiesToAbilitiesMap)(resources);
1349
1445
  }
1350
1446
  const abilities = spaceAbilities[options.spaceId] ?? (0, import_sdk_core5.resourceCapabilitiesToAbilitiesMap)([]);
1351
- const prepared = this.wasm.prepareSession({
1447
+ const prepared = this.prepareSessionWithResources({
1352
1448
  abilities,
1353
1449
  spaceAbilities,
1354
1450
  ...options.rawAbilities !== void 0 ? { rawAbilities: options.rawAbilities } : {},
@@ -1427,8 +1523,10 @@ var NodeUserAuthorization = class {
1427
1523
  this._chainId = chainId;
1428
1524
  await this.resolveTinyCloudHostsForSignIn(address, chainId);
1429
1525
  this.assertSignInCurrent(generation);
1430
- const keyId = `session-${Date.now()}`;
1431
- this.sessionManager.renameSessionKeyId(this.activeSessionKeyId, keyId);
1526
+ await this.refreshNamedKeyFeatureForSignIn(address, chainId);
1527
+ this.assertSignInCurrent(generation);
1528
+ const keyId = `session-${Date.now()}-${Math.random().toString(36).slice(2)}`;
1529
+ this.sessionManager.createSessionKey(keyId);
1432
1530
  this.activeSessionKeyId = keyId;
1433
1531
  const jwkString = this.sessionManager.jwk(keyId);
1434
1532
  if (!jwkString) {
@@ -1439,7 +1537,7 @@ var NodeUserAuthorization = class {
1439
1537
  const spaceId = capabilityPlan.spaceId;
1440
1538
  const now = /* @__PURE__ */ new Date();
1441
1539
  const expirationTime = new Date(now.getTime() + this.sessionExpirationMs);
1442
- const prepared = this.wasm.prepareSession({
1540
+ const prepared = this.prepareSessionWithResources({
1443
1541
  abilities: capabilityPlan.abilities,
1444
1542
  ...capabilityPlan.spaceAbilities !== void 0 ? { spaceAbilities: capabilityPlan.spaceAbilities } : {},
1445
1543
  ...capabilityPlan.rawAbilities !== void 0 ? { rawAbilities: capabilityPlan.rawAbilities } : {},
@@ -1573,9 +1671,9 @@ var NodeUserAuthorization = class {
1573
1671
  refuseCaveated(this.getCapabilityRequest()?.manifests);
1574
1672
  const address = (0, import_sdk_core5.canonicalizeAddress)(await this.signer.getAddress());
1575
1673
  const chainId = await this.signer.getChainId();
1576
- const keyId = `session-${Date.now()}`;
1577
- this.sessionManager.renameSessionKeyId(this.activeSessionKeyId, keyId);
1578
- this.activeSessionKeyId = keyId;
1674
+ await this.refreshNamedKeyFeatureForSignIn(address, chainId);
1675
+ const keyId = `openkey-session-${Date.now()}-${++this.preparedSessionCounter}`;
1676
+ this.sessionManager.createSessionKey(keyId);
1579
1677
  const jwkString = this.sessionManager.jwk(keyId);
1580
1678
  if (!jwkString) {
1581
1679
  throw new Error("Failed to create session key");
@@ -1585,7 +1683,7 @@ var NodeUserAuthorization = class {
1585
1683
  const spaceId = capabilityPlan.spaceId;
1586
1684
  const now = /* @__PURE__ */ new Date();
1587
1685
  const expirationTime = new Date(now.getTime() + this.sessionExpirationMs);
1588
- const prepared = this.wasm.prepareSession({
1686
+ const prepared = this.prepareSessionWithResources({
1589
1687
  abilities: capabilityPlan.abilities,
1590
1688
  ...capabilityPlan.spaceAbilities !== void 0 ? { spaceAbilities: capabilityPlan.spaceAbilities } : {},
1591
1689
  ...capabilityPlan.rawAbilities !== void 0 ? { rawAbilities: capabilityPlan.rawAbilities } : {},
@@ -1656,6 +1754,7 @@ var NodeUserAuthorization = class {
1656
1754
  await this.sessionStorage.save(address, persistedData);
1657
1755
  this._session = clientSession;
1658
1756
  this._tinyCloudSession = tinyCloudSession;
1757
+ this.activeSessionKeyId = keyId;
1659
1758
  this._address = address;
1660
1759
  this._chainId = chainId;
1661
1760
  await this.refreshNodeInfo();
@@ -1702,6 +1801,11 @@ var NodeUserAuthorization = class {
1702
1801
  * @param options Optional overrides for the OpenKey request.
1703
1802
  */
1704
1803
  async signInWithOpenKey(authorizeFn, options) {
1804
+ const pending = await this.prepareOpenKeySignIn(options);
1805
+ return this.completeOpenKeySignIn(pending, authorizeFn(pending.request), options);
1806
+ }
1807
+ /** Finish address, host and SIWE preparation before an approval click. */
1808
+ async prepareOpenKeySignIn(options) {
1705
1809
  refuseCaveated(this.getCapabilityRequest()?.manifests);
1706
1810
  if (options?.host !== void 0) {
1707
1811
  const overrideHost = options.host.trim();
@@ -1713,7 +1817,7 @@ var NodeUserAuthorization = class {
1713
1817
  const signingAddress = (0, import_sdk_core5.canonicalizeAddress)(await this.signer.getAddress());
1714
1818
  const signingChainId = await this.signer.getChainId();
1715
1819
  await this.resolveTinyCloudHostsForSignIn(signingAddress, signingChainId);
1716
- const { prepared, keyId, jwk } = await this.prepareSessionForSigning();
1820
+ const { prepared, keyId, jwk, address, chainId } = await this.prepareSessionForSigning();
1717
1821
  const presentation = this.buildPresentationEnvelope(options?.reason);
1718
1822
  const hostHint = this.primaryTinyCloudHost;
1719
1823
  const originalRequest = {
@@ -1725,12 +1829,32 @@ var NodeUserAuthorization = class {
1725
1829
  keyId: options?.openkeyKeyId,
1726
1830
  presentation
1727
1831
  };
1832
+ return {
1833
+ request: originalRequest,
1834
+ prepared,
1835
+ keyId,
1836
+ jwk,
1837
+ address,
1838
+ chainId,
1839
+ generation: this.sessionGeneration,
1840
+ createdAt: Date.now()
1841
+ };
1842
+ }
1843
+ /** Complete a prepared approval. The caller starts authorization before awaiting this method. */
1844
+ async completeOpenKeySignIn(pending, authorization, options) {
1845
+ if (Date.now() - pending.createdAt > 5 * 6e4) {
1846
+ throw new Error("Prepared OpenKey sign-in expired; prepare again");
1847
+ }
1848
+ if (pending.generation !== this.sessionGeneration) {
1849
+ throw new Error("Prepared OpenKey sign-in expired after sign-out");
1850
+ }
1851
+ const { request: originalRequest, prepared, keyId, jwk } = pending;
1728
1852
  let result;
1729
1853
  let signed;
1730
1854
  let signature;
1731
1855
  for (let attempt = 0; ; ) {
1732
1856
  try {
1733
- result ?? (result = await authorizeFn(originalRequest));
1857
+ result ?? (result = await authorization);
1734
1858
  signed = await this.verifyOpenKeyResult(result, prepared);
1735
1859
  signature = result.signature;
1736
1860
  break;
@@ -2151,8 +2275,52 @@ var NodeUserAuthorization = class {
2151
2275
  }
2152
2276
  };
2153
2277
 
2278
+ // src/authorization/openKeyBridge.ts
2279
+ function wireOpenKeyAuthorize(openkey, opts) {
2280
+ return async (input) => {
2281
+ const result = await openkey.authorizeTinyCloud({
2282
+ protocolVersion: 1,
2283
+ siwe: input.siwe,
2284
+ keyId: input.keyId,
2285
+ jwk: input.jwk,
2286
+ host: input.host,
2287
+ // Forward the presentation envelope verbatim — the OpenKey SDK
2288
+ // validates and size-bounds it before use.
2289
+ presentation: input.presentation
2290
+ }, opts);
2291
+ if (result.protocolVersion !== 1) {
2292
+ throw new Error(
2293
+ `wireOpenKeyAuthorize: OpenKey returned unsupported protocolVersion ${result.protocolVersion}`
2294
+ );
2295
+ }
2296
+ if (typeof result.signedMessage !== "string" || !result.signedMessage) {
2297
+ throw new Error(
2298
+ "wireOpenKeyAuthorize: OpenKey returned no signedMessage \u2014 cannot complete a TinyCloud session"
2299
+ );
2300
+ }
2301
+ if (!Array.isArray(result.selectedActionKeys)) {
2302
+ throw new Error(
2303
+ "wireOpenKeyAuthorize: OpenKey returned no selectedActionKeys"
2304
+ );
2305
+ }
2306
+ if (!Array.isArray(result.permissions)) {
2307
+ throw new Error(
2308
+ "wireOpenKeyAuthorize: OpenKey returned no permissions"
2309
+ );
2310
+ }
2311
+ return {
2312
+ protocolVersion: 1,
2313
+ address: result.address,
2314
+ signature: result.signature,
2315
+ signedMessage: result.signedMessage,
2316
+ selectedActionKeys: result.selectedActionKeys,
2317
+ permissions: result.permissions
2318
+ };
2319
+ };
2320
+ }
2321
+
2154
2322
  // src/recordingServiceContext.ts
2155
- var import_sdk_services2 = require("@tinycloud/sdk-services");
2323
+ var import_sdk_services3 = require("@tinycloud/sdk-services");
2156
2324
  var import_requestLog = require("@tinycloud/sdk-services/telemetry/requestLog");
2157
2325
 
2158
2326
  // src/recordingRegistry.ts
@@ -2197,9 +2365,9 @@ function replicationEvent(sample, op) {
2197
2365
  }
2198
2366
  function createServiceContext(config) {
2199
2367
  const { requestSink, ...base } = config;
2200
- return requestSink === void 0 ? new import_sdk_services2.ServiceContext(base) : new RecordingServiceContext(base, requestSink);
2368
+ return requestSink === void 0 ? new import_sdk_services3.ServiceContext(base) : new RecordingServiceContext(base, requestSink);
2201
2369
  }
2202
- var RecordingServiceContext = class extends import_sdk_services2.ServiceContext {
2370
+ var RecordingServiceContext = class extends import_sdk_services3.ServiceContext {
2203
2371
  constructor(config, sink) {
2204
2372
  const pending = {};
2205
2373
  super({
@@ -2792,9 +2960,11 @@ function deserializeDelegation(data) {
2792
2960
 
2793
2961
  // src/TinyCloudNode.ts
2794
2962
  var import_siwe2 = require("siwe");
2963
+ var import_share_envelope = require("@tinycloud/share-envelope");
2795
2964
  var import_sdk_core11 = require("@tinycloud/sdk-core");
2796
- var import_sdk_services3 = require("@tinycloud/sdk-services");
2965
+ var import_sdk_services4 = require("@tinycloud/sdk-services");
2797
2966
  var import_requestLog2 = require("@tinycloud/sdk-services/telemetry/requestLog");
2967
+ var import_resource2 = require("@tinycloud/sdk-services/encryption/resource");
2798
2968
 
2799
2969
  // src/account/AccountService.ts
2800
2970
  var import_sdk_core7 = require("@tinycloud/sdk-core");
@@ -3271,6 +3441,7 @@ var ROOT_DELEGATION_ACTIONS = [
3271
3441
  import_sdk_core11.DUCKDB.ALL
3272
3442
  ];
3273
3443
  var DEFAULT_SESSION_EXPIRATION_MS = import_sdk_core11.EXPIRY.SESSION_MS;
3444
+ var OWNER_REVOCATION_WINDOW_MS = 10 * 6e4;
3274
3445
  function unifiedRootAttenuation(capabilities) {
3275
3446
  const attenuation = {};
3276
3447
  for (const capability of capabilities) {
@@ -3768,6 +3939,7 @@ var _TinyCloudNode = class _TinyCloudNode {
3768
3939
  constructor(config = {}) {
3769
3940
  this.signer = null;
3770
3941
  this.auth = null;
3942
+ this.consumedOpenKeyPendings = /* @__PURE__ */ new WeakSet();
3771
3943
  this.tc = null;
3772
3944
  this._chainId = 1;
3773
3945
  this.replicaPurgeFailedHandlers = /* @__PURE__ */ new Set();
@@ -3802,6 +3974,7 @@ var _TinyCloudNode = class _TinyCloudNode {
3802
3974
  return void 0;
3803
3975
  }
3804
3976
  };
3977
+ this._keysUnavailable = [];
3805
3978
  this._baseVaults = /* @__PURE__ */ new Map();
3806
3979
  this._baseSecrets = /* @__PURE__ */ new Map();
3807
3980
  this._secrets = /* @__PURE__ */ new Map();
@@ -4629,6 +4802,8 @@ var _TinyCloudNode = class _TinyCloudNode {
4629
4802
  this._hooks = void 0;
4630
4803
  this._vault = void 0;
4631
4804
  this._encryption = void 0;
4805
+ this._namedKeyClient = void 0;
4806
+ this._keysUnavailable = [];
4632
4807
  this._baseVaults.clear();
4633
4808
  this._baseSecrets.clear();
4634
4809
  this._secrets.clear();
@@ -4705,6 +4880,37 @@ var _TinyCloudNode = class _TinyCloudNode {
4705
4880
  }
4706
4881
  await this.signInWith(() => this.auth.signInWithOpenKey(authorizeFn, options));
4707
4882
  }
4883
+ /** Prepare the OpenKey request before the approval click. */
4884
+ async prepareOpenKeySignIn() {
4885
+ if (!this.signer || !this.tc || !this.auth) {
4886
+ throw new Error("Cannot prepare OpenKey sign-in in session-only mode. Provide a signer in config.");
4887
+ }
4888
+ await this.signOutInFlight;
4889
+ await this.accountRegistryTail;
4890
+ await this.wasmBindings.ensureInitialized?.();
4891
+ return this.auth.prepareOpenKeySignIn();
4892
+ }
4893
+ /** Start OpenKey authorization synchronously, then complete normal session setup. */
4894
+ signInWithPreparedOpenKey(pending, authorizeFn) {
4895
+ if (!this.signer || !this.tc || !this.auth || this.signOutInFlight) {
4896
+ throw new Error("Prepared OpenKey sign-in is unavailable");
4897
+ }
4898
+ if (this.consumedOpenKeyPendings.has(pending)) {
4899
+ throw new Error("Prepared OpenKey sign-in was already used");
4900
+ }
4901
+ if (Date.now() - pending.createdAt > 5 * 6e4) {
4902
+ throw new Error("Prepared OpenKey sign-in expired; prepare again");
4903
+ }
4904
+ this.consumedOpenKeyPendings.add(pending);
4905
+ const authorization = authorizeFn(pending.request);
4906
+ void authorization.catch(() => void 0);
4907
+ return this.signInWith(() => {
4908
+ if (this._address !== pending.address || this._chainId !== pending.chainId) {
4909
+ throw new Error("OpenKey account changed after sign-in preparation");
4910
+ }
4911
+ return this.auth.completeOpenKeySignIn(pending, authorization);
4912
+ });
4913
+ }
4708
4914
  async signInWith(performSignIn) {
4709
4915
  refuseCaveated(this.auth?.capabilityRequest?.manifests);
4710
4916
  const epoch = this.sessionEpoch;
@@ -4751,6 +4957,8 @@ var _TinyCloudNode = class _TinyCloudNode {
4751
4957
  this._hooks = void 0;
4752
4958
  this._vault = void 0;
4753
4959
  this._encryption = void 0;
4960
+ this._namedKeyClient = void 0;
4961
+ this._keysUnavailable = [];
4754
4962
  this._baseVaults.clear();
4755
4963
  this._baseSecrets.clear();
4756
4964
  this._secrets.clear();
@@ -4803,6 +5011,26 @@ var _TinyCloudNode = class _TinyCloudNode {
4803
5011
  this.assertSessionEpoch(epoch, "Sign-in");
4804
5012
  await this.ensureRequestedEncryptionNetworks();
4805
5013
  this.assertSessionEpoch(epoch, "Sign-in");
5014
+ this._keysUnavailable = this.auth?.unavailableKeyPaths ?? [];
5015
+ if (this._keysUnavailable.length > 0) {
5016
+ this.notificationHandler.warning(`Named keys unavailable: ${this._keysUnavailable.join(", ")}`);
5017
+ }
5018
+ if (this.nodeFeatures.includes("encryption-named-keys-v1")) {
5019
+ for (const manifest of this.auth?.capabilityRequest?.manifests ?? []) {
5020
+ for (const declaration of manifest.keys ?? []) {
5021
+ if ("path" in declaration && declaration.abilities.includes("key.create") && !this._keysUnavailable.includes(declaration.path)) {
5022
+ try {
5023
+ await this.keys.ensureKey(declaration.path);
5024
+ } catch (error) {
5025
+ this._keysUnavailable.push(declaration.path);
5026
+ const code = error && typeof error === "object" && "code" in error && typeof error.code === "string" && /^[A-Z_]+$/.test(error.code) ? error.code : "UNKNOWN";
5027
+ this.notificationHandler.warning(`Named key unavailable: ${declaration.path} (${code})`);
5028
+ }
5029
+ this.assertSessionEpoch(epoch, "Sign-in");
5030
+ }
5031
+ }
5032
+ }
5033
+ }
4806
5034
  if (!bootstrapped && this.config.manifest === void 0 && this.config.capabilityRequest === void 0) {
4807
5035
  await this.ensureOwnedSpaceHostedById(this.ownedSpaceId(import_sdk_core11.SECRETS_SPACE));
4808
5036
  }
@@ -5379,7 +5607,7 @@ var _TinyCloudNode = class _TinyCloudNode {
5379
5607
  if (!this.currentSessionCoversAccountRegistry()) {
5380
5608
  if (!_TinyCloudNode.accountRegistrySkipLogged) {
5381
5609
  _TinyCloudNode.accountRegistrySkipLogged = true;
5382
- import_sdk_services3.tinyCloudDebugLogger.log("registry.skip");
5610
+ import_sdk_services4.tinyCloudDebugLogger.log("registry.skip");
5383
5611
  }
5384
5612
  return;
5385
5613
  }
@@ -5590,7 +5818,8 @@ var _TinyCloudNode = class _TinyCloudNode {
5590
5818
  const networkIds = /* @__PURE__ */ new Set();
5591
5819
  for (const resource of request.resources) {
5592
5820
  if (resource.service === import_sdk_core11.ENCRYPTION_PERMISSION_SERVICE && resource.path.startsWith("urn:tinycloud:encryption:") && resource.actions.includes(DECRYPT_ACTION2)) {
5593
- networkIds.add(resource.path);
5821
+ const parsed = resource.path.includes("/") ? (0, import_resource2.parseEncryptionResource)(resource.path) : { ...(0, import_sdk_core11.parseNetworkId)(resource.path), kind: "network" };
5822
+ if (parsed.kind === "network") networkIds.add(resource.path);
5594
5823
  }
5595
5824
  }
5596
5825
  return [...networkIds];
@@ -6140,6 +6369,8 @@ var _TinyCloudNode = class _TinyCloudNode {
6140
6369
  this._hooks = stagedGraph.hooks;
6141
6370
  this._vault = stagedGraph.vault;
6142
6371
  this._encryption = stagedGraph.encryption;
6372
+ this._namedKeyClient = void 0;
6373
+ this._keysUnavailable = [];
6143
6374
  this._capabilityRegistry = stagedGraph.capabilityRegistry;
6144
6375
  this._keyProvider = stagedGraph.keyProvider;
6145
6376
  this._sharingService = stagedGraph.sharingService;
@@ -6321,6 +6552,7 @@ var _TinyCloudNode = class _TinyCloudNode {
6321
6552
  invoke: graph.invoke,
6322
6553
  invokeAny: graph.invokeAny,
6323
6554
  signOwnerRevocation: this.signer ? (cid) => this.signOwnerRevocation(cid) : void 0,
6555
+ sessionInvocationSigner: this.sessionInvocationSignerFor(input.serviceSession),
6324
6556
  fetch: graph.fetch
6325
6557
  });
6326
6558
  const keyProvider = new WasmKeyProvider({ sessionManager: input.manager });
@@ -6536,6 +6768,31 @@ var _TinyCloudNode = class _TinyCloudNode {
6536
6768
  if (session === void 0) throw new Error("Not signed in. Call signIn() first.");
6537
6769
  return signBytesWithJwk(bytes, session.jwk);
6538
6770
  }
6771
+ /**
6772
+ * Signs proofless invocations with `session`'s own did:key, for the node's
6773
+ * issued-delegation list and revocation by the delegator. The audience is
6774
+ * this Node's DID; each invocation lives 60 seconds.
6775
+ */
6776
+ sessionInvocationSignerFor(session) {
6777
+ return {
6778
+ did: () => session.verificationMethod.split("#", 1)[0],
6779
+ sign: async (attenuation) => {
6780
+ const now = Math.floor(Date.now() / 1e3);
6781
+ const invocation = await (0, import_sdk_core11.signCompactUcanAuthorization)({
6782
+ issuerDid: session.verificationMethod.split("#", 1)[0],
6783
+ audienceDid: await this.fetchNodeId(),
6784
+ attenuation,
6785
+ facts: [{}],
6786
+ proofs: [],
6787
+ notBefore: now,
6788
+ expiresAt: now + 60,
6789
+ nonce: `urn:uuid:${crypto.randomUUID()}`,
6790
+ sign: (bytes) => signBytesWithJwk(bytes, session.jwk)
6791
+ });
6792
+ return invocation.authorization;
6793
+ }
6794
+ };
6795
+ }
6539
6796
  /** Author a proofless Policy/v3 owner root with the authenticated holder key. */
6540
6797
  async createUnifiedOwnerRoot(input) {
6541
6798
  const ownerDid = this.credentialHolderDid;
@@ -7049,7 +7306,7 @@ var _TinyCloudNode = class _TinyCloudNode {
7049
7306
  );
7050
7307
  graph.assertActive();
7051
7308
  if (!response.ok) {
7052
- const permissionHint = response.status === 401 || response.status === 403 ? (0, import_sdk_services3.parsePermissionHint)({
7309
+ const permissionHint = response.status === 401 || response.status === 403 ? (0, import_sdk_services4.parsePermissionHint)({
7053
7310
  service: "tinycloud.encryption",
7054
7311
  path: networkId,
7055
7312
  actions: ["tinycloud.encryption/decrypt"]
@@ -7070,7 +7327,7 @@ var _TinyCloudNode = class _TinyCloudNode {
7070
7327
  graph.assertActive();
7071
7328
  const signed = await this.signRawNetworkAuthorization({
7072
7329
  targetNode: input.targetNode,
7073
- networkId: input.networkId,
7330
+ networkId: input.body.keyPath === void 0 ? input.networkId : `${input.networkId}/${input.body.keyPath}`,
7074
7331
  action: DECRYPT_ACTION2,
7075
7332
  facts: input.facts
7076
7333
  }, { graph, session });
@@ -7243,6 +7500,7 @@ var _TinyCloudNode = class _TinyCloudNode {
7243
7500
  invoke: graph.invoke,
7244
7501
  invokeAny: graph.invokeAny,
7245
7502
  signOwnerRevocation: this.signer ? (cid) => this.signOwnerRevocation(cid) : void 0,
7503
+ sessionInvocationSigner: this.sessionInvocationSignerFor(serviceSession),
7246
7504
  fetch: graph.fetch
7247
7505
  });
7248
7506
  this._spaceService = new import_sdk_core11.SpaceService({
@@ -7493,7 +7751,7 @@ var _TinyCloudNode = class _TinyCloudNode {
7493
7751
  * Sign a wallet (CACAO) revocation of the delegation `cid` for the node's
7494
7752
  * `POST /revoke`, and return its Authorization header value. The node
7495
7753
  * accepts it when the owner delegated `cid` or owns a space its abilities
7496
- * target, whichever key issued it. Valid for 60 seconds. Requires the
7754
+ * target, whichever key issued it. Valid for 10 minutes. Requires the
7497
7755
  * owner wallet signer.
7498
7756
  */
7499
7757
  async signOwnerRevocation(cid) {
@@ -7509,7 +7767,7 @@ var _TinyCloudNode = class _TinyCloudNode {
7509
7767
  chainId: session.chainId,
7510
7768
  nonce: (0, import_siwe2.generateNonce)(),
7511
7769
  issuedAt: issuedAt.toISOString(),
7512
- expirationTime: new Date(issuedAt.getTime() + 6e4).toISOString()
7770
+ expirationTime: new Date(issuedAt.getTime() + OWNER_REVOCATION_WINDOW_MS).toISOString()
7513
7771
  }).prepareMessage();
7514
7772
  const signature = await this.signer.signMessage(siwe);
7515
7773
  return this.wasmBindings.siweToDelegationHeaders({ siwe, signature }).Authorization;
@@ -7736,6 +7994,40 @@ var _TinyCloudNode = class _TinyCloudNode {
7736
7994
  get encryption() {
7737
7995
  return this.getEncryptionService();
7738
7996
  }
7997
+ /** Named keys bound to the current session. */
7998
+ get keys() {
7999
+ return new Proxy({}, {
8000
+ get: (_target, property) => {
8001
+ if (property === "refreshNodeInfo") {
8002
+ return () => {
8003
+ void this._namedKeyClient?.then((client) => client.refreshNodeInfo());
8004
+ };
8005
+ }
8006
+ if (typeof property !== "string" || ![
8007
+ "getPublicKey",
8008
+ "describeKey",
8009
+ "createKey",
8010
+ "blockKey",
8011
+ "unblockKey",
8012
+ "ensureKey",
8013
+ "encrypt",
8014
+ "decrypt",
8015
+ "decryptMany"
8016
+ ].includes(property)) return void 0;
8017
+ return (...args) => {
8018
+ this._namedKeyClient ?? (this._namedKeyClient = import("./encryption-keys.cjs").then(({ createNamedKeyClient }) => createNamedKeyClient(this)));
8019
+ return this._namedKeyClient.then((client) => {
8020
+ const method = client[property];
8021
+ return method.apply(client, args);
8022
+ });
8023
+ };
8024
+ }
8025
+ });
8026
+ }
8027
+ /** Manifest key paths unavailable during the latest `signIn()` attempt. */
8028
+ get keysUnavailable() {
8029
+ return this._keysUnavailable;
8030
+ }
7739
8031
  async getEncryptionNetwork(nameOrNetworkId = this.getDefaultEncryptionNetworkId()) {
7740
8032
  const networkId = nameOrNetworkId.startsWith("urn:tinycloud:encryption:") ? nameOrNetworkId : this.getDefaultEncryptionNetworkId(nameOrNetworkId);
7741
8033
  return this.fetchEncryptionNetworkAt(
@@ -7863,7 +8155,7 @@ var _TinyCloudNode = class _TinyCloudNode {
7863
8155
  );
7864
8156
  if (result.status !== "ok") {
7865
8157
  if (result.status === "permission_required") {
7866
- const hint = (0, import_sdk_services3.parsePermissionHint)(result.hint);
8158
+ const hint = (0, import_sdk_services4.parsePermissionHint)(result.hint);
7867
8159
  if (hint === void 0) return { status: "read_failed" };
7868
8160
  return {
7869
8161
  status: "permission_required",
@@ -7992,7 +8284,7 @@ var _TinyCloudNode = class _TinyCloudNode {
7992
8284
  if (!session || !Array.isArray(permissions) || permissions.length === 0) {
7993
8285
  return false;
7994
8286
  }
7995
- const expanded = this.expandPermissionEntries(permissions);
8287
+ const expanded = (0, import_sdk_core11.expandRuntimePermissionEntries)(permissions, (0, import_sdk_core11.pkhDid)(session.address, session.chainId));
7996
8288
  if (this.sessionCoversPermissionEntries(session, expanded)) {
7997
8289
  return true;
7998
8290
  }
@@ -8012,7 +8304,7 @@ var _TinyCloudNode = class _TinyCloudNode {
8012
8304
  if (!session || !Array.isArray(permissions) || permissions.length === 0) {
8013
8305
  return [];
8014
8306
  }
8015
- const expanded = this.expandPermissionEntries(permissions);
8307
+ const expanded = (0, import_sdk_core11.expandRuntimePermissionEntries)(permissions, (0, import_sdk_core11.pkhDid)(session.address, session.chainId));
8016
8308
  return this.findRuntimeGrantsForPermissionEntries(expanded, session).map(
8017
8309
  (grant) => grant.delegation
8018
8310
  );
@@ -8187,7 +8479,28 @@ var _TinyCloudNode = class _TinyCloudNode {
8187
8479
  throw new import_sdk_core11.SessionExpiredError(sessionExpiry);
8188
8480
  }
8189
8481
  }
8190
- const expanded = this.expandPermissionEntries(permissions);
8482
+ const expanded = (0, import_sdk_core11.expandRuntimePermissionEntries)(permissions, (0, import_sdk_core11.pkhDid)(session.address, session.chainId));
8483
+ const bindsShareKey = expanded.some((entry) => entry.service === import_sdk_core11.ENCRYPTION_PERMISSION_SERVICE && entry.actions.includes("tinycloud.encryption/key.create"));
8484
+ const captured = bindsShareKey ? { ...session, jwk: { ...options?.expectedSessionKey ?? session.jwk } } : session;
8485
+ const assertCapturedKey = bindsShareKey ? () => {
8486
+ const live = session.jwk;
8487
+ const key = captured.jwk;
8488
+ if (this.currentTinyCloudSession() !== session || session.verificationMethod !== captured.verificationMethod || session.delegationCid !== captured.delegationCid || session.spaceId !== captured.spaceId || session.address !== captured.address || session.chainId !== captured.chainId || live?.kty !== key.kty || live?.crv !== key.crv || live?.x !== key.x || live?.y !== key.y || live?.d !== key.d) {
8489
+ throw new Error("Runtime permission request cancelled because the session key changed.");
8490
+ }
8491
+ } : void 0;
8492
+ const sessionKeyDid = bindsShareKey ? this.sessionDid : void 0;
8493
+ if (bindsShareKey) {
8494
+ const key = captured.jwk;
8495
+ if (key.kty !== "OKP" || key.crv !== "Ed25519" || typeof key.x !== "string") {
8496
+ throw new Error("Runtime permission session key does not match its signed identity.");
8497
+ }
8498
+ const keyDid = (0, import_share_envelope.didKeyFromEd25519PublicKey)(base64UrlDecode(key.x));
8499
+ if (!didPrincipalMatches2(keyDid, sessionKeyDid) || !didPrincipalMatches2(keyDid, captured.verificationMethod) || !didPrincipalMatches2(keyDid, new import_siwe2.SiweMessage(session.siwe).uri)) {
8500
+ throw new Error("Runtime permission session key does not match its signed identity.");
8501
+ }
8502
+ assertCapturedKey();
8503
+ }
8191
8504
  if (this.sessionCoversPermissionEntries(session, expanded)) {
8192
8505
  return [];
8193
8506
  }
@@ -8237,13 +8550,13 @@ var _TinyCloudNode = class _TinyCloudNode {
8237
8550
  return this.wasmBindings.prepareSession({
8238
8551
  abilities,
8239
8552
  ...rawForDelegation.length > 0 ? { rawAbilities: this.permissionsToRawAbilities(rawForDelegation) } : {},
8240
- address: this.wasmBindings.ensureEip55(session.address),
8241
- chainId: session.chainId,
8553
+ address: this.wasmBindings.ensureEip55(captured.address),
8554
+ chainId: captured.chainId,
8242
8555
  domain: this.siweDomain,
8243
8556
  issuedAt: now.toISOString(),
8244
8557
  expirationTime: expiresAt.toISOString(),
8245
8558
  spaceId,
8246
- jwk: session.jwk
8559
+ jwk: captured.jwk
8247
8560
  });
8248
8561
  } catch (error) {
8249
8562
  if (rawForDelegation.some((entry) => this.isRawRevocationPermissionEntry(entry)) && /invalid ReCap resource URI/i.test(error instanceof Error ? error.message : String(error))) {
@@ -8255,8 +8568,13 @@ var _TinyCloudNode = class _TinyCloudNode {
8255
8568
  throw error;
8256
8569
  }
8257
8570
  })();
8571
+ if (bindsShareKey && !didPrincipalMatches2(new import_siwe2.SiweMessage(prepared.siwe).uri, sessionKeyDid)) {
8572
+ throw new Error("Prepared runtime permission targets a different session key.");
8573
+ }
8574
+ assertCapturedKey?.();
8258
8575
  const signature = await this.signer.signMessage(prepared.siwe);
8259
8576
  graph.assertActive();
8577
+ assertCapturedKey?.();
8260
8578
  const delegatedSession = this.wasmBindings.completeSessionSetup({
8261
8579
  ...prepared,
8262
8580
  signature
@@ -8285,18 +8603,19 @@ var _TinyCloudNode = class _TinyCloudNode {
8285
8603
  delegatedSession,
8286
8604
  delegatedEntries,
8287
8605
  spaceId,
8288
- session,
8606
+ captured,
8289
8607
  expiresAt,
8290
8608
  { siwe: prepared.siwe, signature }
8291
8609
  );
8292
8610
  graph.assertActive();
8611
+ assertCapturedKey?.();
8293
8612
  this.runtimePermissionGrants.push({
8294
8613
  session: {
8295
8614
  delegationHeader: delegatedSession.delegationHeader,
8296
8615
  delegationCid: delegatedSession.delegationCid,
8297
8616
  spaceId,
8298
- verificationMethod: session.verificationMethod,
8299
- jwk: session.jwk
8617
+ verificationMethod: captured.verificationMethod,
8618
+ jwk: captured.jwk
8300
8619
  },
8301
8620
  delegation,
8302
8621
  operations: this.permissionOperations(delegatedEntries, spaceId),
@@ -8625,7 +8944,19 @@ var _TinyCloudNode = class _TinyCloudNode {
8625
8944
  "delegateTo requires a non-empty permissions array"
8626
8945
  );
8627
8946
  }
8628
- const expandedEntries = this.expandPermissionEntries(permissions);
8947
+ const expandedEntries = (0, import_sdk_core11.expandDelegationPermissionEntries)(permissions);
8948
+ const namedKeyEntries = expandedEntries.filter((entry) => entry.service === import_sdk_core11.ENCRYPTION_PERMISSION_SERVICE && entry.path.includes("/"));
8949
+ if (namedKeyEntries.length > 0) {
8950
+ if (expandedEntries.some((entry) => entry.service === import_sdk_core11.ENCRYPTION_PERMISSION_SERVICE && !entry.path.includes("/"))) {
8951
+ throw new import_sdk_core11.ManifestValidationError("named-key delegation cannot include bare-network permissions");
8952
+ }
8953
+ if (options?.forceWalletSign) {
8954
+ throw new import_sdk_core11.ManifestValidationError("named-key delegation requires a signed session grant");
8955
+ }
8956
+ const granted = this.projectSignedRecapCapabilities(session.siwe);
8957
+ const { subset, missing } = this.signedCapabilitySubset(namedKeyEntries, granted);
8958
+ if (!subset) throw new import_sdk_core11.PermissionNotInManifestError(missing, granted);
8959
+ }
8629
8960
  const now = /* @__PURE__ */ new Date();
8630
8961
  const expiryMs = resolveExpiryMs(options?.expiry);
8631
8962
  const expirationTime = new Date(now.getTime() + expiryMs);
@@ -10041,6 +10372,7 @@ var import_sdk_core23 = require("@tinycloud/sdk-core");
10041
10372
  PermissionNotInManifestError,
10042
10373
  PrefixedKVService,
10043
10374
  ProtocolMismatchError,
10375
+ RuntimeDelegationRejectedError,
10044
10376
  SECRET_NAME_RE,
10045
10377
  SQLAction,
10046
10378
  SQLService,
@@ -10063,6 +10395,7 @@ var import_sdk_core23 = require("@tinycloud/sdk-core");
10063
10395
  VersionCheckError,
10064
10396
  WasmKeyProvider,
10065
10397
  activateCompactRuntimeDelegation,
10398
+ activateValidatedRuntimeDelegation,
10066
10399
  addressStorageKey,
10067
10400
  buildSpaceUri,
10068
10401
  canonicalizeAddress,
@@ -10092,6 +10425,7 @@ var import_sdk_core23 = require("@tinycloud/sdk-core");
10092
10425
  getNodeReplicationLoaders,
10093
10426
  getTinyCloudDebugLogs,
10094
10427
  installTinyCloudDebugGlobals,
10428
+ isAppKeyHostBound,
10095
10429
  isCapabilitySubset,
10096
10430
  isEvmAddress,
10097
10431
  loadManifest,
@@ -10113,6 +10447,7 @@ var import_sdk_core23 = require("@tinycloud/sdk-core");
10113
10447
  resourceCapabilitiesToSpaceAbilitiesMap,
10114
10448
  serializeDelegation,
10115
10449
  tinyCloudDebugLogger,
10116
- validateManifest
10450
+ validateManifest,
10451
+ wireOpenKeyAuthorize
10117
10452
  });
10118
10453
  //# sourceMappingURL=core.cjs.map