@tinycloud/node-sdk 3.8.0-beta.0 → 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-BtKzLDJX.d.ts → TinyCloudNode-DnD-SH6t.d.ts} +27 -4
  2. package/dist/{TinyCloudNode-ggmlnSfI.d.cts → TinyCloudNode-Dy65bxqs.d.cts} +27 -4
  3. package/dist/{authority-sJ6buHMj.d.ts → authority-BQnTX3fI.d.ts} +21 -5
  4. package/dist/{authority-_SYAE-uV.d.cts → authority-Rbye1U4o.d.cts} +21 -5
  5. package/dist/core.cjs +196 -29
  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 +190 -25
  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 +249 -41
  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 +236 -28
  24. package/dist/index.js.map +1 -1
  25. package/dist/replication/authority.cjs +43 -1
  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 +42 -1
  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");
@@ -375,7 +403,7 @@ function augmentSignInEntriesWithReplication(input) {
375
403
  }
376
404
  eligiblePaths.push(path);
377
405
  }
378
- if (eligiblePaths.length === replication.prefixes.length && eligiblePaths.length > 0 && hasUnrestrictedGetCoverage(input.entries, input.primarySpaceId, "")) {
406
+ if (replication.syncScope !== "prefixes" && eligiblePaths.length === replication.prefixes.length && eligiblePaths.length > 0 && hasUnrestrictedGetCoverage(input.entries, input.primarySpaceId, "")) {
379
407
  return [{
380
408
  service: KV_SERVICE,
381
409
  space: input.primarySpaceId,
@@ -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
  }
@@ -4260,7 +4326,8 @@ var _TinyCloudNode = class _TinyCloudNode {
4260
4326
  replication: config.replication?.enabled === true ? {
4261
4327
  enabled: true,
4262
4328
  prefixes: config.replication.prefixes,
4263
- allowSecrets: config.replication.allowSecrets === true
4329
+ allowSecrets: config.replication.allowSecrets === true,
4330
+ syncScope: config.replication.syncScope
4264
4331
  } : void 0
4265
4332
  });
4266
4333
  this.tc = new import_sdk_core11.TinyCloud(this.auth, {
@@ -4520,6 +4587,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4520
4587
  const leavingAuth = this.auth;
4521
4588
  const leavingContext = this._serviceContext;
4522
4589
  let denied;
4590
+ let erased;
4523
4591
  try {
4524
4592
  this.teardownSession();
4525
4593
  } finally {
@@ -4530,6 +4598,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4530
4598
  try {
4531
4599
  await this.accountRegistryTail;
4532
4600
  await this.quiesceReplicaRuntime();
4601
+ if (denied) erased = await this.eraseReplicaLocalCopy("sign_out", denied.status);
4533
4602
  } finally {
4534
4603
  if (leavingAddress) {
4535
4604
  await (leavingAuth ? leavingAuth.clearPersistedSession(leavingAddress) : this.config.sessionStorage?.clear(leavingAddress));
@@ -4537,8 +4606,37 @@ var _TinyCloudNode = class _TinyCloudNode {
4537
4606
  }
4538
4607
  }
4539
4608
  }
4540
- if (denied?.status === "unguarded") {
4541
- 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;
4542
4640
  }
4543
4641
  }
4544
4642
  /**
@@ -4598,6 +4696,8 @@ var _TinyCloudNode = class _TinyCloudNode {
4598
4696
  this._hooks = void 0;
4599
4697
  this._vault = void 0;
4600
4698
  this._encryption = void 0;
4699
+ this._namedKeyClient = void 0;
4700
+ this._keysUnavailable = [];
4601
4701
  this._baseVaults.clear();
4602
4702
  this._baseSecrets.clear();
4603
4703
  this._secrets.clear();
@@ -4702,6 +4802,10 @@ var _TinyCloudNode = class _TinyCloudNode {
4702
4802
  const priorPrincipal = this.replicaSessionPrincipal ?? (this.address ? (0, import_sdk_core11.pkhDid)(this.address, this._chainId) : void 0);
4703
4803
  if (this.config.replication?.enabled && priorPrincipal && priorPrincipal !== nextPrincipal) {
4704
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
+ }
4705
4809
  this.replicaRestoreGate = denial?.status === "unguarded" ? "gate_unavailable" : "stamp_stale";
4706
4810
  }
4707
4811
  if (address !== this._address || chainId !== this._chainId) {
@@ -4716,6 +4820,8 @@ var _TinyCloudNode = class _TinyCloudNode {
4716
4820
  this._hooks = void 0;
4717
4821
  this._vault = void 0;
4718
4822
  this._encryption = void 0;
4823
+ this._namedKeyClient = void 0;
4824
+ this._keysUnavailable = [];
4719
4825
  this._baseVaults.clear();
4720
4826
  this._baseSecrets.clear();
4721
4827
  this._secrets.clear();
@@ -4768,6 +4874,26 @@ var _TinyCloudNode = class _TinyCloudNode {
4768
4874
  this.assertSessionEpoch(epoch, "Sign-in");
4769
4875
  await this.ensureRequestedEncryptionNetworks();
4770
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
+ }
4771
4897
  if (!bootstrapped && this.config.manifest === void 0 && this.config.capabilityRequest === void 0) {
4772
4898
  await this.ensureOwnedSpaceHostedById(this.ownedSpaceId(import_sdk_core11.SECRETS_SPACE));
4773
4899
  }
@@ -5344,7 +5470,7 @@ var _TinyCloudNode = class _TinyCloudNode {
5344
5470
  if (!this.currentSessionCoversAccountRegistry()) {
5345
5471
  if (!_TinyCloudNode.accountRegistrySkipLogged) {
5346
5472
  _TinyCloudNode.accountRegistrySkipLogged = true;
5347
- import_sdk_services3.tinyCloudDebugLogger.log("registry.skip");
5473
+ import_sdk_services4.tinyCloudDebugLogger.log("registry.skip");
5348
5474
  }
5349
5475
  return;
5350
5476
  }
@@ -5555,7 +5681,8 @@ var _TinyCloudNode = class _TinyCloudNode {
5555
5681
  const networkIds = /* @__PURE__ */ new Set();
5556
5682
  for (const resource of request.resources) {
5557
5683
  if (resource.service === import_sdk_core11.ENCRYPTION_PERMISSION_SERVICE && resource.path.startsWith("urn:tinycloud:encryption:") && resource.actions.includes(DECRYPT_ACTION2)) {
5558
- 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);
5559
5686
  }
5560
5687
  }
5561
5688
  return [...networkIds];
@@ -6105,6 +6232,8 @@ var _TinyCloudNode = class _TinyCloudNode {
6105
6232
  this._hooks = stagedGraph.hooks;
6106
6233
  this._vault = stagedGraph.vault;
6107
6234
  this._encryption = stagedGraph.encryption;
6235
+ this._namedKeyClient = void 0;
6236
+ this._keysUnavailable = [];
6108
6237
  this._capabilityRegistry = stagedGraph.capabilityRegistry;
6109
6238
  this._keyProvider = stagedGraph.keyProvider;
6110
6239
  this._sharingService = stagedGraph.sharingService;
@@ -6682,7 +6811,8 @@ var _TinyCloudNode = class _TinyCloudNode {
6682
6811
  replication: this.config.replication?.enabled === true ? {
6683
6812
  enabled: true,
6684
6813
  prefixes: this.config.replication.prefixes,
6685
- allowSecrets: this.config.replication.allowSecrets === true
6814
+ allowSecrets: this.config.replication.allowSecrets === true,
6815
+ syncScope: this.config.replication.syncScope
6686
6816
  } : void 0
6687
6817
  });
6688
6818
  this.tc = new import_sdk_core11.TinyCloud(this.auth, {
@@ -6744,7 +6874,8 @@ var _TinyCloudNode = class _TinyCloudNode {
6744
6874
  replication: this.config.replication?.enabled === true ? {
6745
6875
  enabled: true,
6746
6876
  prefixes: this.config.replication.prefixes,
6747
- allowSecrets: this.config.replication.allowSecrets === true
6877
+ allowSecrets: this.config.replication.allowSecrets === true,
6878
+ syncScope: this.config.replication.syncScope
6748
6879
  } : void 0
6749
6880
  });
6750
6881
  this.tc = new import_sdk_core11.TinyCloud(this.auth, {
@@ -7012,7 +7143,7 @@ var _TinyCloudNode = class _TinyCloudNode {
7012
7143
  );
7013
7144
  graph.assertActive();
7014
7145
  if (!response.ok) {
7015
- 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)({
7016
7147
  service: "tinycloud.encryption",
7017
7148
  path: networkId,
7018
7149
  actions: ["tinycloud.encryption/decrypt"]
@@ -7456,7 +7587,7 @@ var _TinyCloudNode = class _TinyCloudNode {
7456
7587
  * Sign a wallet (CACAO) revocation of the delegation `cid` for the node's
7457
7588
  * `POST /revoke`, and return its Authorization header value. The node
7458
7589
  * accepts it when the owner delegated `cid` or owns a space its abilities
7459
- * target, whichever key issued it. Valid for 60 seconds. Requires the
7590
+ * target, whichever key issued it. Valid for 10 minutes. Requires the
7460
7591
  * owner wallet signer.
7461
7592
  */
7462
7593
  async signOwnerRevocation(cid) {
@@ -7472,7 +7603,7 @@ var _TinyCloudNode = class _TinyCloudNode {
7472
7603
  chainId: session.chainId,
7473
7604
  nonce: (0, import_siwe2.generateNonce)(),
7474
7605
  issuedAt: issuedAt.toISOString(),
7475
- expirationTime: new Date(issuedAt.getTime() + 6e4).toISOString()
7606
+ expirationTime: new Date(issuedAt.getTime() + OWNER_REVOCATION_WINDOW_MS).toISOString()
7476
7607
  }).prepareMessage();
7477
7608
  const signature = await this.signer.signMessage(siwe);
7478
7609
  return this.wasmBindings.siweToDelegationHeaders({ siwe, signature }).Authorization;
@@ -7699,6 +7830,40 @@ var _TinyCloudNode = class _TinyCloudNode {
7699
7830
  get encryption() {
7700
7831
  return this.getEncryptionService();
7701
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
+ }
7702
7867
  async getEncryptionNetwork(nameOrNetworkId = this.getDefaultEncryptionNetworkId()) {
7703
7868
  const networkId = nameOrNetworkId.startsWith("urn:tinycloud:encryption:") ? nameOrNetworkId : this.getDefaultEncryptionNetworkId(nameOrNetworkId);
7704
7869
  return this.fetchEncryptionNetworkAt(
@@ -7826,7 +7991,7 @@ var _TinyCloudNode = class _TinyCloudNode {
7826
7991
  );
7827
7992
  if (result.status !== "ok") {
7828
7993
  if (result.status === "permission_required") {
7829
- const hint = (0, import_sdk_services3.parsePermissionHint)(result.hint);
7994
+ const hint = (0, import_sdk_services4.parsePermissionHint)(result.hint);
7830
7995
  if (hint === void 0) return { status: "read_failed" };
7831
7996
  return {
7832
7997
  status: "permission_required",
@@ -10004,6 +10169,7 @@ var import_sdk_core23 = require("@tinycloud/sdk-core");
10004
10169
  PermissionNotInManifestError,
10005
10170
  PrefixedKVService,
10006
10171
  ProtocolMismatchError,
10172
+ RuntimeDelegationRejectedError,
10007
10173
  SECRET_NAME_RE,
10008
10174
  SQLAction,
10009
10175
  SQLService,
@@ -10026,6 +10192,7 @@ var import_sdk_core23 = require("@tinycloud/sdk-core");
10026
10192
  VersionCheckError,
10027
10193
  WasmKeyProvider,
10028
10194
  activateCompactRuntimeDelegation,
10195
+ activateValidatedRuntimeDelegation,
10029
10196
  addressStorageKey,
10030
10197
  buildSpaceUri,
10031
10198
  canonicalizeAddress,