@tinycloud/node-sdk 3.3.0-beta.9 → 3.3.1-beta.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.js CHANGED
@@ -18114,24 +18114,17 @@ function signedAuthorityFromCompactUcan(authorization) {
18114
18114
  ));
18115
18115
  continue;
18116
18116
  }
18117
- const nativeMatch = /^(tinycloud:[^/]+)\/(kv|sql|duckdb|hooks)\/(.*)$/.exec(resource);
18118
- const policyMatch = /^tinycloud:\/\/([^/]+)\/(kv|sql|duckdb|hooks)\/(.*)$/.exec(resource);
18119
- const match = nativeMatch ?? policyMatch;
18117
+ const match = /^(?:tinycloud:\/\/([^/]+)|(tinycloud:[^/]+))\/(kv|sql|duckdb|hooks)(?:\/|$)(.*)$/.exec(resource);
18120
18118
  if (!match) {
18121
18119
  throw new Error(
18122
18120
  `Validated runtime delegation has an unsupported signed resource '${resource}'.`
18123
18121
  );
18124
18122
  }
18125
- const [, space, shortService, path] = match;
18123
+ const [, policySpace, nativeSpace, shortService, path] = match;
18126
18124
  const service = SERVICE_BY_SHORT_NAME[shortService];
18127
- if (service === void 0) {
18128
- throw new Error(
18129
- `Validated runtime delegation has an unsupported signed service '${shortService}'.`
18130
- );
18131
- }
18132
18125
  permissions.push(...groupedSignedEntries(
18133
18126
  service,
18134
- space,
18127
+ nativeSpace ?? policySpace,
18135
18128
  path,
18136
18129
  abilities
18137
18130
  ));
@@ -18557,6 +18550,8 @@ import {
18557
18550
  canonicalizeAddress as canonicalizeAddress2,
18558
18551
  pkhDid as pkhDid2,
18559
18552
  resolveTinyCloudHosts as resolveTinyCloudHosts2,
18553
+ resolveFailClosedTinyCloudHosts as resolveFailClosedTinyCloudHosts2,
18554
+ createInMemoryRegistrySequenceStore as createInMemoryRegistrySequenceStore2,
18560
18555
  publishLocationRecord,
18561
18556
  principalDidEquals as principalDidEquals3,
18562
18557
  parseNetworkId as parseNetworkId2,
@@ -18577,7 +18572,8 @@ import {
18577
18572
  serviceError
18578
18573
  } from "@tinycloud/sdk-core";
18579
18574
  import {
18580
- parsePermissionHint
18575
+ parsePermissionHint,
18576
+ tinyCloudDebugLogger
18581
18577
  } from "@tinycloud/sdk-services";
18582
18578
 
18583
18579
  // src/authorization/NodeUserAuthorization.ts
@@ -18598,6 +18594,11 @@ import {
18598
18594
  resourceCapabilitiesToAbilitiesMap,
18599
18595
  resourceCapabilitiesToSpaceAbilitiesMap,
18600
18596
  resolveTinyCloudHosts,
18597
+ resolveFailClosedTinyCloudHosts,
18598
+ lookupOwnerRegistryVerdict,
18599
+ assertSpaceCreationAllowed,
18600
+ createInMemoryRegistrySequenceStore,
18601
+ registryOwnerSubjectV1,
18601
18602
  EXPIRY,
18602
18603
  canonicalizeAddress,
18603
18604
  makePkhSpaceId,
@@ -18812,6 +18813,11 @@ var NodeUserAuthorization = class {
18812
18813
  this.tinycloudHosts = config.tinycloudHosts;
18813
18814
  this.tinycloudRegistryUrl = config.tinycloudRegistryUrl;
18814
18815
  this.tinycloudFallbackHosts = config.tinycloudFallbackHosts;
18816
+ this.registryPolicy = config.registryPolicy;
18817
+ this.newUserHost = config.newUserHost;
18818
+ this.registryChainId = config.registryChainId;
18819
+ this.registrySequenceStore = config.registrySequenceStore ?? createInMemoryRegistrySequenceStore();
18820
+ this.onNodeNotFound = config.onNodeNotFound;
18815
18821
  this.autoDiscoverLocalNode = config.autoDiscoverLocalNode;
18816
18822
  this.localNodeUrl = config.localNodeUrl;
18817
18823
  this.localLinkName = config.localLinkName;
@@ -18923,6 +18929,7 @@ var NodeUserAuthorization = class {
18923
18929
  this._nodeFeaturesKnown = nodeFeatures !== void 0;
18924
18930
  this._nodeInfoIdentity = void 0;
18925
18931
  this.tinycloudHosts = hosts && hosts.length > 0 ? [...hosts] : void 0;
18932
+ this.registryVerdict = void 0;
18926
18933
  this._lastActivationSkippedSpaceIds = [];
18927
18934
  this._lastActivationActivatedSpaceIds = [];
18928
18935
  if (!session) {
@@ -18973,6 +18980,23 @@ var NodeUserAuthorization = class {
18973
18980
  if (this.tinycloudHosts && this.tinycloudHosts.length > 0) {
18974
18981
  return;
18975
18982
  }
18983
+ if (this.registryPolicy === "fail-closed") {
18984
+ const resolved2 = await resolveFailClosedTinyCloudHosts(address, {
18985
+ registryUrl: this.tinycloudRegistryUrl,
18986
+ registryChainId: this.registryChainId,
18987
+ newUserHost: this.newUserHost,
18988
+ sequenceStore: this.registrySequenceStore,
18989
+ onNodeNotFound: this.onNodeNotFound,
18990
+ autoDiscoverLocalNode: this.autoDiscoverLocalNode,
18991
+ localNodeUrl: this.localNodeUrl,
18992
+ localLinkName: this.localLinkName,
18993
+ expectedNodeDid: this.expectedNodeDid,
18994
+ localNodeIdentityStore: this.localNodeIdentityStore
18995
+ });
18996
+ this.tinycloudHosts = resolved2.hosts;
18997
+ this.registryVerdict = resolved2.verdict;
18998
+ return;
18999
+ }
18976
19000
  const subject = pkhDid(address, chainId);
18977
19001
  const resolved = await resolveTinyCloudHosts(subject, {
18978
19002
  registryUrl: this.tinycloudRegistryUrl,
@@ -18985,6 +19009,32 @@ var NodeUserAuthorization = class {
18985
19009
  });
18986
19010
  this.tinycloudHosts = resolved.hosts;
18987
19011
  }
19012
+ /**
19013
+ * Space-creation guard (TC-1443). Under `registryPolicy: "fail-closed"`,
19014
+ * throws unless the owner's registry verdict allows a space on the primary
19015
+ * host: `NodeRegistryEntryExistsError` when the owner's record does not
19016
+ * list it, `RegistryUnavailableError` when the registry gives no
19017
+ * verdict. Reuses this session's sign-in verdict unless it was
19018
+ * `unavailable`, else looks it up now (explicit-host apps). Under the
19019
+ * default policy it returns at once and performs no lookup.
19020
+ */
19021
+ async assertMayCreateSpace() {
19022
+ if (this.registryPolicy !== "fail-closed") return;
19023
+ if (this._address === void 0) {
19024
+ throw new Error("Must be signed in to create a space");
19025
+ }
19026
+ await this.ensureTinyCloudHosts();
19027
+ const subject = registryOwnerSubjectV1(this._address, this.registryChainId);
19028
+ let verdict = this.registryVerdict;
19029
+ if (verdict?.subject !== subject || verdict.status === "unavailable") {
19030
+ verdict = await lookupOwnerRegistryVerdict(subject, {
19031
+ registryUrl: this.tinycloudRegistryUrl,
19032
+ sequenceStore: this.registrySequenceStore
19033
+ });
19034
+ this.registryVerdict = verdict;
19035
+ }
19036
+ assertSpaceCreationAllowed(verdict, this.primaryTinyCloudHost);
19037
+ }
18988
19038
  requireTinyCloudHosts() {
18989
19039
  if (!this.tinycloudHosts || this.tinycloudHosts.length === 0) {
18990
19040
  throw new Error("TinyCloud hosts have not been resolved. Call signIn() first.");
@@ -19433,6 +19483,7 @@ var NodeUserAuthorization = class {
19433
19483
  if (!handler) {
19434
19484
  return;
19435
19485
  }
19486
+ await this.guardSpaceCreation(handler, creationContext);
19436
19487
  const confirmed = await handler.confirmSpaceCreation(creationContext);
19437
19488
  if (!confirmed) {
19438
19489
  return;
@@ -19473,6 +19524,7 @@ var NodeUserAuthorization = class {
19473
19524
  if (!handler) {
19474
19525
  return;
19475
19526
  }
19527
+ await this.guardSpaceCreation(handler, creationContext);
19476
19528
  const confirmed = await handler.confirmSpaceCreation(creationContext);
19477
19529
  if (!confirmed) {
19478
19530
  return;
@@ -19514,6 +19566,16 @@ var NodeUserAuthorization = class {
19514
19566
  { cause: result }
19515
19567
  );
19516
19568
  }
19569
+ /** {@link assertMayCreateSpace}, reporting a refusal to the creation handler before rethrowing it. */
19570
+ async guardSpaceCreation(handler, context) {
19571
+ try {
19572
+ await this.assertMayCreateSpace();
19573
+ } catch (error) {
19574
+ const failure = error instanceof Error ? error : new Error(String(error));
19575
+ handler.onSpaceCreationFailed?.(context, failure);
19576
+ throw failure;
19577
+ }
19578
+ }
19517
19579
  recordActivationSkippedSpaces(result, primarySpaceId) {
19518
19580
  if (result.success) {
19519
19581
  this._lastActivationSkippedSpaceIds = result.skipped ?? [];
@@ -21086,6 +21148,12 @@ function describeHostFailure2(result) {
21086
21148
  const text = result.error?.trim();
21087
21149
  return text ? `${result.status} - ${text}` : `${result.status}`;
21088
21150
  }
21151
+ function assertDelegationActivated(result, delegation, requireActivation) {
21152
+ const skipped = requireActivation ? (delegation.resources?.map((r2) => r2.space) ?? [delegation.spaceId]).filter((space) => result.skipped?.includes(space)) : [];
21153
+ if (result.success && skipped.length === 0) return;
21154
+ const detail = result.success ? `the node skipped ${[...new Set(skipped)].join(", ")} (space not hosted on the node)` : describeHostFailure2(result);
21155
+ throw Object.assign(new Error(`Failed to activate delegation with host: ${detail}`), { cause: result });
21156
+ }
21089
21157
  function ownerDelegationPermissions(params) {
21090
21158
  const hasPermissions = Object.prototype.hasOwnProperty.call(params, "permissions");
21091
21159
  const hasLegacy = Object.prototype.hasOwnProperty.call(params, "path") || Object.prototype.hasOwnProperty.call(params, "actions");
@@ -21168,7 +21236,7 @@ function ownerPermissionsToAbilities(permissions) {
21168
21236
  }
21169
21237
  return { abilities, rawAbilities };
21170
21238
  }
21171
- var TERMINAL_DELEGATION_REQUIRES_SESSION_KEY = "disableSubDelegation requires a session-key delegation";
21239
+ 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";
21172
21240
  function base64UrlEncode(bytes) {
21173
21241
  const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
21174
21242
  let output = "";
@@ -21482,7 +21550,9 @@ var _TinyCloudNode = class _TinyCloudNode {
21482
21550
  this.explicitHost = config.host;
21483
21551
  this.config = {
21484
21552
  ...config,
21485
- host: config.host ?? DEFAULT_HOST
21553
+ host: config.host ?? DEFAULT_HOST,
21554
+ // One store for every authorization this client creates.
21555
+ registrySequenceStore: config.registrySequenceStore ?? createInMemoryRegistrySequenceStore2()
21486
21556
  };
21487
21557
  if (this.config.replication?.enabled) {
21488
21558
  const runtime = getNodeReplicationLoaders().runtime().then(({ ReplicationRuntime: ReplicationRuntime2 }) => {
@@ -21717,7 +21787,6 @@ var _TinyCloudNode = class _TinyCloudNode {
21717
21787
  if (!this.wasmBindings.computeCid) throw new Error("computeCid is unavailable");
21718
21788
  return this.wasmBindings.computeCid(data, codec);
21719
21789
  },
21720
- // delegationManager: undefined - not needed for receive()
21721
21790
  createKVService: (config) => {
21722
21791
  const prefix = config.pathPrefix?.replace(/\/$/, "");
21723
21792
  const kvService = new KVService2({ prefix });
@@ -21725,7 +21794,8 @@ var _TinyCloudNode = class _TinyCloudNode {
21725
21794
  invoke: config.invoke,
21726
21795
  fetch: config.fetch ?? globalThis.fetch.bind(globalThis),
21727
21796
  hosts: config.hosts,
21728
- telemetry: this.config.telemetry
21797
+ telemetry: this.config.telemetry,
21798
+ retryPolicy: this.config.retryPolicy
21729
21799
  }));
21730
21800
  kvContext.setSession(config.session);
21731
21801
  kvService.initialize(kvContext);
@@ -21752,6 +21822,7 @@ var _TinyCloudNode = class _TinyCloudNode {
21752
21822
  tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
21753
21823
  tinycloudRegistryUrl: config.tinycloudRegistryUrl,
21754
21824
  tinycloudFallbackHosts: config.tinycloudFallbackHosts,
21825
+ ...this.registryPolicyConfig(),
21755
21826
  autoDiscoverLocalNode: config.autoDiscoverLocalNode,
21756
21827
  localNodeUrl: config.localNodeUrl,
21757
21828
  localLinkName: config.localLinkName,
@@ -21775,7 +21846,8 @@ var _TinyCloudNode = class _TinyCloudNode {
21775
21846
  });
21776
21847
  this.tc = new TinyCloud(this.auth, {
21777
21848
  invokeAny: this.invokeAnyWithRuntimePermissions,
21778
- telemetry: config.telemetry
21849
+ telemetry: config.telemetry,
21850
+ retryPolicy: config.retryPolicy
21779
21851
  });
21780
21852
  }
21781
21853
  shouldUseBootstrapSignInRequest(config) {
@@ -22137,7 +22209,8 @@ var _TinyCloudNode = class _TinyCloudNode {
22137
22209
  this.auth = oldAuth;
22138
22210
  this.tc = this.auth ? new TinyCloud(this.auth, {
22139
22211
  invokeAny: this.invokeAnyWithRuntimePermissions,
22140
- telemetry: this.config.telemetry
22212
+ telemetry: this.config.telemetry,
22213
+ retryPolicy: this.config.retryPolicy
22141
22214
  }) : null;
22142
22215
  this._capabilityRegistry = new CapabilityKeyRegistry();
22143
22216
  this._keyProvider = new WasmKeyProvider({ sessionManager: this.sessionManager });
@@ -22457,6 +22530,9 @@ var _TinyCloudNode = class _TinyCloudNode {
22457
22530
  const primarySession = auth.tinyCloudSession;
22458
22531
  const defaultSpaceId = this.ownedSpaceId("default");
22459
22532
  const canReusePrimaryBootstrapSession = primarySession?.spaceId === defaultSpaceId && auth.capabilityRequest === BOOTSTRAP_SESSION_REQUESTS.default;
22533
+ if (steps.some((step) => step.kind === "host")) {
22534
+ await this.assertMayCreateSpace();
22535
+ }
22460
22536
  for (const step of steps) {
22461
22537
  if (step.kind !== "session") continue;
22462
22538
  graph.assertActive();
@@ -22785,7 +22861,8 @@ var _TinyCloudNode = class _TinyCloudNode {
22785
22861
  invokeAny: context.invokeAny,
22786
22862
  fetch: context.fetch,
22787
22863
  hosts: context.hosts,
22788
- telemetry: this.config.telemetry
22864
+ telemetry: this.config.telemetry,
22865
+ retryPolicy: this.config.retryPolicy
22789
22866
  }));
22790
22867
  scoped.setOperationAbortSignal(signal);
22791
22868
  scoped.setSession({ ...session, spaceId });
@@ -22832,6 +22909,10 @@ var _TinyCloudNode = class _TinyCloudNode {
22832
22909
  return;
22833
22910
  }
22834
22911
  if (!this.currentSessionCoversAccountRegistry()) {
22912
+ if (!_TinyCloudNode.accountRegistrySkipLogged) {
22913
+ _TinyCloudNode.accountRegistrySkipLogged = true;
22914
+ tinyCloudDebugLogger.log("registry.skip");
22915
+ }
22835
22916
  return;
22836
22917
  }
22837
22918
  const controller = new AbortController();
@@ -22847,7 +22928,8 @@ var _TinyCloudNode = class _TinyCloudNode {
22847
22928
  invokeAny: sharedContext.invokeAny,
22848
22929
  fetch: sharedContext.fetch,
22849
22930
  hosts: sharedContext.hosts,
22850
- telemetry: this.config.telemetry
22931
+ telemetry: this.config.telemetry,
22932
+ retryPolicy: this.config.retryPolicy
22851
22933
  }));
22852
22934
  context.setOperationAbortSignal(controller.signal);
22853
22935
  const current = this.currentTinyCloudSession();
@@ -23098,6 +23180,8 @@ var _TinyCloudNode = class _TinyCloudNode {
23098
23180
  { cause: activation }
23099
23181
  );
23100
23182
  }
23183
+ await this.assertMayCreateSpace();
23184
+ if (signal?.aborted) throw signal.reason;
23101
23185
  const created = await this.hostOwnedSpaceOnce(spaceId, signal);
23102
23186
  if (!created.success) {
23103
23187
  throw Object.assign(
@@ -23211,6 +23295,12 @@ var _TinyCloudNode = class _TinyCloudNode {
23211
23295
  * session's recap doesn't cover it and the registry has no record; the host
23212
23296
  * SIWE is idempotent server-side. Must be called after {@link signIn}.
23213
23297
  *
23298
+ * Under `registryPolicy: "fail-closed"`, hosting is refused with
23299
+ * `NodeRegistryEntryExistsError` when the owner's registry record does not
23300
+ * list the host, or `RegistryUnavailableError` when the registry gives no
23301
+ * verdict. {@link hostOwnedSpace} itself is an explicit action and is not
23302
+ * guarded.
23303
+ *
23214
23304
  * @param name - The owned space name (e.g. `"secrets"`).
23215
23305
  * @returns The hosted owned-space URI.
23216
23306
  */
@@ -23222,8 +23312,25 @@ var _TinyCloudNode = class _TinyCloudNode {
23222
23312
  if (this.hostedOwnedSpaceIds.has(spaceId) || await this.isOwnedSpaceRegistered(spaceId)) {
23223
23313
  return spaceId;
23224
23314
  }
23315
+ await this.assertMayCreateSpace();
23225
23316
  return this.hostOwnedSpace(name);
23226
23317
  }
23318
+ /**
23319
+ * Space-creation guard (TC-1443) for the automatic hosting paths: sign-in's
23320
+ * owned spaces, account bootstrap, {@link ensureOwnedSpaceHosted} and
23321
+ * {@link ensurePublicSpace}. Only `registryPolicy: "fail-closed"` consults
23322
+ * the registry ({@link NodeUserAuthorization.assertMayCreateSpace}); the
23323
+ * default policy performs no lookup.
23324
+ */
23325
+ async assertMayCreateSpace() {
23326
+ if (this.config.registryPolicy !== "fail-closed") return;
23327
+ await this.auth.assertMayCreateSpace();
23328
+ }
23329
+ /** Registry-policy settings shared by every authorization this client creates. */
23330
+ registryPolicyConfig() {
23331
+ const { registryPolicy, newUserHost, registryChainId, registrySequenceStore, onNodeNotFound } = this.config;
23332
+ return { registryPolicy, newUserHost, registryChainId, registrySequenceStore, onNodeNotFound };
23333
+ }
23227
23334
  /**
23228
23335
  * Submit the host delegation for an owned space, sharing one in-flight
23229
23336
  * request (and so one approval) between concurrent callers in this session,
@@ -23664,7 +23771,8 @@ var _TinyCloudNode = class _TinyCloudNode {
23664
23771
  const graph = this.createServiceGraphLifetime();
23665
23772
  const core = this.auth ? new TinyCloud(this.auth, {
23666
23773
  invokeAny: graph.invokeAny,
23667
- telemetry: this.config.telemetry
23774
+ telemetry: this.config.telemetry,
23775
+ retryPolicy: this.config.retryPolicy
23668
23776
  }) : null;
23669
23777
  if (core) {
23670
23778
  core.initializeServices(graph.invoke, [input.host], graph.fetch);
@@ -23676,7 +23784,8 @@ var _TinyCloudNode = class _TinyCloudNode {
23676
23784
  invokeAny: graph.invokeAny,
23677
23785
  fetch: graph.fetch,
23678
23786
  hosts: [input.host],
23679
- telemetry: this.config.telemetry
23787
+ telemetry: this.config.telemetry,
23788
+ retryPolicy: this.config.retryPolicy
23680
23789
  }));
23681
23790
  const kv = new KVService2({});
23682
23791
  kv.initialize(serviceContext);
@@ -23762,7 +23871,8 @@ var _TinyCloudNode = class _TinyCloudNode {
23762
23871
  invoke: config.invoke,
23763
23872
  fetch: config.fetch ?? globalThis.fetch.bind(globalThis),
23764
23873
  hosts: config.hosts,
23765
- telemetry: this.config.telemetry
23874
+ telemetry: this.config.telemetry,
23875
+ retryPolicy: this.config.retryPolicy
23766
23876
  }));
23767
23877
  context.setSession(config.session);
23768
23878
  service.initialize(context);
@@ -23772,7 +23882,6 @@ var _TinyCloudNode = class _TinyCloudNode {
23772
23882
  });
23773
23883
  sharingService.updateConfig({
23774
23884
  session: input.serviceSession,
23775
- delegationManager,
23776
23885
  sessionExpiry: input.sessionExpiry,
23777
23886
  createDelegationWasm: (params) => {
23778
23887
  graph.assertActive();
@@ -23803,7 +23912,8 @@ var _TinyCloudNode = class _TinyCloudNode {
23803
23912
  invokeAny: graph.invokeAny,
23804
23913
  fetch: graph.fetch,
23805
23914
  hosts: [input.host],
23806
- telemetry: this.config.telemetry
23915
+ telemetry: this.config.telemetry,
23916
+ retryPolicy: this.config.retryPolicy
23807
23917
  }));
23808
23918
  context.setSession({ ...input.serviceSession, spaceId });
23809
23919
  scopedKv.initialize(context);
@@ -23818,7 +23928,8 @@ var _TinyCloudNode = class _TinyCloudNode {
23818
23928
  invokeAny: graph.invokeAny,
23819
23929
  fetch: graph.fetch,
23820
23930
  hosts: [input.host],
23821
- telemetry: this.config.telemetry
23931
+ telemetry: this.config.telemetry,
23932
+ retryPolicy: this.config.retryPolicy
23822
23933
  }));
23823
23934
  context.setSession({ ...input.serviceSession, spaceId });
23824
23935
  scopedKv.initialize(context);
@@ -23901,6 +24012,21 @@ var _TinyCloudNode = class _TinyCloudNode {
23901
24012
  if (address === void 0 || chainId === void 0) {
23902
24013
  return void 0;
23903
24014
  }
24015
+ if (this.config.registryPolicy === "fail-closed") {
24016
+ const resolved2 = await resolveFailClosedTinyCloudHosts2(address, {
24017
+ registryUrl: this.config.tinycloudRegistryUrl,
24018
+ registryChainId: this.config.registryChainId,
24019
+ newUserHost: this.config.newUserHost,
24020
+ sequenceStore: this.config.registrySequenceStore,
24021
+ onNodeNotFound: this.config.onNodeNotFound,
24022
+ autoDiscoverLocalNode: this.config.autoDiscoverLocalNode,
24023
+ localNodeUrl: this.config.localNodeUrl,
24024
+ localLinkName: this.config.localLinkName,
24025
+ expectedNodeDid: this.config.expectedNodeDid,
24026
+ localNodeIdentityStore: this.config.localNodeIdentityStore
24027
+ });
24028
+ return resolved2.hosts[0];
24029
+ }
23904
24030
  const resolved = await resolveTinyCloudHosts2(pkhDid2(address, chainId), {
23905
24031
  registryUrl: this.config.tinycloudRegistryUrl,
23906
24032
  fallbackHosts: this.config.tinycloudFallbackHosts,
@@ -24093,6 +24219,7 @@ var _TinyCloudNode = class _TinyCloudNode {
24093
24219
  tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
24094
24220
  tinycloudRegistryUrl: this.config.tinycloudRegistryUrl,
24095
24221
  tinycloudFallbackHosts: this.config.tinycloudFallbackHosts,
24222
+ ...this.registryPolicyConfig(),
24096
24223
  autoDiscoverLocalNode: this.config.autoDiscoverLocalNode,
24097
24224
  localNodeUrl: this.config.localNodeUrl,
24098
24225
  localLinkName: this.config.localLinkName,
@@ -24114,7 +24241,8 @@ var _TinyCloudNode = class _TinyCloudNode {
24114
24241
  });
24115
24242
  this.tc = new TinyCloud(this.auth, {
24116
24243
  invokeAny: this.invokeAnyWithRuntimePermissions,
24117
- telemetry: this.config.telemetry
24244
+ telemetry: this.config.telemetry,
24245
+ retryPolicy: this.config.retryPolicy
24118
24246
  });
24119
24247
  this.config.prefix = prefix;
24120
24248
  }
@@ -24152,6 +24280,7 @@ var _TinyCloudNode = class _TinyCloudNode {
24152
24280
  tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
24153
24281
  tinycloudRegistryUrl: this.config.tinycloudRegistryUrl,
24154
24282
  tinycloudFallbackHosts: this.config.tinycloudFallbackHosts,
24283
+ ...this.registryPolicyConfig(),
24155
24284
  autoDiscoverLocalNode: this.config.autoDiscoverLocalNode,
24156
24285
  localNodeUrl: this.config.localNodeUrl,
24157
24286
  localLinkName: this.config.localLinkName,
@@ -24173,7 +24302,8 @@ var _TinyCloudNode = class _TinyCloudNode {
24173
24302
  });
24174
24303
  this.tc = new TinyCloud(this.auth, {
24175
24304
  invokeAny: this.invokeAnyWithRuntimePermissions,
24176
- telemetry: this.config.telemetry
24305
+ telemetry: this.config.telemetry,
24306
+ retryPolicy: this.config.retryPolicy
24177
24307
  });
24178
24308
  this.config.prefix = prefix;
24179
24309
  }
@@ -24197,7 +24327,8 @@ var _TinyCloudNode = class _TinyCloudNode {
24197
24327
  invokeAny: this._serviceGraph.invokeAny,
24198
24328
  fetch: this._serviceGraph.fetch,
24199
24329
  hosts: [this.config.host],
24200
- telemetry: this.config.telemetry
24330
+ telemetry: this.config.telemetry,
24331
+ retryPolicy: this.config.retryPolicy
24201
24332
  }));
24202
24333
  this._kv = new KVService2({});
24203
24334
  this._kv.initialize(this._serviceContext);
@@ -24244,7 +24375,8 @@ var _TinyCloudNode = class _TinyCloudNode {
24244
24375
  invokeAny: this._serviceContext.invokeAny,
24245
24376
  fetch: this._serviceContext.fetch,
24246
24377
  hosts: this._serviceContext.hosts,
24247
- telemetry: this.config.telemetry
24378
+ telemetry: this.config.telemetry,
24379
+ retryPolicy: this.config.retryPolicy
24248
24380
  }));
24249
24381
  const session = this._serviceContext.session;
24250
24382
  if (session) {
@@ -24695,7 +24827,6 @@ var _TinyCloudNode = class _TinyCloudNode {
24695
24827
  assertActive: () => graph.assertActive(),
24696
24828
  keyProvider: this._keyProvider,
24697
24829
  registry: this._capabilityRegistry,
24698
- delegationManager: this._delegationManager,
24699
24830
  sessionExpiry: this.getSessionExpiry(),
24700
24831
  createDelegationWasm: (params) => {
24701
24832
  graph.assertActive();
@@ -24712,7 +24843,8 @@ var _TinyCloudNode = class _TinyCloudNode {
24712
24843
  invoke: config.invoke,
24713
24844
  fetch: config.fetch ?? graph.fetch,
24714
24845
  hosts: config.hosts,
24715
- telemetry: this.config.telemetry
24846
+ telemetry: this.config.telemetry,
24847
+ retryPolicy: this.config.retryPolicy
24716
24848
  }));
24717
24849
  context.setSession(config.session);
24718
24850
  service.initialize(context);
@@ -25033,7 +25165,8 @@ var _TinyCloudNode = class _TinyCloudNode {
25033
25165
  invokeAny: this._serviceContext.invokeAny,
25034
25166
  fetch: this._serviceContext.fetch,
25035
25167
  hosts: this._serviceContext.hosts,
25036
- telemetry: this.config.telemetry
25168
+ telemetry: this.config.telemetry,
25169
+ retryPolicy: this.config.retryPolicy
25037
25170
  }));
25038
25171
  spaceScopedContext.setSession({ ...this._serviceContext.session, spaceId });
25039
25172
  sql.initialize(spaceScopedContext);
@@ -25236,6 +25369,7 @@ var _TinyCloudNode = class _TinyCloudNode {
25236
25369
  }
25237
25370
  };
25238
25371
  }
25372
+ if (result.status === "aad_mismatch") return { status: "read_failed" };
25239
25373
  return { status: result.status };
25240
25374
  }
25241
25375
  if (!isSecretPayload(result.entry.value)) {
@@ -25677,16 +25811,8 @@ var _TinyCloudNode = class _TinyCloudNode {
25677
25811
  * ```typescript
25678
25812
  * const delegations = alice.delegationManager;
25679
25813
  *
25680
- * // Create a delegation
25681
- * const result = await delegations.create({
25682
- * delegateDID: bob.did,
25683
- * path: "shared/",
25684
- * actions: ["tinycloud.kv/get", "tinycloud.kv/put"],
25685
- * expiry: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours
25686
- * });
25687
- *
25688
- * // List delegations
25689
- * const listResult = await delegations.list();
25814
+ * // Query account delegations
25815
+ * const page = await delegations.query();
25690
25816
  *
25691
25817
  * // Revoke a delegation
25692
25818
  * await delegations.revoke(delegationCid);
@@ -25788,7 +25914,9 @@ var _TinyCloudNode = class _TinyCloudNode {
25788
25914
  * Ensure the user's public space exists and is accessible.
25789
25915
  * Creates the space and activates a session delegation for it.
25790
25916
  * This is the trigger for lazy public space creation — call it
25791
- * before writing to spaces.get('public').kv.
25917
+ * before writing to spaces.get('public').kv. The vault calls it on unlock.
25918
+ * Under `registryPolicy: "fail-closed"` creation goes through the
25919
+ * space-creation guard.
25792
25920
  */
25793
25921
  async ensurePublicSpace() {
25794
25922
  if (!this.auth || !this.session || !this.signer) {
@@ -25798,6 +25926,7 @@ var _TinyCloudNode = class _TinyCloudNode {
25798
25926
  if (!publicSpaceId) {
25799
25927
  throw new Error("Public space not enabled. Set enablePublicSpace: true in config.");
25800
25928
  }
25929
+ await this.assertMayCreateSpace();
25801
25930
  await this.auth.hostPublicSpace(publicSpaceId);
25802
25931
  const kvActions = [KV3.PUT, KV3.GET, KV3.DEL, KV3.LIST, KV3.METADATA];
25803
25932
  const abilities = { kv: { "": kvActions } };
@@ -25855,7 +25984,8 @@ var _TinyCloudNode = class _TinyCloudNode {
25855
25984
  invoke: this._serviceGraph.invoke,
25856
25985
  fetch: this._serviceGraph.fetch,
25857
25986
  hosts: this._serviceContext.hosts,
25858
- telemetry: this.config.telemetry
25987
+ telemetry: this.config.telemetry,
25988
+ retryPolicy: this.config.retryPolicy
25859
25989
  }));
25860
25990
  publicContext.setSession({
25861
25991
  delegationHeader: delegationSession.delegationHeader,
@@ -25884,32 +26014,6 @@ var _TinyCloudNode = class _TinyCloudNode {
25884
26014
  // ===========================================================================
25885
26015
  // v2 Delegation Convenience Methods
25886
26016
  // ===========================================================================
25887
- /**
25888
- * Create a delegation using the v2 DelegationManager.
25889
- *
25890
- * This is a convenience method that wraps DelegationManager.create().
25891
- * For more control, use `this.delegationManager` directly.
25892
- *
25893
- * @param params - Delegation parameters
25894
- * @returns Result containing the created Delegation
25895
- *
25896
- * @example
25897
- * ```typescript
25898
- * const result = await alice.delegate({
25899
- * delegateDID: bob.did,
25900
- * path: "shared/",
25901
- * actions: ["tinycloud.kv/get", "tinycloud.kv/put"],
25902
- * expiry: new Date(Date.now() + 24 * 60 * 60 * 1000),
25903
- * });
25904
- *
25905
- * if (result.ok) {
25906
- * console.log("Delegation created:", result.data.cid);
25907
- * }
25908
- * ```
25909
- */
25910
- async delegate(params) {
25911
- return this.delegationManager.create(params);
25912
- }
25913
26017
  /**
25914
26018
  * Revoke a delegation with the owner wallet when available. Session-only
25915
26019
  * clients continue to use their delegated revocation authority.
@@ -25935,14 +26039,6 @@ var _TinyCloudNode = class _TinyCloudNode {
25935
26039
  0x55n
25936
26040
  );
25937
26041
  }
25938
- /**
25939
- * List all delegations for the current session's space.
25940
- *
25941
- * @returns Result containing an array of Delegations
25942
- */
25943
- async listDelegations() {
25944
- return this.delegationManager.list();
25945
- }
25946
26042
  /**
25947
26043
  * Check if the current session has permission for a path and action.
25948
26044
  *
@@ -26002,6 +26098,9 @@ var _TinyCloudNode = class _TinyCloudNode {
26002
26098
  "delegateTo: onPrepared cannot be combined with forceWalletSign=true; the wallet-signed path activates the delegation before it could be handed to onPrepared."
26003
26099
  );
26004
26100
  }
26101
+ if (options?.disableSubDelegation && did.startsWith("did:pkh:")) {
26102
+ throw new Error(TERMINAL_DELEGATION_REQUIRES_SESSION_KEY);
26103
+ }
26005
26104
  const session = this.currentTinyCloudSession();
26006
26105
  if (!session) {
26007
26106
  throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
@@ -26067,7 +26166,8 @@ var _TinyCloudNode = class _TinyCloudNode {
26067
26166
  plan.grant,
26068
26167
  plan.operations,
26069
26168
  onPrepared,
26070
- options?.disableSubDelegation === true
26169
+ options?.disableSubDelegation === true,
26170
+ options?.requireActivation === true
26071
26171
  );
26072
26172
  return { delegation: delegation2, prompted: false };
26073
26173
  }
@@ -26077,7 +26177,8 @@ var _TinyCloudNode = class _TinyCloudNode {
26077
26177
  effectiveExpiration,
26078
26178
  session,
26079
26179
  onPrepared,
26080
- options?.disableSubDelegation === true
26180
+ options?.disableSubDelegation === true,
26181
+ options?.requireActivation === true
26081
26182
  );
26082
26183
  return { delegation, prompted: false };
26083
26184
  }
@@ -26308,7 +26409,7 @@ var _TinyCloudNode = class _TinyCloudNode {
26308
26409
  *
26309
26410
  * @internal
26310
26411
  */
26311
- async createDelegationViaWasmPath(did, entries, expirationTime, session, onPrepared, disableSubDelegation = false) {
26412
+ async createDelegationViaWasmPath(did, entries, expirationTime, session, onPrepared, disableSubDelegation = false, requireActivation = false) {
26312
26413
  if (entries.length === 0) {
26313
26414
  throw new Error(
26314
26415
  "createDelegationViaWasmPath requires a non-empty entries array"
@@ -26392,15 +26493,10 @@ var _TinyCloudNode = class _TinyCloudNode {
26392
26493
  this.config.host,
26393
26494
  delegationHeader
26394
26495
  );
26395
- if (!activateResult.success) {
26396
- throw Object.assign(
26397
- new Error(`Failed to activate delegation with host: ${describeHostFailure2(activateResult)}`),
26398
- { cause: activateResult }
26399
- );
26400
- }
26496
+ assertDelegationActivated(activateResult, delegation, requireActivation);
26401
26497
  return delegation;
26402
26498
  }
26403
- async createDelegationViaRuntimeGrant(did, entries, expirationTime, grant, requestedOperations, onPrepared, disableSubDelegation = false) {
26499
+ async createDelegationViaRuntimeGrant(did, entries, expirationTime, grant, requestedOperations, onPrepared, disableSubDelegation = false, requireActivation = false) {
26404
26500
  this.assertRuntimeGrantCaveatsPreservable(entries, requestedOperations, grant);
26405
26501
  this.assertDelegationCaveatsPreservable(entries);
26406
26502
  const result = this.createDelegationWrapper({
@@ -26434,12 +26530,7 @@ var _TinyCloudNode = class _TinyCloudNode {
26434
26530
  targetHost,
26435
26531
  delegationHeader
26436
26532
  );
26437
- if (!activateResult.success) {
26438
- throw Object.assign(
26439
- new Error(`Failed to activate delegation with host: ${describeHostFailure2(activateResult)}`),
26440
- { cause: activateResult }
26441
- );
26442
- }
26533
+ assertDelegationActivated(activateResult, delegation, requireActivation);
26443
26534
  return delegation;
26444
26535
  }
26445
26536
  resolvePermissionSpace(space, session) {
@@ -27386,6 +27477,8 @@ var _TinyCloudNode = class _TinyCloudNode {
27386
27477
  };
27387
27478
  }
27388
27479
  };
27480
+ /** Set once per process: the registry sync was skipped for want of account access. */
27481
+ _TinyCloudNode.accountRegistrySkipLogged = false;
27389
27482
  // ===========================================================================
27390
27483
  // Capability-chain delegation (spec: .claude/specs/capability-chain.md)
27391
27484
  // ===========================================================================
@@ -27440,7 +27533,7 @@ import {
27440
27533
  principalDidEquals as principalDidEquals4,
27441
27534
  parseCanonicalNetworkId,
27442
27535
  TinyCloudDebugLogger,
27443
- tinyCloudDebugLogger,
27536
+ tinyCloudDebugLogger as tinyCloudDebugLogger2,
27444
27537
  enableTinyCloudDebug,
27445
27538
  disableTinyCloudDebug,
27446
27539
  getTinyCloudDebugLogs,
@@ -28945,6 +29038,34 @@ assertSyncEffective_fn = async function() {
28945
29038
  throw new ReplicaStorageError("GRANT_EXPIRED", "The replica grant's effective lifetime has expired.");
28946
29039
  }
28947
29040
  };
29041
+ async function listDir(dir) {
29042
+ try {
29043
+ return await readdir(dir);
29044
+ } catch (error) {
29045
+ if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
29046
+ return [];
29047
+ }
29048
+ throw error;
29049
+ }
29050
+ }
29051
+ async function collectProfileOrphans(root, collectOrphans) {
29052
+ for (const partition of await listDir(root)) {
29053
+ const replicasDir = join2(root, partition, "replicas");
29054
+ for (const replicaName of await listDir(replicasDir)) {
29055
+ await collectOrphans(join2(replicasDir, replicaName));
29056
+ }
29057
+ }
29058
+ }
29059
+ async function profileContentBytes(root, contentBytes) {
29060
+ let total = 0;
29061
+ for (const partition of await listDir(root)) {
29062
+ const replicasDir = join2(root, partition, "replicas");
29063
+ for (const replicaName of await listDir(replicasDir)) {
29064
+ total += await contentBytes(join2(replicasDir, replicaName));
29065
+ }
29066
+ }
29067
+ return total;
29068
+ }
28948
29069
  function createSqliteReplicaStorage(options) {
28949
29070
  const root = options.dir;
28950
29071
  const profileDir = dirname2(options.gateFile ?? join2(dirname2(root), REPLICA_GATE_FILE));
@@ -29939,10 +30060,29 @@ function createSqliteReplicaStorage(options) {
29939
30060
  const replicaHash = replicaHashOf(spec.prefix, device.did);
29940
30061
  partitionId = `${identityKeyHash}:${replicaHash}`;
29941
30062
  const replicaDir = join2(idDir, "replicas", replicaHash);
29942
- const store = await sqlite.SqliteReplicaStore.open(replicaDir, {
29943
- create: true,
29944
- guard: storeGuard
29945
- });
30063
+ const quota = spec.quota?.maxBytes ?? options.maxBytes;
30064
+ if (quota !== void 0 && (!Number.isSafeInteger(quota) || quota < 0)) {
30065
+ throw new TypeError(`maxBytes must be a non-negative safe integer, got ${quota}.`);
30066
+ }
30067
+ if (quota !== void 0) await collectProfileOrphans(root, sqlite.SqliteReplicaStore.collectOrphans);
30068
+ const store = await sqlite.SqliteReplicaStore.open(
30069
+ replicaDir,
30070
+ quota === void 0 ? { create: true, guard: storeGuard } : {
30071
+ create: true,
30072
+ guard: storeGuard,
30073
+ partitionId: basename(idDir),
30074
+ admit: async (page) => {
30075
+ await collectProfileOrphans(root, sqlite.SqliteReplicaStore.collectOrphans);
30076
+ const used = await profileContentBytes(root, sqlite.SqliteReplicaStore.contentBytes);
30077
+ if (page.blobBytes > 0 && used + page.blobBytes > quota) {
30078
+ throw new replica.ReplicaError(
30079
+ replica.ReplicaErrorCode.STORAGE_BUDGET,
30080
+ `Profile storage holds ${used} bytes; this page adds ${page.blobBytes} against a ${quota}-byte quota. Nothing was committed.`
30081
+ );
30082
+ }
30083
+ }
30084
+ }
30085
+ );
29946
30086
  let generation;
29947
30087
  try {
29948
30088
  try {
@@ -30369,7 +30509,7 @@ export {
30369
30509
  siweRuntimeDelegation,
30370
30510
  sqliteReplicaStorage,
30371
30511
  storageUsageState,
30372
- tinyCloudDebugLogger,
30512
+ tinyCloudDebugLogger2 as tinyCloudDebugLogger,
30373
30513
  validateEnvelope,
30374
30514
  validateManifest,
30375
30515
  verifyDecryptResponse,