@tinycloud/node-sdk 2.4.0-beta.9 → 2.4.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.
@@ -1,6 +1,6 @@
1
1
  import { ISessionStorage, PersistedSessionData, AutoSignStrategy, AutoRejectStrategy, CallbackStrategy, IUserAuthorization, ISigner, ISpaceCreationHandler, IWasmBindings, SiweConfig, Manifest, ComposedManifestRequest, ClientSession, TinyCloudSession, Extension, SignInOptions, Delegation, DelegatedResource, PermissionEntry, TelemetryConfig, IKVService, ISQLService, IDuckDbService, IHooksService, INotificationHandler, IENSResolver, AccountService, IDataVaultService, IEncryptionService, NetworkDescriptor, ISecretsService, ICapabilityKeyRegistry, DelegationManager, ISpaceService, ISpace, ISharingService, CreateDelegationParams, DelegationResult, ResolvedDelegate, KeyProvider, ISessionManager, JWK } from '@tinycloud/sdk-core';
2
2
  import { EventEmitter } from 'events';
3
- import { InvokeFunction } from '@tinycloud/sdk-services';
3
+ import { InvokeFunction, InvokeAnyFunction } from '@tinycloud/sdk-services';
4
4
 
5
5
  /**
6
6
  * In-memory session storage for Node.js.
@@ -230,6 +230,11 @@ interface NodeUserAuthorizationConfig {
230
230
  /** Include implicit account registry permissions when composing `manifest`. Default true. */
231
231
  includeAccountRegistryPermissions?: boolean;
232
232
  }
233
+ interface CreateBootstrapSessionOptions {
234
+ spaceId: string;
235
+ capabilityRequest: ComposedManifestRequest;
236
+ rawAbilities?: Record<string, string[]>;
237
+ }
233
238
  /**
234
239
  * Node.js implementation of IUserAuthorization.
235
240
  *
@@ -296,6 +301,7 @@ declare class NodeUserAuthorization implements IUserAuthorization {
296
301
  private _address?;
297
302
  private _chainId?;
298
303
  private _nodeFeatures;
304
+ private _lastActivationSkippedSpaceIds;
299
305
  constructor(config: NodeUserAuthorizationConfig);
300
306
  /**
301
307
  * Return the manifest currently driving sign-in behavior, or
@@ -332,12 +338,36 @@ declare class NodeUserAuthorization implements IUserAuthorization {
332
338
  * `siwe` is the load-bearing one because `extractSiweExpiration` returns
333
339
  * undefined for missing SIWEs and the SDK then treats the session as
334
340
  * expired-at-epoch-zero.
341
+ *
342
+ * @param hosts - The TinyCloud hosts this session was created against,
343
+ * as persisted in {@link PersistedSessionData.tinycloudHosts}. When
344
+ * present (and non-empty) they are adopted directly so the restored
345
+ * session resolves to the same node as the original sign-in without
346
+ * re-running registry/fallback resolution. When absent (old session)
347
+ * hosts are resolved lazily on the first host-needing call via
348
+ * {@link ensureTinyCloudHosts}.
349
+ */
350
+ setRestoredTinyCloudSession(session: TinyCloudSession, hosts?: string[]): void;
351
+ /**
352
+ * Ensure `tinycloudHosts` are resolved before a host-needing call.
353
+ *
354
+ * Fresh sign-in resolves hosts up front; a restored session may not have
355
+ * (old persisted sessions predate {@link PersistedSessionData.tinycloudHosts}).
356
+ * This guard makes a restored session resolve the node exactly like a fresh
357
+ * sign-in — persisted hosts are preferred (already set by
358
+ * {@link setRestoredTinyCloudSession}), otherwise the registry/fallback
359
+ * resolution runs lazily here. Idempotent: {@link resolveTinyCloudHostsForSignIn}
360
+ * early-returns when hosts are already set.
361
+ *
362
+ * Throws if hosts are unset and the restored session has no address/chainId
363
+ * to resolve from — a real failure that must surface, not be masked.
335
364
  */
336
- setRestoredTinyCloudSession(session: TinyCloudSession): void;
365
+ ensureTinyCloudHosts(): Promise<void>;
337
366
  private resolveTinyCloudHostsForSignIn;
338
367
  private requireTinyCloudHosts;
339
368
  private get primaryTinyCloudHost();
340
369
  get nodeFeatures(): string[];
370
+ get lastActivationSkippedSpaceIds(): string[];
341
371
  /**
342
372
  * Compute the `abilities` map the WASM `prepareSession` call should
343
373
  * see at sign-in time.
@@ -410,6 +440,8 @@ declare class NodeUserAuthorization implements IUserAuthorization {
410
440
  * @throws Error if space creation fails
411
441
  */
412
442
  ensureSpaceExists(): Promise<void>;
443
+ private recordActivationSkippedSpaces;
444
+ createBootstrapSession(options: CreateBootstrapSessionOptions): Promise<TinyCloudSession>;
413
445
  /**
414
446
  * Sign in and create a new session.
415
447
  *
@@ -650,7 +682,7 @@ declare class DelegatedAccess {
650
682
  private _sql;
651
683
  private _duckdb;
652
684
  private _hooks;
653
- constructor(session: TinyCloudSession, delegation: PortableDelegation, host: string, invoke: InvokeFunction, telemetry?: TelemetryConfig);
685
+ constructor(session: TinyCloudSession, delegation: PortableDelegation, host: string, invoke: InvokeFunction, invokeAny?: InvokeAnyFunction, telemetry?: TelemetryConfig);
654
686
  /**
655
687
  * Get the delegation this access was created from.
656
688
  */
@@ -731,6 +763,8 @@ interface TinyCloudNodeConfig {
731
763
  privateKey?: string;
732
764
  /** Custom signer implementation. If provided, takes precedence over privateKey. */
733
765
  signer?: ISigner;
766
+ /** Strategy for root signature requests. Defaults to auto-sign for local keys. */
767
+ signStrategy?: SignStrategy;
734
768
  /** Explicit TinyCloud server URL. When omitted, signIn resolves the user's host. */
735
769
  host?: string;
736
770
  /** TinyCloud location registry URL. Default: https://registry.tinycloud.xyz. */
@@ -786,6 +820,8 @@ interface TinyCloudNodeConfig {
786
820
  capabilityRequest?: ComposedManifestRequest;
787
821
  /** Include implicit account registry permissions when composing `manifest`. Default true. */
788
822
  includeAccountRegistryPermissions?: boolean;
823
+ /** Run canonical first-account bootstrap when fresh account state is detected. Default true. */
824
+ autoBootstrapAccount?: boolean;
789
825
  /** Default-off service telemetry. */
790
826
  telemetry?: TelemetryConfig;
791
827
  }
@@ -857,8 +893,8 @@ declare class TinyCloudNode {
857
893
  private _hooks?;
858
894
  private _vault?;
859
895
  private _encryption?;
860
- private _baseSecrets?;
861
- private _secrets?;
896
+ private _baseSecrets;
897
+ private _secrets;
862
898
  private _account?;
863
899
  /** Cached public KV with proper delegation (set by ensurePublicSpace) */
864
900
  private _publicKV?;
@@ -915,6 +951,7 @@ declare class TinyCloudNode {
915
951
  * @internal
916
952
  */
917
953
  private setupAuth;
954
+ private shouldUseBootstrapSignInRequest;
918
955
  private syncResolvedHostFromAuth;
919
956
  /**
920
957
  * Install or replace the manifest that drives the SIWE recap at
@@ -976,6 +1013,11 @@ declare class TinyCloudNode {
976
1013
  * Available after signIn().
977
1014
  */
978
1015
  get session(): TinyCloudSession | undefined;
1016
+ /**
1017
+ * Get the currently active session in the shape callers can persist and later
1018
+ * pass back to {@link restoreSession}.
1019
+ */
1020
+ get restorableSession(): TinyCloudSession | undefined;
979
1021
  /**
980
1022
  * Sign in and create a new session.
981
1023
  * This creates the user's space if it doesn't exist.
@@ -985,12 +1027,16 @@ declare class TinyCloudNode {
985
1027
  */
986
1028
  signIn(options?: SignInOptions): Promise<void>;
987
1029
  private ownedSpaceId;
1030
+ private bootstrapAccountIfNeeded;
1031
+ private isFreshBootstrapAccount;
1032
+ private runAccountBootstrap;
1033
+ private registerBootstrapRuntimeGrant;
988
1034
  private writeManifestRegistryRecords;
989
1035
  private scheduleAccountRegistrySync;
990
1036
  private withAccountRegistryRetry;
991
1037
  private requestedEncryptionNetworkIds;
992
1038
  private ensureRequestedEncryptionNetworks;
993
- private ensureOwnedSpaceHosted;
1039
+ private ensureOwnedSpaceHostedById;
994
1040
  /**
995
1041
  * Host one of this user's owned spaces by name (e.g. `"applications"`).
996
1042
  *
@@ -1001,7 +1047,7 @@ declare class TinyCloudNode {
1001
1047
  * caller is the root authority of their own owned spaces, so no additional
1002
1048
  * delegation is required.
1003
1049
  *
1004
- * Unlike {@link ensureOwnedSpaceHosted}, this always submits the host
1050
+ * Unlike {@link ensureOwnedSpaceHostedById}, this always submits the host
1005
1051
  * delegation rather than inferring hosting from session activation: a space
1006
1052
  * the current session has never referenced is reported neither as
1007
1053
  * `activated` nor `skipped`, so activation-based detection would wrongly
@@ -1012,6 +1058,56 @@ declare class TinyCloudNode {
1012
1058
  * @returns The hosted space URI.
1013
1059
  */
1014
1060
  hostOwnedSpace(name: string): Promise<string>;
1061
+ /**
1062
+ * Ensure one of this user's owned spaces (e.g. `"secrets"`) is hosted on the
1063
+ * server.
1064
+ *
1065
+ * At sign-in, a full-authority session auto-hosts the owner's `secrets`
1066
+ * space, but a session created with a manifest / capabilityRequest does not.
1067
+ * Such a session can therefore hold valid `tinycloud.kv/*` capabilities for
1068
+ * the owned `secrets` space yet still fail its first scoped
1069
+ * `secrets.put(...)` with `404 Space not found`, because the space was never
1070
+ * registered on the node.
1071
+ *
1072
+ * Calling this resolves `name` to the owner's owned-space URI
1073
+ * (`tinycloud:pkh:eip155:<chain>:<addr>:<name>`). It first consults the
1074
+ * account-space spaces registry (`account/spaces/{space_id}`, the canonical
1075
+ * KV source of truth, fronted by a best-effort SQLite index): if the space is
1076
+ * already registered/hosted it returns the URI WITHOUT submitting a host
1077
+ * delegation, avoiding a redundant host-SIWE signature prompt for owners who
1078
+ * already use the space. Only when the space is absent — or the registry
1079
+ * check fails for any reason (e.g. a cold SQLite index reporting
1080
+ * `no such table: spaces`) — does it fall through to {@link hostOwnedSpace}.
1081
+ *
1082
+ * The registry check is purely an optimization: any failure falls back to
1083
+ * hosting, and the host SIWE is idempotent server-side, so re-hosting an
1084
+ * existing space remains a safe no-op. Must be called after {@link signIn}.
1085
+ *
1086
+ * @param name - The owned space name (e.g. `"secrets"`).
1087
+ * @returns The hosted owned-space URI.
1088
+ */
1089
+ ensureOwnedSpaceHosted(name: string): Promise<string>;
1090
+ /**
1091
+ * Check whether an owned space is already registered/hosted by consulting the
1092
+ * account spaces registry.
1093
+ *
1094
+ * Source of truth is the canonical KV registry record
1095
+ * `account/spaces/{space_id}`, read here via `account.spaces.get(spaceId)`.
1096
+ * The KV path is used (rather than `syncAccessible()`) because it works under
1097
+ * a manifest/recap session with NO extra prompt: the composed manifest recap
1098
+ * already grants `tinycloud.kv get/list` on the account space `spaces/`
1099
+ * prefix, whereas `syncAccessible()` depends on `tinycloud.space/list`, which
1100
+ * a recap session does not hold. Before reading, it consults the fast SQLite
1101
+ * index (`account.index.spaces.list()`) as a best-effort short-circuit; on a
1102
+ * cold index (`no such table: spaces`) or any other index failure it falls
1103
+ * back to the canonical KV read.
1104
+ *
1105
+ * This is a best-effort optimization. ANY failure of the check path (missing
1106
+ * table, KV error, missing record, thrown exception) resolves to `false` so
1107
+ * the caller falls through to hosting — per the directive, "if it fails in any
1108
+ * way then create the space".
1109
+ */
1110
+ private isOwnedSpaceRegistered;
1015
1111
  /**
1016
1112
  * Restore a previously established session from stored delegation data.
1017
1113
  *
@@ -1046,7 +1142,32 @@ declare class TinyCloudNode {
1046
1142
  * for callers that need to round-trip the full session shape.
1047
1143
  */
1048
1144
  signature?: string;
1145
+ /**
1146
+ * The TinyCloud hosts this session was created against (from
1147
+ * {@link PersistedSessionData.tinycloudHosts}). When present they are
1148
+ * adopted so the restored session targets the same node as the
1149
+ * original sign-in — without this, service calls fall back to the
1150
+ * default host and the auth layer throws "TinyCloud hosts have not
1151
+ * been resolved". When absent (old persisted session) hosts resolve
1152
+ * lazily via the registry/fallback on the first host-needing call.
1153
+ */
1154
+ tinycloudHosts?: string[];
1049
1155
  }): Promise<void>;
1156
+ /**
1157
+ * Resolve the host a restored session should target.
1158
+ *
1159
+ * Mirrors fresh sign-in host resolution but for the restore path:
1160
+ * an explicit/pinned host always wins, then the hosts the session was
1161
+ * persisted with, then a lazy registry/fallback resolution for sessions
1162
+ * that predate the persisted `tinycloudHosts` field. Returns `undefined`
1163
+ * only when there's nothing to resolve from (no explicit host, no
1164
+ * persisted hosts, and no address/chainId) — in which case the existing
1165
+ * `config.host` (default) is left in place.
1166
+ *
1167
+ * Resolution failures are surfaced, not swallowed: a genuinely broken
1168
+ * registry lookup throws rather than silently falling back to a wrong host.
1169
+ */
1170
+ private resolveRestoredHost;
1050
1171
  /**
1051
1172
  * Resolve the currently-active TinyCloudSession, preferring the auth
1052
1173
  * layer's value (wallet mode) and falling back to the node-level
@@ -1106,6 +1227,8 @@ declare class TinyCloudNode {
1106
1227
  private initializeServices;
1107
1228
  private createSpaceScopedKVService;
1108
1229
  getDefaultEncryptionNetworkId(name?: string): string;
1230
+ getEncryptionNetworkIdForSpace(spaceId: string, name?: string): string;
1231
+ private ownerDidFromSpaceId;
1109
1232
  private requireServiceSession;
1110
1233
  private createEncryptionCrypto;
1111
1234
  private fetchNodeId;
@@ -1211,6 +1334,10 @@ declare class TinyCloudNode {
1211
1334
  * App-facing secrets API backed by the `secrets` space vault.
1212
1335
  */
1213
1336
  get secrets(): ISecretsService;
1337
+ /**
1338
+ * App-facing secrets API backed by the requested space's vault.
1339
+ */
1340
+ secretsForSpace(spaceId: string): ISecretsService;
1214
1341
  private getBaseSecrets;
1215
1342
  /**
1216
1343
  * Hooks write stream subscription API.
@@ -1,6 +1,6 @@
1
1
  import { ISessionStorage, PersistedSessionData, AutoSignStrategy, AutoRejectStrategy, CallbackStrategy, IUserAuthorization, ISigner, ISpaceCreationHandler, IWasmBindings, SiweConfig, Manifest, ComposedManifestRequest, ClientSession, TinyCloudSession, Extension, SignInOptions, Delegation, DelegatedResource, PermissionEntry, TelemetryConfig, IKVService, ISQLService, IDuckDbService, IHooksService, INotificationHandler, IENSResolver, AccountService, IDataVaultService, IEncryptionService, NetworkDescriptor, ISecretsService, ICapabilityKeyRegistry, DelegationManager, ISpaceService, ISpace, ISharingService, CreateDelegationParams, DelegationResult, ResolvedDelegate, KeyProvider, ISessionManager, JWK } from '@tinycloud/sdk-core';
2
2
  import { EventEmitter } from 'events';
3
- import { InvokeFunction } from '@tinycloud/sdk-services';
3
+ import { InvokeFunction, InvokeAnyFunction } from '@tinycloud/sdk-services';
4
4
 
5
5
  /**
6
6
  * In-memory session storage for Node.js.
@@ -230,6 +230,11 @@ interface NodeUserAuthorizationConfig {
230
230
  /** Include implicit account registry permissions when composing `manifest`. Default true. */
231
231
  includeAccountRegistryPermissions?: boolean;
232
232
  }
233
+ interface CreateBootstrapSessionOptions {
234
+ spaceId: string;
235
+ capabilityRequest: ComposedManifestRequest;
236
+ rawAbilities?: Record<string, string[]>;
237
+ }
233
238
  /**
234
239
  * Node.js implementation of IUserAuthorization.
235
240
  *
@@ -296,6 +301,7 @@ declare class NodeUserAuthorization implements IUserAuthorization {
296
301
  private _address?;
297
302
  private _chainId?;
298
303
  private _nodeFeatures;
304
+ private _lastActivationSkippedSpaceIds;
299
305
  constructor(config: NodeUserAuthorizationConfig);
300
306
  /**
301
307
  * Return the manifest currently driving sign-in behavior, or
@@ -332,12 +338,36 @@ declare class NodeUserAuthorization implements IUserAuthorization {
332
338
  * `siwe` is the load-bearing one because `extractSiweExpiration` returns
333
339
  * undefined for missing SIWEs and the SDK then treats the session as
334
340
  * expired-at-epoch-zero.
341
+ *
342
+ * @param hosts - The TinyCloud hosts this session was created against,
343
+ * as persisted in {@link PersistedSessionData.tinycloudHosts}. When
344
+ * present (and non-empty) they are adopted directly so the restored
345
+ * session resolves to the same node as the original sign-in without
346
+ * re-running registry/fallback resolution. When absent (old session)
347
+ * hosts are resolved lazily on the first host-needing call via
348
+ * {@link ensureTinyCloudHosts}.
349
+ */
350
+ setRestoredTinyCloudSession(session: TinyCloudSession, hosts?: string[]): void;
351
+ /**
352
+ * Ensure `tinycloudHosts` are resolved before a host-needing call.
353
+ *
354
+ * Fresh sign-in resolves hosts up front; a restored session may not have
355
+ * (old persisted sessions predate {@link PersistedSessionData.tinycloudHosts}).
356
+ * This guard makes a restored session resolve the node exactly like a fresh
357
+ * sign-in — persisted hosts are preferred (already set by
358
+ * {@link setRestoredTinyCloudSession}), otherwise the registry/fallback
359
+ * resolution runs lazily here. Idempotent: {@link resolveTinyCloudHostsForSignIn}
360
+ * early-returns when hosts are already set.
361
+ *
362
+ * Throws if hosts are unset and the restored session has no address/chainId
363
+ * to resolve from — a real failure that must surface, not be masked.
335
364
  */
336
- setRestoredTinyCloudSession(session: TinyCloudSession): void;
365
+ ensureTinyCloudHosts(): Promise<void>;
337
366
  private resolveTinyCloudHostsForSignIn;
338
367
  private requireTinyCloudHosts;
339
368
  private get primaryTinyCloudHost();
340
369
  get nodeFeatures(): string[];
370
+ get lastActivationSkippedSpaceIds(): string[];
341
371
  /**
342
372
  * Compute the `abilities` map the WASM `prepareSession` call should
343
373
  * see at sign-in time.
@@ -410,6 +440,8 @@ declare class NodeUserAuthorization implements IUserAuthorization {
410
440
  * @throws Error if space creation fails
411
441
  */
412
442
  ensureSpaceExists(): Promise<void>;
443
+ private recordActivationSkippedSpaces;
444
+ createBootstrapSession(options: CreateBootstrapSessionOptions): Promise<TinyCloudSession>;
413
445
  /**
414
446
  * Sign in and create a new session.
415
447
  *
@@ -650,7 +682,7 @@ declare class DelegatedAccess {
650
682
  private _sql;
651
683
  private _duckdb;
652
684
  private _hooks;
653
- constructor(session: TinyCloudSession, delegation: PortableDelegation, host: string, invoke: InvokeFunction, telemetry?: TelemetryConfig);
685
+ constructor(session: TinyCloudSession, delegation: PortableDelegation, host: string, invoke: InvokeFunction, invokeAny?: InvokeAnyFunction, telemetry?: TelemetryConfig);
654
686
  /**
655
687
  * Get the delegation this access was created from.
656
688
  */
@@ -731,6 +763,8 @@ interface TinyCloudNodeConfig {
731
763
  privateKey?: string;
732
764
  /** Custom signer implementation. If provided, takes precedence over privateKey. */
733
765
  signer?: ISigner;
766
+ /** Strategy for root signature requests. Defaults to auto-sign for local keys. */
767
+ signStrategy?: SignStrategy;
734
768
  /** Explicit TinyCloud server URL. When omitted, signIn resolves the user's host. */
735
769
  host?: string;
736
770
  /** TinyCloud location registry URL. Default: https://registry.tinycloud.xyz. */
@@ -786,6 +820,8 @@ interface TinyCloudNodeConfig {
786
820
  capabilityRequest?: ComposedManifestRequest;
787
821
  /** Include implicit account registry permissions when composing `manifest`. Default true. */
788
822
  includeAccountRegistryPermissions?: boolean;
823
+ /** Run canonical first-account bootstrap when fresh account state is detected. Default true. */
824
+ autoBootstrapAccount?: boolean;
789
825
  /** Default-off service telemetry. */
790
826
  telemetry?: TelemetryConfig;
791
827
  }
@@ -857,8 +893,8 @@ declare class TinyCloudNode {
857
893
  private _hooks?;
858
894
  private _vault?;
859
895
  private _encryption?;
860
- private _baseSecrets?;
861
- private _secrets?;
896
+ private _baseSecrets;
897
+ private _secrets;
862
898
  private _account?;
863
899
  /** Cached public KV with proper delegation (set by ensurePublicSpace) */
864
900
  private _publicKV?;
@@ -915,6 +951,7 @@ declare class TinyCloudNode {
915
951
  * @internal
916
952
  */
917
953
  private setupAuth;
954
+ private shouldUseBootstrapSignInRequest;
918
955
  private syncResolvedHostFromAuth;
919
956
  /**
920
957
  * Install or replace the manifest that drives the SIWE recap at
@@ -976,6 +1013,11 @@ declare class TinyCloudNode {
976
1013
  * Available after signIn().
977
1014
  */
978
1015
  get session(): TinyCloudSession | undefined;
1016
+ /**
1017
+ * Get the currently active session in the shape callers can persist and later
1018
+ * pass back to {@link restoreSession}.
1019
+ */
1020
+ get restorableSession(): TinyCloudSession | undefined;
979
1021
  /**
980
1022
  * Sign in and create a new session.
981
1023
  * This creates the user's space if it doesn't exist.
@@ -985,12 +1027,16 @@ declare class TinyCloudNode {
985
1027
  */
986
1028
  signIn(options?: SignInOptions): Promise<void>;
987
1029
  private ownedSpaceId;
1030
+ private bootstrapAccountIfNeeded;
1031
+ private isFreshBootstrapAccount;
1032
+ private runAccountBootstrap;
1033
+ private registerBootstrapRuntimeGrant;
988
1034
  private writeManifestRegistryRecords;
989
1035
  private scheduleAccountRegistrySync;
990
1036
  private withAccountRegistryRetry;
991
1037
  private requestedEncryptionNetworkIds;
992
1038
  private ensureRequestedEncryptionNetworks;
993
- private ensureOwnedSpaceHosted;
1039
+ private ensureOwnedSpaceHostedById;
994
1040
  /**
995
1041
  * Host one of this user's owned spaces by name (e.g. `"applications"`).
996
1042
  *
@@ -1001,7 +1047,7 @@ declare class TinyCloudNode {
1001
1047
  * caller is the root authority of their own owned spaces, so no additional
1002
1048
  * delegation is required.
1003
1049
  *
1004
- * Unlike {@link ensureOwnedSpaceHosted}, this always submits the host
1050
+ * Unlike {@link ensureOwnedSpaceHostedById}, this always submits the host
1005
1051
  * delegation rather than inferring hosting from session activation: a space
1006
1052
  * the current session has never referenced is reported neither as
1007
1053
  * `activated` nor `skipped`, so activation-based detection would wrongly
@@ -1012,6 +1058,56 @@ declare class TinyCloudNode {
1012
1058
  * @returns The hosted space URI.
1013
1059
  */
1014
1060
  hostOwnedSpace(name: string): Promise<string>;
1061
+ /**
1062
+ * Ensure one of this user's owned spaces (e.g. `"secrets"`) is hosted on the
1063
+ * server.
1064
+ *
1065
+ * At sign-in, a full-authority session auto-hosts the owner's `secrets`
1066
+ * space, but a session created with a manifest / capabilityRequest does not.
1067
+ * Such a session can therefore hold valid `tinycloud.kv/*` capabilities for
1068
+ * the owned `secrets` space yet still fail its first scoped
1069
+ * `secrets.put(...)` with `404 Space not found`, because the space was never
1070
+ * registered on the node.
1071
+ *
1072
+ * Calling this resolves `name` to the owner's owned-space URI
1073
+ * (`tinycloud:pkh:eip155:<chain>:<addr>:<name>`). It first consults the
1074
+ * account-space spaces registry (`account/spaces/{space_id}`, the canonical
1075
+ * KV source of truth, fronted by a best-effort SQLite index): if the space is
1076
+ * already registered/hosted it returns the URI WITHOUT submitting a host
1077
+ * delegation, avoiding a redundant host-SIWE signature prompt for owners who
1078
+ * already use the space. Only when the space is absent — or the registry
1079
+ * check fails for any reason (e.g. a cold SQLite index reporting
1080
+ * `no such table: spaces`) — does it fall through to {@link hostOwnedSpace}.
1081
+ *
1082
+ * The registry check is purely an optimization: any failure falls back to
1083
+ * hosting, and the host SIWE is idempotent server-side, so re-hosting an
1084
+ * existing space remains a safe no-op. Must be called after {@link signIn}.
1085
+ *
1086
+ * @param name - The owned space name (e.g. `"secrets"`).
1087
+ * @returns The hosted owned-space URI.
1088
+ */
1089
+ ensureOwnedSpaceHosted(name: string): Promise<string>;
1090
+ /**
1091
+ * Check whether an owned space is already registered/hosted by consulting the
1092
+ * account spaces registry.
1093
+ *
1094
+ * Source of truth is the canonical KV registry record
1095
+ * `account/spaces/{space_id}`, read here via `account.spaces.get(spaceId)`.
1096
+ * The KV path is used (rather than `syncAccessible()`) because it works under
1097
+ * a manifest/recap session with NO extra prompt: the composed manifest recap
1098
+ * already grants `tinycloud.kv get/list` on the account space `spaces/`
1099
+ * prefix, whereas `syncAccessible()` depends on `tinycloud.space/list`, which
1100
+ * a recap session does not hold. Before reading, it consults the fast SQLite
1101
+ * index (`account.index.spaces.list()`) as a best-effort short-circuit; on a
1102
+ * cold index (`no such table: spaces`) or any other index failure it falls
1103
+ * back to the canonical KV read.
1104
+ *
1105
+ * This is a best-effort optimization. ANY failure of the check path (missing
1106
+ * table, KV error, missing record, thrown exception) resolves to `false` so
1107
+ * the caller falls through to hosting — per the directive, "if it fails in any
1108
+ * way then create the space".
1109
+ */
1110
+ private isOwnedSpaceRegistered;
1015
1111
  /**
1016
1112
  * Restore a previously established session from stored delegation data.
1017
1113
  *
@@ -1046,7 +1142,32 @@ declare class TinyCloudNode {
1046
1142
  * for callers that need to round-trip the full session shape.
1047
1143
  */
1048
1144
  signature?: string;
1145
+ /**
1146
+ * The TinyCloud hosts this session was created against (from
1147
+ * {@link PersistedSessionData.tinycloudHosts}). When present they are
1148
+ * adopted so the restored session targets the same node as the
1149
+ * original sign-in — without this, service calls fall back to the
1150
+ * default host and the auth layer throws "TinyCloud hosts have not
1151
+ * been resolved". When absent (old persisted session) hosts resolve
1152
+ * lazily via the registry/fallback on the first host-needing call.
1153
+ */
1154
+ tinycloudHosts?: string[];
1049
1155
  }): Promise<void>;
1156
+ /**
1157
+ * Resolve the host a restored session should target.
1158
+ *
1159
+ * Mirrors fresh sign-in host resolution but for the restore path:
1160
+ * an explicit/pinned host always wins, then the hosts the session was
1161
+ * persisted with, then a lazy registry/fallback resolution for sessions
1162
+ * that predate the persisted `tinycloudHosts` field. Returns `undefined`
1163
+ * only when there's nothing to resolve from (no explicit host, no
1164
+ * persisted hosts, and no address/chainId) — in which case the existing
1165
+ * `config.host` (default) is left in place.
1166
+ *
1167
+ * Resolution failures are surfaced, not swallowed: a genuinely broken
1168
+ * registry lookup throws rather than silently falling back to a wrong host.
1169
+ */
1170
+ private resolveRestoredHost;
1050
1171
  /**
1051
1172
  * Resolve the currently-active TinyCloudSession, preferring the auth
1052
1173
  * layer's value (wallet mode) and falling back to the node-level
@@ -1106,6 +1227,8 @@ declare class TinyCloudNode {
1106
1227
  private initializeServices;
1107
1228
  private createSpaceScopedKVService;
1108
1229
  getDefaultEncryptionNetworkId(name?: string): string;
1230
+ getEncryptionNetworkIdForSpace(spaceId: string, name?: string): string;
1231
+ private ownerDidFromSpaceId;
1109
1232
  private requireServiceSession;
1110
1233
  private createEncryptionCrypto;
1111
1234
  private fetchNodeId;
@@ -1211,6 +1334,10 @@ declare class TinyCloudNode {
1211
1334
  * App-facing secrets API backed by the `secrets` space vault.
1212
1335
  */
1213
1336
  get secrets(): ISecretsService;
1337
+ /**
1338
+ * App-facing secrets API backed by the requested space's vault.
1339
+ */
1340
+ secretsForSpace(spaceId: string): ISecretsService;
1214
1341
  private getBaseSecrets;
1215
1342
  /**
1216
1343
  * Hooks write stream subscription API.