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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -18243,24 +18243,17 @@ function signedAuthorityFromCompactUcan(authorization) {
18243
18243
  ));
18244
18244
  continue;
18245
18245
  }
18246
- const nativeMatch = /^(tinycloud:[^/]+)\/(kv|sql|duckdb|hooks)\/(.*)$/.exec(resource);
18247
- const policyMatch = /^tinycloud:\/\/([^/]+)\/(kv|sql|duckdb|hooks)\/(.*)$/.exec(resource);
18248
- const match = nativeMatch ?? policyMatch;
18246
+ const match = /^(?:tinycloud:\/\/([^/]+)|(tinycloud:[^/]+))\/(kv|sql|duckdb|hooks)(?:\/|$)(.*)$/.exec(resource);
18249
18247
  if (!match) {
18250
18248
  throw new Error(
18251
18249
  `Validated runtime delegation has an unsupported signed resource '${resource}'.`
18252
18250
  );
18253
18251
  }
18254
- const [, space, shortService, path] = match;
18252
+ const [, policySpace, nativeSpace, shortService, path] = match;
18255
18253
  const service = SERVICE_BY_SHORT_NAME[shortService];
18256
- if (service === void 0) {
18257
- throw new Error(
18258
- `Validated runtime delegation has an unsupported signed service '${shortService}'.`
18259
- );
18260
- }
18261
18254
  permissions.push(...groupedSignedEntries(
18262
18255
  service,
18263
- space,
18256
+ nativeSpace ?? policySpace,
18264
18257
  path,
18265
18258
  abilities
18266
18259
  ));
@@ -18835,6 +18828,11 @@ var NodeUserAuthorization = class {
18835
18828
  this.tinycloudHosts = config.tinycloudHosts;
18836
18829
  this.tinycloudRegistryUrl = config.tinycloudRegistryUrl;
18837
18830
  this.tinycloudFallbackHosts = config.tinycloudFallbackHosts;
18831
+ this.registryPolicy = config.registryPolicy;
18832
+ this.newUserHost = config.newUserHost;
18833
+ this.registryChainId = config.registryChainId;
18834
+ this.registrySequenceStore = config.registrySequenceStore ?? (0, import_sdk_core4.createInMemoryRegistrySequenceStore)();
18835
+ this.onNodeNotFound = config.onNodeNotFound;
18838
18836
  this.autoDiscoverLocalNode = config.autoDiscoverLocalNode;
18839
18837
  this.localNodeUrl = config.localNodeUrl;
18840
18838
  this.localLinkName = config.localLinkName;
@@ -18946,6 +18944,7 @@ var NodeUserAuthorization = class {
18946
18944
  this._nodeFeaturesKnown = nodeFeatures !== void 0;
18947
18945
  this._nodeInfoIdentity = void 0;
18948
18946
  this.tinycloudHosts = hosts && hosts.length > 0 ? [...hosts] : void 0;
18947
+ this.registryVerdict = void 0;
18949
18948
  this._lastActivationSkippedSpaceIds = [];
18950
18949
  this._lastActivationActivatedSpaceIds = [];
18951
18950
  if (!session) {
@@ -18996,6 +18995,23 @@ var NodeUserAuthorization = class {
18996
18995
  if (this.tinycloudHosts && this.tinycloudHosts.length > 0) {
18997
18996
  return;
18998
18997
  }
18998
+ if (this.registryPolicy === "fail-closed") {
18999
+ const resolved2 = await (0, import_sdk_core4.resolveFailClosedTinyCloudHosts)(address, {
19000
+ registryUrl: this.tinycloudRegistryUrl,
19001
+ registryChainId: this.registryChainId,
19002
+ newUserHost: this.newUserHost,
19003
+ sequenceStore: this.registrySequenceStore,
19004
+ onNodeNotFound: this.onNodeNotFound,
19005
+ autoDiscoverLocalNode: this.autoDiscoverLocalNode,
19006
+ localNodeUrl: this.localNodeUrl,
19007
+ localLinkName: this.localLinkName,
19008
+ expectedNodeDid: this.expectedNodeDid,
19009
+ localNodeIdentityStore: this.localNodeIdentityStore
19010
+ });
19011
+ this.tinycloudHosts = resolved2.hosts;
19012
+ this.registryVerdict = resolved2.verdict;
19013
+ return;
19014
+ }
18999
19015
  const subject = (0, import_sdk_core4.pkhDid)(address, chainId);
19000
19016
  const resolved = await (0, import_sdk_core4.resolveTinyCloudHosts)(subject, {
19001
19017
  registryUrl: this.tinycloudRegistryUrl,
@@ -19008,6 +19024,32 @@ var NodeUserAuthorization = class {
19008
19024
  });
19009
19025
  this.tinycloudHosts = resolved.hosts;
19010
19026
  }
19027
+ /**
19028
+ * Space-creation guard (TC-1443). Under `registryPolicy: "fail-closed"`,
19029
+ * throws unless the owner's registry verdict allows a space on the primary
19030
+ * host: `NodeRegistryEntryExistsError` when the owner's record does not
19031
+ * list it, `RegistryUnavailableError` when the registry gives no
19032
+ * verdict. Reuses this session's sign-in verdict unless it was
19033
+ * `unavailable`, else looks it up now (explicit-host apps). Under the
19034
+ * default policy it returns at once and performs no lookup.
19035
+ */
19036
+ async assertMayCreateSpace() {
19037
+ if (this.registryPolicy !== "fail-closed") return;
19038
+ if (this._address === void 0) {
19039
+ throw new Error("Must be signed in to create a space");
19040
+ }
19041
+ await this.ensureTinyCloudHosts();
19042
+ const subject = (0, import_sdk_core4.registryOwnerSubjectV1)(this._address, this.registryChainId);
19043
+ let verdict = this.registryVerdict;
19044
+ if (verdict?.subject !== subject || verdict.status === "unavailable") {
19045
+ verdict = await (0, import_sdk_core4.lookupOwnerRegistryVerdict)(subject, {
19046
+ registryUrl: this.tinycloudRegistryUrl,
19047
+ sequenceStore: this.registrySequenceStore
19048
+ });
19049
+ this.registryVerdict = verdict;
19050
+ }
19051
+ (0, import_sdk_core4.assertSpaceCreationAllowed)(verdict, this.primaryTinyCloudHost);
19052
+ }
19011
19053
  requireTinyCloudHosts() {
19012
19054
  if (!this.tinycloudHosts || this.tinycloudHosts.length === 0) {
19013
19055
  throw new Error("TinyCloud hosts have not been resolved. Call signIn() first.");
@@ -19456,6 +19498,7 @@ var NodeUserAuthorization = class {
19456
19498
  if (!handler) {
19457
19499
  return;
19458
19500
  }
19501
+ await this.guardSpaceCreation(handler, creationContext);
19459
19502
  const confirmed = await handler.confirmSpaceCreation(creationContext);
19460
19503
  if (!confirmed) {
19461
19504
  return;
@@ -19496,6 +19539,7 @@ var NodeUserAuthorization = class {
19496
19539
  if (!handler) {
19497
19540
  return;
19498
19541
  }
19542
+ await this.guardSpaceCreation(handler, creationContext);
19499
19543
  const confirmed = await handler.confirmSpaceCreation(creationContext);
19500
19544
  if (!confirmed) {
19501
19545
  return;
@@ -19537,6 +19581,16 @@ var NodeUserAuthorization = class {
19537
19581
  { cause: result }
19538
19582
  );
19539
19583
  }
19584
+ /** {@link assertMayCreateSpace}, reporting a refusal to the creation handler before rethrowing it. */
19585
+ async guardSpaceCreation(handler, context) {
19586
+ try {
19587
+ await this.assertMayCreateSpace();
19588
+ } catch (error) {
19589
+ const failure = error instanceof Error ? error : new Error(String(error));
19590
+ handler.onSpaceCreationFailed?.(context, failure);
19591
+ throw failure;
19592
+ }
19593
+ }
19540
19594
  recordActivationSkippedSpaces(result, primarySpaceId) {
19541
19595
  if (result.success) {
19542
19596
  this._lastActivationSkippedSpaceIds = result.skipped ?? [];
@@ -21093,6 +21147,12 @@ function describeHostFailure2(result) {
21093
21147
  const text = result.error?.trim();
21094
21148
  return text ? `${result.status} - ${text}` : `${result.status}`;
21095
21149
  }
21150
+ function assertDelegationActivated(result, delegation, requireActivation) {
21151
+ const skipped = requireActivation ? (delegation.resources?.map((r2) => r2.space) ?? [delegation.spaceId]).filter((space) => result.skipped?.includes(space)) : [];
21152
+ if (result.success && skipped.length === 0) return;
21153
+ const detail = result.success ? `the node skipped ${[...new Set(skipped)].join(", ")} (space not hosted on the node)` : describeHostFailure2(result);
21154
+ throw Object.assign(new Error(`Failed to activate delegation with host: ${detail}`), { cause: result });
21155
+ }
21096
21156
  function ownerDelegationPermissions(params) {
21097
21157
  const hasPermissions = Object.prototype.hasOwnProperty.call(params, "permissions");
21098
21158
  const hasLegacy = Object.prototype.hasOwnProperty.call(params, "path") || Object.prototype.hasOwnProperty.call(params, "actions");
@@ -21175,7 +21235,7 @@ function ownerPermissionsToAbilities(permissions) {
21175
21235
  }
21176
21236
  return { abilities, rawAbilities };
21177
21237
  }
21178
- var TERMINAL_DELEGATION_REQUIRES_SESSION_KEY = "disableSubDelegation requires a session-key delegation";
21238
+ var TERMINAL_DELEGATION_REQUIRES_SESSION_KEY = "disableSubDelegation requires a session-key delegation to a did:key (not did:pkh); use the session-key DID or drop the flag";
21179
21239
  function base64UrlEncode(bytes) {
21180
21240
  const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
21181
21241
  let output = "";
@@ -21489,7 +21549,9 @@ var _TinyCloudNode = class _TinyCloudNode {
21489
21549
  this.explicitHost = config.host;
21490
21550
  this.config = {
21491
21551
  ...config,
21492
- host: config.host ?? DEFAULT_HOST
21552
+ host: config.host ?? DEFAULT_HOST,
21553
+ // One store for every authorization this client creates.
21554
+ registrySequenceStore: config.registrySequenceStore ?? (0, import_sdk_core9.createInMemoryRegistrySequenceStore)()
21493
21555
  };
21494
21556
  if (this.config.replication?.enabled) {
21495
21557
  const runtime = getNodeReplicationLoaders().runtime().then(({ ReplicationRuntime: ReplicationRuntime2 }) => {
@@ -21724,7 +21786,6 @@ var _TinyCloudNode = class _TinyCloudNode {
21724
21786
  if (!this.wasmBindings.computeCid) throw new Error("computeCid is unavailable");
21725
21787
  return this.wasmBindings.computeCid(data, codec);
21726
21788
  },
21727
- // delegationManager: undefined - not needed for receive()
21728
21789
  createKVService: (config) => {
21729
21790
  const prefix = config.pathPrefix?.replace(/\/$/, "");
21730
21791
  const kvService = new import_sdk_core9.KVService({ prefix });
@@ -21732,7 +21793,8 @@ var _TinyCloudNode = class _TinyCloudNode {
21732
21793
  invoke: config.invoke,
21733
21794
  fetch: config.fetch ?? globalThis.fetch.bind(globalThis),
21734
21795
  hosts: config.hosts,
21735
- telemetry: this.config.telemetry
21796
+ telemetry: this.config.telemetry,
21797
+ retryPolicy: this.config.retryPolicy
21736
21798
  }));
21737
21799
  kvContext.setSession(config.session);
21738
21800
  kvService.initialize(kvContext);
@@ -21759,6 +21821,7 @@ var _TinyCloudNode = class _TinyCloudNode {
21759
21821
  tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
21760
21822
  tinycloudRegistryUrl: config.tinycloudRegistryUrl,
21761
21823
  tinycloudFallbackHosts: config.tinycloudFallbackHosts,
21824
+ ...this.registryPolicyConfig(),
21762
21825
  autoDiscoverLocalNode: config.autoDiscoverLocalNode,
21763
21826
  localNodeUrl: config.localNodeUrl,
21764
21827
  localLinkName: config.localLinkName,
@@ -21782,7 +21845,8 @@ var _TinyCloudNode = class _TinyCloudNode {
21782
21845
  });
21783
21846
  this.tc = new import_sdk_core9.TinyCloud(this.auth, {
21784
21847
  invokeAny: this.invokeAnyWithRuntimePermissions,
21785
- telemetry: config.telemetry
21848
+ telemetry: config.telemetry,
21849
+ retryPolicy: config.retryPolicy
21786
21850
  });
21787
21851
  }
21788
21852
  shouldUseBootstrapSignInRequest(config) {
@@ -22144,7 +22208,8 @@ var _TinyCloudNode = class _TinyCloudNode {
22144
22208
  this.auth = oldAuth;
22145
22209
  this.tc = this.auth ? new import_sdk_core9.TinyCloud(this.auth, {
22146
22210
  invokeAny: this.invokeAnyWithRuntimePermissions,
22147
- telemetry: this.config.telemetry
22211
+ telemetry: this.config.telemetry,
22212
+ retryPolicy: this.config.retryPolicy
22148
22213
  }) : null;
22149
22214
  this._capabilityRegistry = new import_sdk_core9.CapabilityKeyRegistry();
22150
22215
  this._keyProvider = new WasmKeyProvider({ sessionManager: this.sessionManager });
@@ -22464,6 +22529,9 @@ var _TinyCloudNode = class _TinyCloudNode {
22464
22529
  const primarySession = auth.tinyCloudSession;
22465
22530
  const defaultSpaceId = this.ownedSpaceId("default");
22466
22531
  const canReusePrimaryBootstrapSession = primarySession?.spaceId === defaultSpaceId && auth.capabilityRequest === import_sdk_core9.BOOTSTRAP_SESSION_REQUESTS.default;
22532
+ if (steps.some((step) => step.kind === "host")) {
22533
+ await this.assertMayCreateSpace();
22534
+ }
22467
22535
  for (const step of steps) {
22468
22536
  if (step.kind !== "session") continue;
22469
22537
  graph.assertActive();
@@ -22792,7 +22860,8 @@ var _TinyCloudNode = class _TinyCloudNode {
22792
22860
  invokeAny: context.invokeAny,
22793
22861
  fetch: context.fetch,
22794
22862
  hosts: context.hosts,
22795
- telemetry: this.config.telemetry
22863
+ telemetry: this.config.telemetry,
22864
+ retryPolicy: this.config.retryPolicy
22796
22865
  }));
22797
22866
  scoped.setOperationAbortSignal(signal);
22798
22867
  scoped.setSession({ ...session, spaceId });
@@ -22839,6 +22908,10 @@ var _TinyCloudNode = class _TinyCloudNode {
22839
22908
  return;
22840
22909
  }
22841
22910
  if (!this.currentSessionCoversAccountRegistry()) {
22911
+ if (!_TinyCloudNode.accountRegistrySkipLogged) {
22912
+ _TinyCloudNode.accountRegistrySkipLogged = true;
22913
+ import_sdk_services2.tinyCloudDebugLogger.log("registry.skip");
22914
+ }
22842
22915
  return;
22843
22916
  }
22844
22917
  const controller = new AbortController();
@@ -22854,7 +22927,8 @@ var _TinyCloudNode = class _TinyCloudNode {
22854
22927
  invokeAny: sharedContext.invokeAny,
22855
22928
  fetch: sharedContext.fetch,
22856
22929
  hosts: sharedContext.hosts,
22857
- telemetry: this.config.telemetry
22930
+ telemetry: this.config.telemetry,
22931
+ retryPolicy: this.config.retryPolicy
22858
22932
  }));
22859
22933
  context.setOperationAbortSignal(controller.signal);
22860
22934
  const current = this.currentTinyCloudSession();
@@ -23105,6 +23179,8 @@ var _TinyCloudNode = class _TinyCloudNode {
23105
23179
  { cause: activation }
23106
23180
  );
23107
23181
  }
23182
+ await this.assertMayCreateSpace();
23183
+ if (signal?.aborted) throw signal.reason;
23108
23184
  const created = await this.hostOwnedSpaceOnce(spaceId, signal);
23109
23185
  if (!created.success) {
23110
23186
  throw Object.assign(
@@ -23218,6 +23294,12 @@ var _TinyCloudNode = class _TinyCloudNode {
23218
23294
  * session's recap doesn't cover it and the registry has no record; the host
23219
23295
  * SIWE is idempotent server-side. Must be called after {@link signIn}.
23220
23296
  *
23297
+ * Under `registryPolicy: "fail-closed"`, hosting is refused with
23298
+ * `NodeRegistryEntryExistsError` when the owner's registry record does not
23299
+ * list the host, or `RegistryUnavailableError` when the registry gives no
23300
+ * verdict. {@link hostOwnedSpace} itself is an explicit action and is not
23301
+ * guarded.
23302
+ *
23221
23303
  * @param name - The owned space name (e.g. `"secrets"`).
23222
23304
  * @returns The hosted owned-space URI.
23223
23305
  */
@@ -23229,8 +23311,25 @@ var _TinyCloudNode = class _TinyCloudNode {
23229
23311
  if (this.hostedOwnedSpaceIds.has(spaceId) || await this.isOwnedSpaceRegistered(spaceId)) {
23230
23312
  return spaceId;
23231
23313
  }
23314
+ await this.assertMayCreateSpace();
23232
23315
  return this.hostOwnedSpace(name);
23233
23316
  }
23317
+ /**
23318
+ * Space-creation guard (TC-1443) for the automatic hosting paths: sign-in's
23319
+ * owned spaces, account bootstrap, {@link ensureOwnedSpaceHosted} and
23320
+ * {@link ensurePublicSpace}. Only `registryPolicy: "fail-closed"` consults
23321
+ * the registry ({@link NodeUserAuthorization.assertMayCreateSpace}); the
23322
+ * default policy performs no lookup.
23323
+ */
23324
+ async assertMayCreateSpace() {
23325
+ if (this.config.registryPolicy !== "fail-closed") return;
23326
+ await this.auth.assertMayCreateSpace();
23327
+ }
23328
+ /** Registry-policy settings shared by every authorization this client creates. */
23329
+ registryPolicyConfig() {
23330
+ const { registryPolicy, newUserHost, registryChainId, registrySequenceStore, onNodeNotFound } = this.config;
23331
+ return { registryPolicy, newUserHost, registryChainId, registrySequenceStore, onNodeNotFound };
23332
+ }
23234
23333
  /**
23235
23334
  * Submit the host delegation for an owned space, sharing one in-flight
23236
23335
  * request (and so one approval) between concurrent callers in this session,
@@ -23671,7 +23770,8 @@ var _TinyCloudNode = class _TinyCloudNode {
23671
23770
  const graph = this.createServiceGraphLifetime();
23672
23771
  const core = this.auth ? new import_sdk_core9.TinyCloud(this.auth, {
23673
23772
  invokeAny: graph.invokeAny,
23674
- telemetry: this.config.telemetry
23773
+ telemetry: this.config.telemetry,
23774
+ retryPolicy: this.config.retryPolicy
23675
23775
  }) : null;
23676
23776
  if (core) {
23677
23777
  core.initializeServices(graph.invoke, [input.host], graph.fetch);
@@ -23683,7 +23783,8 @@ var _TinyCloudNode = class _TinyCloudNode {
23683
23783
  invokeAny: graph.invokeAny,
23684
23784
  fetch: graph.fetch,
23685
23785
  hosts: [input.host],
23686
- telemetry: this.config.telemetry
23786
+ telemetry: this.config.telemetry,
23787
+ retryPolicy: this.config.retryPolicy
23687
23788
  }));
23688
23789
  const kv = new import_sdk_core9.KVService({});
23689
23790
  kv.initialize(serviceContext);
@@ -23769,7 +23870,8 @@ var _TinyCloudNode = class _TinyCloudNode {
23769
23870
  invoke: config.invoke,
23770
23871
  fetch: config.fetch ?? globalThis.fetch.bind(globalThis),
23771
23872
  hosts: config.hosts,
23772
- telemetry: this.config.telemetry
23873
+ telemetry: this.config.telemetry,
23874
+ retryPolicy: this.config.retryPolicy
23773
23875
  }));
23774
23876
  context.setSession(config.session);
23775
23877
  service.initialize(context);
@@ -23779,7 +23881,6 @@ var _TinyCloudNode = class _TinyCloudNode {
23779
23881
  });
23780
23882
  sharingService.updateConfig({
23781
23883
  session: input.serviceSession,
23782
- delegationManager,
23783
23884
  sessionExpiry: input.sessionExpiry,
23784
23885
  createDelegationWasm: (params) => {
23785
23886
  graph.assertActive();
@@ -23810,7 +23911,8 @@ var _TinyCloudNode = class _TinyCloudNode {
23810
23911
  invokeAny: graph.invokeAny,
23811
23912
  fetch: graph.fetch,
23812
23913
  hosts: [input.host],
23813
- telemetry: this.config.telemetry
23914
+ telemetry: this.config.telemetry,
23915
+ retryPolicy: this.config.retryPolicy
23814
23916
  }));
23815
23917
  context.setSession({ ...input.serviceSession, spaceId });
23816
23918
  scopedKv.initialize(context);
@@ -23825,7 +23927,8 @@ var _TinyCloudNode = class _TinyCloudNode {
23825
23927
  invokeAny: graph.invokeAny,
23826
23928
  fetch: graph.fetch,
23827
23929
  hosts: [input.host],
23828
- telemetry: this.config.telemetry
23930
+ telemetry: this.config.telemetry,
23931
+ retryPolicy: this.config.retryPolicy
23829
23932
  }));
23830
23933
  context.setSession({ ...input.serviceSession, spaceId });
23831
23934
  scopedKv.initialize(context);
@@ -23908,6 +24011,21 @@ var _TinyCloudNode = class _TinyCloudNode {
23908
24011
  if (address === void 0 || chainId === void 0) {
23909
24012
  return void 0;
23910
24013
  }
24014
+ if (this.config.registryPolicy === "fail-closed") {
24015
+ const resolved2 = await (0, import_sdk_core9.resolveFailClosedTinyCloudHosts)(address, {
24016
+ registryUrl: this.config.tinycloudRegistryUrl,
24017
+ registryChainId: this.config.registryChainId,
24018
+ newUserHost: this.config.newUserHost,
24019
+ sequenceStore: this.config.registrySequenceStore,
24020
+ onNodeNotFound: this.config.onNodeNotFound,
24021
+ autoDiscoverLocalNode: this.config.autoDiscoverLocalNode,
24022
+ localNodeUrl: this.config.localNodeUrl,
24023
+ localLinkName: this.config.localLinkName,
24024
+ expectedNodeDid: this.config.expectedNodeDid,
24025
+ localNodeIdentityStore: this.config.localNodeIdentityStore
24026
+ });
24027
+ return resolved2.hosts[0];
24028
+ }
23911
24029
  const resolved = await (0, import_sdk_core9.resolveTinyCloudHosts)((0, import_sdk_core9.pkhDid)(address, chainId), {
23912
24030
  registryUrl: this.config.tinycloudRegistryUrl,
23913
24031
  fallbackHosts: this.config.tinycloudFallbackHosts,
@@ -24100,6 +24218,7 @@ var _TinyCloudNode = class _TinyCloudNode {
24100
24218
  tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
24101
24219
  tinycloudRegistryUrl: this.config.tinycloudRegistryUrl,
24102
24220
  tinycloudFallbackHosts: this.config.tinycloudFallbackHosts,
24221
+ ...this.registryPolicyConfig(),
24103
24222
  autoDiscoverLocalNode: this.config.autoDiscoverLocalNode,
24104
24223
  localNodeUrl: this.config.localNodeUrl,
24105
24224
  localLinkName: this.config.localLinkName,
@@ -24121,7 +24240,8 @@ var _TinyCloudNode = class _TinyCloudNode {
24121
24240
  });
24122
24241
  this.tc = new import_sdk_core9.TinyCloud(this.auth, {
24123
24242
  invokeAny: this.invokeAnyWithRuntimePermissions,
24124
- telemetry: this.config.telemetry
24243
+ telemetry: this.config.telemetry,
24244
+ retryPolicy: this.config.retryPolicy
24125
24245
  });
24126
24246
  this.config.prefix = prefix;
24127
24247
  }
@@ -24159,6 +24279,7 @@ var _TinyCloudNode = class _TinyCloudNode {
24159
24279
  tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
24160
24280
  tinycloudRegistryUrl: this.config.tinycloudRegistryUrl,
24161
24281
  tinycloudFallbackHosts: this.config.tinycloudFallbackHosts,
24282
+ ...this.registryPolicyConfig(),
24162
24283
  autoDiscoverLocalNode: this.config.autoDiscoverLocalNode,
24163
24284
  localNodeUrl: this.config.localNodeUrl,
24164
24285
  localLinkName: this.config.localLinkName,
@@ -24180,7 +24301,8 @@ var _TinyCloudNode = class _TinyCloudNode {
24180
24301
  });
24181
24302
  this.tc = new import_sdk_core9.TinyCloud(this.auth, {
24182
24303
  invokeAny: this.invokeAnyWithRuntimePermissions,
24183
- telemetry: this.config.telemetry
24304
+ telemetry: this.config.telemetry,
24305
+ retryPolicy: this.config.retryPolicy
24184
24306
  });
24185
24307
  this.config.prefix = prefix;
24186
24308
  }
@@ -24204,7 +24326,8 @@ var _TinyCloudNode = class _TinyCloudNode {
24204
24326
  invokeAny: this._serviceGraph.invokeAny,
24205
24327
  fetch: this._serviceGraph.fetch,
24206
24328
  hosts: [this.config.host],
24207
- telemetry: this.config.telemetry
24329
+ telemetry: this.config.telemetry,
24330
+ retryPolicy: this.config.retryPolicy
24208
24331
  }));
24209
24332
  this._kv = new import_sdk_core9.KVService({});
24210
24333
  this._kv.initialize(this._serviceContext);
@@ -24251,7 +24374,8 @@ var _TinyCloudNode = class _TinyCloudNode {
24251
24374
  invokeAny: this._serviceContext.invokeAny,
24252
24375
  fetch: this._serviceContext.fetch,
24253
24376
  hosts: this._serviceContext.hosts,
24254
- telemetry: this.config.telemetry
24377
+ telemetry: this.config.telemetry,
24378
+ retryPolicy: this.config.retryPolicy
24255
24379
  }));
24256
24380
  const session = this._serviceContext.session;
24257
24381
  if (session) {
@@ -24702,7 +24826,6 @@ var _TinyCloudNode = class _TinyCloudNode {
24702
24826
  assertActive: () => graph.assertActive(),
24703
24827
  keyProvider: this._keyProvider,
24704
24828
  registry: this._capabilityRegistry,
24705
- delegationManager: this._delegationManager,
24706
24829
  sessionExpiry: this.getSessionExpiry(),
24707
24830
  createDelegationWasm: (params) => {
24708
24831
  graph.assertActive();
@@ -24719,7 +24842,8 @@ var _TinyCloudNode = class _TinyCloudNode {
24719
24842
  invoke: config.invoke,
24720
24843
  fetch: config.fetch ?? graph.fetch,
24721
24844
  hosts: config.hosts,
24722
- telemetry: this.config.telemetry
24845
+ telemetry: this.config.telemetry,
24846
+ retryPolicy: this.config.retryPolicy
24723
24847
  }));
24724
24848
  context.setSession(config.session);
24725
24849
  service.initialize(context);
@@ -25040,7 +25164,8 @@ var _TinyCloudNode = class _TinyCloudNode {
25040
25164
  invokeAny: this._serviceContext.invokeAny,
25041
25165
  fetch: this._serviceContext.fetch,
25042
25166
  hosts: this._serviceContext.hosts,
25043
- telemetry: this.config.telemetry
25167
+ telemetry: this.config.telemetry,
25168
+ retryPolicy: this.config.retryPolicy
25044
25169
  }));
25045
25170
  spaceScopedContext.setSession({ ...this._serviceContext.session, spaceId });
25046
25171
  sql.initialize(spaceScopedContext);
@@ -25243,6 +25368,7 @@ var _TinyCloudNode = class _TinyCloudNode {
25243
25368
  }
25244
25369
  };
25245
25370
  }
25371
+ if (result.status === "aad_mismatch") return { status: "read_failed" };
25246
25372
  return { status: result.status };
25247
25373
  }
25248
25374
  if (!isSecretPayload(result.entry.value)) {
@@ -25684,16 +25810,8 @@ var _TinyCloudNode = class _TinyCloudNode {
25684
25810
  * ```typescript
25685
25811
  * const delegations = alice.delegationManager;
25686
25812
  *
25687
- * // Create a delegation
25688
- * const result = await delegations.create({
25689
- * delegateDID: bob.did,
25690
- * path: "shared/",
25691
- * actions: ["tinycloud.kv/get", "tinycloud.kv/put"],
25692
- * expiry: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours
25693
- * });
25694
- *
25695
- * // List delegations
25696
- * const listResult = await delegations.list();
25813
+ * // Query account delegations
25814
+ * const page = await delegations.query();
25697
25815
  *
25698
25816
  * // Revoke a delegation
25699
25817
  * await delegations.revoke(delegationCid);
@@ -25795,7 +25913,9 @@ var _TinyCloudNode = class _TinyCloudNode {
25795
25913
  * Ensure the user's public space exists and is accessible.
25796
25914
  * Creates the space and activates a session delegation for it.
25797
25915
  * This is the trigger for lazy public space creation — call it
25798
- * before writing to spaces.get('public').kv.
25916
+ * before writing to spaces.get('public').kv. The vault calls it on unlock.
25917
+ * Under `registryPolicy: "fail-closed"` creation goes through the
25918
+ * space-creation guard.
25799
25919
  */
25800
25920
  async ensurePublicSpace() {
25801
25921
  if (!this.auth || !this.session || !this.signer) {
@@ -25805,6 +25925,7 @@ var _TinyCloudNode = class _TinyCloudNode {
25805
25925
  if (!publicSpaceId) {
25806
25926
  throw new Error("Public space not enabled. Set enablePublicSpace: true in config.");
25807
25927
  }
25928
+ await this.assertMayCreateSpace();
25808
25929
  await this.auth.hostPublicSpace(publicSpaceId);
25809
25930
  const kvActions = [import_sdk_core9.KV.PUT, import_sdk_core9.KV.GET, import_sdk_core9.KV.DEL, import_sdk_core9.KV.LIST, import_sdk_core9.KV.METADATA];
25810
25931
  const abilities = { kv: { "": kvActions } };
@@ -25862,7 +25983,8 @@ var _TinyCloudNode = class _TinyCloudNode {
25862
25983
  invoke: this._serviceGraph.invoke,
25863
25984
  fetch: this._serviceGraph.fetch,
25864
25985
  hosts: this._serviceContext.hosts,
25865
- telemetry: this.config.telemetry
25986
+ telemetry: this.config.telemetry,
25987
+ retryPolicy: this.config.retryPolicy
25866
25988
  }));
25867
25989
  publicContext.setSession({
25868
25990
  delegationHeader: delegationSession.delegationHeader,
@@ -25891,32 +26013,6 @@ var _TinyCloudNode = class _TinyCloudNode {
25891
26013
  // ===========================================================================
25892
26014
  // v2 Delegation Convenience Methods
25893
26015
  // ===========================================================================
25894
- /**
25895
- * Create a delegation using the v2 DelegationManager.
25896
- *
25897
- * This is a convenience method that wraps DelegationManager.create().
25898
- * For more control, use `this.delegationManager` directly.
25899
- *
25900
- * @param params - Delegation parameters
25901
- * @returns Result containing the created Delegation
25902
- *
25903
- * @example
25904
- * ```typescript
25905
- * const result = await alice.delegate({
25906
- * delegateDID: bob.did,
25907
- * path: "shared/",
25908
- * actions: ["tinycloud.kv/get", "tinycloud.kv/put"],
25909
- * expiry: new Date(Date.now() + 24 * 60 * 60 * 1000),
25910
- * });
25911
- *
25912
- * if (result.ok) {
25913
- * console.log("Delegation created:", result.data.cid);
25914
- * }
25915
- * ```
25916
- */
25917
- async delegate(params) {
25918
- return this.delegationManager.create(params);
25919
- }
25920
26016
  /**
25921
26017
  * Revoke a delegation with the owner wallet when available. Session-only
25922
26018
  * clients continue to use their delegated revocation authority.
@@ -25942,14 +26038,6 @@ var _TinyCloudNode = class _TinyCloudNode {
25942
26038
  0x55n
25943
26039
  );
25944
26040
  }
25945
- /**
25946
- * List all delegations for the current session's space.
25947
- *
25948
- * @returns Result containing an array of Delegations
25949
- */
25950
- async listDelegations() {
25951
- return this.delegationManager.list();
25952
- }
25953
26041
  /**
25954
26042
  * Check if the current session has permission for a path and action.
25955
26043
  *
@@ -26009,6 +26097,9 @@ var _TinyCloudNode = class _TinyCloudNode {
26009
26097
  "delegateTo: onPrepared cannot be combined with forceWalletSign=true; the wallet-signed path activates the delegation before it could be handed to onPrepared."
26010
26098
  );
26011
26099
  }
26100
+ if (options?.disableSubDelegation && did.startsWith("did:pkh:")) {
26101
+ throw new Error(TERMINAL_DELEGATION_REQUIRES_SESSION_KEY);
26102
+ }
26012
26103
  const session = this.currentTinyCloudSession();
26013
26104
  if (!session) {
26014
26105
  throw new import_sdk_core9.SessionExpiredError(/* @__PURE__ */ new Date(0));
@@ -26074,7 +26165,8 @@ var _TinyCloudNode = class _TinyCloudNode {
26074
26165
  plan.grant,
26075
26166
  plan.operations,
26076
26167
  onPrepared,
26077
- options?.disableSubDelegation === true
26168
+ options?.disableSubDelegation === true,
26169
+ options?.requireActivation === true
26078
26170
  );
26079
26171
  return { delegation: delegation2, prompted: false };
26080
26172
  }
@@ -26084,7 +26176,8 @@ var _TinyCloudNode = class _TinyCloudNode {
26084
26176
  effectiveExpiration,
26085
26177
  session,
26086
26178
  onPrepared,
26087
- options?.disableSubDelegation === true
26179
+ options?.disableSubDelegation === true,
26180
+ options?.requireActivation === true
26088
26181
  );
26089
26182
  return { delegation, prompted: false };
26090
26183
  }
@@ -26315,7 +26408,7 @@ var _TinyCloudNode = class _TinyCloudNode {
26315
26408
  *
26316
26409
  * @internal
26317
26410
  */
26318
- async createDelegationViaWasmPath(did, entries, expirationTime, session, onPrepared, disableSubDelegation = false) {
26411
+ async createDelegationViaWasmPath(did, entries, expirationTime, session, onPrepared, disableSubDelegation = false, requireActivation = false) {
26319
26412
  if (entries.length === 0) {
26320
26413
  throw new Error(
26321
26414
  "createDelegationViaWasmPath requires a non-empty entries array"
@@ -26399,15 +26492,10 @@ var _TinyCloudNode = class _TinyCloudNode {
26399
26492
  this.config.host,
26400
26493
  delegationHeader
26401
26494
  );
26402
- if (!activateResult.success) {
26403
- throw Object.assign(
26404
- new Error(`Failed to activate delegation with host: ${describeHostFailure2(activateResult)}`),
26405
- { cause: activateResult }
26406
- );
26407
- }
26495
+ assertDelegationActivated(activateResult, delegation, requireActivation);
26408
26496
  return delegation;
26409
26497
  }
26410
- async createDelegationViaRuntimeGrant(did, entries, expirationTime, grant, requestedOperations, onPrepared, disableSubDelegation = false) {
26498
+ async createDelegationViaRuntimeGrant(did, entries, expirationTime, grant, requestedOperations, onPrepared, disableSubDelegation = false, requireActivation = false) {
26411
26499
  this.assertRuntimeGrantCaveatsPreservable(entries, requestedOperations, grant);
26412
26500
  this.assertDelegationCaveatsPreservable(entries);
26413
26501
  const result = this.createDelegationWrapper({
@@ -26441,12 +26529,7 @@ var _TinyCloudNode = class _TinyCloudNode {
26441
26529
  targetHost,
26442
26530
  delegationHeader
26443
26531
  );
26444
- if (!activateResult.success) {
26445
- throw Object.assign(
26446
- new Error(`Failed to activate delegation with host: ${describeHostFailure2(activateResult)}`),
26447
- { cause: activateResult }
26448
- );
26449
- }
26532
+ assertDelegationActivated(activateResult, delegation, requireActivation);
26450
26533
  return delegation;
26451
26534
  }
26452
26535
  resolvePermissionSpace(space, session) {
@@ -27393,6 +27476,8 @@ var _TinyCloudNode = class _TinyCloudNode {
27393
27476
  };
27394
27477
  }
27395
27478
  };
27479
+ /** Set once per process: the registry sync was skipped for want of account access. */
27480
+ _TinyCloudNode.accountRegistrySkipLogged = false;
27396
27481
  // ===========================================================================
27397
27482
  // Capability-chain delegation (spec: .claude/specs/capability-chain.md)
27398
27483
  // ===========================================================================
@@ -28810,6 +28895,34 @@ assertSyncEffective_fn = async function() {
28810
28895
  throw new ReplicaStorageError("GRANT_EXPIRED", "The replica grant's effective lifetime has expired.");
28811
28896
  }
28812
28897
  };
28898
+ async function listDir(dir) {
28899
+ try {
28900
+ return await (0, import_promises.readdir)(dir);
28901
+ } catch (error) {
28902
+ if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
28903
+ return [];
28904
+ }
28905
+ throw error;
28906
+ }
28907
+ }
28908
+ async function collectProfileOrphans(root, collectOrphans) {
28909
+ for (const partition of await listDir(root)) {
28910
+ const replicasDir = (0, import_node_path.join)(root, partition, "replicas");
28911
+ for (const replicaName of await listDir(replicasDir)) {
28912
+ await collectOrphans((0, import_node_path.join)(replicasDir, replicaName));
28913
+ }
28914
+ }
28915
+ }
28916
+ async function profileContentBytes(root, contentBytes) {
28917
+ let total = 0;
28918
+ for (const partition of await listDir(root)) {
28919
+ const replicasDir = (0, import_node_path.join)(root, partition, "replicas");
28920
+ for (const replicaName of await listDir(replicasDir)) {
28921
+ total += await contentBytes((0, import_node_path.join)(replicasDir, replicaName));
28922
+ }
28923
+ }
28924
+ return total;
28925
+ }
28813
28926
  function createSqliteReplicaStorage(options) {
28814
28927
  const root = options.dir;
28815
28928
  const profileDir = (0, import_node_path.dirname)(options.gateFile ?? (0, import_node_path.join)((0, import_node_path.dirname)(root), import_contractConstants.REPLICA_GATE_FILE));
@@ -29804,10 +29917,29 @@ function createSqliteReplicaStorage(options) {
29804
29917
  const replicaHash = replicaHashOf(spec.prefix, device.did);
29805
29918
  partitionId = `${identityKeyHash}:${replicaHash}`;
29806
29919
  const replicaDir = (0, import_node_path.join)(idDir, "replicas", replicaHash);
29807
- const store = await sqlite.SqliteReplicaStore.open(replicaDir, {
29808
- create: true,
29809
- guard: storeGuard
29810
- });
29920
+ const quota = spec.quota?.maxBytes ?? options.maxBytes;
29921
+ if (quota !== void 0 && (!Number.isSafeInteger(quota) || quota < 0)) {
29922
+ throw new TypeError(`maxBytes must be a non-negative safe integer, got ${quota}.`);
29923
+ }
29924
+ if (quota !== void 0) await collectProfileOrphans(root, sqlite.SqliteReplicaStore.collectOrphans);
29925
+ const store = await sqlite.SqliteReplicaStore.open(
29926
+ replicaDir,
29927
+ quota === void 0 ? { create: true, guard: storeGuard } : {
29928
+ create: true,
29929
+ guard: storeGuard,
29930
+ partitionId: (0, import_node_path.basename)(idDir),
29931
+ admit: async (page) => {
29932
+ await collectProfileOrphans(root, sqlite.SqliteReplicaStore.collectOrphans);
29933
+ const used = await profileContentBytes(root, sqlite.SqliteReplicaStore.contentBytes);
29934
+ if (page.blobBytes > 0 && used + page.blobBytes > quota) {
29935
+ throw new replica.ReplicaError(
29936
+ replica.ReplicaErrorCode.STORAGE_BUDGET,
29937
+ `Profile storage holds ${used} bytes; this page adds ${page.blobBytes} against a ${quota}-byte quota. Nothing was committed.`
29938
+ );
29939
+ }
29940
+ }
29941
+ }
29942
+ );
29811
29943
  let generation;
29812
29944
  try {
29813
29945
  try {