@tinycloud/node-sdk 3.3.0-beta.18 → 3.3.0-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import * as _tinycloud_sdk_core from '@tinycloud/sdk-core';
2
- import { AutoSignStrategy, AutoRejectStrategy, CallbackStrategy, CapabilityPresentationEnvelopeV1, IUserAuthorization, ISigner, ISessionStorage, ISpaceCreationHandler, LocalNodeIdentityStore, IWasmBindings, ISessionManager, SiweConfig, Manifest, ComposedManifestRequest, ReplicationOptions, TinyCloudSession, PersistedSessionData, ClientSession, PermissionEntry, Extension, SignRequest, SpaceHostResult, SignInOptions, Delegation, DelegatedResource, TelemetryConfig, ServiceContext, IKVService, ISQLService, IDuckDbService, IHooksService, CreateOwnerDelegationParams as CreateOwnerDelegationParams$1, OwnerDelegationReceipt as OwnerDelegationReceipt$1, StorageFullEvent, IStorageService, INotificationHandler, IENSResolver, KVReplicaStorage, AccountService, InvokeAnyFunction as InvokeAnyFunction$1, UnifiedPolicyCapability, RegisterPolicyV3Input, RegisterPolicyV3Receipt, CanonicalJson, ShareDeliveryAuthorizationV3Receipt, IDataVaultService, IEncryptionService, NetworkDescriptor, ISecretsService, ICapabilityKeyRegistry, DelegationManager, ISpaceService, ISpace, ISharingService, CreateDelegationParams, DelegationResult, DelegationRevocationReceipt, DelegationStatus, ServiceSession, ReplicationControl, ResolvedDelegate } from '@tinycloud/sdk-core';
2
+ import { AutoSignStrategy, AutoRejectStrategy, CallbackStrategy, CapabilityPresentationEnvelopeV1, IUserAuthorization, ISigner, ISessionStorage, ISpaceCreationHandler, RegistryPolicy, RegistrySequenceStore, OnNodeNotFound, LocalNodeIdentityStore, IWasmBindings, ISessionManager, SiweConfig, Manifest, ComposedManifestRequest, ReplicationOptions, TinyCloudSession, PersistedSessionData, ClientSession, PermissionEntry, Extension, SignRequest, SpaceHostResult, SignInOptions, Delegation, DelegatedResource, TelemetryConfig, ServiceContext, IKVService, ISQLService, IDuckDbService, IHooksService, CreateOwnerDelegationParams as CreateOwnerDelegationParams$1, OwnerDelegationReceipt as OwnerDelegationReceipt$1, StorageFullEvent, IStorageService, INotificationHandler, IENSResolver, KVReplicaStorage, AccountService, InvokeAnyFunction as InvokeAnyFunction$1, UnifiedPolicyCapability, RegisterPolicyV3Input, RegisterPolicyV3Receipt, CanonicalJson, ShareDeliveryAuthorizationV3Receipt, IDataVaultService, IEncryptionService, NetworkDescriptor, ISecretsService, ICapabilityKeyRegistry, DelegationManager, ISpaceService, ISpace, ISharingService, CreateDelegationParams, DelegationResult, DelegationRevocationReceipt, DelegationStatus, ServiceSession, ReplicationControl, ResolvedDelegate } from '@tinycloud/sdk-core';
3
3
  import { ReplicaSessionStamp } from '@tinycloud/sdk-services/kv/replication';
4
4
  import { EventEmitter } from 'events';
5
5
  import { InvokeFunction, InvokeAnyFunction, FetchFunction } from '@tinycloud/sdk-services';
@@ -181,8 +181,46 @@ interface NodeUserAuthorizationConfig {
181
181
  tinycloudHosts?: string[];
182
182
  /** TinyCloud location registry URL. Default: https://registry.tinycloud.xyz. */
183
183
  tinycloudRegistryUrl?: string | null;
184
- /** Fallback TinyCloud hosts. Default: hosted TinyCloud node. */
184
+ /**
185
+ * Fallback TinyCloud hosts. Default: hosted TinyCloud node. Used only by the
186
+ * default `registryPolicy` ("legacy-fallback"), whose silent fallback is
187
+ * deprecated and will be removed in a future major release.
188
+ */
185
189
  tinycloudFallbackHosts?: string[] | null;
190
+ /**
191
+ * How sign-in uses the location registry when no host is configured.
192
+ *
193
+ * - `"legacy-fallback"` (default, deprecated): a registry miss or failure
194
+ * silently uses `tinycloudFallbackHosts`, with a one-time warning.
195
+ * - `"fail-closed"`: sign-in acts on the registry verdict for the signer
196
+ * address. Registered owners go to their listed hosts, unregistered owners
197
+ * to `newUserHost`, and an unavailable registry is
198
+ * `RegistryUnavailableError`, never a new account. Automatic space
199
+ * creation is refused on a host the owner's record does not list
200
+ * (`NodeRegistryEntryExistsError`) or when the registry gives no verdict
201
+ * (`RegistryUnavailableError`).
202
+ */
203
+ registryPolicy?: RegistryPolicy;
204
+ /**
205
+ * `fail-closed` only: host for owners with no registry record (and only
206
+ * them). Default https://node.tinycloud.xyz. `null` refuses them with
207
+ * `NodeNotFoundError`.
208
+ */
209
+ newUserHost?: string | null;
210
+ /**
211
+ * `fail-closed` only: chain ID of the v1 registry subject, which is built
212
+ * from the signer address, never the session chain. Default 1.
213
+ */
214
+ registryChainId?: number;
215
+ /** `fail-closed` only: highest registry sequence seen per owner. Default: in-memory. */
216
+ registrySequenceStore?: RegistrySequenceStore;
217
+ /**
218
+ * `fail-closed` only: called when sign-in resolves no host
219
+ * (`NodeNotFoundError` or `RegistryUnavailableError`) to retry, use another
220
+ * host (not identity-checked; it persists with the session), or abort.
221
+ * Without it, sign-in throws the error.
222
+ */
223
+ onNodeNotFound?: OnNodeNotFound;
186
224
  /** Probe configured/registered local nodes before registry/fallback resolution. Default: true. */
187
225
  autoDiscoverLocalNode?: boolean;
188
226
  /** Local loopback node URL to probe. Omit to avoid probing loopback. */
@@ -295,6 +333,13 @@ declare class NodeUserAuthorization implements IUserAuthorization {
295
333
  private tinycloudHosts?;
296
334
  private readonly tinycloudRegistryUrl?;
297
335
  private readonly tinycloudFallbackHosts?;
336
+ private readonly registryPolicy?;
337
+ private readonly newUserHost?;
338
+ private readonly registryChainId?;
339
+ private readonly registrySequenceStore;
340
+ private readonly onNodeNotFound?;
341
+ /** Verdict from the last `fail-closed` host resolution, reused by {@link assertMayCreateSpace}. */
342
+ private registryVerdict?;
298
343
  private readonly autoDiscoverLocalNode?;
299
344
  private readonly localNodeUrl?;
300
345
  private readonly localLinkName?;
@@ -415,6 +460,16 @@ declare class NodeUserAuthorization implements IUserAuthorization {
415
460
  */
416
461
  ensureTinyCloudHosts(): Promise<void>;
417
462
  private resolveTinyCloudHostsForSignIn;
463
+ /**
464
+ * Space-creation guard (TC-1443). Under `registryPolicy: "fail-closed"`,
465
+ * throws unless the owner's registry verdict allows a space on the primary
466
+ * host: `NodeRegistryEntryExistsError` when the owner's record does not
467
+ * list it, `RegistryUnavailableError` when the registry gives no
468
+ * verdict. Reuses this session's sign-in verdict unless it was
469
+ * `unavailable`, else looks it up now (explicit-host apps). Under the
470
+ * default policy it returns at once and performs no lookup.
471
+ */
472
+ assertMayCreateSpace(): Promise<void>;
418
473
  private requireTinyCloudHosts;
419
474
  private get primaryTinyCloudHost();
420
475
  get nodeFeatures(): string[];
@@ -551,6 +606,8 @@ declare class NodeUserAuthorization implements IUserAuthorization {
551
606
  * @throws Error if space creation fails
552
607
  */
553
608
  ensureSpaceExists(): Promise<void>;
609
+ /** {@link assertMayCreateSpace}, reporting a refusal to the creation handler before rethrowing it. */
610
+ private guardSpaceCreation;
554
611
  private recordActivationSkippedSpaces;
555
612
  createBootstrapSession(options: CreateBootstrapSessionOptions): Promise<TinyCloudSession>;
556
613
  private persistedData;
@@ -1175,8 +1232,35 @@ interface TinyCloudNodeConfig {
1175
1232
  host?: string;
1176
1233
  /** TinyCloud location registry URL. Default: https://registry.tinycloud.xyz. */
1177
1234
  tinycloudRegistryUrl?: string | null;
1178
- /** Fallback TinyCloud hosts. Default: hosted TinyCloud node. */
1235
+ /**
1236
+ * Fallback TinyCloud hosts. Default: hosted TinyCloud node. Used only by the
1237
+ * default `registryPolicy` ("legacy-fallback"), whose silent fallback is
1238
+ * deprecated and will be removed in a future major release.
1239
+ */
1179
1240
  tinycloudFallbackHosts?: string[] | null;
1241
+ /**
1242
+ * How sign-in uses the location registry when no `host` is configured.
1243
+ * Default `"legacy-fallback"` (deprecated): a registry miss or failure
1244
+ * silently uses `tinycloudFallbackHosts`, with a one-time warning.
1245
+ * `"fail-closed"`: routing acts on the registry verdict for the signer
1246
+ * address, an unavailable registry is `RegistryUnavailableError` (never a
1247
+ * new account), record hosts are used only if HTTPS or loopback, and
1248
+ * automatic space creation (sign-in, account bootstrap, owned-space
1249
+ * hosting, the public space the vault creates) is refused on a host the
1250
+ * owner's registry record does not list (`NodeRegistryEntryExistsError`)
1251
+ * or when the registry gives no verdict. An explicit `hostOwnedSpace()`
1252
+ * call is not guarded. `newUserHost` governs routing only: a pinned host
1253
+ * may still create spaces for an unregistered owner.
1254
+ */
1255
+ registryPolicy?: RegistryPolicy;
1256
+ /** `fail-closed` only: host for owners with no registry record. Default https://node.tinycloud.xyz; `null` refuses them with `NodeNotFoundError`. */
1257
+ newUserHost?: string | null;
1258
+ /** `fail-closed` only: chain ID of the v1 registry subject built from the signer address (never the session chain). Default 1. */
1259
+ registryChainId?: number;
1260
+ /** `fail-closed` only: highest registry sequence seen per owner. Default: in-memory for this client. */
1261
+ registrySequenceStore?: RegistrySequenceStore;
1262
+ /** `fail-closed` only: retry, use another host (not identity-checked; it persists with the session), or abort when sign-in resolves no host. Without it, sign-in throws. */
1263
+ onNodeNotFound?: OnNodeNotFound;
1180
1264
  /** Probe configured/registered local nodes before registry/fallback resolution. Default: true. */
1181
1265
  autoDiscoverLocalNode?: boolean;
1182
1266
  /** Local loopback node URL to probe. Omit to avoid probing loopback. */
@@ -1904,10 +1988,26 @@ declare class TinyCloudNode {
1904
1988
  * session's recap doesn't cover it and the registry has no record; the host
1905
1989
  * SIWE is idempotent server-side. Must be called after {@link signIn}.
1906
1990
  *
1991
+ * Under `registryPolicy: "fail-closed"`, hosting is refused with
1992
+ * `NodeRegistryEntryExistsError` when the owner's registry record does not
1993
+ * list the host, or `RegistryUnavailableError` when the registry gives no
1994
+ * verdict. {@link hostOwnedSpace} itself is an explicit action and is not
1995
+ * guarded.
1996
+ *
1907
1997
  * @param name - The owned space name (e.g. `"secrets"`).
1908
1998
  * @returns The hosted owned-space URI.
1909
1999
  */
1910
2000
  ensureOwnedSpaceHosted(name: string): Promise<string>;
2001
+ /**
2002
+ * Space-creation guard (TC-1443) for the automatic hosting paths: sign-in's
2003
+ * owned spaces, account bootstrap, {@link ensureOwnedSpaceHosted} and
2004
+ * {@link ensurePublicSpace}. Only `registryPolicy: "fail-closed"` consults
2005
+ * the registry ({@link NodeUserAuthorization.assertMayCreateSpace}); the
2006
+ * default policy performs no lookup.
2007
+ */
2008
+ private assertMayCreateSpace;
2009
+ /** Registry-policy settings shared by every authorization this client creates. */
2010
+ private registryPolicyConfig;
1911
2011
  /**
1912
2012
  * Submit the host delegation for an owned space, sharing one in-flight
1913
2013
  * request (and so one approval) between concurrent callers in this session,
@@ -2457,7 +2557,9 @@ declare class TinyCloudNode {
2457
2557
  * Ensure the user's public space exists and is accessible.
2458
2558
  * Creates the space and activates a session delegation for it.
2459
2559
  * This is the trigger for lazy public space creation — call it
2460
- * before writing to spaces.get('public').kv.
2560
+ * before writing to spaces.get('public').kv. The vault calls it on unlock.
2561
+ * Under `registryPolicy: "fail-closed"` creation goes through the
2562
+ * space-creation guard.
2461
2563
  */
2462
2564
  ensurePublicSpace(): Promise<void>;
2463
2565
  /**
@@ -1,5 +1,5 @@
1
1
  import * as _tinycloud_sdk_core from '@tinycloud/sdk-core';
2
- import { AutoSignStrategy, AutoRejectStrategy, CallbackStrategy, CapabilityPresentationEnvelopeV1, IUserAuthorization, ISigner, ISessionStorage, ISpaceCreationHandler, LocalNodeIdentityStore, IWasmBindings, ISessionManager, SiweConfig, Manifest, ComposedManifestRequest, ReplicationOptions, TinyCloudSession, PersistedSessionData, ClientSession, PermissionEntry, Extension, SignRequest, SpaceHostResult, SignInOptions, Delegation, DelegatedResource, TelemetryConfig, ServiceContext, IKVService, ISQLService, IDuckDbService, IHooksService, CreateOwnerDelegationParams as CreateOwnerDelegationParams$1, OwnerDelegationReceipt as OwnerDelegationReceipt$1, StorageFullEvent, IStorageService, INotificationHandler, IENSResolver, KVReplicaStorage, AccountService, InvokeAnyFunction as InvokeAnyFunction$1, UnifiedPolicyCapability, RegisterPolicyV3Input, RegisterPolicyV3Receipt, CanonicalJson, ShareDeliveryAuthorizationV3Receipt, IDataVaultService, IEncryptionService, NetworkDescriptor, ISecretsService, ICapabilityKeyRegistry, DelegationManager, ISpaceService, ISpace, ISharingService, CreateDelegationParams, DelegationResult, DelegationRevocationReceipt, DelegationStatus, ServiceSession, ReplicationControl, ResolvedDelegate } from '@tinycloud/sdk-core';
2
+ import { AutoSignStrategy, AutoRejectStrategy, CallbackStrategy, CapabilityPresentationEnvelopeV1, IUserAuthorization, ISigner, ISessionStorage, ISpaceCreationHandler, RegistryPolicy, RegistrySequenceStore, OnNodeNotFound, LocalNodeIdentityStore, IWasmBindings, ISessionManager, SiweConfig, Manifest, ComposedManifestRequest, ReplicationOptions, TinyCloudSession, PersistedSessionData, ClientSession, PermissionEntry, Extension, SignRequest, SpaceHostResult, SignInOptions, Delegation, DelegatedResource, TelemetryConfig, ServiceContext, IKVService, ISQLService, IDuckDbService, IHooksService, CreateOwnerDelegationParams as CreateOwnerDelegationParams$1, OwnerDelegationReceipt as OwnerDelegationReceipt$1, StorageFullEvent, IStorageService, INotificationHandler, IENSResolver, KVReplicaStorage, AccountService, InvokeAnyFunction as InvokeAnyFunction$1, UnifiedPolicyCapability, RegisterPolicyV3Input, RegisterPolicyV3Receipt, CanonicalJson, ShareDeliveryAuthorizationV3Receipt, IDataVaultService, IEncryptionService, NetworkDescriptor, ISecretsService, ICapabilityKeyRegistry, DelegationManager, ISpaceService, ISpace, ISharingService, CreateDelegationParams, DelegationResult, DelegationRevocationReceipt, DelegationStatus, ServiceSession, ReplicationControl, ResolvedDelegate } from '@tinycloud/sdk-core';
3
3
  import { ReplicaSessionStamp } from '@tinycloud/sdk-services/kv/replication';
4
4
  import { EventEmitter } from 'events';
5
5
  import { InvokeFunction, InvokeAnyFunction, FetchFunction } from '@tinycloud/sdk-services';
@@ -181,8 +181,46 @@ interface NodeUserAuthorizationConfig {
181
181
  tinycloudHosts?: string[];
182
182
  /** TinyCloud location registry URL. Default: https://registry.tinycloud.xyz. */
183
183
  tinycloudRegistryUrl?: string | null;
184
- /** Fallback TinyCloud hosts. Default: hosted TinyCloud node. */
184
+ /**
185
+ * Fallback TinyCloud hosts. Default: hosted TinyCloud node. Used only by the
186
+ * default `registryPolicy` ("legacy-fallback"), whose silent fallback is
187
+ * deprecated and will be removed in a future major release.
188
+ */
185
189
  tinycloudFallbackHosts?: string[] | null;
190
+ /**
191
+ * How sign-in uses the location registry when no host is configured.
192
+ *
193
+ * - `"legacy-fallback"` (default, deprecated): a registry miss or failure
194
+ * silently uses `tinycloudFallbackHosts`, with a one-time warning.
195
+ * - `"fail-closed"`: sign-in acts on the registry verdict for the signer
196
+ * address. Registered owners go to their listed hosts, unregistered owners
197
+ * to `newUserHost`, and an unavailable registry is
198
+ * `RegistryUnavailableError`, never a new account. Automatic space
199
+ * creation is refused on a host the owner's record does not list
200
+ * (`NodeRegistryEntryExistsError`) or when the registry gives no verdict
201
+ * (`RegistryUnavailableError`).
202
+ */
203
+ registryPolicy?: RegistryPolicy;
204
+ /**
205
+ * `fail-closed` only: host for owners with no registry record (and only
206
+ * them). Default https://node.tinycloud.xyz. `null` refuses them with
207
+ * `NodeNotFoundError`.
208
+ */
209
+ newUserHost?: string | null;
210
+ /**
211
+ * `fail-closed` only: chain ID of the v1 registry subject, which is built
212
+ * from the signer address, never the session chain. Default 1.
213
+ */
214
+ registryChainId?: number;
215
+ /** `fail-closed` only: highest registry sequence seen per owner. Default: in-memory. */
216
+ registrySequenceStore?: RegistrySequenceStore;
217
+ /**
218
+ * `fail-closed` only: called when sign-in resolves no host
219
+ * (`NodeNotFoundError` or `RegistryUnavailableError`) to retry, use another
220
+ * host (not identity-checked; it persists with the session), or abort.
221
+ * Without it, sign-in throws the error.
222
+ */
223
+ onNodeNotFound?: OnNodeNotFound;
186
224
  /** Probe configured/registered local nodes before registry/fallback resolution. Default: true. */
187
225
  autoDiscoverLocalNode?: boolean;
188
226
  /** Local loopback node URL to probe. Omit to avoid probing loopback. */
@@ -295,6 +333,13 @@ declare class NodeUserAuthorization implements IUserAuthorization {
295
333
  private tinycloudHosts?;
296
334
  private readonly tinycloudRegistryUrl?;
297
335
  private readonly tinycloudFallbackHosts?;
336
+ private readonly registryPolicy?;
337
+ private readonly newUserHost?;
338
+ private readonly registryChainId?;
339
+ private readonly registrySequenceStore;
340
+ private readonly onNodeNotFound?;
341
+ /** Verdict from the last `fail-closed` host resolution, reused by {@link assertMayCreateSpace}. */
342
+ private registryVerdict?;
298
343
  private readonly autoDiscoverLocalNode?;
299
344
  private readonly localNodeUrl?;
300
345
  private readonly localLinkName?;
@@ -415,6 +460,16 @@ declare class NodeUserAuthorization implements IUserAuthorization {
415
460
  */
416
461
  ensureTinyCloudHosts(): Promise<void>;
417
462
  private resolveTinyCloudHostsForSignIn;
463
+ /**
464
+ * Space-creation guard (TC-1443). Under `registryPolicy: "fail-closed"`,
465
+ * throws unless the owner's registry verdict allows a space on the primary
466
+ * host: `NodeRegistryEntryExistsError` when the owner's record does not
467
+ * list it, `RegistryUnavailableError` when the registry gives no
468
+ * verdict. Reuses this session's sign-in verdict unless it was
469
+ * `unavailable`, else looks it up now (explicit-host apps). Under the
470
+ * default policy it returns at once and performs no lookup.
471
+ */
472
+ assertMayCreateSpace(): Promise<void>;
418
473
  private requireTinyCloudHosts;
419
474
  private get primaryTinyCloudHost();
420
475
  get nodeFeatures(): string[];
@@ -551,6 +606,8 @@ declare class NodeUserAuthorization implements IUserAuthorization {
551
606
  * @throws Error if space creation fails
552
607
  */
553
608
  ensureSpaceExists(): Promise<void>;
609
+ /** {@link assertMayCreateSpace}, reporting a refusal to the creation handler before rethrowing it. */
610
+ private guardSpaceCreation;
554
611
  private recordActivationSkippedSpaces;
555
612
  createBootstrapSession(options: CreateBootstrapSessionOptions): Promise<TinyCloudSession>;
556
613
  private persistedData;
@@ -1175,8 +1232,35 @@ interface TinyCloudNodeConfig {
1175
1232
  host?: string;
1176
1233
  /** TinyCloud location registry URL. Default: https://registry.tinycloud.xyz. */
1177
1234
  tinycloudRegistryUrl?: string | null;
1178
- /** Fallback TinyCloud hosts. Default: hosted TinyCloud node. */
1235
+ /**
1236
+ * Fallback TinyCloud hosts. Default: hosted TinyCloud node. Used only by the
1237
+ * default `registryPolicy` ("legacy-fallback"), whose silent fallback is
1238
+ * deprecated and will be removed in a future major release.
1239
+ */
1179
1240
  tinycloudFallbackHosts?: string[] | null;
1241
+ /**
1242
+ * How sign-in uses the location registry when no `host` is configured.
1243
+ * Default `"legacy-fallback"` (deprecated): a registry miss or failure
1244
+ * silently uses `tinycloudFallbackHosts`, with a one-time warning.
1245
+ * `"fail-closed"`: routing acts on the registry verdict for the signer
1246
+ * address, an unavailable registry is `RegistryUnavailableError` (never a
1247
+ * new account), record hosts are used only if HTTPS or loopback, and
1248
+ * automatic space creation (sign-in, account bootstrap, owned-space
1249
+ * hosting, the public space the vault creates) is refused on a host the
1250
+ * owner's registry record does not list (`NodeRegistryEntryExistsError`)
1251
+ * or when the registry gives no verdict. An explicit `hostOwnedSpace()`
1252
+ * call is not guarded. `newUserHost` governs routing only: a pinned host
1253
+ * may still create spaces for an unregistered owner.
1254
+ */
1255
+ registryPolicy?: RegistryPolicy;
1256
+ /** `fail-closed` only: host for owners with no registry record. Default https://node.tinycloud.xyz; `null` refuses them with `NodeNotFoundError`. */
1257
+ newUserHost?: string | null;
1258
+ /** `fail-closed` only: chain ID of the v1 registry subject built from the signer address (never the session chain). Default 1. */
1259
+ registryChainId?: number;
1260
+ /** `fail-closed` only: highest registry sequence seen per owner. Default: in-memory for this client. */
1261
+ registrySequenceStore?: RegistrySequenceStore;
1262
+ /** `fail-closed` only: retry, use another host (not identity-checked; it persists with the session), or abort when sign-in resolves no host. Without it, sign-in throws. */
1263
+ onNodeNotFound?: OnNodeNotFound;
1180
1264
  /** Probe configured/registered local nodes before registry/fallback resolution. Default: true. */
1181
1265
  autoDiscoverLocalNode?: boolean;
1182
1266
  /** Local loopback node URL to probe. Omit to avoid probing loopback. */
@@ -1904,10 +1988,26 @@ declare class TinyCloudNode {
1904
1988
  * session's recap doesn't cover it and the registry has no record; the host
1905
1989
  * SIWE is idempotent server-side. Must be called after {@link signIn}.
1906
1990
  *
1991
+ * Under `registryPolicy: "fail-closed"`, hosting is refused with
1992
+ * `NodeRegistryEntryExistsError` when the owner's registry record does not
1993
+ * list the host, or `RegistryUnavailableError` when the registry gives no
1994
+ * verdict. {@link hostOwnedSpace} itself is an explicit action and is not
1995
+ * guarded.
1996
+ *
1907
1997
  * @param name - The owned space name (e.g. `"secrets"`).
1908
1998
  * @returns The hosted owned-space URI.
1909
1999
  */
1910
2000
  ensureOwnedSpaceHosted(name: string): Promise<string>;
2001
+ /**
2002
+ * Space-creation guard (TC-1443) for the automatic hosting paths: sign-in's
2003
+ * owned spaces, account bootstrap, {@link ensureOwnedSpaceHosted} and
2004
+ * {@link ensurePublicSpace}. Only `registryPolicy: "fail-closed"` consults
2005
+ * the registry ({@link NodeUserAuthorization.assertMayCreateSpace}); the
2006
+ * default policy performs no lookup.
2007
+ */
2008
+ private assertMayCreateSpace;
2009
+ /** Registry-policy settings shared by every authorization this client creates. */
2010
+ private registryPolicyConfig;
1911
2011
  /**
1912
2012
  * Submit the host delegation for an owned space, sharing one in-flight
1913
2013
  * request (and so one approval) between concurrent callers in this session,
@@ -2457,7 +2557,9 @@ declare class TinyCloudNode {
2457
2557
  * Ensure the user's public space exists and is accessible.
2458
2558
  * Creates the space and activates a session delegation for it.
2459
2559
  * This is the trigger for lazy public space creation — call it
2460
- * before writing to spaces.get('public').kv.
2560
+ * before writing to spaces.get('public').kv. The vault calls it on unlock.
2561
+ * Under `registryPolicy: "fail-closed"` creation goes through the
2562
+ * space-creation guard.
2461
2563
  */
2462
2564
  ensurePublicSpace(): Promise<void>;
2463
2565
  /**
@@ -1,7 +1,7 @@
1
1
  import { ReplicationOptions, PermissionEntry, TinyCloudSession, KVReplicaStorage, ServiceContext, IWasmBindings, WasmRecapEntry } from '@tinycloud/sdk-core';
2
2
  import { ReplicaConsentAnchor, AuthorityRefusal, ReplicationAuthority } from '@tinycloud/sdk-services';
3
3
  import { ReplicaSessionStamp, ReplicaDenyInput } from '@tinycloud/sdk-services/kv/replication';
4
- import { T as TinyCloudNode } from './TinyCloudNode-DmF9QUcS.cjs';
4
+ import { T as TinyCloudNode } from './TinyCloudNode-BPDOmuf6.cjs';
5
5
 
6
6
  /** Constructor-time validation; the space-dependent secrets check runs at sign-in and open. */
7
7
  declare function assertValidReplicationConfig(replication: (ReplicationOptions & {
@@ -1,7 +1,7 @@
1
1
  import { ReplicationOptions, PermissionEntry, TinyCloudSession, KVReplicaStorage, ServiceContext, IWasmBindings, WasmRecapEntry } from '@tinycloud/sdk-core';
2
2
  import { ReplicaConsentAnchor, AuthorityRefusal, ReplicationAuthority } from '@tinycloud/sdk-services';
3
3
  import { ReplicaSessionStamp, ReplicaDenyInput } from '@tinycloud/sdk-services/kv/replication';
4
- import { T as TinyCloudNode } from './TinyCloudNode-DmF9QUcS.js';
4
+ import { T as TinyCloudNode } from './TinyCloudNode-BPDOmuf6.js';
5
5
 
6
6
  /** Constructor-time validation; the space-dependent secrets check runs at sign-in and open. */
7
7
  declare function assertValidReplicationConfig(replication: (ReplicationOptions & {
package/dist/core.cjs CHANGED
@@ -517,6 +517,11 @@ var NodeUserAuthorization = class {
517
517
  this.tinycloudHosts = config.tinycloudHosts;
518
518
  this.tinycloudRegistryUrl = config.tinycloudRegistryUrl;
519
519
  this.tinycloudFallbackHosts = config.tinycloudFallbackHosts;
520
+ this.registryPolicy = config.registryPolicy;
521
+ this.newUserHost = config.newUserHost;
522
+ this.registryChainId = config.registryChainId;
523
+ this.registrySequenceStore = config.registrySequenceStore ?? (0, import_sdk_core4.createInMemoryRegistrySequenceStore)();
524
+ this.onNodeNotFound = config.onNodeNotFound;
520
525
  this.autoDiscoverLocalNode = config.autoDiscoverLocalNode;
521
526
  this.localNodeUrl = config.localNodeUrl;
522
527
  this.localLinkName = config.localLinkName;
@@ -628,6 +633,7 @@ var NodeUserAuthorization = class {
628
633
  this._nodeFeaturesKnown = nodeFeatures !== void 0;
629
634
  this._nodeInfoIdentity = void 0;
630
635
  this.tinycloudHosts = hosts && hosts.length > 0 ? [...hosts] : void 0;
636
+ this.registryVerdict = void 0;
631
637
  this._lastActivationSkippedSpaceIds = [];
632
638
  this._lastActivationActivatedSpaceIds = [];
633
639
  if (!session) {
@@ -678,6 +684,23 @@ var NodeUserAuthorization = class {
678
684
  if (this.tinycloudHosts && this.tinycloudHosts.length > 0) {
679
685
  return;
680
686
  }
687
+ if (this.registryPolicy === "fail-closed") {
688
+ const resolved2 = await (0, import_sdk_core4.resolveFailClosedTinyCloudHosts)(address, {
689
+ registryUrl: this.tinycloudRegistryUrl,
690
+ registryChainId: this.registryChainId,
691
+ newUserHost: this.newUserHost,
692
+ sequenceStore: this.registrySequenceStore,
693
+ onNodeNotFound: this.onNodeNotFound,
694
+ autoDiscoverLocalNode: this.autoDiscoverLocalNode,
695
+ localNodeUrl: this.localNodeUrl,
696
+ localLinkName: this.localLinkName,
697
+ expectedNodeDid: this.expectedNodeDid,
698
+ localNodeIdentityStore: this.localNodeIdentityStore
699
+ });
700
+ this.tinycloudHosts = resolved2.hosts;
701
+ this.registryVerdict = resolved2.verdict;
702
+ return;
703
+ }
681
704
  const subject = (0, import_sdk_core4.pkhDid)(address, chainId);
682
705
  const resolved = await (0, import_sdk_core4.resolveTinyCloudHosts)(subject, {
683
706
  registryUrl: this.tinycloudRegistryUrl,
@@ -690,6 +713,32 @@ var NodeUserAuthorization = class {
690
713
  });
691
714
  this.tinycloudHosts = resolved.hosts;
692
715
  }
716
+ /**
717
+ * Space-creation guard (TC-1443). Under `registryPolicy: "fail-closed"`,
718
+ * throws unless the owner's registry verdict allows a space on the primary
719
+ * host: `NodeRegistryEntryExistsError` when the owner's record does not
720
+ * list it, `RegistryUnavailableError` when the registry gives no
721
+ * verdict. Reuses this session's sign-in verdict unless it was
722
+ * `unavailable`, else looks it up now (explicit-host apps). Under the
723
+ * default policy it returns at once and performs no lookup.
724
+ */
725
+ async assertMayCreateSpace() {
726
+ if (this.registryPolicy !== "fail-closed") return;
727
+ if (this._address === void 0) {
728
+ throw new Error("Must be signed in to create a space");
729
+ }
730
+ await this.ensureTinyCloudHosts();
731
+ const subject = (0, import_sdk_core4.registryOwnerSubjectV1)(this._address, this.registryChainId);
732
+ let verdict = this.registryVerdict;
733
+ if (verdict?.subject !== subject || verdict.status === "unavailable") {
734
+ verdict = await (0, import_sdk_core4.lookupOwnerRegistryVerdict)(subject, {
735
+ registryUrl: this.tinycloudRegistryUrl,
736
+ sequenceStore: this.registrySequenceStore
737
+ });
738
+ this.registryVerdict = verdict;
739
+ }
740
+ (0, import_sdk_core4.assertSpaceCreationAllowed)(verdict, this.primaryTinyCloudHost);
741
+ }
693
742
  requireTinyCloudHosts() {
694
743
  if (!this.tinycloudHosts || this.tinycloudHosts.length === 0) {
695
744
  throw new Error("TinyCloud hosts have not been resolved. Call signIn() first.");
@@ -1138,6 +1187,7 @@ var NodeUserAuthorization = class {
1138
1187
  if (!handler) {
1139
1188
  return;
1140
1189
  }
1190
+ await this.guardSpaceCreation(handler, creationContext);
1141
1191
  const confirmed = await handler.confirmSpaceCreation(creationContext);
1142
1192
  if (!confirmed) {
1143
1193
  return;
@@ -1178,6 +1228,7 @@ var NodeUserAuthorization = class {
1178
1228
  if (!handler) {
1179
1229
  return;
1180
1230
  }
1231
+ await this.guardSpaceCreation(handler, creationContext);
1181
1232
  const confirmed = await handler.confirmSpaceCreation(creationContext);
1182
1233
  if (!confirmed) {
1183
1234
  return;
@@ -1219,6 +1270,16 @@ var NodeUserAuthorization = class {
1219
1270
  { cause: result }
1220
1271
  );
1221
1272
  }
1273
+ /** {@link assertMayCreateSpace}, reporting a refusal to the creation handler before rethrowing it. */
1274
+ async guardSpaceCreation(handler, context) {
1275
+ try {
1276
+ await this.assertMayCreateSpace();
1277
+ } catch (error) {
1278
+ const failure = error instanceof Error ? error : new Error(String(error));
1279
+ handler.onSpaceCreationFailed?.(context, failure);
1280
+ throw failure;
1281
+ }
1282
+ }
1222
1283
  recordActivationSkippedSpaces(result, primarySpaceId) {
1223
1284
  if (result.success) {
1224
1285
  this._lastActivationSkippedSpaceIds = result.skipped ?? [];
@@ -3661,7 +3722,9 @@ var _TinyCloudNode = class _TinyCloudNode {
3661
3722
  this.explicitHost = config.host;
3662
3723
  this.config = {
3663
3724
  ...config,
3664
- host: config.host ?? DEFAULT_HOST
3725
+ host: config.host ?? DEFAULT_HOST,
3726
+ // One store for every authorization this client creates.
3727
+ registrySequenceStore: config.registrySequenceStore ?? (0, import_sdk_core10.createInMemoryRegistrySequenceStore)()
3665
3728
  };
3666
3729
  if (this.config.replication?.enabled) {
3667
3730
  const runtime = getNodeReplicationLoaders().runtime().then(({ ReplicationRuntime }) => {
@@ -3931,6 +3994,7 @@ var _TinyCloudNode = class _TinyCloudNode {
3931
3994
  tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
3932
3995
  tinycloudRegistryUrl: config.tinycloudRegistryUrl,
3933
3996
  tinycloudFallbackHosts: config.tinycloudFallbackHosts,
3997
+ ...this.registryPolicyConfig(),
3934
3998
  autoDiscoverLocalNode: config.autoDiscoverLocalNode,
3935
3999
  localNodeUrl: config.localNodeUrl,
3936
4000
  localLinkName: config.localLinkName,
@@ -4636,6 +4700,9 @@ var _TinyCloudNode = class _TinyCloudNode {
4636
4700
  const primarySession = auth.tinyCloudSession;
4637
4701
  const defaultSpaceId = this.ownedSpaceId("default");
4638
4702
  const canReusePrimaryBootstrapSession = primarySession?.spaceId === defaultSpaceId && auth.capabilityRequest === import_sdk_core10.BOOTSTRAP_SESSION_REQUESTS.default;
4703
+ if (steps.some((step) => step.kind === "host")) {
4704
+ await this.assertMayCreateSpace();
4705
+ }
4639
4706
  for (const step of steps) {
4640
4707
  if (step.kind !== "session") continue;
4641
4708
  graph.assertActive();
@@ -5277,6 +5344,8 @@ var _TinyCloudNode = class _TinyCloudNode {
5277
5344
  { cause: activation }
5278
5345
  );
5279
5346
  }
5347
+ await this.assertMayCreateSpace();
5348
+ if (signal?.aborted) throw signal.reason;
5280
5349
  const created = await this.hostOwnedSpaceOnce(spaceId, signal);
5281
5350
  if (!created.success) {
5282
5351
  throw Object.assign(
@@ -5390,6 +5459,12 @@ var _TinyCloudNode = class _TinyCloudNode {
5390
5459
  * session's recap doesn't cover it and the registry has no record; the host
5391
5460
  * SIWE is idempotent server-side. Must be called after {@link signIn}.
5392
5461
  *
5462
+ * Under `registryPolicy: "fail-closed"`, hosting is refused with
5463
+ * `NodeRegistryEntryExistsError` when the owner's registry record does not
5464
+ * list the host, or `RegistryUnavailableError` when the registry gives no
5465
+ * verdict. {@link hostOwnedSpace} itself is an explicit action and is not
5466
+ * guarded.
5467
+ *
5393
5468
  * @param name - The owned space name (e.g. `"secrets"`).
5394
5469
  * @returns The hosted owned-space URI.
5395
5470
  */
@@ -5401,8 +5476,25 @@ var _TinyCloudNode = class _TinyCloudNode {
5401
5476
  if (this.hostedOwnedSpaceIds.has(spaceId) || await this.isOwnedSpaceRegistered(spaceId)) {
5402
5477
  return spaceId;
5403
5478
  }
5479
+ await this.assertMayCreateSpace();
5404
5480
  return this.hostOwnedSpace(name);
5405
5481
  }
5482
+ /**
5483
+ * Space-creation guard (TC-1443) for the automatic hosting paths: sign-in's
5484
+ * owned spaces, account bootstrap, {@link ensureOwnedSpaceHosted} and
5485
+ * {@link ensurePublicSpace}. Only `registryPolicy: "fail-closed"` consults
5486
+ * the registry ({@link NodeUserAuthorization.assertMayCreateSpace}); the
5487
+ * default policy performs no lookup.
5488
+ */
5489
+ async assertMayCreateSpace() {
5490
+ if (this.config.registryPolicy !== "fail-closed") return;
5491
+ await this.auth.assertMayCreateSpace();
5492
+ }
5493
+ /** Registry-policy settings shared by every authorization this client creates. */
5494
+ registryPolicyConfig() {
5495
+ const { registryPolicy, newUserHost, registryChainId, registrySequenceStore, onNodeNotFound } = this.config;
5496
+ return { registryPolicy, newUserHost, registryChainId, registrySequenceStore, onNodeNotFound };
5497
+ }
5406
5498
  /**
5407
5499
  * Submit the host delegation for an owned space, sharing one in-flight
5408
5500
  * request (and so one approval) between concurrent callers in this session,
@@ -6080,6 +6172,21 @@ var _TinyCloudNode = class _TinyCloudNode {
6080
6172
  if (address === void 0 || chainId === void 0) {
6081
6173
  return void 0;
6082
6174
  }
6175
+ if (this.config.registryPolicy === "fail-closed") {
6176
+ const resolved2 = await (0, import_sdk_core10.resolveFailClosedTinyCloudHosts)(address, {
6177
+ registryUrl: this.config.tinycloudRegistryUrl,
6178
+ registryChainId: this.config.registryChainId,
6179
+ newUserHost: this.config.newUserHost,
6180
+ sequenceStore: this.config.registrySequenceStore,
6181
+ onNodeNotFound: this.config.onNodeNotFound,
6182
+ autoDiscoverLocalNode: this.config.autoDiscoverLocalNode,
6183
+ localNodeUrl: this.config.localNodeUrl,
6184
+ localLinkName: this.config.localLinkName,
6185
+ expectedNodeDid: this.config.expectedNodeDid,
6186
+ localNodeIdentityStore: this.config.localNodeIdentityStore
6187
+ });
6188
+ return resolved2.hosts[0];
6189
+ }
6083
6190
  const resolved = await (0, import_sdk_core10.resolveTinyCloudHosts)((0, import_sdk_core10.pkhDid)(address, chainId), {
6084
6191
  registryUrl: this.config.tinycloudRegistryUrl,
6085
6192
  fallbackHosts: this.config.tinycloudFallbackHosts,
@@ -6272,6 +6379,7 @@ var _TinyCloudNode = class _TinyCloudNode {
6272
6379
  tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
6273
6380
  tinycloudRegistryUrl: this.config.tinycloudRegistryUrl,
6274
6381
  tinycloudFallbackHosts: this.config.tinycloudFallbackHosts,
6382
+ ...this.registryPolicyConfig(),
6275
6383
  autoDiscoverLocalNode: this.config.autoDiscoverLocalNode,
6276
6384
  localNodeUrl: this.config.localNodeUrl,
6277
6385
  localLinkName: this.config.localLinkName,
@@ -6331,6 +6439,7 @@ var _TinyCloudNode = class _TinyCloudNode {
6331
6439
  tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
6332
6440
  tinycloudRegistryUrl: this.config.tinycloudRegistryUrl,
6333
6441
  tinycloudFallbackHosts: this.config.tinycloudFallbackHosts,
6442
+ ...this.registryPolicyConfig(),
6334
6443
  autoDiscoverLocalNode: this.config.autoDiscoverLocalNode,
6335
6444
  localNodeUrl: this.config.localNodeUrl,
6336
6445
  localLinkName: this.config.localLinkName,
@@ -7968,7 +8077,9 @@ var _TinyCloudNode = class _TinyCloudNode {
7968
8077
  * Ensure the user's public space exists and is accessible.
7969
8078
  * Creates the space and activates a session delegation for it.
7970
8079
  * This is the trigger for lazy public space creation — call it
7971
- * before writing to spaces.get('public').kv.
8080
+ * before writing to spaces.get('public').kv. The vault calls it on unlock.
8081
+ * Under `registryPolicy: "fail-closed"` creation goes through the
8082
+ * space-creation guard.
7972
8083
  */
7973
8084
  async ensurePublicSpace() {
7974
8085
  if (!this.auth || !this.session || !this.signer) {
@@ -7978,6 +8089,7 @@ var _TinyCloudNode = class _TinyCloudNode {
7978
8089
  if (!publicSpaceId) {
7979
8090
  throw new Error("Public space not enabled. Set enablePublicSpace: true in config.");
7980
8091
  }
8092
+ await this.assertMayCreateSpace();
7981
8093
  await this.auth.hostPublicSpace(publicSpaceId);
7982
8094
  const kvActions = [import_sdk_core10.KV.PUT, import_sdk_core10.KV.GET, import_sdk_core10.KV.DEL, import_sdk_core10.KV.LIST, import_sdk_core10.KV.METADATA];
7983
8095
  const abilities = { kv: { "": kvActions } };