@tinycloud/node-sdk 2.4.0 → 2.5.0-beta.1

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,4 +1,4 @@
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';
1
+ import { ISessionStorage, PersistedSessionData, AutoSignStrategy, AutoRejectStrategy, CallbackStrategy, IUserAuthorization, ISigner, ISpaceCreationHandler, IWasmBindings, SiweConfig, Manifest, ComposedManifestRequest, ClientSession, TinyCloudSession, Extension, SignRequest, 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
3
  import { InvokeFunction, InvokeAnyFunction } from '@tinycloud/sdk-services';
4
4
 
@@ -430,7 +430,7 @@ declare class NodeUserAuthorization implements IUserAuthorization {
430
430
  * Create a specific owned space on the server via host delegation.
431
431
  * Used by manifest registry setup for the account space.
432
432
  */
433
- hostOwnedSpace(spaceId: string): Promise<boolean>;
433
+ hostOwnedSpace(spaceId: string, purpose?: SignRequest["purpose"]): Promise<boolean>;
434
434
  /**
435
435
  * Ensure the user's space exists on the TinyCloud server.
436
436
  * Creates the space if it doesn't exist and autoCreateSpace is enabled.
@@ -469,7 +469,7 @@ declare class NodeUserAuthorization implements IUserAuthorization {
469
469
  /**
470
470
  * Sign a message with the connected signer.
471
471
  */
472
- signMessage(message: string): Promise<string>;
472
+ signMessage(message: string, purpose?: SignRequest["purpose"]): Promise<string>;
473
473
  /**
474
474
  * Prepare a session for external signing.
475
475
  *
@@ -893,8 +893,8 @@ declare class TinyCloudNode {
893
893
  private _hooks?;
894
894
  private _vault?;
895
895
  private _encryption?;
896
- private _baseSecrets?;
897
- private _secrets?;
896
+ private _baseSecrets;
897
+ private _secrets;
898
898
  private _account?;
899
899
  /** Cached public KV with proper delegation (set by ensurePublicSpace) */
900
900
  private _publicKV?;
@@ -917,6 +917,27 @@ declare class TinyCloudNode {
917
917
  * {@link currentTinyCloudSession} as a fallback for `auth.tinyCloudSession`.
918
918
  */
919
919
  private _restoredTcSession?;
920
+ /**
921
+ * True when the last signIn() detected an interactive signer and skipped
922
+ * client-side bootstrap. Apps can read this to know whether bootstrap was
923
+ * deferred to the server (OpenKey) or requires a separate user action.
924
+ */
925
+ private _bootstrapSkipped;
926
+ /**
927
+ * Outcome of the last signIn()'s account-bootstrap attempt. `skipped` is
928
+ * true when bootstrap did not complete (interactive signer, auto-sign
929
+ * denied, or a bootstrap step failed); `reason` carries the cause so apps
930
+ * can surface a "finish account setup" call-to-action.
931
+ */
932
+ private _bootstrapStatus;
933
+ /** Whether the last signIn() skipped client-side bootstrap because the
934
+ * signer is interactive (browser wallet / EIP-1193 provider). */
935
+ get bootstrapSkipped(): boolean;
936
+ /** Outcome of the last signIn()'s account-bootstrap attempt. */
937
+ get bootstrapStatus(): {
938
+ skipped: boolean;
939
+ reason?: string;
940
+ };
920
941
  private get nodeFeatures();
921
942
  /** SIWE domain — uses config override or defaults to app.tinycloud.xyz */
922
943
  private get siweDomain();
@@ -1013,6 +1034,11 @@ declare class TinyCloudNode {
1013
1034
  * Available after signIn().
1014
1035
  */
1015
1036
  get session(): TinyCloudSession | undefined;
1037
+ /**
1038
+ * Get the currently active session in the shape callers can persist and later
1039
+ * pass back to {@link restoreSession}.
1040
+ */
1041
+ get restorableSession(): TinyCloudSession | undefined;
1016
1042
  /**
1017
1043
  * Sign in and create a new session.
1018
1044
  * This creates the user's space if it doesn't exist.
@@ -1222,6 +1248,8 @@ declare class TinyCloudNode {
1222
1248
  private initializeServices;
1223
1249
  private createSpaceScopedKVService;
1224
1250
  getDefaultEncryptionNetworkId(name?: string): string;
1251
+ getEncryptionNetworkIdForSpace(spaceId: string, name?: string): string;
1252
+ private ownerDidFromSpaceId;
1225
1253
  private requireServiceSession;
1226
1254
  private createEncryptionCrypto;
1227
1255
  private fetchNodeId;
@@ -1327,6 +1355,10 @@ declare class TinyCloudNode {
1327
1355
  * App-facing secrets API backed by the `secrets` space vault.
1328
1356
  */
1329
1357
  get secrets(): ISecretsService;
1358
+ /**
1359
+ * App-facing secrets API backed by the requested space's vault.
1360
+ */
1361
+ secretsForSpace(spaceId: string): ISecretsService;
1330
1362
  private getBaseSecrets;
1331
1363
  /**
1332
1364
  * Hooks write stream subscription API.
@@ -1,4 +1,4 @@
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';
1
+ import { ISessionStorage, PersistedSessionData, AutoSignStrategy, AutoRejectStrategy, CallbackStrategy, IUserAuthorization, ISigner, ISpaceCreationHandler, IWasmBindings, SiweConfig, Manifest, ComposedManifestRequest, ClientSession, TinyCloudSession, Extension, SignRequest, 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
3
  import { InvokeFunction, InvokeAnyFunction } from '@tinycloud/sdk-services';
4
4
 
@@ -430,7 +430,7 @@ declare class NodeUserAuthorization implements IUserAuthorization {
430
430
  * Create a specific owned space on the server via host delegation.
431
431
  * Used by manifest registry setup for the account space.
432
432
  */
433
- hostOwnedSpace(spaceId: string): Promise<boolean>;
433
+ hostOwnedSpace(spaceId: string, purpose?: SignRequest["purpose"]): Promise<boolean>;
434
434
  /**
435
435
  * Ensure the user's space exists on the TinyCloud server.
436
436
  * Creates the space if it doesn't exist and autoCreateSpace is enabled.
@@ -469,7 +469,7 @@ declare class NodeUserAuthorization implements IUserAuthorization {
469
469
  /**
470
470
  * Sign a message with the connected signer.
471
471
  */
472
- signMessage(message: string): Promise<string>;
472
+ signMessage(message: string, purpose?: SignRequest["purpose"]): Promise<string>;
473
473
  /**
474
474
  * Prepare a session for external signing.
475
475
  *
@@ -893,8 +893,8 @@ declare class TinyCloudNode {
893
893
  private _hooks?;
894
894
  private _vault?;
895
895
  private _encryption?;
896
- private _baseSecrets?;
897
- private _secrets?;
896
+ private _baseSecrets;
897
+ private _secrets;
898
898
  private _account?;
899
899
  /** Cached public KV with proper delegation (set by ensurePublicSpace) */
900
900
  private _publicKV?;
@@ -917,6 +917,27 @@ declare class TinyCloudNode {
917
917
  * {@link currentTinyCloudSession} as a fallback for `auth.tinyCloudSession`.
918
918
  */
919
919
  private _restoredTcSession?;
920
+ /**
921
+ * True when the last signIn() detected an interactive signer and skipped
922
+ * client-side bootstrap. Apps can read this to know whether bootstrap was
923
+ * deferred to the server (OpenKey) or requires a separate user action.
924
+ */
925
+ private _bootstrapSkipped;
926
+ /**
927
+ * Outcome of the last signIn()'s account-bootstrap attempt. `skipped` is
928
+ * true when bootstrap did not complete (interactive signer, auto-sign
929
+ * denied, or a bootstrap step failed); `reason` carries the cause so apps
930
+ * can surface a "finish account setup" call-to-action.
931
+ */
932
+ private _bootstrapStatus;
933
+ /** Whether the last signIn() skipped client-side bootstrap because the
934
+ * signer is interactive (browser wallet / EIP-1193 provider). */
935
+ get bootstrapSkipped(): boolean;
936
+ /** Outcome of the last signIn()'s account-bootstrap attempt. */
937
+ get bootstrapStatus(): {
938
+ skipped: boolean;
939
+ reason?: string;
940
+ };
920
941
  private get nodeFeatures();
921
942
  /** SIWE domain — uses config override or defaults to app.tinycloud.xyz */
922
943
  private get siweDomain();
@@ -1013,6 +1034,11 @@ declare class TinyCloudNode {
1013
1034
  * Available after signIn().
1014
1035
  */
1015
1036
  get session(): TinyCloudSession | undefined;
1037
+ /**
1038
+ * Get the currently active session in the shape callers can persist and later
1039
+ * pass back to {@link restoreSession}.
1040
+ */
1041
+ get restorableSession(): TinyCloudSession | undefined;
1016
1042
  /**
1017
1043
  * Sign in and create a new session.
1018
1044
  * This creates the user's space if it doesn't exist.
@@ -1222,6 +1248,8 @@ declare class TinyCloudNode {
1222
1248
  private initializeServices;
1223
1249
  private createSpaceScopedKVService;
1224
1250
  getDefaultEncryptionNetworkId(name?: string): string;
1251
+ getEncryptionNetworkIdForSpace(spaceId: string, name?: string): string;
1252
+ private ownerDidFromSpaceId;
1225
1253
  private requireServiceSession;
1226
1254
  private createEncryptionCrypto;
1227
1255
  private fetchNodeId;
@@ -1327,6 +1355,10 @@ declare class TinyCloudNode {
1327
1355
  * App-facing secrets API backed by the `secrets` space vault.
1328
1356
  */
1329
1357
  get secrets(): ISecretsService;
1358
+ /**
1359
+ * App-facing secrets API backed by the requested space's vault.
1360
+ */
1361
+ secretsForSpace(spaceId: string): ISecretsService;
1330
1362
  private getBaseSecrets;
1331
1363
  /**
1332
1364
  * Hooks write stream subscription API.
package/dist/core.cjs CHANGED
@@ -702,7 +702,7 @@ var NodeUserAuthorization = class {
702
702
  * Create the space on the TinyCloud server (host delegation).
703
703
  * This registers the user as the owner of the space.
704
704
  */
705
- async hostSpace(targetSpaceId) {
705
+ async hostSpace(targetSpaceId, purpose) {
706
706
  if (!this._tinyCloudSession || !this._address || !this._chainId) {
707
707
  throw new Error("Must be signed in to host space");
708
708
  }
@@ -718,7 +718,7 @@ var NodeUserAuthorization = class {
718
718
  spaceId,
719
719
  peerId
720
720
  });
721
- const signature = await this.signMessage(siwe);
721
+ const signature = await this.signMessage(siwe, purpose);
722
722
  const headers = this.wasm.siweToDelegationHeaders({ siwe, signature });
723
723
  const result = await (0, import_sdk_core3.submitHostDelegation)(host, headers);
724
724
  return result.success;
@@ -734,8 +734,8 @@ var NodeUserAuthorization = class {
734
734
  * Create a specific owned space on the server via host delegation.
735
735
  * Used by manifest registry setup for the account space.
736
736
  */
737
- async hostOwnedSpace(spaceId) {
738
- return this.hostSpace(spaceId);
737
+ async hostOwnedSpace(spaceId, purpose) {
738
+ return this.hostSpace(spaceId, purpose);
739
739
  }
740
740
  /**
741
741
  * Ensure the user's space exists on the TinyCloud server.
@@ -885,7 +885,8 @@ var NodeUserAuthorization = class {
885
885
  address,
886
886
  chainId,
887
887
  message: prepared.siwe,
888
- type: "siwe"
888
+ type: "siwe",
889
+ purpose: "bootstrap-session"
889
890
  });
890
891
  const session = this.wasm.completeSessionSetup({
891
892
  ...prepared,
@@ -949,7 +950,8 @@ var NodeUserAuthorization = class {
949
950
  address,
950
951
  chainId,
951
952
  message: prepared.siwe,
952
- type: "siwe"
953
+ type: "siwe",
954
+ purpose: "sign-in"
953
955
  });
954
956
  const session = this.wasm.completeSessionSetup({
955
957
  ...prepared,
@@ -1037,7 +1039,7 @@ var NodeUserAuthorization = class {
1037
1039
  /**
1038
1040
  * Sign a message with the connected signer.
1039
1041
  */
1040
- async signMessage(message) {
1042
+ async signMessage(message, purpose) {
1041
1043
  if (!this._address) {
1042
1044
  this._address = (0, import_sdk_core3.canonicalizeAddress)(await this.signer.getAddress());
1043
1045
  }
@@ -1048,7 +1050,8 @@ var NodeUserAuthorization = class {
1048
1050
  address: this._address,
1049
1051
  chainId: this._chainId,
1050
1052
  message,
1051
- type: "message"
1053
+ type: "message",
1054
+ ...purpose ? { purpose } : {}
1052
1055
  });
1053
1056
  }
1054
1057
  /**
@@ -1523,12 +1526,12 @@ function displayActionUrn(action) {
1523
1526
  function secretActionName(action) {
1524
1527
  return action;
1525
1528
  }
1526
- function secretPermissionEntries(name, options, action, encryptionNetworkId) {
1529
+ function secretPermissionEntries(name, options, action, space, encryptionNetworkId) {
1527
1530
  const entries = [];
1528
1531
  const path = action === "list" ? (0, import_sdk_core7.resolveSecretListPrefix)(options) : (0, import_sdk_core7.resolveSecretPath)(name, options).permissionPaths.vault;
1529
1532
  entries.push({
1530
1533
  service: "tinycloud.kv",
1531
- space: SECRETS_SPACE,
1534
+ space,
1532
1535
  path,
1533
1536
  actions: [secretActionName(action)],
1534
1537
  skipPrefix: true
@@ -1543,11 +1546,23 @@ function secretPermissionEntries(name, options, action, encryptionNetworkId) {
1543
1546
  }
1544
1547
  return entries;
1545
1548
  }
1549
+ function normalizeSpace(space, resolveSpace) {
1550
+ if (!space) return void 0;
1551
+ if (space.startsWith("tinycloud:")) return space;
1552
+ return resolveSpace?.(space) ?? space;
1553
+ }
1554
+ function spaceMatches(granted, requested, resolveSpace) {
1555
+ if (!granted || !requested) return false;
1556
+ return normalizeSpace(granted, resolveSpace) === normalizeSpace(requested, resolveSpace);
1557
+ }
1546
1558
  var NodeSecretsService = class {
1547
1559
  constructor(config) {
1548
1560
  this.config = config;
1549
1561
  this.shouldRestoreUnlock = false;
1550
1562
  }
1563
+ get space() {
1564
+ return this.config.space ?? SECRETS_SPACE;
1565
+ }
1551
1566
  get vault() {
1552
1567
  return this.service.vault;
1553
1568
  }
@@ -1600,6 +1615,7 @@ var NodeSecretsService = class {
1600
1615
  name,
1601
1616
  options,
1602
1617
  action,
1618
+ this.space,
1603
1619
  action === "get" ? this.config.getEncryptionNetworkId?.() : void 0
1604
1620
  );
1605
1621
  } catch (error) {
@@ -1649,7 +1665,7 @@ var NodeSecretsService = class {
1649
1665
  (entry) => manifests.some((candidate) => {
1650
1666
  const resolved = (0, import_sdk_core7.resolveManifest)(candidate);
1651
1667
  return resolved.resources.some(
1652
- (resource) => resource.service === entry.service && resource.space === entry.space && resource.path === entry.path && entry.actions.every((action) => resource.actions.includes(action))
1668
+ (resource) => resource.service === entry.service && spaceMatches(resource.space, entry.space, this.config.resolveSpace) && resource.path === entry.path && entry.actions.every((action) => resource.actions.includes(action))
1653
1669
  );
1654
1670
  })
1655
1671
  );
@@ -1666,6 +1682,15 @@ var DEFAULT_SESSION_EXPIRATION_MS = import_sdk_core8.EXPIRY.SESSION_MS;
1666
1682
  function isOpenKeyAutoSignStrategy(strategy) {
1667
1683
  return strategy?.openKeyAutoSign === true;
1668
1684
  }
1685
+ function isInteractiveSigner(config) {
1686
+ if (config.privateKey) {
1687
+ return false;
1688
+ }
1689
+ if (isOpenKeyAutoSignStrategy(config.signStrategy)) {
1690
+ return false;
1691
+ }
1692
+ return config.signer !== void 0;
1693
+ }
1669
1694
  function didPrincipalMatches2(actual, expected) {
1670
1695
  try {
1671
1696
  return (0, import_sdk_core8.principalDidEquals)(actual, expected);
@@ -1673,6 +1698,20 @@ function didPrincipalMatches2(actual, expected) {
1673
1698
  return actual === expected;
1674
1699
  }
1675
1700
  }
1701
+ function sharingActionsToAbilities(path, actions) {
1702
+ var _a;
1703
+ const abilities = {};
1704
+ for (const action of actions) {
1705
+ const slash = action.indexOf("/");
1706
+ if (slash === -1) return void 0;
1707
+ const shortService = import_sdk_core8.SERVICE_LONG_TO_SHORT[action.slice(0, slash)];
1708
+ if (shortService === void 0) return void 0;
1709
+ abilities[shortService] ?? (abilities[shortService] = {});
1710
+ (_a = abilities[shortService])[path] ?? (_a[path] = []);
1711
+ abilities[shortService][path].push(action);
1712
+ }
1713
+ return Object.keys(abilities).length > 0 ? abilities : void 0;
1714
+ }
1676
1715
  function base64UrlEncode(bytes) {
1677
1716
  const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
1678
1717
  let output = "";
@@ -1785,7 +1824,24 @@ var _TinyCloudNode = class _TinyCloudNode {
1785
1824
  this.auth = null;
1786
1825
  this.tc = null;
1787
1826
  this._chainId = 1;
1827
+ this._baseSecrets = /* @__PURE__ */ new Map();
1828
+ this._secrets = /* @__PURE__ */ new Map();
1788
1829
  this.runtimePermissionGrants = [];
1830
+ /**
1831
+ * True when the last signIn() detected an interactive signer and skipped
1832
+ * client-side bootstrap. Apps can read this to know whether bootstrap was
1833
+ * deferred to the server (OpenKey) or requires a separate user action.
1834
+ */
1835
+ this._bootstrapSkipped = false;
1836
+ /**
1837
+ * Outcome of the last signIn()'s account-bootstrap attempt. `skipped` is
1838
+ * true when bootstrap did not complete (interactive signer, auto-sign
1839
+ * denied, or a bootstrap step failed); `reason` carries the cause so apps
1840
+ * can surface a "finish account setup" call-to-action.
1841
+ */
1842
+ this._bootstrapStatus = {
1843
+ skipped: false
1844
+ };
1789
1845
  this.invokeWithRuntimePermissions = (session, service, path, action, facts) => {
1790
1846
  return this.wasmBindings.invoke(
1791
1847
  this.selectInvocationSession(session, service, path, action),
@@ -1878,6 +1934,15 @@ var _TinyCloudNode = class _TinyCloudNode {
1878
1934
  static registerNodeDefaults(defaults) {
1879
1935
  _TinyCloudNode.nodeDefaults = defaults;
1880
1936
  }
1937
+ /** Whether the last signIn() skipped client-side bootstrap because the
1938
+ * signer is interactive (browser wallet / EIP-1193 provider). */
1939
+ get bootstrapSkipped() {
1940
+ return this._bootstrapSkipped;
1941
+ }
1942
+ /** Outcome of the last signIn()'s account-bootstrap attempt. */
1943
+ get bootstrapStatus() {
1944
+ return this._bootstrapStatus;
1945
+ }
1881
1946
  get nodeFeatures() {
1882
1947
  return this.auth?.nodeFeatures ?? [];
1883
1948
  }
@@ -2047,6 +2112,13 @@ var _TinyCloudNode = class _TinyCloudNode {
2047
2112
  get session() {
2048
2113
  return this.auth?.tinyCloudSession;
2049
2114
  }
2115
+ /**
2116
+ * Get the currently active session in the shape callers can persist and later
2117
+ * pass back to {@link restoreSession}.
2118
+ */
2119
+ get restorableSession() {
2120
+ return this.currentTinyCloudSession();
2121
+ }
2050
2122
  /**
2051
2123
  * Sign in and create a new session.
2052
2124
  * This creates the user's space if it doesn't exist.
@@ -2069,8 +2141,8 @@ var _TinyCloudNode = class _TinyCloudNode {
2069
2141
  this._hooks = void 0;
2070
2142
  this._vault = void 0;
2071
2143
  this._encryption = void 0;
2072
- this._baseSecrets = void 0;
2073
- this._secrets = void 0;
2144
+ this._baseSecrets.clear();
2145
+ this._secrets.clear();
2074
2146
  this._spaceService = void 0;
2075
2147
  this._serviceContext = void 0;
2076
2148
  this.runtimePermissionGrants = [];
@@ -2094,17 +2166,38 @@ var _TinyCloudNode = class _TinyCloudNode {
2094
2166
  return this.wasmBindings.makeSpaceId(this._address, this._chainId, name);
2095
2167
  }
2096
2168
  async bootstrapAccountIfNeeded() {
2169
+ this._bootstrapSkipped = false;
2170
+ this._bootstrapStatus = { skipped: false };
2097
2171
  if (this.config.autoBootstrapAccount === false) {
2098
2172
  return false;
2099
2173
  }
2100
2174
  if (!this.auth || !this._address) {
2101
2175
  return false;
2102
2176
  }
2177
+ if (isInteractiveSigner(this.config)) {
2178
+ console.debug(
2179
+ "[TinyCloudNode] bootstrap skipped: interactive signer detected. Server-side bootstrap (OpenKey) is expected to have provisioned the account."
2180
+ );
2181
+ this._bootstrapSkipped = true;
2182
+ this._bootstrapStatus = { skipped: true, reason: "interactive-signer" };
2183
+ return false;
2184
+ }
2103
2185
  const steps = (0, import_sdk_core8.bootstrapSteps)(this._address, this._chainId);
2104
2186
  if (!await this.isFreshBootstrapAccount(steps)) {
2105
2187
  return false;
2106
2188
  }
2107
- await this.runAccountBootstrap(steps);
2189
+ try {
2190
+ await this.runAccountBootstrap(steps);
2191
+ } catch (err) {
2192
+ const reason = err instanceof Error ? err.message : String(err);
2193
+ this._bootstrapSkipped = true;
2194
+ this._bootstrapStatus = { skipped: true, reason };
2195
+ this.notificationHandler.warning(
2196
+ `Account bootstrap did not complete: ${reason}`
2197
+ );
2198
+ console.warn(`[TinyCloudNode] account bootstrap failed: ${reason}`);
2199
+ return false;
2200
+ }
2108
2201
  return true;
2109
2202
  }
2110
2203
  async isFreshBootstrapAccount(steps) {
@@ -2156,16 +2249,23 @@ var _TinyCloudNode = class _TinyCloudNode {
2156
2249
  if (rawAbilities) {
2157
2250
  rawAbilitiesBySpace.set(step.space, rawAbilities);
2158
2251
  }
2159
- const session = await auth.createBootstrapSession({
2160
- spaceId: step.spaceId,
2161
- capabilityRequest: step.request ?? import_sdk_core8.BOOTSTRAP_SESSION_REQUESTS[step.space],
2162
- rawAbilities
2163
- });
2252
+ let session;
2253
+ try {
2254
+ session = await auth.createBootstrapSession({
2255
+ spaceId: step.spaceId,
2256
+ capabilityRequest: step.request ?? import_sdk_core8.BOOTSTRAP_SESSION_REQUESTS[step.space],
2257
+ rawAbilities
2258
+ });
2259
+ } catch (err) {
2260
+ throw new Error(
2261
+ `Account bootstrap aborted: signature rejected for space "${step.space}". Cause: ${err instanceof Error ? err.message : String(err)}`
2262
+ );
2263
+ }
2164
2264
  sessions.set(step.space, session);
2165
2265
  }
2166
2266
  for (const step of steps) {
2167
2267
  if (step.kind !== "host") continue;
2168
- const hosted = await auth.hostOwnedSpace(step.spaceId);
2268
+ const hosted = await auth.hostOwnedSpace(step.spaceId, "bootstrap-host");
2169
2269
  if (!hosted) {
2170
2270
  throw new Error(`Failed to host bootstrap space: ${step.spaceId}`);
2171
2271
  }
@@ -2556,8 +2656,8 @@ var _TinyCloudNode = class _TinyCloudNode {
2556
2656
  this._hooks = void 0;
2557
2657
  this._vault = void 0;
2558
2658
  this._encryption = void 0;
2559
- this._baseSecrets = void 0;
2560
- this._secrets = void 0;
2659
+ this._baseSecrets.clear();
2660
+ this._secrets.clear();
2561
2661
  this._spaceService = void 0;
2562
2662
  this._serviceContext = void 0;
2563
2663
  this.runtimePermissionGrants = [];
@@ -2847,6 +2947,20 @@ var _TinyCloudNode = class _TinyCloudNode {
2847
2947
  getDefaultEncryptionNetworkId(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
2848
2948
  return `urn:tinycloud:encryption:${this.did}:${name}`;
2849
2949
  }
2950
+ getEncryptionNetworkIdForSpace(spaceId, name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
2951
+ const ownerDid = this.ownerDidFromSpaceId(spaceId) ?? this.did;
2952
+ return `urn:tinycloud:encryption:${ownerDid}:${name}`;
2953
+ }
2954
+ ownerDidFromSpaceId(spaceId) {
2955
+ if (!spaceId.startsWith("tinycloud:")) return void 0;
2956
+ const body = spaceId.slice("tinycloud:".length);
2957
+ const lastSeparator = body.lastIndexOf(":");
2958
+ if (lastSeparator <= 0) return void 0;
2959
+ const owner = body.slice(0, lastSeparator);
2960
+ if (owner.startsWith("did:")) return owner;
2961
+ if (!owner.includes(":")) return void 0;
2962
+ return `did:${owner}`;
2963
+ }
2850
2964
  requireServiceSession() {
2851
2965
  const session = this._serviceContext?.session;
2852
2966
  if (!session) {
@@ -3034,7 +3148,7 @@ var _TinyCloudNode = class _TinyCloudNode {
3034
3148
  spaceId,
3035
3149
  crypto: vaultCrypto,
3036
3150
  encryption: {
3037
- networkId: this.getDefaultEncryptionNetworkId(),
3151
+ networkId: this.getEncryptionNetworkIdForSpace(spaceId),
3038
3152
  service: this.getEncryptionService(),
3039
3153
  decryptCapabilityProof: () => ({
3040
3154
  proofs: [this.requireServiceSession().delegationCid]
@@ -3165,6 +3279,9 @@ var _TinyCloudNode = class _TinyCloudNode {
3165
3279
  }
3166
3280
  return vaultService;
3167
3281
  },
3282
+ createSecretsService: (spaceId) => {
3283
+ return this.secretsForSpace(spaceId);
3284
+ },
3168
3285
  // Enable space.delegations.create() via SIWE-based delegation
3169
3286
  createDelegation: async (params) => {
3170
3287
  try {
@@ -3287,11 +3404,10 @@ var _TinyCloudNode = class _TinyCloudNode {
3287
3404
  try {
3288
3405
  const host = this.config.host;
3289
3406
  const now = /* @__PURE__ */ new Date();
3290
- const abilities = {
3291
- kv: {
3292
- [params.path]: params.actions
3293
- }
3294
- };
3407
+ const abilities = sharingActionsToAbilities(params.path, params.actions);
3408
+ if (!abilities) {
3409
+ return void 0;
3410
+ }
3295
3411
  const prepared = this.wasmBindings.prepareSession({
3296
3412
  abilities,
3297
3413
  address: this.wasmBindings.ensureEip55(session.address),
@@ -3547,27 +3663,49 @@ var _TinyCloudNode = class _TinyCloudNode {
3547
3663
  if (!this._spaceService) {
3548
3664
  throw new Error("Not signed in. Call signIn() first.");
3549
3665
  }
3550
- if (!this._secrets) {
3551
- this._secrets = new NodeSecretsService({
3552
- getService: () => this.getBaseSecrets(),
3666
+ return this.secretsForSpace("secrets");
3667
+ }
3668
+ /**
3669
+ * App-facing secrets API backed by the requested space's vault.
3670
+ */
3671
+ secretsForSpace(spaceId) {
3672
+ if (!this._spaceService) {
3673
+ throw new Error("Not signed in. Call signIn() first.");
3674
+ }
3675
+ const resolvedSpace = spaceId.startsWith("tinycloud:") ? spaceId : this.ownedSpaceId(spaceId);
3676
+ let secrets = this._secrets.get(resolvedSpace);
3677
+ if (!secrets) {
3678
+ secrets = new NodeSecretsService({
3679
+ getService: () => this.getBaseSecrets(resolvedSpace),
3680
+ space: resolvedSpace,
3553
3681
  getManifest: () => this.manifest,
3554
3682
  hasPermissions: (permissions) => this.hasRuntimePermissions(permissions),
3555
3683
  grantPermissions: (additional) => this.grantRuntimePermissions(additional),
3556
3684
  canEscalate: () => this.signer !== void 0 && this.tc !== void 0,
3557
- getEncryptionNetworkId: () => this.getDefaultEncryptionNetworkId(),
3685
+ getEncryptionNetworkId: () => this.getEncryptionNetworkIdForSpace(resolvedSpace),
3686
+ resolveSpace: (space) => space.startsWith("tinycloud:") ? space : this.ownedSpaceId(space),
3558
3687
  getUnlockSigner: () => this.signer ?? void 0
3559
3688
  });
3689
+ this._secrets.set(resolvedSpace, secrets);
3560
3690
  }
3561
- return this._secrets;
3691
+ return secrets;
3562
3692
  }
3563
- getBaseSecrets() {
3693
+ getBaseSecrets(spaceId) {
3564
3694
  if (!this._spaceService) {
3565
3695
  throw new Error("Not signed in. Call signIn() first.");
3566
3696
  }
3567
- if (!this._baseSecrets) {
3568
- this._baseSecrets = new import_sdk_core8.SecretsService(() => this.space("secrets").vault);
3697
+ const resolvedSpace = spaceId.startsWith("tinycloud:") ? spaceId : this.ownedSpaceId(spaceId);
3698
+ let secrets = this._baseSecrets.get(resolvedSpace);
3699
+ if (!secrets) {
3700
+ const kvService = this.createSpaceScopedKVService(resolvedSpace);
3701
+ const vaultService = this.createVaultService(resolvedSpace, kvService);
3702
+ if (this._serviceContext) {
3703
+ vaultService.initialize(this._serviceContext);
3704
+ }
3705
+ secrets = new import_sdk_core8.SecretsService(() => vaultService);
3706
+ this._baseSecrets.set(resolvedSpace, secrets);
3569
3707
  }
3570
- return this._baseSecrets;
3708
+ return secrets;
3571
3709
  }
3572
3710
  /**
3573
3711
  * Hooks write stream subscription API.