@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.
package/dist/core.js CHANGED
@@ -18,7 +18,14 @@ import {
18
18
  pkhDid as pkhDid3,
19
19
  principalDid,
20
20
  principalDidEquals as principalDidEquals3,
21
- parseCanonicalNetworkId
21
+ parseCanonicalNetworkId,
22
+ TinyCloudDebugLogger,
23
+ tinyCloudDebugLogger,
24
+ enableTinyCloudDebug,
25
+ disableTinyCloudDebug,
26
+ getTinyCloudDebugLogs,
27
+ clearTinyCloudDebugLogs,
28
+ installTinyCloudDebugGlobals
22
29
  } from "@tinycloud/sdk-core";
23
30
 
24
31
  // src/storage/MemorySessionStorage.ts
@@ -234,6 +241,7 @@ import {
234
241
  } from "@tinycloud/sdk-core";
235
242
 
236
243
  // src/authorization/strategies.ts
244
+ import { createOpenKeyCallbackSigningStrategy } from "@tinycloud/sdk-core";
237
245
  var defaultSignStrategy = { type: "auto-sign" };
238
246
 
239
247
  // src/authorization/NodeUserAuthorization.ts
@@ -260,6 +268,7 @@ var NodeUserAuthorization = class {
260
268
  constructor(config) {
261
269
  this.extensions = [];
262
270
  this._nodeFeatures = [];
271
+ this._lastActivationSkippedSpaceIds = [];
263
272
  this.wasm = config.wasmBindings;
264
273
  this.signer = config.signer;
265
274
  this.signStrategy = config.signStrategy ?? defaultSignStrategy;
@@ -282,7 +291,7 @@ var NodeUserAuthorization = class {
282
291
  "": [
283
292
  "tinycloud.sql/read",
284
293
  "tinycloud.sql/write",
285
- "tinycloud.sql/ddl",
294
+ "tinycloud.sql/schema",
286
295
  "tinycloud.sql/admin",
287
296
  "tinycloud.sql/export"
288
297
  ]
@@ -374,12 +383,48 @@ var NodeUserAuthorization = class {
374
383
  * `siwe` is the load-bearing one because `extractSiweExpiration` returns
375
384
  * undefined for missing SIWEs and the SDK then treats the session as
376
385
  * expired-at-epoch-zero.
386
+ *
387
+ * @param hosts - The TinyCloud hosts this session was created against,
388
+ * as persisted in {@link PersistedSessionData.tinycloudHosts}. When
389
+ * present (and non-empty) they are adopted directly so the restored
390
+ * session resolves to the same node as the original sign-in without
391
+ * re-running registry/fallback resolution. When absent (old session)
392
+ * hosts are resolved lazily on the first host-needing call via
393
+ * {@link ensureTinyCloudHosts}.
377
394
  */
378
- setRestoredTinyCloudSession(session) {
395
+ setRestoredTinyCloudSession(session, hosts) {
379
396
  const address = canonicalizeAddress(session.address);
380
397
  this._tinyCloudSession = { ...session, address };
381
398
  this._address = address;
382
399
  this._chainId = session.chainId;
400
+ if ((!this.tinycloudHosts || this.tinycloudHosts.length === 0) && hosts && hosts.length > 0) {
401
+ this.tinycloudHosts = [...hosts];
402
+ }
403
+ }
404
+ /**
405
+ * Ensure `tinycloudHosts` are resolved before a host-needing call.
406
+ *
407
+ * Fresh sign-in resolves hosts up front; a restored session may not have
408
+ * (old persisted sessions predate {@link PersistedSessionData.tinycloudHosts}).
409
+ * This guard makes a restored session resolve the node exactly like a fresh
410
+ * sign-in — persisted hosts are preferred (already set by
411
+ * {@link setRestoredTinyCloudSession}), otherwise the registry/fallback
412
+ * resolution runs lazily here. Idempotent: {@link resolveTinyCloudHostsForSignIn}
413
+ * early-returns when hosts are already set.
414
+ *
415
+ * Throws if hosts are unset and the restored session has no address/chainId
416
+ * to resolve from — a real failure that must surface, not be masked.
417
+ */
418
+ async ensureTinyCloudHosts() {
419
+ if (this.tinycloudHosts && this.tinycloudHosts.length > 0) {
420
+ return;
421
+ }
422
+ if (this._address === void 0 || this._chainId === void 0) {
423
+ throw new Error(
424
+ "Cannot resolve TinyCloud hosts: no address/chainId available. Sign in or restore a session first."
425
+ );
426
+ }
427
+ await this.resolveTinyCloudHostsForSignIn(this._address, this._chainId);
383
428
  }
384
429
  async resolveTinyCloudHostsForSignIn(address, chainId) {
385
430
  if (this.tinycloudHosts && this.tinycloudHosts.length > 0) {
@@ -404,6 +449,9 @@ var NodeUserAuthorization = class {
404
449
  get nodeFeatures() {
405
450
  return this._nodeFeatures;
406
451
  }
452
+ get lastActivationSkippedSpaceIds() {
453
+ return [...this._lastActivationSkippedSpaceIds];
454
+ }
407
455
  /**
408
456
  * Compute the `abilities` map the WASM `prepareSession` call should
409
457
  * see at sign-in time.
@@ -576,6 +624,7 @@ var NodeUserAuthorization = class {
576
624
  if (!this._tinyCloudSession || !this._address || !this._chainId) {
577
625
  throw new Error("Must be signed in to host space");
578
626
  }
627
+ await this.ensureTinyCloudHosts();
579
628
  const host = this.primaryTinyCloudHost;
580
629
  const spaceId = targetSpaceId ?? this._tinyCloudSession.spaceId;
581
630
  const peerId = await fetchPeerId(host, spaceId);
@@ -618,12 +667,14 @@ var NodeUserAuthorization = class {
618
667
  if (!this._tinyCloudSession) {
619
668
  throw new Error("Must be signed in to ensure space exists");
620
669
  }
670
+ await this.ensureTinyCloudHosts();
621
671
  const host = this.primaryTinyCloudHost;
622
672
  const primarySpaceId = this._tinyCloudSession.spaceId;
623
673
  const result = await activateSessionWithHost(
624
674
  host,
625
675
  this._tinyCloudSession.delegationHeader
626
676
  );
677
+ this.recordActivationSkippedSpaces(result, primarySpaceId);
627
678
  const handler = this.spaceCreationHandler ?? (this.autoCreateSpace ? new AutoApproveSpaceCreationHandler() : void 0);
628
679
  const creationContext = {
629
680
  spaceId: primarySpaceId,
@@ -711,6 +762,66 @@ var NodeUserAuthorization = class {
711
762
  }
712
763
  throw new Error(`Failed to activate session: ${result.error}`);
713
764
  }
765
+ recordActivationSkippedSpaces(result, primarySpaceId) {
766
+ if (result.success) {
767
+ this._lastActivationSkippedSpaceIds = result.skipped ?? [];
768
+ return;
769
+ }
770
+ this._lastActivationSkippedSpaceIds = result.status === 404 ? [primarySpaceId] : [];
771
+ }
772
+ async createBootstrapSession(options) {
773
+ if (!this._address || !this._chainId) {
774
+ throw new Error("Must be signed in before creating bootstrap sessions");
775
+ }
776
+ const address = this._address;
777
+ const chainId = this._chainId;
778
+ const keyId = `bootstrap-${Date.now()}-${Math.random().toString(36).slice(2)}`;
779
+ this.sessionManager.createSessionKey(keyId);
780
+ const jwkString = this.sessionManager.jwk(keyId);
781
+ if (!jwkString) {
782
+ throw new Error("Failed to create bootstrap session key");
783
+ }
784
+ const jwk = JSON.parse(jwkString);
785
+ const now = /* @__PURE__ */ new Date();
786
+ const expirationTime = new Date(now.getTime() + this.sessionExpirationMs);
787
+ const abilities = resourceCapabilitiesToAbilitiesMap(
788
+ options.capabilityRequest.resources
789
+ );
790
+ const prepared = this.wasm.prepareSession({
791
+ abilities,
792
+ ...options.rawAbilities !== void 0 ? { rawAbilities: options.rawAbilities } : {},
793
+ address,
794
+ chainId,
795
+ domain: this.domain,
796
+ issuedAt: now.toISOString(),
797
+ expirationTime: expirationTime.toISOString(),
798
+ spaceId: options.spaceId,
799
+ jwk,
800
+ ...this.buildSiweOverrides()
801
+ });
802
+ const signature = await this.requestSignature({
803
+ address,
804
+ chainId,
805
+ message: prepared.siwe,
806
+ type: "siwe"
807
+ });
808
+ const session = this.wasm.completeSessionSetup({
809
+ ...prepared,
810
+ signature
811
+ });
812
+ return {
813
+ address,
814
+ chainId,
815
+ sessionKey: keyId,
816
+ spaceId: options.spaceId,
817
+ delegationCid: session.delegationCid,
818
+ delegationHeader: session.delegationHeader,
819
+ verificationMethod: this.sessionManager.getDID(keyId),
820
+ jwk,
821
+ siwe: prepared.siwe,
822
+ signature
823
+ };
824
+ }
714
825
  /**
715
826
  * Sign in and create a new session.
716
827
  *
@@ -799,7 +910,8 @@ var NodeUserAuthorization = class {
799
910
  },
800
911
  expiresAt: expirationTime.toISOString(),
801
912
  createdAt: now.toISOString(),
802
- version: "1.0"
913
+ version: "1.0",
914
+ tinycloudHosts: this.tinycloudHosts
803
915
  };
804
916
  await this.sessionStorage.save(address, persistedData);
805
917
  this._session = clientSession;
@@ -966,7 +1078,8 @@ var NodeUserAuthorization = class {
966
1078
  },
967
1079
  expiresAt,
968
1080
  createdAt,
969
- version: "1.0"
1081
+ version: "1.0",
1082
+ tinycloudHosts: this.tinycloudHosts
970
1083
  };
971
1084
  await this.sessionStorage.save(address, persistedData);
972
1085
  this._session = clientSession;
@@ -1077,6 +1190,11 @@ import {
1077
1190
  UnsupportedFeatureError,
1078
1191
  makePublicSpaceId,
1079
1192
  ACCOUNT_REGISTRY_SPACE,
1193
+ BOOTSTRAP_SESSION_REQUESTS,
1194
+ SECRET_RECORDS_SCHEMA,
1195
+ SECRETS_SPACE as SECRETS_SPACE2,
1196
+ TINYCLOUD_SECRETS_BOOTSTRAP_MANIFEST,
1197
+ bootstrapSteps,
1080
1198
  ENCRYPTION_PERMISSION_SERVICE as ENCRYPTION_PERMISSION_SERVICE2,
1081
1199
  PermissionNotInManifestError,
1082
1200
  SessionExpiredError,
@@ -1090,6 +1208,7 @@ import {
1090
1208
  verifyDidKeyEd25519Signature,
1091
1209
  canonicalizeAddress as canonicalizeAddress2,
1092
1210
  pkhDid as pkhDid2,
1211
+ resolveTinyCloudHosts as resolveTinyCloudHosts2,
1093
1212
  principalDidEquals as principalDidEquals2,
1094
1213
  parseNetworkId as parseNetworkId2
1095
1214
  } from "@tinycloud/sdk-core";
@@ -1106,12 +1225,13 @@ import {
1106
1225
  ServiceContext
1107
1226
  } from "@tinycloud/sdk-core";
1108
1227
  var DelegatedAccess = class {
1109
- constructor(session, delegation, host, invoke, telemetry) {
1228
+ constructor(session, delegation, host, invoke, invokeAny, telemetry) {
1110
1229
  this.session = session;
1111
1230
  this._delegation = delegation;
1112
1231
  this.host = host;
1113
1232
  this._serviceContext = new ServiceContext({
1114
1233
  invoke,
1234
+ invokeAny,
1115
1235
  fetch: globalThis.fetch.bind(globalThis),
1116
1236
  hosts: [host],
1117
1237
  telemetry
@@ -1378,12 +1498,12 @@ function displayActionUrn(action) {
1378
1498
  function secretActionName(action) {
1379
1499
  return action;
1380
1500
  }
1381
- function secretPermissionEntries(name, options, action, encryptionNetworkId) {
1501
+ function secretPermissionEntries(name, options, action, space, encryptionNetworkId) {
1382
1502
  const entries = [];
1383
1503
  const path = action === "list" ? resolveSecretListPrefix(options) : resolveSecretPath(name, options).permissionPaths.vault;
1384
1504
  entries.push({
1385
1505
  service: "tinycloud.kv",
1386
- space: SECRETS_SPACE,
1506
+ space,
1387
1507
  path,
1388
1508
  actions: [secretActionName(action)],
1389
1509
  skipPrefix: true
@@ -1398,11 +1518,23 @@ function secretPermissionEntries(name, options, action, encryptionNetworkId) {
1398
1518
  }
1399
1519
  return entries;
1400
1520
  }
1521
+ function normalizeSpace(space, resolveSpace) {
1522
+ if (!space) return void 0;
1523
+ if (space.startsWith("tinycloud:")) return space;
1524
+ return resolveSpace?.(space) ?? space;
1525
+ }
1526
+ function spaceMatches(granted, requested, resolveSpace) {
1527
+ if (!granted || !requested) return false;
1528
+ return normalizeSpace(granted, resolveSpace) === normalizeSpace(requested, resolveSpace);
1529
+ }
1401
1530
  var NodeSecretsService = class {
1402
1531
  constructor(config) {
1403
1532
  this.config = config;
1404
1533
  this.shouldRestoreUnlock = false;
1405
1534
  }
1535
+ get space() {
1536
+ return this.config.space ?? SECRETS_SPACE;
1537
+ }
1406
1538
  get vault() {
1407
1539
  return this.service.vault;
1408
1540
  }
@@ -1455,6 +1587,7 @@ var NodeSecretsService = class {
1455
1587
  name,
1456
1588
  options,
1457
1589
  action,
1590
+ this.space,
1458
1591
  action === "get" ? this.config.getEncryptionNetworkId?.() : void 0
1459
1592
  );
1460
1593
  } catch (error) {
@@ -1504,7 +1637,7 @@ var NodeSecretsService = class {
1504
1637
  (entry) => manifests.some((candidate) => {
1505
1638
  const resolved = resolveManifest(candidate);
1506
1639
  return resolved.resources.some(
1507
- (resource) => resource.service === entry.service && resource.space === entry.space && resource.path === entry.path && entry.actions.every((action) => resource.actions.includes(action))
1640
+ (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))
1508
1641
  );
1509
1642
  })
1510
1643
  );
@@ -1518,6 +1651,9 @@ var NETWORK_CREATE_ACTION2 = "tinycloud.encryption/network.create";
1518
1651
  var DECRYPT_ACTION2 = "tinycloud.encryption/decrypt";
1519
1652
  var NETWORK_ADMIN_TYPE = "tinycloud.encryption.network-admin/v1";
1520
1653
  var DEFAULT_SESSION_EXPIRATION_MS = EXPIRY3.SESSION_MS;
1654
+ function isOpenKeyAutoSignStrategy(strategy) {
1655
+ return strategy?.openKeyAutoSign === true;
1656
+ }
1521
1657
  function didPrincipalMatches2(actual, expected) {
1522
1658
  try {
1523
1659
  return principalDidEquals2(actual, expected);
@@ -1525,6 +1661,20 @@ function didPrincipalMatches2(actual, expected) {
1525
1661
  return actual === expected;
1526
1662
  }
1527
1663
  }
1664
+ function sharingActionsToAbilities(path, actions) {
1665
+ var _a;
1666
+ const abilities = {};
1667
+ for (const action of actions) {
1668
+ const slash = action.indexOf("/");
1669
+ if (slash === -1) return void 0;
1670
+ const shortService = SERVICE_LONG_TO_SHORT[action.slice(0, slash)];
1671
+ if (shortService === void 0) return void 0;
1672
+ abilities[shortService] ?? (abilities[shortService] = {});
1673
+ (_a = abilities[shortService])[path] ?? (_a[path] = []);
1674
+ abilities[shortService][path].push(action);
1675
+ }
1676
+ return Object.keys(abilities).length > 0 ? abilities : void 0;
1677
+ }
1528
1678
  function base64UrlEncode(bytes) {
1529
1679
  const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
1530
1680
  let output = "";
@@ -1637,6 +1787,8 @@ var _TinyCloudNode = class _TinyCloudNode {
1637
1787
  this.auth = null;
1638
1788
  this.tc = null;
1639
1789
  this._chainId = 1;
1790
+ this._baseSecrets = /* @__PURE__ */ new Map();
1791
+ this._secrets = /* @__PURE__ */ new Map();
1640
1792
  this.runtimePermissionGrants = [];
1641
1793
  this.invokeWithRuntimePermissions = (session, service, path, action, facts) => {
1642
1794
  return this.wasmBindings.invoke(
@@ -1742,9 +1894,10 @@ var _TinyCloudNode = class _TinyCloudNode {
1742
1894
  * @internal
1743
1895
  */
1744
1896
  setupAuth(config) {
1897
+ const useBootstrapSignInRequest = this.shouldUseBootstrapSignInRequest(config);
1745
1898
  this.auth = new NodeUserAuthorization({
1746
1899
  signer: this.signer,
1747
- signStrategy: { type: "auto-sign" },
1900
+ signStrategy: config.signStrategy ?? { type: "auto-sign" },
1748
1901
  wasmBindings: this.wasmBindings,
1749
1902
  sessionStorage: config.sessionStorage ?? new MemorySessionStorage(),
1750
1903
  domain: this.siweDomain,
@@ -1753,20 +1906,23 @@ var _TinyCloudNode = class _TinyCloudNode {
1753
1906
  tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
1754
1907
  tinycloudRegistryUrl: config.tinycloudRegistryUrl,
1755
1908
  tinycloudFallbackHosts: config.tinycloudFallbackHosts,
1756
- autoCreateSpace: config.autoCreateSpace,
1909
+ autoCreateSpace: useBootstrapSignInRequest ? false : config.autoCreateSpace,
1757
1910
  enablePublicSpace: config.enablePublicSpace ?? true,
1758
- spaceCreationHandler: config.spaceCreationHandler,
1911
+ spaceCreationHandler: useBootstrapSignInRequest ? void 0 : config.spaceCreationHandler,
1759
1912
  nonce: config.nonce,
1760
1913
  siweConfig: config.siweConfig,
1761
- manifest: config.manifest,
1762
- capabilityRequest: config.capabilityRequest,
1763
- includeAccountRegistryPermissions: config.includeAccountRegistryPermissions
1914
+ manifest: useBootstrapSignInRequest ? void 0 : config.manifest,
1915
+ capabilityRequest: useBootstrapSignInRequest ? BOOTSTRAP_SESSION_REQUESTS.default : config.capabilityRequest,
1916
+ includeAccountRegistryPermissions: useBootstrapSignInRequest ? false : config.includeAccountRegistryPermissions
1764
1917
  });
1765
1918
  this.tc = new TinyCloud(this.auth, {
1766
1919
  invokeAny: this.invokeAnyWithRuntimePermissions,
1767
1920
  telemetry: config.telemetry
1768
1921
  });
1769
1922
  }
1923
+ shouldUseBootstrapSignInRequest(config) {
1924
+ return config.autoBootstrapAccount !== false && config.manifest === void 0 && config.capabilityRequest === void 0 && (config.prefix ?? "default") === "default" && isOpenKeyAutoSignStrategy(config.signStrategy);
1925
+ }
1770
1926
  syncResolvedHostFromAuth() {
1771
1927
  const host = this.auth?.hosts[0];
1772
1928
  if (host) {
@@ -1881,7 +2037,7 @@ var _TinyCloudNode = class _TinyCloudNode {
1881
2037
  getAccountDb: () => this.accountSpaceId ? this.sqlForSpace(this.accountSpaceId).db("account") : void 0,
1882
2038
  ensureAccountSpaceHosted: async () => {
1883
2039
  if (this.accountSpaceId && this.auth) {
1884
- await this.ensureOwnedSpaceHosted(this.accountSpaceId);
2040
+ await this.ensureOwnedSpaceHostedById(this.accountSpaceId);
1885
2041
  }
1886
2042
  }
1887
2043
  });
@@ -1895,6 +2051,13 @@ var _TinyCloudNode = class _TinyCloudNode {
1895
2051
  get session() {
1896
2052
  return this.auth?.tinyCloudSession;
1897
2053
  }
2054
+ /**
2055
+ * Get the currently active session in the shape callers can persist and later
2056
+ * pass back to {@link restoreSession}.
2057
+ */
2058
+ get restorableSession() {
2059
+ return this.currentTinyCloudSession();
2060
+ }
1898
2061
  /**
1899
2062
  * Sign in and create a new session.
1900
2063
  * This creates the user's space if it doesn't exist.
@@ -1917,19 +2080,22 @@ var _TinyCloudNode = class _TinyCloudNode {
1917
2080
  this._hooks = void 0;
1918
2081
  this._vault = void 0;
1919
2082
  this._encryption = void 0;
1920
- this._baseSecrets = void 0;
1921
- this._secrets = void 0;
2083
+ this._baseSecrets.clear();
2084
+ this._secrets.clear();
1922
2085
  this._spaceService = void 0;
1923
2086
  this._serviceContext = void 0;
1924
2087
  this.runtimePermissionGrants = [];
1925
2088
  await this.tc.signIn(options);
1926
2089
  this.syncResolvedHostFromAuth();
1927
2090
  this.initializeServices();
2091
+ const bootstrapped = await this.bootstrapAccountIfNeeded();
1928
2092
  await this.ensureRequestedEncryptionNetworks();
1929
- if (this.config.manifest === void 0 && this.config.capabilityRequest === void 0) {
1930
- await this.ensureOwnedSpaceHosted(this.ownedSpaceId("secrets"));
2093
+ if (!bootstrapped && this.config.manifest === void 0 && this.config.capabilityRequest === void 0) {
2094
+ await this.ensureOwnedSpaceHostedById(this.ownedSpaceId(SECRETS_SPACE2));
2095
+ }
2096
+ if (!bootstrapped) {
2097
+ this.scheduleAccountRegistrySync();
1931
2098
  }
1932
- this.scheduleAccountRegistrySync();
1933
2099
  this.notificationHandler.success("Successfully signed in");
1934
2100
  }
1935
2101
  ownedSpaceId(name) {
@@ -1938,6 +2104,208 @@ var _TinyCloudNode = class _TinyCloudNode {
1938
2104
  }
1939
2105
  return this.wasmBindings.makeSpaceId(this._address, this._chainId, name);
1940
2106
  }
2107
+ async bootstrapAccountIfNeeded() {
2108
+ if (this.config.autoBootstrapAccount === false) {
2109
+ return false;
2110
+ }
2111
+ if (!this.auth || !this._address) {
2112
+ return false;
2113
+ }
2114
+ const steps = bootstrapSteps(this._address, this._chainId);
2115
+ if (!await this.isFreshBootstrapAccount(steps)) {
2116
+ return false;
2117
+ }
2118
+ await this.runAccountBootstrap(steps);
2119
+ return true;
2120
+ }
2121
+ async isFreshBootstrapAccount(steps) {
2122
+ const enshrinedSpaceIds = /* @__PURE__ */ new Set();
2123
+ for (const step of steps) {
2124
+ if (step.kind === "session") {
2125
+ enshrinedSpaceIds.add(step.spaceId);
2126
+ }
2127
+ }
2128
+ const skipped = this.auth.lastActivationSkippedSpaceIds;
2129
+ if (skipped.some((spaceId) => enshrinedSpaceIds.has(spaceId))) {
2130
+ return true;
2131
+ }
2132
+ try {
2133
+ const indexed = await this.account.index.spaces.list();
2134
+ if (indexed.ok && indexed.data.length === 0) {
2135
+ return true;
2136
+ }
2137
+ } catch {
2138
+ }
2139
+ try {
2140
+ const spaces = await this.account.spaces.list();
2141
+ return spaces.ok && spaces.data.length === 0;
2142
+ } catch {
2143
+ return false;
2144
+ }
2145
+ }
2146
+ async runAccountBootstrap(steps) {
2147
+ if (!this.auth || !this._address) {
2148
+ throw new Error("Account bootstrap requires an active wallet session");
2149
+ }
2150
+ const host = this.hosts[0] ?? this.config.host;
2151
+ if (!host) {
2152
+ throw new Error("Account bootstrap requires a TinyCloud host");
2153
+ }
2154
+ const auth = this.auth;
2155
+ const sessions = /* @__PURE__ */ new Map();
2156
+ const rawAbilitiesBySpace = /* @__PURE__ */ new Map();
2157
+ const primarySession = auth.tinyCloudSession;
2158
+ const defaultSpaceId = this.ownedSpaceId("default");
2159
+ const canReusePrimaryBootstrapSession = primarySession?.spaceId === defaultSpaceId && auth.capabilityRequest === BOOTSTRAP_SESSION_REQUESTS.default;
2160
+ for (const step of steps) {
2161
+ if (step.kind !== "session") continue;
2162
+ if (step.space === "default" && canReusePrimaryBootstrapSession && primarySession) {
2163
+ sessions.set(step.space, primarySession);
2164
+ continue;
2165
+ }
2166
+ const rawAbilities = step.rawAbilities;
2167
+ if (rawAbilities) {
2168
+ rawAbilitiesBySpace.set(step.space, rawAbilities);
2169
+ }
2170
+ const session = await auth.createBootstrapSession({
2171
+ spaceId: step.spaceId,
2172
+ capabilityRequest: step.request ?? BOOTSTRAP_SESSION_REQUESTS[step.space],
2173
+ rawAbilities
2174
+ });
2175
+ sessions.set(step.space, session);
2176
+ }
2177
+ for (const step of steps) {
2178
+ if (step.kind !== "host") continue;
2179
+ const hosted = await auth.hostOwnedSpace(step.spaceId);
2180
+ if (!hosted) {
2181
+ throw new Error(`Failed to host bootstrap space: ${step.spaceId}`);
2182
+ }
2183
+ }
2184
+ await new Promise((resolve) => setTimeout(resolve, 100));
2185
+ for (const step of steps) {
2186
+ if (step.kind !== "activate") continue;
2187
+ const session = sessions.get(step.space);
2188
+ if (!session) {
2189
+ throw new Error(`Missing bootstrap session for ${step.space}`);
2190
+ }
2191
+ const activated = await activateSessionWithHost2(host, session.delegationHeader);
2192
+ if (!activated.success || activated.skipped?.includes(step.spaceId)) {
2193
+ throw new Error(
2194
+ `Failed to activate bootstrap session for ${step.spaceId}: ${activated.error ?? "space was skipped"}`
2195
+ );
2196
+ }
2197
+ this.registerBootstrapRuntimeGrant(
2198
+ session,
2199
+ BOOTSTRAP_SESSION_REQUESTS[step.space],
2200
+ rawAbilitiesBySpace.get(step.space)
2201
+ );
2202
+ }
2203
+ for (const step of steps) {
2204
+ if (step.kind === "account-index-schema") {
2205
+ const ensured = await this.account.index.ensure();
2206
+ if (!ensured.ok) {
2207
+ throw new Error(`Failed to create account index schema: ${ensured.error.message}`);
2208
+ }
2209
+ }
2210
+ if (step.kind === "seed-spaces") {
2211
+ for (const space of step.spaces) {
2212
+ const registered = await this.account.spaces.register({
2213
+ spaceId: space.spaceId,
2214
+ name: space.name,
2215
+ ownerDid: this.did,
2216
+ type: "owned",
2217
+ permissions: ["*"],
2218
+ status: "active"
2219
+ });
2220
+ if (!registered.ok) {
2221
+ throw new Error(
2222
+ `Failed to seed account space ${space.spaceId}: ${registered.error.message}`
2223
+ );
2224
+ }
2225
+ }
2226
+ }
2227
+ if (step.kind === "seed-applications") {
2228
+ const registered = await this.account.applications.register(
2229
+ step.manifests.length > 0 ? [...step.manifests] : TINYCLOUD_SECRETS_BOOTSTRAP_MANIFEST
2230
+ );
2231
+ if (!registered.ok) {
2232
+ throw new Error(`Failed to seed bootstrap applications: ${registered.error.message}`);
2233
+ }
2234
+ }
2235
+ if (step.kind === "encryption-network-create") {
2236
+ await this.ensureEncryptionNetwork(step.networkId);
2237
+ }
2238
+ if (step.kind === "secret-records-schema") {
2239
+ const db = this.sqlForSpace(step.spaceId).db(step.database);
2240
+ const migrated = await db.migrations.apply({
2241
+ namespace: "tinycloud.secrets.records",
2242
+ migrations: [
2243
+ {
2244
+ id: "001_initial",
2245
+ sql: [...SECRET_RECORDS_SCHEMA]
2246
+ }
2247
+ ]
2248
+ });
2249
+ if (!migrated.ok) {
2250
+ throw new Error(
2251
+ `Failed to create secret_records schema: ${migrated.error.message}`
2252
+ );
2253
+ }
2254
+ }
2255
+ }
2256
+ }
2257
+ registerBootstrapRuntimeGrant(session, request, rawAbilities) {
2258
+ const operations = [];
2259
+ for (const resource of request.resources) {
2260
+ const service = resource.service.startsWith("tinycloud.") ? this.shortServiceName(resource.service) : resource.service;
2261
+ const spaceId = resource.space.startsWith("tinycloud:") ? resource.space : this.ownedSpaceId(resource.space);
2262
+ for (const action of resource.actions) {
2263
+ operations.push({
2264
+ spaceId,
2265
+ service,
2266
+ path: resource.path,
2267
+ action
2268
+ });
2269
+ }
2270
+ }
2271
+ for (const [resource, actions2] of Object.entries(rawAbilities ?? {})) {
2272
+ for (const action of actions2) {
2273
+ operations.push({
2274
+ resource,
2275
+ service: "encryption",
2276
+ path: resource,
2277
+ action
2278
+ });
2279
+ }
2280
+ }
2281
+ const expiresAt = extractSiweExpiration(session.siwe) ?? this.getSessionExpiry();
2282
+ const actions = [...new Set(operations.map((operation) => operation.action))];
2283
+ this.runtimePermissionGrants.push({
2284
+ session: {
2285
+ delegationHeader: session.delegationHeader,
2286
+ delegationCid: session.delegationCid,
2287
+ spaceId: session.spaceId,
2288
+ verificationMethod: session.verificationMethod,
2289
+ jwk: session.jwk
2290
+ },
2291
+ delegation: {
2292
+ cid: session.delegationCid,
2293
+ delegationHeader: session.delegationHeader,
2294
+ delegateDID: session.verificationMethod,
2295
+ delegatorDID: this.did,
2296
+ spaceId: session.spaceId,
2297
+ path: "",
2298
+ actions,
2299
+ expiry: expiresAt,
2300
+ allowSubDelegation: true,
2301
+ ownerAddress: session.address,
2302
+ chainId: session.chainId,
2303
+ host: this.config.host
2304
+ },
2305
+ operations,
2306
+ expiresAt
2307
+ });
2308
+ }
1941
2309
  async writeManifestRegistryRecords() {
1942
2310
  const request = this.capabilityRequest;
1943
2311
  if (!request || request.registryRecords.length === 0) {
@@ -1947,7 +2315,7 @@ var _TinyCloudNode = class _TinyCloudNode {
1947
2315
  throw new Error("Manifest registry write requires wallet mode");
1948
2316
  }
1949
2317
  const accountSpaceId = this.ownedSpaceId(ACCOUNT_REGISTRY_SPACE);
1950
- await this.ensureOwnedSpaceHosted(accountSpaceId);
2318
+ await this.ensureOwnedSpaceHostedById(accountSpaceId);
1951
2319
  const result = await this.account.applications.register(request.manifests);
1952
2320
  if (!result.ok) {
1953
2321
  throw new Error(
@@ -1957,6 +2325,7 @@ var _TinyCloudNode = class _TinyCloudNode {
1957
2325
  }
1958
2326
  scheduleAccountRegistrySync() {
1959
2327
  void this.withAccountRegistryRetry(async () => {
2328
+ void this.account.index.ensure();
1960
2329
  await this.writeManifestRegistryRecords();
1961
2330
  const spaces = await this.account.spaces.syncAccessible();
1962
2331
  if (!spaces.ok) {
@@ -2008,7 +2377,7 @@ var _TinyCloudNode = class _TinyCloudNode {
2008
2377
  await this.ensureEncryptionNetwork(networkId);
2009
2378
  }
2010
2379
  }
2011
- async ensureOwnedSpaceHosted(spaceId) {
2380
+ async ensureOwnedSpaceHostedById(spaceId) {
2012
2381
  if (!this.auth) {
2013
2382
  throw new Error("Owned space hosting requires wallet mode");
2014
2383
  }
@@ -2051,7 +2420,7 @@ var _TinyCloudNode = class _TinyCloudNode {
2051
2420
  * caller is the root authority of their own owned spaces, so no additional
2052
2421
  * delegation is required.
2053
2422
  *
2054
- * Unlike {@link ensureOwnedSpaceHosted}, this always submits the host
2423
+ * Unlike {@link ensureOwnedSpaceHostedById}, this always submits the host
2055
2424
  * delegation rather than inferring hosting from session activation: a space
2056
2425
  * the current session has never referenced is reported neither as
2057
2426
  * `activated` nor `skipped`, so activation-based detection would wrongly
@@ -2096,6 +2465,91 @@ var _TinyCloudNode = class _TinyCloudNode {
2096
2465
  });
2097
2466
  return spaceId;
2098
2467
  }
2468
+ /**
2469
+ * Ensure one of this user's owned spaces (e.g. `"secrets"`) is hosted on the
2470
+ * server.
2471
+ *
2472
+ * At sign-in, a full-authority session auto-hosts the owner's `secrets`
2473
+ * space, but a session created with a manifest / capabilityRequest does not.
2474
+ * Such a session can therefore hold valid `tinycloud.kv/*` capabilities for
2475
+ * the owned `secrets` space yet still fail its first scoped
2476
+ * `secrets.put(...)` with `404 Space not found`, because the space was never
2477
+ * registered on the node.
2478
+ *
2479
+ * Calling this resolves `name` to the owner's owned-space URI
2480
+ * (`tinycloud:pkh:eip155:<chain>:<addr>:<name>`). It first consults the
2481
+ * account-space spaces registry (`account/spaces/{space_id}`, the canonical
2482
+ * KV source of truth, fronted by a best-effort SQLite index): if the space is
2483
+ * already registered/hosted it returns the URI WITHOUT submitting a host
2484
+ * delegation, avoiding a redundant host-SIWE signature prompt for owners who
2485
+ * already use the space. Only when the space is absent — or the registry
2486
+ * check fails for any reason (e.g. a cold SQLite index reporting
2487
+ * `no such table: spaces`) — does it fall through to {@link hostOwnedSpace}.
2488
+ *
2489
+ * The registry check is purely an optimization: any failure falls back to
2490
+ * hosting, and the host SIWE is idempotent server-side, so re-hosting an
2491
+ * existing space remains a safe no-op. Must be called after {@link signIn}.
2492
+ *
2493
+ * @param name - The owned space name (e.g. `"secrets"`).
2494
+ * @returns The hosted owned-space URI.
2495
+ */
2496
+ async ensureOwnedSpaceHosted(name) {
2497
+ if (!this.auth || !this.auth.tinyCloudSession) {
2498
+ throw new Error("Not signed in. Call signIn() first.");
2499
+ }
2500
+ const spaceId = this.ownedSpaceId(name);
2501
+ if (await this.isOwnedSpaceRegistered(spaceId)) {
2502
+ return spaceId;
2503
+ }
2504
+ const hosted = await this.hostOwnedSpace(name);
2505
+ try {
2506
+ await this.account.spaces.register({
2507
+ spaceId,
2508
+ name,
2509
+ ownerDid: this.did,
2510
+ type: "owned",
2511
+ permissions: ["*"],
2512
+ status: "active"
2513
+ });
2514
+ } catch {
2515
+ }
2516
+ return hosted;
2517
+ }
2518
+ /**
2519
+ * Check whether an owned space is already registered/hosted by consulting the
2520
+ * account spaces registry.
2521
+ *
2522
+ * Source of truth is the canonical KV registry record
2523
+ * `account/spaces/{space_id}`, read here via `account.spaces.get(spaceId)`.
2524
+ * The KV path is used (rather than `syncAccessible()`) because it works under
2525
+ * a manifest/recap session with NO extra prompt: the composed manifest recap
2526
+ * already grants `tinycloud.kv get/list` on the account space `spaces/`
2527
+ * prefix, whereas `syncAccessible()` depends on `tinycloud.space/list`, which
2528
+ * a recap session does not hold. Before reading, it consults the fast SQLite
2529
+ * index (`account.index.spaces.list()`) as a best-effort short-circuit; on a
2530
+ * cold index (`no such table: spaces`) or any other index failure it falls
2531
+ * back to the canonical KV read.
2532
+ *
2533
+ * This is a best-effort optimization. ANY failure of the check path (missing
2534
+ * table, KV error, missing record, thrown exception) resolves to `false` so
2535
+ * the caller falls through to hosting — per the directive, "if it fails in any
2536
+ * way then create the space".
2537
+ */
2538
+ async isOwnedSpaceRegistered(spaceId) {
2539
+ try {
2540
+ const indexed = await this.account.index.spaces.list();
2541
+ if (indexed.ok && indexed.data.some((space) => space.spaceId === spaceId)) {
2542
+ return true;
2543
+ }
2544
+ } catch {
2545
+ }
2546
+ try {
2547
+ const record = await this.account.spaces.get(spaceId);
2548
+ return record.ok;
2549
+ } catch {
2550
+ return false;
2551
+ }
2552
+ }
2099
2553
  /**
2100
2554
  * Restore a previously established session from stored delegation data.
2101
2555
  *
@@ -2113,8 +2567,8 @@ var _TinyCloudNode = class _TinyCloudNode {
2113
2567
  this._hooks = void 0;
2114
2568
  this._vault = void 0;
2115
2569
  this._encryption = void 0;
2116
- this._baseSecrets = void 0;
2117
- this._secrets = void 0;
2570
+ this._baseSecrets.clear();
2571
+ this._secrets.clear();
2118
2572
  this._spaceService = void 0;
2119
2573
  this._serviceContext = void 0;
2120
2574
  this.runtimePermissionGrants = [];
@@ -2125,6 +2579,14 @@ var _TinyCloudNode = class _TinyCloudNode {
2125
2579
  if (sessionData.chainId) {
2126
2580
  this._chainId = sessionData.chainId;
2127
2581
  }
2582
+ const resolvedHost = await this.resolveRestoredHost(
2583
+ sessionData.tinycloudHosts,
2584
+ restoredAddress,
2585
+ sessionData.chainId
2586
+ );
2587
+ if (resolvedHost) {
2588
+ this.config.host = resolvedHost;
2589
+ }
2128
2590
  this._serviceContext = new ServiceContext2({
2129
2591
  invoke: this.invokeWithRuntimePermissions,
2130
2592
  invokeAny: this.invokeAnyWithRuntimePermissions,
@@ -2170,12 +2632,45 @@ var _TinyCloudNode = class _TinyCloudNode {
2170
2632
  signature: sessionData.signature ?? ""
2171
2633
  };
2172
2634
  if (this.auth) {
2173
- this.auth.setRestoredTinyCloudSession(tcSession);
2635
+ this.auth.setRestoredTinyCloudSession(
2636
+ tcSession,
2637
+ this.config.host ? [this.config.host] : void 0
2638
+ );
2174
2639
  } else {
2175
2640
  this._restoredTcSession = tcSession;
2176
2641
  }
2177
2642
  }
2178
2643
  }
2644
+ /**
2645
+ * Resolve the host a restored session should target.
2646
+ *
2647
+ * Mirrors fresh sign-in host resolution but for the restore path:
2648
+ * an explicit/pinned host always wins, then the hosts the session was
2649
+ * persisted with, then a lazy registry/fallback resolution for sessions
2650
+ * that predate the persisted `tinycloudHosts` field. Returns `undefined`
2651
+ * only when there's nothing to resolve from (no explicit host, no
2652
+ * persisted hosts, and no address/chainId) — in which case the existing
2653
+ * `config.host` (default) is left in place.
2654
+ *
2655
+ * Resolution failures are surfaced, not swallowed: a genuinely broken
2656
+ * registry lookup throws rather than silently falling back to a wrong host.
2657
+ */
2658
+ async resolveRestoredHost(persistedHosts, address, chainId) {
2659
+ if (this.explicitHost) {
2660
+ return this.explicitHost;
2661
+ }
2662
+ if (persistedHosts && persistedHosts.length > 0) {
2663
+ return persistedHosts[0];
2664
+ }
2665
+ if (address === void 0 || chainId === void 0) {
2666
+ return void 0;
2667
+ }
2668
+ const resolved = await resolveTinyCloudHosts2(pkhDid2(address, chainId), {
2669
+ registryUrl: this.config.tinycloudRegistryUrl,
2670
+ fallbackHosts: this.config.tinycloudFallbackHosts
2671
+ });
2672
+ return resolved.hosts[0];
2673
+ }
2179
2674
  /**
2180
2675
  * Resolve the currently-active TinyCloudSession, preferring the auth
2181
2676
  * layer's value (wallet mode) and falling back to the node-level
@@ -2220,9 +2715,11 @@ var _TinyCloudNode = class _TinyCloudNode {
2220
2715
  );
2221
2716
  }
2222
2717
  this.signer = _TinyCloudNode.nodeDefaults.createSigner(privateKey);
2718
+ const authConfig = { ...this.config, prefix };
2719
+ const useBootstrapSignInRequest = this.shouldUseBootstrapSignInRequest(authConfig);
2223
2720
  this.auth = new NodeUserAuthorization({
2224
2721
  signer: this.signer,
2225
- signStrategy: { type: "auto-sign" },
2722
+ signStrategy: this.config.signStrategy ?? { type: "auto-sign" },
2226
2723
  wasmBindings: this.wasmBindings,
2227
2724
  sessionStorage: options?.sessionStorage ?? this.config.sessionStorage ?? new MemorySessionStorage(),
2228
2725
  domain: this.siweDomain,
@@ -2231,14 +2728,14 @@ var _TinyCloudNode = class _TinyCloudNode {
2231
2728
  tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
2232
2729
  tinycloudRegistryUrl: this.config.tinycloudRegistryUrl,
2233
2730
  tinycloudFallbackHosts: this.config.tinycloudFallbackHosts,
2234
- autoCreateSpace: this.config.autoCreateSpace,
2731
+ autoCreateSpace: useBootstrapSignInRequest ? false : this.config.autoCreateSpace,
2235
2732
  enablePublicSpace: this.config.enablePublicSpace ?? true,
2236
- spaceCreationHandler: this.config.spaceCreationHandler,
2733
+ spaceCreationHandler: useBootstrapSignInRequest ? void 0 : this.config.spaceCreationHandler,
2237
2734
  nonce: this.config.nonce,
2238
2735
  siweConfig: this.config.siweConfig,
2239
- manifest: this.config.manifest,
2240
- capabilityRequest: this.config.capabilityRequest,
2241
- includeAccountRegistryPermissions: this.config.includeAccountRegistryPermissions
2736
+ manifest: useBootstrapSignInRequest ? void 0 : this.config.manifest,
2737
+ capabilityRequest: useBootstrapSignInRequest ? BOOTSTRAP_SESSION_REQUESTS.default : this.config.capabilityRequest,
2738
+ includeAccountRegistryPermissions: useBootstrapSignInRequest ? false : this.config.includeAccountRegistryPermissions
2242
2739
  });
2243
2740
  this.tc = new TinyCloud(this.auth, {
2244
2741
  invokeAny: this.invokeAnyWithRuntimePermissions,
@@ -2265,9 +2762,11 @@ var _TinyCloudNode = class _TinyCloudNode {
2265
2762
  }
2266
2763
  const prefix = options?.prefix ?? "default";
2267
2764
  this.signer = signer;
2765
+ const authConfig = { ...this.config, prefix };
2766
+ const useBootstrapSignInRequest = this.shouldUseBootstrapSignInRequest(authConfig);
2268
2767
  this.auth = new NodeUserAuthorization({
2269
2768
  signer: this.signer,
2270
- signStrategy: { type: "auto-sign" },
2769
+ signStrategy: this.config.signStrategy ?? { type: "auto-sign" },
2271
2770
  wasmBindings: this.wasmBindings,
2272
2771
  sessionStorage: options?.sessionStorage ?? this.config.sessionStorage ?? new MemorySessionStorage(),
2273
2772
  domain: this.siweDomain,
@@ -2276,14 +2775,14 @@ var _TinyCloudNode = class _TinyCloudNode {
2276
2775
  tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
2277
2776
  tinycloudRegistryUrl: this.config.tinycloudRegistryUrl,
2278
2777
  tinycloudFallbackHosts: this.config.tinycloudFallbackHosts,
2279
- autoCreateSpace: this.config.autoCreateSpace,
2778
+ autoCreateSpace: useBootstrapSignInRequest ? false : this.config.autoCreateSpace,
2280
2779
  enablePublicSpace: this.config.enablePublicSpace ?? true,
2281
- spaceCreationHandler: this.config.spaceCreationHandler,
2780
+ spaceCreationHandler: useBootstrapSignInRequest ? void 0 : this.config.spaceCreationHandler,
2282
2781
  nonce: this.config.nonce,
2283
2782
  siweConfig: this.config.siweConfig,
2284
- manifest: this.config.manifest,
2285
- capabilityRequest: this.config.capabilityRequest,
2286
- includeAccountRegistryPermissions: this.config.includeAccountRegistryPermissions
2783
+ manifest: useBootstrapSignInRequest ? void 0 : this.config.manifest,
2784
+ capabilityRequest: useBootstrapSignInRequest ? BOOTSTRAP_SESSION_REQUESTS.default : this.config.capabilityRequest,
2785
+ includeAccountRegistryPermissions: useBootstrapSignInRequest ? false : this.config.includeAccountRegistryPermissions
2287
2786
  });
2288
2787
  this.tc = new TinyCloud(this.auth, {
2289
2788
  invokeAny: this.invokeAnyWithRuntimePermissions,
@@ -2359,6 +2858,20 @@ var _TinyCloudNode = class _TinyCloudNode {
2359
2858
  getDefaultEncryptionNetworkId(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
2360
2859
  return `urn:tinycloud:encryption:${this.did}:${name}`;
2361
2860
  }
2861
+ getEncryptionNetworkIdForSpace(spaceId, name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
2862
+ const ownerDid = this.ownerDidFromSpaceId(spaceId) ?? this.did;
2863
+ return `urn:tinycloud:encryption:${ownerDid}:${name}`;
2864
+ }
2865
+ ownerDidFromSpaceId(spaceId) {
2866
+ if (!spaceId.startsWith("tinycloud:")) return void 0;
2867
+ const body = spaceId.slice("tinycloud:".length);
2868
+ const lastSeparator = body.lastIndexOf(":");
2869
+ if (lastSeparator <= 0) return void 0;
2870
+ const owner = body.slice(0, lastSeparator);
2871
+ if (owner.startsWith("did:")) return owner;
2872
+ if (!owner.includes(":")) return void 0;
2873
+ return `did:${owner}`;
2874
+ }
2362
2875
  requireServiceSession() {
2363
2876
  const session = this._serviceContext?.session;
2364
2877
  if (!session) {
@@ -2546,7 +3059,7 @@ var _TinyCloudNode = class _TinyCloudNode {
2546
3059
  spaceId,
2547
3060
  crypto: vaultCrypto,
2548
3061
  encryption: {
2549
- networkId: this.getDefaultEncryptionNetworkId(),
3062
+ networkId: this.getEncryptionNetworkIdForSpace(spaceId),
2550
3063
  service: this.getEncryptionService(),
2551
3064
  decryptCapabilityProof: () => ({
2552
3065
  proofs: [this.requireServiceSession().delegationCid]
@@ -2677,6 +3190,9 @@ var _TinyCloudNode = class _TinyCloudNode {
2677
3190
  }
2678
3191
  return vaultService;
2679
3192
  },
3193
+ createSecretsService: (spaceId) => {
3194
+ return this.secretsForSpace(spaceId);
3195
+ },
2680
3196
  // Enable space.delegations.create() via SIWE-based delegation
2681
3197
  createDelegation: async (params) => {
2682
3198
  try {
@@ -2799,11 +3315,10 @@ var _TinyCloudNode = class _TinyCloudNode {
2799
3315
  try {
2800
3316
  const host = this.config.host;
2801
3317
  const now = /* @__PURE__ */ new Date();
2802
- const abilities = {
2803
- kv: {
2804
- [params.path]: params.actions
2805
- }
2806
- };
3318
+ const abilities = sharingActionsToAbilities(params.path, params.actions);
3319
+ if (!abilities) {
3320
+ return void 0;
3321
+ }
2807
3322
  const prepared = this.wasmBindings.prepareSession({
2808
3323
  abilities,
2809
3324
  address: this.wasmBindings.ensureEip55(session.address),
@@ -3059,27 +3574,49 @@ var _TinyCloudNode = class _TinyCloudNode {
3059
3574
  if (!this._spaceService) {
3060
3575
  throw new Error("Not signed in. Call signIn() first.");
3061
3576
  }
3062
- if (!this._secrets) {
3063
- this._secrets = new NodeSecretsService({
3064
- getService: () => this.getBaseSecrets(),
3577
+ return this.secretsForSpace("secrets");
3578
+ }
3579
+ /**
3580
+ * App-facing secrets API backed by the requested space's vault.
3581
+ */
3582
+ secretsForSpace(spaceId) {
3583
+ if (!this._spaceService) {
3584
+ throw new Error("Not signed in. Call signIn() first.");
3585
+ }
3586
+ const resolvedSpace = spaceId.startsWith("tinycloud:") ? spaceId : this.ownedSpaceId(spaceId);
3587
+ let secrets = this._secrets.get(resolvedSpace);
3588
+ if (!secrets) {
3589
+ secrets = new NodeSecretsService({
3590
+ getService: () => this.getBaseSecrets(resolvedSpace),
3591
+ space: resolvedSpace,
3065
3592
  getManifest: () => this.manifest,
3066
3593
  hasPermissions: (permissions) => this.hasRuntimePermissions(permissions),
3067
3594
  grantPermissions: (additional) => this.grantRuntimePermissions(additional),
3068
3595
  canEscalate: () => this.signer !== void 0 && this.tc !== void 0,
3069
- getEncryptionNetworkId: () => this.getDefaultEncryptionNetworkId(),
3596
+ getEncryptionNetworkId: () => this.getEncryptionNetworkIdForSpace(resolvedSpace),
3597
+ resolveSpace: (space) => space.startsWith("tinycloud:") ? space : this.ownedSpaceId(space),
3070
3598
  getUnlockSigner: () => this.signer ?? void 0
3071
3599
  });
3600
+ this._secrets.set(resolvedSpace, secrets);
3072
3601
  }
3073
- return this._secrets;
3602
+ return secrets;
3074
3603
  }
3075
- getBaseSecrets() {
3604
+ getBaseSecrets(spaceId) {
3076
3605
  if (!this._spaceService) {
3077
3606
  throw new Error("Not signed in. Call signIn() first.");
3078
3607
  }
3079
- if (!this._baseSecrets) {
3080
- this._baseSecrets = new SecretsService(() => this.space("secrets").vault);
3608
+ const resolvedSpace = spaceId.startsWith("tinycloud:") ? spaceId : this.ownedSpaceId(spaceId);
3609
+ let secrets = this._baseSecrets.get(resolvedSpace);
3610
+ if (!secrets) {
3611
+ const kvService = this.createSpaceScopedKVService(resolvedSpace);
3612
+ const vaultService = this.createVaultService(resolvedSpace, kvService);
3613
+ if (this._serviceContext) {
3614
+ vaultService.initialize(this._serviceContext);
3615
+ }
3616
+ secrets = new SecretsService(() => vaultService);
3617
+ this._baseSecrets.set(resolvedSpace, secrets);
3081
3618
  }
3082
- return this._baseSecrets;
3619
+ return secrets;
3083
3620
  }
3084
3621
  /**
3085
3622
  * Hooks write stream subscription API.
@@ -4493,6 +5030,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4493
5030
  delegation,
4494
5031
  targetHost,
4495
5032
  this.wasmBindings.invoke,
5033
+ this.wasmBindings.invokeAny,
4496
5034
  this.config.telemetry
4497
5035
  );
4498
5036
  }
@@ -4558,6 +5096,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4558
5096
  delegation,
4559
5097
  targetHost,
4560
5098
  this.wasmBindings.invoke,
5099
+ this.wasmBindings.invokeAny,
4561
5100
  this.config.telemetry
4562
5101
  );
4563
5102
  }
@@ -4790,6 +5329,7 @@ export {
4790
5329
  SpaceErrorCodes,
4791
5330
  SpaceService2 as SpaceService,
4792
5331
  TinyCloud2 as TinyCloud,
5332
+ TinyCloudDebugLogger,
4793
5333
  TinyCloudNode,
4794
5334
  UnsupportedFeatureError2 as UnsupportedFeatureError,
4795
5335
  VAULT_PERMISSION_SERVICE,
@@ -4805,8 +5345,10 @@ export {
4805
5345
  canonicalizeNetworkId,
4806
5346
  canonicalizeSecretScope,
4807
5347
  checkNodeInfo2 as checkNodeInfo,
5348
+ clearTinyCloudDebugLogs,
4808
5349
  composeManifestRequest2 as composeManifestRequest,
4809
5350
  createCapabilityKeyRegistry,
5351
+ createOpenKeyCallbackSigningStrategy,
4810
5352
  createSharingService,
4811
5353
  createSpaceService,
4812
5354
  createVaultCrypto2 as createVaultCrypto,
@@ -4816,9 +5358,13 @@ export {
4816
5358
  deserializeDelegation,
4817
5359
  didCacheKey,
4818
5360
  didEquals,
5361
+ disableTinyCloudDebug,
5362
+ enableTinyCloudDebug,
4819
5363
  expandActionShortNames,
4820
5364
  expandPermissionEntries2 as expandPermissionEntries,
4821
5365
  expandPermissionEntry,
5366
+ getTinyCloudDebugLogs,
5367
+ installTinyCloudDebugGlobals,
4822
5368
  isCapabilitySubset2 as isCapabilitySubset,
4823
5369
  isEvmAddress,
4824
5370
  loadManifest,
@@ -4836,6 +5382,7 @@ export {
4836
5382
  resolveSecretPath2 as resolveSecretPath,
4837
5383
  resourceCapabilitiesToSpaceAbilitiesMap2 as resourceCapabilitiesToSpaceAbilitiesMap,
4838
5384
  serializeDelegation,
5385
+ tinyCloudDebugLogger,
4839
5386
  validateManifest
4840
5387
  };
4841
5388
  //# sourceMappingURL=core.js.map