@tinycloud/node-sdk 3.8.0-beta.1 → 3.8.0-beta.10

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 (39) hide show
  1. package/dist/{TinyCloudNode-Ckqu_fqH.d.ts → TinyCloudNode-DnD-SH6t.d.ts} +26 -3
  2. package/dist/{TinyCloudNode-DDfx8Fx5.d.cts → TinyCloudNode-Dy65bxqs.d.cts} +26 -3
  3. package/dist/{authority-DcyJpajL.d.ts → authority-BQnTX3fI.d.ts} +20 -4
  4. package/dist/{authority-BwM_s3Ya.d.cts → authority-Rbye1U4o.d.cts} +20 -4
  5. package/dist/core.cjs +189 -25
  6. package/dist/core.cjs.map +1 -1
  7. package/dist/core.d.cts +5 -4
  8. package/dist/core.d.ts +5 -4
  9. package/dist/core.js +183 -21
  10. package/dist/core.js.map +1 -1
  11. package/dist/credentials-core.d.cts +1 -1
  12. package/dist/credentials-core.d.ts +1 -1
  13. package/dist/credentials.d.cts +1 -1
  14. package/dist/credentials.d.ts +1 -1
  15. package/dist/{delegation-0LKZkmP2.d.cts → delegation-Ckc3UETl.d.cts} +1 -1
  16. package/dist/{delegation-0LKZkmP2.d.ts → delegation-Ckc3UETl.d.ts} +1 -1
  17. package/dist/encryption-keys.d.cts +2 -2
  18. package/dist/encryption-keys.d.ts +2 -2
  19. package/dist/index.cjs +239 -37
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +4 -3
  22. package/dist/index.d.ts +4 -3
  23. package/dist/index.js +226 -24
  24. package/dist/index.js.map +1 -1
  25. package/dist/replication/authority.cjs +39 -0
  26. package/dist/replication/authority.cjs.map +1 -1
  27. package/dist/replication/authority.d.cts +4 -3
  28. package/dist/replication/authority.d.ts +4 -3
  29. package/dist/replication/authority.js +38 -0
  30. package/dist/replication/authority.js.map +1 -1
  31. package/dist/replication/runtime.cjs +1 -0
  32. package/dist/replication/runtime.cjs.map +1 -1
  33. package/dist/replication/runtime.d.cts +1 -1
  34. package/dist/replication/runtime.d.ts +1 -1
  35. package/dist/replication/runtime.js +1 -0
  36. package/dist/replication/runtime.js.map +1 -1
  37. package/dist/{runtime-v62FzQHz.d.ts → runtime-CO4VXnPq.d.ts} +2 -1
  38. package/dist/{runtime-D1o9Eybt.d.cts → runtime-mRhSu-vJ.d.cts} +2 -1
  39. package/package.json +4 -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,
@@ -345,6 +347,32 @@ var FileSessionStorage = class {
345
347
  var import_node_crypto = require("crypto");
346
348
  var import_siwe = require("siwe");
347
349
  var import_sdk_core5 = require("@tinycloud/sdk-core");
350
+ var import_resource = require("@tinycloud/sdk-services/encryption/resource");
351
+ var import_sdk_services2 = require("@tinycloud/sdk-services");
352
+
353
+ // src/authorization/appKeyHostBinding.ts
354
+ var DOMAIN_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
355
+ var IPV4 = /^\d+\.\d+\.\d+\.\d+$/;
356
+ var APP_ID = /^[a-z0-9][a-z0-9._-]{0,63}$/;
357
+ function appKeyHostBinding(domain, appId) {
358
+ if (!APP_ID.test(appId)) return false;
359
+ if (domain.endsWith(":")) return false;
360
+ let url;
361
+ try {
362
+ url = new URL(`https://${domain}`);
363
+ } catch {
364
+ return false;
365
+ }
366
+ if (url.protocol !== "https:" || url.port !== "" || url.username !== "" || url.password !== "") return false;
367
+ if (url.pathname !== "/" || url.search !== "" || url.hash !== "") return false;
368
+ const host = url.hostname;
369
+ if (host.startsWith("[") || host.endsWith(".") || IPV4.test(host)) return false;
370
+ const labels = host.split(".");
371
+ if (labels.length < 2 || labels[labels.length - 1] === "localhost") return false;
372
+ if (!labels.every((label) => DOMAIN_LABEL.test(label))) return false;
373
+ const reversed = labels.reverse().join(".");
374
+ return appId === reversed || appId.startsWith(`${reversed}.`);
375
+ }
348
376
 
349
377
  // src/replication/authority-sign-in.ts
350
378
  var import_sdk_core2 = require("@tinycloud/sdk-core");
@@ -832,6 +860,23 @@ var NodeUserAuthorization = class {
832
860
  );
833
861
  return this._capabilityRequest;
834
862
  }
863
+ async refreshNamedKeyFeatureForSignIn(address, chainId) {
864
+ const request = this.getCapabilityRequest();
865
+ const siweDomain = this.siweConfig?.domain ?? this.domain;
866
+ const hasNamedKeys = request?.manifests.some((manifest) => manifest.keys?.length && appKeyHostBinding(siweDomain, manifest.keys_app_id ?? manifest.app_id));
867
+ if (hasNamedKeys) {
868
+ await this.resolveTinyCloudHostsForSignIn(address, chainId);
869
+ await this.refreshNodeInfo();
870
+ }
871
+ }
872
+ /** Manifest key paths that this sign-in cannot grant. */
873
+ get unavailableKeyPaths() {
874
+ const request = this.getCapabilityRequest();
875
+ const siweDomain = this.siweConfig?.domain ?? this.domain;
876
+ return (request?.manifests ?? []).flatMap(
877
+ (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)
878
+ );
879
+ }
835
880
  resolveSpaceName(space, address, chainId) {
836
881
  if (space.startsWith("tinycloud:")) {
837
882
  return space;
@@ -1023,15 +1068,31 @@ var NodeUserAuthorization = class {
1023
1068
  }
1024
1069
  const rawAbilities = {};
1025
1070
  const currentDid = (0, import_sdk_core5.pkhDid)(address, chainId);
1071
+ if (this._nodeFeatures.includes("encryption-named-keys-v1")) {
1072
+ const siweDomain = this.siweConfig?.domain ?? this.domain;
1073
+ for (const manifest of request.manifests) {
1074
+ if (!appKeyHostBinding(siweDomain, manifest.keys_app_id ?? manifest.app_id)) continue;
1075
+ for (const declaration of manifest.keys ?? []) {
1076
+ const path = "path" in declaration ? declaration.path : declaration.prefix;
1077
+ const resource = `urn:tinycloud:encryption:${currentDid}:default/${path}`;
1078
+ for (const ability of declaration.abilities) {
1079
+ addRawAbility(rawAbilities, resource, `tinycloud.encryption/${ability}`);
1080
+ }
1081
+ }
1082
+ }
1083
+ }
1026
1084
  const spaceResources = request.resources.filter((entry) => {
1027
1085
  if (entry.service !== import_sdk_core5.ENCRYPTION_PERMISSION_SERVICE) {
1028
1086
  return true;
1029
1087
  }
1088
+ const parsed = entry.path.includes("/") ? (0, import_resource.parseEncryptionResource)(entry.path) : { ...(0, import_sdk_services2.parseNetworkId)(entry.path), kind: "network" };
1089
+ if (parsed.kind !== "network") {
1090
+ throw new import_sdk_core5.ManifestValidationError("named keys go in manifest.keys");
1091
+ }
1030
1092
  for (const action of entry.actions) {
1031
1093
  addRawAbility(rawAbilities, entry.path, action);
1032
1094
  }
1033
- if (entry.actions.includes(DECRYPT_ACTION)) {
1034
- const parsed = (0, import_sdk_core5.parseNetworkId)(entry.path);
1095
+ if (entry.actions.includes(DECRYPT_ACTION) && parsed.kind === "network") {
1035
1096
  if (didPrincipalMatches(parsed.ownerDid, currentDid)) {
1036
1097
  addRawAbility(rawAbilities, entry.path, NETWORK_CREATE_ACTION);
1037
1098
  }
@@ -1427,6 +1488,8 @@ var NodeUserAuthorization = class {
1427
1488
  this._chainId = chainId;
1428
1489
  await this.resolveTinyCloudHostsForSignIn(address, chainId);
1429
1490
  this.assertSignInCurrent(generation);
1491
+ await this.refreshNamedKeyFeatureForSignIn(address, chainId);
1492
+ this.assertSignInCurrent(generation);
1430
1493
  const keyId = `session-${Date.now()}`;
1431
1494
  this.sessionManager.renameSessionKeyId(this.activeSessionKeyId, keyId);
1432
1495
  this.activeSessionKeyId = keyId;
@@ -1573,6 +1636,7 @@ var NodeUserAuthorization = class {
1573
1636
  refuseCaveated(this.getCapabilityRequest()?.manifests);
1574
1637
  const address = (0, import_sdk_core5.canonicalizeAddress)(await this.signer.getAddress());
1575
1638
  const chainId = await this.signer.getChainId();
1639
+ await this.refreshNamedKeyFeatureForSignIn(address, chainId);
1576
1640
  const keyId = `session-${Date.now()}`;
1577
1641
  this.sessionManager.renameSessionKeyId(this.activeSessionKeyId, keyId);
1578
1642
  this.activeSessionKeyId = keyId;
@@ -2152,7 +2216,7 @@ var NodeUserAuthorization = class {
2152
2216
  };
2153
2217
 
2154
2218
  // src/recordingServiceContext.ts
2155
- var import_sdk_services2 = require("@tinycloud/sdk-services");
2219
+ var import_sdk_services3 = require("@tinycloud/sdk-services");
2156
2220
  var import_requestLog = require("@tinycloud/sdk-services/telemetry/requestLog");
2157
2221
 
2158
2222
  // src/recordingRegistry.ts
@@ -2197,9 +2261,9 @@ function replicationEvent(sample, op) {
2197
2261
  }
2198
2262
  function createServiceContext(config) {
2199
2263
  const { requestSink, ...base } = config;
2200
- return requestSink === void 0 ? new import_sdk_services2.ServiceContext(base) : new RecordingServiceContext(base, requestSink);
2264
+ return requestSink === void 0 ? new import_sdk_services3.ServiceContext(base) : new RecordingServiceContext(base, requestSink);
2201
2265
  }
2202
- var RecordingServiceContext = class extends import_sdk_services2.ServiceContext {
2266
+ var RecordingServiceContext = class extends import_sdk_services3.ServiceContext {
2203
2267
  constructor(config, sink) {
2204
2268
  const pending = {};
2205
2269
  super({
@@ -2793,8 +2857,9 @@ function deserializeDelegation(data) {
2793
2857
  // src/TinyCloudNode.ts
2794
2858
  var import_siwe2 = require("siwe");
2795
2859
  var import_sdk_core11 = require("@tinycloud/sdk-core");
2796
- var import_sdk_services3 = require("@tinycloud/sdk-services");
2860
+ var import_sdk_services4 = require("@tinycloud/sdk-services");
2797
2861
  var import_requestLog2 = require("@tinycloud/sdk-services/telemetry/requestLog");
2862
+ var import_resource2 = require("@tinycloud/sdk-services/encryption/resource");
2798
2863
 
2799
2864
  // src/account/AccountService.ts
2800
2865
  var import_sdk_core7 = require("@tinycloud/sdk-core");
@@ -3271,6 +3336,7 @@ var ROOT_DELEGATION_ACTIONS = [
3271
3336
  import_sdk_core11.DUCKDB.ALL
3272
3337
  ];
3273
3338
  var DEFAULT_SESSION_EXPIRATION_MS = import_sdk_core11.EXPIRY.SESSION_MS;
3339
+ var OWNER_REVOCATION_WINDOW_MS = 10 * 6e4;
3274
3340
  function unifiedRootAttenuation(capabilities) {
3275
3341
  const attenuation = {};
3276
3342
  for (const capability of capabilities) {
@@ -3735,6 +3801,7 @@ function replicaRestoreRefusal(error) {
3735
3801
  case "purge_pending":
3736
3802
  case "logout_pending":
3737
3803
  case "gate_unavailable":
3804
+ case "wallet_provider_required":
3738
3805
  return error.gate;
3739
3806
  }
3740
3807
  }
@@ -3769,6 +3836,7 @@ var _TinyCloudNode = class _TinyCloudNode {
3769
3836
  this.auth = null;
3770
3837
  this.tc = null;
3771
3838
  this._chainId = 1;
3839
+ this.replicaPurgeFailedHandlers = /* @__PURE__ */ new Set();
3772
3840
  /** Mints the replica session stamp on each fresh sign-in (TC-858 §4.1). Undefined when replication is off. */
3773
3841
  this.establishReplicaStamp = async (session) => {
3774
3842
  const replication = this.config.replication;
@@ -3800,6 +3868,7 @@ var _TinyCloudNode = class _TinyCloudNode {
3800
3868
  return void 0;
3801
3869
  }
3802
3870
  };
3871
+ this._keysUnavailable = [];
3803
3872
  this._baseVaults = /* @__PURE__ */ new Map();
3804
3873
  this._baseSecrets = /* @__PURE__ */ new Map();
3805
3874
  this._secrets = /* @__PURE__ */ new Map();
@@ -3975,6 +4044,7 @@ var _TinyCloudNode = class _TinyCloudNode {
3975
4044
  return instance ? instance.control.purge(input) : runtime.then((ready) => ready.control.purge(input));
3976
4045
  },
3977
4046
  clearPending: async () => (await runtime).control.clearPending(),
4047
+ purgeLocalCopy: async () => (await this.eraseReplicaLocalCopy("purge_local_copy"))?.purge ?? { ok: false, retryNow: false },
3978
4048
  close: async () => (await runtime).control.close()
3979
4049
  };
3980
4050
  }
@@ -4029,18 +4099,14 @@ var _TinyCloudNode = class _TinyCloudNode {
4029
4099
  get freshEncryptionNetworkCreated() {
4030
4100
  return this._freshEncryptionNetworkCreated;
4031
4101
  }
4032
- /**
4033
- * Subscribe to `storage.full`. It fires on the first write any service had
4034
- * rejected because the owner's TinyCloud storage is full
4035
- * (`STORAGE_QUOTA_EXCEEDED` or `STORAGE_LIMIT_REACHED`), so an app can show
4036
- * one read-only banner. Reads keep working. For the signed-in account's own
4037
- * storage it fires once per episode: the episode ends only when a write to
4038
- * that storage succeeds, or on sign-out. A `storage.status()` read does not
4039
- * end it. A rejection on another owner's delegated space fires once per
4040
- * owner, until that owner's `storage.status()` reports space again. Returns
4041
- * the unsubscribe function.
4042
- */
4043
4102
  on(event, handler) {
4103
+ if (event === "replica.purge.failed") {
4104
+ const listener = handler;
4105
+ this.replicaPurgeFailedHandlers.add(listener);
4106
+ return () => {
4107
+ this.replicaPurgeFailedHandlers.delete(listener);
4108
+ };
4109
+ }
4044
4110
  if (event !== "storage.full") {
4045
4111
  throw new Error(`Unknown TinyCloud event: ${String(event)}`);
4046
4112
  }
@@ -4521,6 +4587,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4521
4587
  const leavingAuth = this.auth;
4522
4588
  const leavingContext = this._serviceContext;
4523
4589
  let denied;
4590
+ let erased;
4524
4591
  try {
4525
4592
  this.teardownSession();
4526
4593
  } finally {
@@ -4531,6 +4598,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4531
4598
  try {
4532
4599
  await this.accountRegistryTail;
4533
4600
  await this.quiesceReplicaRuntime();
4601
+ if (denied) erased = await this.eraseReplicaLocalCopy("sign_out", denied.status);
4534
4602
  } finally {
4535
4603
  if (leavingAddress) {
4536
4604
  await (leavingAuth ? leavingAuth.clearPersistedSession(leavingAddress) : this.config.sessionStorage?.clear(leavingAddress));
@@ -4538,8 +4606,37 @@ var _TinyCloudNode = class _TinyCloudNode {
4538
4606
  }
4539
4607
  }
4540
4608
  }
4541
- if (denied?.status === "unguarded") {
4542
- throw new Error(`Replica gate was not denied (${denied.code ?? "unknown"}); the replica may still be restorable.`);
4609
+ if (denied?.status === "unguarded" && erased?.outcome.status !== "erased") {
4610
+ throw Object.assign(
4611
+ new Error(`Replica gate was not denied (${denied.code ?? "unknown"}); the replica may still be restorable.`),
4612
+ {
4613
+ code: "REPLICA_PURGE_FAILED",
4614
+ reason: "gate_unavailable",
4615
+ retryNow: erased?.outcome.status === "failed"
4616
+ }
4617
+ );
4618
+ }
4619
+ }
4620
+ /**
4621
+ * Erases the browser local copy after a deny (retry, event and next-load marker live in the lazy
4622
+ * replication authority). Never rejects; `undefined` when replication is off or the storage keeps
4623
+ * no browser database.
4624
+ */
4625
+ async eraseReplicaLocalCopy(trigger, denyStatus) {
4626
+ const replication = this.config.replication;
4627
+ if (!replication?.enabled) return void 0;
4628
+ try {
4629
+ const { eraseReplicaLocalCopy } = await getNodeReplicationLoaders().authority();
4630
+ const erased = await eraseReplicaLocalCopy(
4631
+ replication.storage.lifecycle,
4632
+ trigger,
4633
+ denyStatus,
4634
+ this.replicaPurgeFailedHandlers
4635
+ );
4636
+ if (erased?.logoutPending) this.replicaRestoreGate = "logout_pending";
4637
+ return erased;
4638
+ } catch {
4639
+ return void 0;
4543
4640
  }
4544
4641
  }
4545
4642
  /**
@@ -4599,6 +4696,8 @@ var _TinyCloudNode = class _TinyCloudNode {
4599
4696
  this._hooks = void 0;
4600
4697
  this._vault = void 0;
4601
4698
  this._encryption = void 0;
4699
+ this._namedKeyClient = void 0;
4700
+ this._keysUnavailable = [];
4602
4701
  this._baseVaults.clear();
4603
4702
  this._baseSecrets.clear();
4604
4703
  this._secrets.clear();
@@ -4703,6 +4802,10 @@ var _TinyCloudNode = class _TinyCloudNode {
4703
4802
  const priorPrincipal = this.replicaSessionPrincipal ?? (this.address ? (0, import_sdk_core11.pkhDid)(this.address, this._chainId) : void 0);
4704
4803
  if (this.config.replication?.enabled && priorPrincipal && priorPrincipal !== nextPrincipal) {
4705
4804
  const denial = await this.denyReplicaGate({ reason: "account_switch", principal: priorPrincipal, next: nextPrincipal });
4805
+ if (denial) {
4806
+ await this.quiesceReplicaRuntime();
4807
+ await this.eraseReplicaLocalCopy("account_switch", denial.status);
4808
+ }
4706
4809
  this.replicaRestoreGate = denial?.status === "unguarded" ? "gate_unavailable" : "stamp_stale";
4707
4810
  }
4708
4811
  if (address !== this._address || chainId !== this._chainId) {
@@ -4717,6 +4820,8 @@ var _TinyCloudNode = class _TinyCloudNode {
4717
4820
  this._hooks = void 0;
4718
4821
  this._vault = void 0;
4719
4822
  this._encryption = void 0;
4823
+ this._namedKeyClient = void 0;
4824
+ this._keysUnavailable = [];
4720
4825
  this._baseVaults.clear();
4721
4826
  this._baseSecrets.clear();
4722
4827
  this._secrets.clear();
@@ -4769,6 +4874,26 @@ var _TinyCloudNode = class _TinyCloudNode {
4769
4874
  this.assertSessionEpoch(epoch, "Sign-in");
4770
4875
  await this.ensureRequestedEncryptionNetworks();
4771
4876
  this.assertSessionEpoch(epoch, "Sign-in");
4877
+ this._keysUnavailable = this.auth?.unavailableKeyPaths ?? [];
4878
+ if (this._keysUnavailable.length > 0) {
4879
+ this.notificationHandler.warning(`Named keys unavailable: ${this._keysUnavailable.join(", ")}`);
4880
+ }
4881
+ if (this.nodeFeatures.includes("encryption-named-keys-v1")) {
4882
+ for (const manifest of this.auth?.capabilityRequest?.manifests ?? []) {
4883
+ for (const declaration of manifest.keys ?? []) {
4884
+ if ("path" in declaration && declaration.abilities.includes("key.create") && !this._keysUnavailable.includes(declaration.path)) {
4885
+ try {
4886
+ await this.keys.ensureKey(declaration.path);
4887
+ } catch (error) {
4888
+ this._keysUnavailable.push(declaration.path);
4889
+ const code = error && typeof error === "object" && "code" in error && typeof error.code === "string" && /^[A-Z_]+$/.test(error.code) ? error.code : "UNKNOWN";
4890
+ this.notificationHandler.warning(`Named key unavailable: ${declaration.path} (${code})`);
4891
+ }
4892
+ this.assertSessionEpoch(epoch, "Sign-in");
4893
+ }
4894
+ }
4895
+ }
4896
+ }
4772
4897
  if (!bootstrapped && this.config.manifest === void 0 && this.config.capabilityRequest === void 0) {
4773
4898
  await this.ensureOwnedSpaceHostedById(this.ownedSpaceId(import_sdk_core11.SECRETS_SPACE));
4774
4899
  }
@@ -5345,7 +5470,7 @@ var _TinyCloudNode = class _TinyCloudNode {
5345
5470
  if (!this.currentSessionCoversAccountRegistry()) {
5346
5471
  if (!_TinyCloudNode.accountRegistrySkipLogged) {
5347
5472
  _TinyCloudNode.accountRegistrySkipLogged = true;
5348
- import_sdk_services3.tinyCloudDebugLogger.log("registry.skip");
5473
+ import_sdk_services4.tinyCloudDebugLogger.log("registry.skip");
5349
5474
  }
5350
5475
  return;
5351
5476
  }
@@ -5556,7 +5681,8 @@ var _TinyCloudNode = class _TinyCloudNode {
5556
5681
  const networkIds = /* @__PURE__ */ new Set();
5557
5682
  for (const resource of request.resources) {
5558
5683
  if (resource.service === import_sdk_core11.ENCRYPTION_PERMISSION_SERVICE && resource.path.startsWith("urn:tinycloud:encryption:") && resource.actions.includes(DECRYPT_ACTION2)) {
5559
- networkIds.add(resource.path);
5684
+ const parsed = resource.path.includes("/") ? (0, import_resource2.parseEncryptionResource)(resource.path) : { ...(0, import_sdk_core11.parseNetworkId)(resource.path), kind: "network" };
5685
+ if (parsed.kind === "network") networkIds.add(resource.path);
5560
5686
  }
5561
5687
  }
5562
5688
  return [...networkIds];
@@ -6106,6 +6232,8 @@ var _TinyCloudNode = class _TinyCloudNode {
6106
6232
  this._hooks = stagedGraph.hooks;
6107
6233
  this._vault = stagedGraph.vault;
6108
6234
  this._encryption = stagedGraph.encryption;
6235
+ this._namedKeyClient = void 0;
6236
+ this._keysUnavailable = [];
6109
6237
  this._capabilityRegistry = stagedGraph.capabilityRegistry;
6110
6238
  this._keyProvider = stagedGraph.keyProvider;
6111
6239
  this._sharingService = stagedGraph.sharingService;
@@ -7015,7 +7143,7 @@ var _TinyCloudNode = class _TinyCloudNode {
7015
7143
  );
7016
7144
  graph.assertActive();
7017
7145
  if (!response.ok) {
7018
- const permissionHint = response.status === 401 || response.status === 403 ? (0, import_sdk_services3.parsePermissionHint)({
7146
+ const permissionHint = response.status === 401 || response.status === 403 ? (0, import_sdk_services4.parsePermissionHint)({
7019
7147
  service: "tinycloud.encryption",
7020
7148
  path: networkId,
7021
7149
  actions: ["tinycloud.encryption/decrypt"]
@@ -7459,7 +7587,7 @@ var _TinyCloudNode = class _TinyCloudNode {
7459
7587
  * Sign a wallet (CACAO) revocation of the delegation `cid` for the node's
7460
7588
  * `POST /revoke`, and return its Authorization header value. The node
7461
7589
  * accepts it when the owner delegated `cid` or owns a space its abilities
7462
- * target, whichever key issued it. Valid for 60 seconds. Requires the
7590
+ * target, whichever key issued it. Valid for 10 minutes. Requires the
7463
7591
  * owner wallet signer.
7464
7592
  */
7465
7593
  async signOwnerRevocation(cid) {
@@ -7475,7 +7603,7 @@ var _TinyCloudNode = class _TinyCloudNode {
7475
7603
  chainId: session.chainId,
7476
7604
  nonce: (0, import_siwe2.generateNonce)(),
7477
7605
  issuedAt: issuedAt.toISOString(),
7478
- expirationTime: new Date(issuedAt.getTime() + 6e4).toISOString()
7606
+ expirationTime: new Date(issuedAt.getTime() + OWNER_REVOCATION_WINDOW_MS).toISOString()
7479
7607
  }).prepareMessage();
7480
7608
  const signature = await this.signer.signMessage(siwe);
7481
7609
  return this.wasmBindings.siweToDelegationHeaders({ siwe, signature }).Authorization;
@@ -7702,6 +7830,40 @@ var _TinyCloudNode = class _TinyCloudNode {
7702
7830
  get encryption() {
7703
7831
  return this.getEncryptionService();
7704
7832
  }
7833
+ /** Named keys bound to the current session. */
7834
+ get keys() {
7835
+ return new Proxy({}, {
7836
+ get: (_target, property) => {
7837
+ if (property === "refreshNodeInfo") {
7838
+ return () => {
7839
+ void this._namedKeyClient?.then((client) => client.refreshNodeInfo());
7840
+ };
7841
+ }
7842
+ if (typeof property !== "string" || ![
7843
+ "getPublicKey",
7844
+ "describeKey",
7845
+ "createKey",
7846
+ "blockKey",
7847
+ "unblockKey",
7848
+ "ensureKey",
7849
+ "encrypt",
7850
+ "decrypt",
7851
+ "decryptMany"
7852
+ ].includes(property)) return void 0;
7853
+ return (...args) => {
7854
+ this._namedKeyClient ?? (this._namedKeyClient = import("./encryption-keys.cjs").then(({ createNamedKeyClient }) => createNamedKeyClient(this)));
7855
+ return this._namedKeyClient.then((client) => {
7856
+ const method = client[property];
7857
+ return method.apply(client, args);
7858
+ });
7859
+ };
7860
+ }
7861
+ });
7862
+ }
7863
+ /** Manifest key paths unavailable during the latest `signIn()` attempt. */
7864
+ get keysUnavailable() {
7865
+ return this._keysUnavailable;
7866
+ }
7705
7867
  async getEncryptionNetwork(nameOrNetworkId = this.getDefaultEncryptionNetworkId()) {
7706
7868
  const networkId = nameOrNetworkId.startsWith("urn:tinycloud:encryption:") ? nameOrNetworkId : this.getDefaultEncryptionNetworkId(nameOrNetworkId);
7707
7869
  return this.fetchEncryptionNetworkAt(
@@ -7829,7 +7991,7 @@ var _TinyCloudNode = class _TinyCloudNode {
7829
7991
  );
7830
7992
  if (result.status !== "ok") {
7831
7993
  if (result.status === "permission_required") {
7832
- const hint = (0, import_sdk_services3.parsePermissionHint)(result.hint);
7994
+ const hint = (0, import_sdk_services4.parsePermissionHint)(result.hint);
7833
7995
  if (hint === void 0) return { status: "read_failed" };
7834
7996
  return {
7835
7997
  status: "permission_required",
@@ -10007,6 +10169,7 @@ var import_sdk_core23 = require("@tinycloud/sdk-core");
10007
10169
  PermissionNotInManifestError,
10008
10170
  PrefixedKVService,
10009
10171
  ProtocolMismatchError,
10172
+ RuntimeDelegationRejectedError,
10010
10173
  SECRET_NAME_RE,
10011
10174
  SQLAction,
10012
10175
  SQLService,
@@ -10029,6 +10192,7 @@ var import_sdk_core23 = require("@tinycloud/sdk-core");
10029
10192
  VersionCheckError,
10030
10193
  WasmKeyProvider,
10031
10194
  activateCompactRuntimeDelegation,
10195
+ activateValidatedRuntimeDelegation,
10032
10196
  addressStorageKey,
10033
10197
  buildSpaceUri,
10034
10198
  canonicalizeAddress,