@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/index.js CHANGED
@@ -17173,6 +17173,11 @@ import {
17173
17173
  UnsupportedFeatureError,
17174
17174
  makePublicSpaceId,
17175
17175
  ACCOUNT_REGISTRY_SPACE,
17176
+ BOOTSTRAP_SESSION_REQUESTS,
17177
+ SECRET_RECORDS_SCHEMA,
17178
+ SECRETS_SPACE as SECRETS_SPACE2,
17179
+ TINYCLOUD_SECRETS_BOOTSTRAP_MANIFEST,
17180
+ bootstrapSteps,
17176
17181
  ENCRYPTION_PERMISSION_SERVICE as ENCRYPTION_PERMISSION_SERVICE2,
17177
17182
  PermissionNotInManifestError,
17178
17183
  SessionExpiredError,
@@ -17186,6 +17191,7 @@ import {
17186
17191
  verifyDidKeyEd25519Signature,
17187
17192
  canonicalizeAddress as canonicalizeAddress2,
17188
17193
  pkhDid as pkhDid2,
17194
+ resolveTinyCloudHosts as resolveTinyCloudHosts2,
17189
17195
  principalDidEquals as principalDidEquals2,
17190
17196
  parseNetworkId as parseNetworkId2
17191
17197
  } from "@tinycloud/sdk-core";
@@ -17212,6 +17218,7 @@ import {
17212
17218
  } from "@tinycloud/sdk-core";
17213
17219
 
17214
17220
  // src/authorization/strategies.ts
17221
+ import { createOpenKeyCallbackSigningStrategy } from "@tinycloud/sdk-core";
17215
17222
  var defaultSignStrategy = { type: "auto-sign" };
17216
17223
 
17217
17224
  // src/storage/MemorySessionStorage.ts
@@ -17309,6 +17316,7 @@ var NodeUserAuthorization = class {
17309
17316
  constructor(config) {
17310
17317
  this.extensions = [];
17311
17318
  this._nodeFeatures = [];
17319
+ this._lastActivationSkippedSpaceIds = [];
17312
17320
  this.wasm = config.wasmBindings;
17313
17321
  this.signer = config.signer;
17314
17322
  this.signStrategy = config.signStrategy ?? defaultSignStrategy;
@@ -17331,7 +17339,7 @@ var NodeUserAuthorization = class {
17331
17339
  "": [
17332
17340
  "tinycloud.sql/read",
17333
17341
  "tinycloud.sql/write",
17334
- "tinycloud.sql/ddl",
17342
+ "tinycloud.sql/schema",
17335
17343
  "tinycloud.sql/admin",
17336
17344
  "tinycloud.sql/export"
17337
17345
  ]
@@ -17423,12 +17431,48 @@ var NodeUserAuthorization = class {
17423
17431
  * `siwe` is the load-bearing one because `extractSiweExpiration` returns
17424
17432
  * undefined for missing SIWEs and the SDK then treats the session as
17425
17433
  * expired-at-epoch-zero.
17434
+ *
17435
+ * @param hosts - The TinyCloud hosts this session was created against,
17436
+ * as persisted in {@link PersistedSessionData.tinycloudHosts}. When
17437
+ * present (and non-empty) they are adopted directly so the restored
17438
+ * session resolves to the same node as the original sign-in without
17439
+ * re-running registry/fallback resolution. When absent (old session)
17440
+ * hosts are resolved lazily on the first host-needing call via
17441
+ * {@link ensureTinyCloudHosts}.
17426
17442
  */
17427
- setRestoredTinyCloudSession(session) {
17443
+ setRestoredTinyCloudSession(session, hosts) {
17428
17444
  const address = canonicalizeAddress(session.address);
17429
17445
  this._tinyCloudSession = { ...session, address };
17430
17446
  this._address = address;
17431
17447
  this._chainId = session.chainId;
17448
+ if ((!this.tinycloudHosts || this.tinycloudHosts.length === 0) && hosts && hosts.length > 0) {
17449
+ this.tinycloudHosts = [...hosts];
17450
+ }
17451
+ }
17452
+ /**
17453
+ * Ensure `tinycloudHosts` are resolved before a host-needing call.
17454
+ *
17455
+ * Fresh sign-in resolves hosts up front; a restored session may not have
17456
+ * (old persisted sessions predate {@link PersistedSessionData.tinycloudHosts}).
17457
+ * This guard makes a restored session resolve the node exactly like a fresh
17458
+ * sign-in — persisted hosts are preferred (already set by
17459
+ * {@link setRestoredTinyCloudSession}), otherwise the registry/fallback
17460
+ * resolution runs lazily here. Idempotent: {@link resolveTinyCloudHostsForSignIn}
17461
+ * early-returns when hosts are already set.
17462
+ *
17463
+ * Throws if hosts are unset and the restored session has no address/chainId
17464
+ * to resolve from — a real failure that must surface, not be masked.
17465
+ */
17466
+ async ensureTinyCloudHosts() {
17467
+ if (this.tinycloudHosts && this.tinycloudHosts.length > 0) {
17468
+ return;
17469
+ }
17470
+ if (this._address === void 0 || this._chainId === void 0) {
17471
+ throw new Error(
17472
+ "Cannot resolve TinyCloud hosts: no address/chainId available. Sign in or restore a session first."
17473
+ );
17474
+ }
17475
+ await this.resolveTinyCloudHostsForSignIn(this._address, this._chainId);
17432
17476
  }
17433
17477
  async resolveTinyCloudHostsForSignIn(address, chainId) {
17434
17478
  if (this.tinycloudHosts && this.tinycloudHosts.length > 0) {
@@ -17453,6 +17497,9 @@ var NodeUserAuthorization = class {
17453
17497
  get nodeFeatures() {
17454
17498
  return this._nodeFeatures;
17455
17499
  }
17500
+ get lastActivationSkippedSpaceIds() {
17501
+ return [...this._lastActivationSkippedSpaceIds];
17502
+ }
17456
17503
  /**
17457
17504
  * Compute the `abilities` map the WASM `prepareSession` call should
17458
17505
  * see at sign-in time.
@@ -17625,6 +17672,7 @@ var NodeUserAuthorization = class {
17625
17672
  if (!this._tinyCloudSession || !this._address || !this._chainId) {
17626
17673
  throw new Error("Must be signed in to host space");
17627
17674
  }
17675
+ await this.ensureTinyCloudHosts();
17628
17676
  const host = this.primaryTinyCloudHost;
17629
17677
  const spaceId = targetSpaceId ?? this._tinyCloudSession.spaceId;
17630
17678
  const peerId = await fetchPeerId(host, spaceId);
@@ -17667,12 +17715,14 @@ var NodeUserAuthorization = class {
17667
17715
  if (!this._tinyCloudSession) {
17668
17716
  throw new Error("Must be signed in to ensure space exists");
17669
17717
  }
17718
+ await this.ensureTinyCloudHosts();
17670
17719
  const host = this.primaryTinyCloudHost;
17671
17720
  const primarySpaceId = this._tinyCloudSession.spaceId;
17672
17721
  const result = await activateSessionWithHost(
17673
17722
  host,
17674
17723
  this._tinyCloudSession.delegationHeader
17675
17724
  );
17725
+ this.recordActivationSkippedSpaces(result, primarySpaceId);
17676
17726
  const handler = this.spaceCreationHandler ?? (this.autoCreateSpace ? new AutoApproveSpaceCreationHandler() : void 0);
17677
17727
  const creationContext = {
17678
17728
  spaceId: primarySpaceId,
@@ -17760,6 +17810,66 @@ var NodeUserAuthorization = class {
17760
17810
  }
17761
17811
  throw new Error(`Failed to activate session: ${result.error}`);
17762
17812
  }
17813
+ recordActivationSkippedSpaces(result, primarySpaceId) {
17814
+ if (result.success) {
17815
+ this._lastActivationSkippedSpaceIds = result.skipped ?? [];
17816
+ return;
17817
+ }
17818
+ this._lastActivationSkippedSpaceIds = result.status === 404 ? [primarySpaceId] : [];
17819
+ }
17820
+ async createBootstrapSession(options) {
17821
+ if (!this._address || !this._chainId) {
17822
+ throw new Error("Must be signed in before creating bootstrap sessions");
17823
+ }
17824
+ const address = this._address;
17825
+ const chainId = this._chainId;
17826
+ const keyId = `bootstrap-${Date.now()}-${Math.random().toString(36).slice(2)}`;
17827
+ this.sessionManager.createSessionKey(keyId);
17828
+ const jwkString = this.sessionManager.jwk(keyId);
17829
+ if (!jwkString) {
17830
+ throw new Error("Failed to create bootstrap session key");
17831
+ }
17832
+ const jwk = JSON.parse(jwkString);
17833
+ const now = /* @__PURE__ */ new Date();
17834
+ const expirationTime = new Date(now.getTime() + this.sessionExpirationMs);
17835
+ const abilities = resourceCapabilitiesToAbilitiesMap(
17836
+ options.capabilityRequest.resources
17837
+ );
17838
+ const prepared = this.wasm.prepareSession({
17839
+ abilities,
17840
+ ...options.rawAbilities !== void 0 ? { rawAbilities: options.rawAbilities } : {},
17841
+ address,
17842
+ chainId,
17843
+ domain: this.domain,
17844
+ issuedAt: now.toISOString(),
17845
+ expirationTime: expirationTime.toISOString(),
17846
+ spaceId: options.spaceId,
17847
+ jwk,
17848
+ ...this.buildSiweOverrides()
17849
+ });
17850
+ const signature2 = await this.requestSignature({
17851
+ address,
17852
+ chainId,
17853
+ message: prepared.siwe,
17854
+ type: "siwe"
17855
+ });
17856
+ const session = this.wasm.completeSessionSetup({
17857
+ ...prepared,
17858
+ signature: signature2
17859
+ });
17860
+ return {
17861
+ address,
17862
+ chainId,
17863
+ sessionKey: keyId,
17864
+ spaceId: options.spaceId,
17865
+ delegationCid: session.delegationCid,
17866
+ delegationHeader: session.delegationHeader,
17867
+ verificationMethod: this.sessionManager.getDID(keyId),
17868
+ jwk,
17869
+ siwe: prepared.siwe,
17870
+ signature: signature2
17871
+ };
17872
+ }
17763
17873
  /**
17764
17874
  * Sign in and create a new session.
17765
17875
  *
@@ -17848,7 +17958,8 @@ var NodeUserAuthorization = class {
17848
17958
  },
17849
17959
  expiresAt: expirationTime.toISOString(),
17850
17960
  createdAt: now.toISOString(),
17851
- version: "1.0"
17961
+ version: "1.0",
17962
+ tinycloudHosts: this.tinycloudHosts
17852
17963
  };
17853
17964
  await this.sessionStorage.save(address, persistedData);
17854
17965
  this._session = clientSession;
@@ -18015,7 +18126,8 @@ var NodeUserAuthorization = class {
18015
18126
  },
18016
18127
  expiresAt,
18017
18128
  createdAt,
18018
- version: "1.0"
18129
+ version: "1.0",
18130
+ tinycloudHosts: this.tinycloudHosts
18019
18131
  };
18020
18132
  await this.sessionStorage.save(address, persistedData);
18021
18133
  this._session = clientSession;
@@ -18117,12 +18229,13 @@ import {
18117
18229
  ServiceContext
18118
18230
  } from "@tinycloud/sdk-core";
18119
18231
  var DelegatedAccess = class {
18120
- constructor(session, delegation, host, invoke2, telemetry) {
18232
+ constructor(session, delegation, host, invoke2, invokeAny2, telemetry) {
18121
18233
  this.session = session;
18122
18234
  this._delegation = delegation;
18123
18235
  this.host = host;
18124
18236
  this._serviceContext = new ServiceContext({
18125
18237
  invoke: invoke2,
18238
+ invokeAny: invokeAny2,
18126
18239
  fetch: globalThis.fetch.bind(globalThis),
18127
18240
  hosts: [host],
18128
18241
  telemetry
@@ -18389,12 +18502,12 @@ function displayActionUrn(action) {
18389
18502
  function secretActionName(action) {
18390
18503
  return action;
18391
18504
  }
18392
- function secretPermissionEntries(name, options, action, encryptionNetworkId) {
18505
+ function secretPermissionEntries(name, options, action, space, encryptionNetworkId) {
18393
18506
  const entries = [];
18394
18507
  const path = action === "list" ? resolveSecretListPrefix(options) : resolveSecretPath(name, options).permissionPaths.vault;
18395
18508
  entries.push({
18396
18509
  service: "tinycloud.kv",
18397
- space: SECRETS_SPACE,
18510
+ space,
18398
18511
  path,
18399
18512
  actions: [secretActionName(action)],
18400
18513
  skipPrefix: true
@@ -18409,11 +18522,23 @@ function secretPermissionEntries(name, options, action, encryptionNetworkId) {
18409
18522
  }
18410
18523
  return entries;
18411
18524
  }
18525
+ function normalizeSpace(space, resolveSpace) {
18526
+ if (!space) return void 0;
18527
+ if (space.startsWith("tinycloud:")) return space;
18528
+ return resolveSpace?.(space) ?? space;
18529
+ }
18530
+ function spaceMatches(granted, requested, resolveSpace) {
18531
+ if (!granted || !requested) return false;
18532
+ return normalizeSpace(granted, resolveSpace) === normalizeSpace(requested, resolveSpace);
18533
+ }
18412
18534
  var NodeSecretsService = class {
18413
18535
  constructor(config) {
18414
18536
  this.config = config;
18415
18537
  this.shouldRestoreUnlock = false;
18416
18538
  }
18539
+ get space() {
18540
+ return this.config.space ?? SECRETS_SPACE;
18541
+ }
18417
18542
  get vault() {
18418
18543
  return this.service.vault;
18419
18544
  }
@@ -18466,6 +18591,7 @@ var NodeSecretsService = class {
18466
18591
  name,
18467
18592
  options,
18468
18593
  action,
18594
+ this.space,
18469
18595
  action === "get" ? this.config.getEncryptionNetworkId?.() : void 0
18470
18596
  );
18471
18597
  } catch (error) {
@@ -18515,7 +18641,7 @@ var NodeSecretsService = class {
18515
18641
  (entry) => manifests.some((candidate) => {
18516
18642
  const resolved = resolveManifest(candidate);
18517
18643
  return resolved.resources.some(
18518
- (resource) => resource.service === entry.service && resource.space === entry.space && resource.path === entry.path && entry.actions.every((action) => resource.actions.includes(action))
18644
+ (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))
18519
18645
  );
18520
18646
  })
18521
18647
  );
@@ -18529,6 +18655,9 @@ var NETWORK_CREATE_ACTION2 = "tinycloud.encryption/network.create";
18529
18655
  var DECRYPT_ACTION2 = "tinycloud.encryption/decrypt";
18530
18656
  var NETWORK_ADMIN_TYPE = "tinycloud.encryption.network-admin/v1";
18531
18657
  var DEFAULT_SESSION_EXPIRATION_MS = EXPIRY3.SESSION_MS;
18658
+ function isOpenKeyAutoSignStrategy(strategy) {
18659
+ return strategy?.openKeyAutoSign === true;
18660
+ }
18532
18661
  function didPrincipalMatches2(actual, expected) {
18533
18662
  try {
18534
18663
  return principalDidEquals2(actual, expected);
@@ -18536,6 +18665,20 @@ function didPrincipalMatches2(actual, expected) {
18536
18665
  return actual === expected;
18537
18666
  }
18538
18667
  }
18668
+ function sharingActionsToAbilities(path, actions) {
18669
+ var _a;
18670
+ const abilities = {};
18671
+ for (const action of actions) {
18672
+ const slash = action.indexOf("/");
18673
+ if (slash === -1) return void 0;
18674
+ const shortService = SERVICE_LONG_TO_SHORT[action.slice(0, slash)];
18675
+ if (shortService === void 0) return void 0;
18676
+ abilities[shortService] ?? (abilities[shortService] = {});
18677
+ (_a = abilities[shortService])[path] ?? (_a[path] = []);
18678
+ abilities[shortService][path].push(action);
18679
+ }
18680
+ return Object.keys(abilities).length > 0 ? abilities : void 0;
18681
+ }
18539
18682
  function base64UrlEncode(bytes) {
18540
18683
  const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
18541
18684
  let output = "";
@@ -18648,6 +18791,8 @@ var _TinyCloudNode = class _TinyCloudNode {
18648
18791
  this.auth = null;
18649
18792
  this.tc = null;
18650
18793
  this._chainId = 1;
18794
+ this._baseSecrets = /* @__PURE__ */ new Map();
18795
+ this._secrets = /* @__PURE__ */ new Map();
18651
18796
  this.runtimePermissionGrants = [];
18652
18797
  this.invokeWithRuntimePermissions = (session, service, path, action, facts) => {
18653
18798
  return this.wasmBindings.invoke(
@@ -18753,9 +18898,10 @@ var _TinyCloudNode = class _TinyCloudNode {
18753
18898
  * @internal
18754
18899
  */
18755
18900
  setupAuth(config) {
18901
+ const useBootstrapSignInRequest = this.shouldUseBootstrapSignInRequest(config);
18756
18902
  this.auth = new NodeUserAuthorization({
18757
18903
  signer: this.signer,
18758
- signStrategy: { type: "auto-sign" },
18904
+ signStrategy: config.signStrategy ?? { type: "auto-sign" },
18759
18905
  wasmBindings: this.wasmBindings,
18760
18906
  sessionStorage: config.sessionStorage ?? new MemorySessionStorage(),
18761
18907
  domain: this.siweDomain,
@@ -18764,20 +18910,23 @@ var _TinyCloudNode = class _TinyCloudNode {
18764
18910
  tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
18765
18911
  tinycloudRegistryUrl: config.tinycloudRegistryUrl,
18766
18912
  tinycloudFallbackHosts: config.tinycloudFallbackHosts,
18767
- autoCreateSpace: config.autoCreateSpace,
18913
+ autoCreateSpace: useBootstrapSignInRequest ? false : config.autoCreateSpace,
18768
18914
  enablePublicSpace: config.enablePublicSpace ?? true,
18769
- spaceCreationHandler: config.spaceCreationHandler,
18915
+ spaceCreationHandler: useBootstrapSignInRequest ? void 0 : config.spaceCreationHandler,
18770
18916
  nonce: config.nonce,
18771
18917
  siweConfig: config.siweConfig,
18772
- manifest: config.manifest,
18773
- capabilityRequest: config.capabilityRequest,
18774
- includeAccountRegistryPermissions: config.includeAccountRegistryPermissions
18918
+ manifest: useBootstrapSignInRequest ? void 0 : config.manifest,
18919
+ capabilityRequest: useBootstrapSignInRequest ? BOOTSTRAP_SESSION_REQUESTS.default : config.capabilityRequest,
18920
+ includeAccountRegistryPermissions: useBootstrapSignInRequest ? false : config.includeAccountRegistryPermissions
18775
18921
  });
18776
18922
  this.tc = new TinyCloud(this.auth, {
18777
18923
  invokeAny: this.invokeAnyWithRuntimePermissions,
18778
18924
  telemetry: config.telemetry
18779
18925
  });
18780
18926
  }
18927
+ shouldUseBootstrapSignInRequest(config) {
18928
+ return config.autoBootstrapAccount !== false && config.manifest === void 0 && config.capabilityRequest === void 0 && (config.prefix ?? "default") === "default" && isOpenKeyAutoSignStrategy(config.signStrategy);
18929
+ }
18781
18930
  syncResolvedHostFromAuth() {
18782
18931
  const host = this.auth?.hosts[0];
18783
18932
  if (host) {
@@ -18892,7 +19041,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18892
19041
  getAccountDb: () => this.accountSpaceId ? this.sqlForSpace(this.accountSpaceId).db("account") : void 0,
18893
19042
  ensureAccountSpaceHosted: async () => {
18894
19043
  if (this.accountSpaceId && this.auth) {
18895
- await this.ensureOwnedSpaceHosted(this.accountSpaceId);
19044
+ await this.ensureOwnedSpaceHostedById(this.accountSpaceId);
18896
19045
  }
18897
19046
  }
18898
19047
  });
@@ -18906,6 +19055,13 @@ var _TinyCloudNode = class _TinyCloudNode {
18906
19055
  get session() {
18907
19056
  return this.auth?.tinyCloudSession;
18908
19057
  }
19058
+ /**
19059
+ * Get the currently active session in the shape callers can persist and later
19060
+ * pass back to {@link restoreSession}.
19061
+ */
19062
+ get restorableSession() {
19063
+ return this.currentTinyCloudSession();
19064
+ }
18909
19065
  /**
18910
19066
  * Sign in and create a new session.
18911
19067
  * This creates the user's space if it doesn't exist.
@@ -18928,19 +19084,22 @@ var _TinyCloudNode = class _TinyCloudNode {
18928
19084
  this._hooks = void 0;
18929
19085
  this._vault = void 0;
18930
19086
  this._encryption = void 0;
18931
- this._baseSecrets = void 0;
18932
- this._secrets = void 0;
19087
+ this._baseSecrets.clear();
19088
+ this._secrets.clear();
18933
19089
  this._spaceService = void 0;
18934
19090
  this._serviceContext = void 0;
18935
19091
  this.runtimePermissionGrants = [];
18936
19092
  await this.tc.signIn(options);
18937
19093
  this.syncResolvedHostFromAuth();
18938
19094
  this.initializeServices();
19095
+ const bootstrapped = await this.bootstrapAccountIfNeeded();
18939
19096
  await this.ensureRequestedEncryptionNetworks();
18940
- if (this.config.manifest === void 0 && this.config.capabilityRequest === void 0) {
18941
- await this.ensureOwnedSpaceHosted(this.ownedSpaceId("secrets"));
19097
+ if (!bootstrapped && this.config.manifest === void 0 && this.config.capabilityRequest === void 0) {
19098
+ await this.ensureOwnedSpaceHostedById(this.ownedSpaceId(SECRETS_SPACE2));
19099
+ }
19100
+ if (!bootstrapped) {
19101
+ this.scheduleAccountRegistrySync();
18942
19102
  }
18943
- this.scheduleAccountRegistrySync();
18944
19103
  this.notificationHandler.success("Successfully signed in");
18945
19104
  }
18946
19105
  ownedSpaceId(name) {
@@ -18949,6 +19108,208 @@ var _TinyCloudNode = class _TinyCloudNode {
18949
19108
  }
18950
19109
  return this.wasmBindings.makeSpaceId(this._address, this._chainId, name);
18951
19110
  }
19111
+ async bootstrapAccountIfNeeded() {
19112
+ if (this.config.autoBootstrapAccount === false) {
19113
+ return false;
19114
+ }
19115
+ if (!this.auth || !this._address) {
19116
+ return false;
19117
+ }
19118
+ const steps = bootstrapSteps(this._address, this._chainId);
19119
+ if (!await this.isFreshBootstrapAccount(steps)) {
19120
+ return false;
19121
+ }
19122
+ await this.runAccountBootstrap(steps);
19123
+ return true;
19124
+ }
19125
+ async isFreshBootstrapAccount(steps) {
19126
+ const enshrinedSpaceIds = /* @__PURE__ */ new Set();
19127
+ for (const step of steps) {
19128
+ if (step.kind === "session") {
19129
+ enshrinedSpaceIds.add(step.spaceId);
19130
+ }
19131
+ }
19132
+ const skipped = this.auth.lastActivationSkippedSpaceIds;
19133
+ if (skipped.some((spaceId) => enshrinedSpaceIds.has(spaceId))) {
19134
+ return true;
19135
+ }
19136
+ try {
19137
+ const indexed = await this.account.index.spaces.list();
19138
+ if (indexed.ok && indexed.data.length === 0) {
19139
+ return true;
19140
+ }
19141
+ } catch {
19142
+ }
19143
+ try {
19144
+ const spaces = await this.account.spaces.list();
19145
+ return spaces.ok && spaces.data.length === 0;
19146
+ } catch {
19147
+ return false;
19148
+ }
19149
+ }
19150
+ async runAccountBootstrap(steps) {
19151
+ if (!this.auth || !this._address) {
19152
+ throw new Error("Account bootstrap requires an active wallet session");
19153
+ }
19154
+ const host = this.hosts[0] ?? this.config.host;
19155
+ if (!host) {
19156
+ throw new Error("Account bootstrap requires a TinyCloud host");
19157
+ }
19158
+ const auth = this.auth;
19159
+ const sessions = /* @__PURE__ */ new Map();
19160
+ const rawAbilitiesBySpace = /* @__PURE__ */ new Map();
19161
+ const primarySession = auth.tinyCloudSession;
19162
+ const defaultSpaceId = this.ownedSpaceId("default");
19163
+ const canReusePrimaryBootstrapSession = primarySession?.spaceId === defaultSpaceId && auth.capabilityRequest === BOOTSTRAP_SESSION_REQUESTS.default;
19164
+ for (const step of steps) {
19165
+ if (step.kind !== "session") continue;
19166
+ if (step.space === "default" && canReusePrimaryBootstrapSession && primarySession) {
19167
+ sessions.set(step.space, primarySession);
19168
+ continue;
19169
+ }
19170
+ const rawAbilities = step.rawAbilities;
19171
+ if (rawAbilities) {
19172
+ rawAbilitiesBySpace.set(step.space, rawAbilities);
19173
+ }
19174
+ const session = await auth.createBootstrapSession({
19175
+ spaceId: step.spaceId,
19176
+ capabilityRequest: step.request ?? BOOTSTRAP_SESSION_REQUESTS[step.space],
19177
+ rawAbilities
19178
+ });
19179
+ sessions.set(step.space, session);
19180
+ }
19181
+ for (const step of steps) {
19182
+ if (step.kind !== "host") continue;
19183
+ const hosted = await auth.hostOwnedSpace(step.spaceId);
19184
+ if (!hosted) {
19185
+ throw new Error(`Failed to host bootstrap space: ${step.spaceId}`);
19186
+ }
19187
+ }
19188
+ await new Promise((resolve) => setTimeout(resolve, 100));
19189
+ for (const step of steps) {
19190
+ if (step.kind !== "activate") continue;
19191
+ const session = sessions.get(step.space);
19192
+ if (!session) {
19193
+ throw new Error(`Missing bootstrap session for ${step.space}`);
19194
+ }
19195
+ const activated = await activateSessionWithHost2(host, session.delegationHeader);
19196
+ if (!activated.success || activated.skipped?.includes(step.spaceId)) {
19197
+ throw new Error(
19198
+ `Failed to activate bootstrap session for ${step.spaceId}: ${activated.error ?? "space was skipped"}`
19199
+ );
19200
+ }
19201
+ this.registerBootstrapRuntimeGrant(
19202
+ session,
19203
+ BOOTSTRAP_SESSION_REQUESTS[step.space],
19204
+ rawAbilitiesBySpace.get(step.space)
19205
+ );
19206
+ }
19207
+ for (const step of steps) {
19208
+ if (step.kind === "account-index-schema") {
19209
+ const ensured = await this.account.index.ensure();
19210
+ if (!ensured.ok) {
19211
+ throw new Error(`Failed to create account index schema: ${ensured.error.message}`);
19212
+ }
19213
+ }
19214
+ if (step.kind === "seed-spaces") {
19215
+ for (const space of step.spaces) {
19216
+ const registered = await this.account.spaces.register({
19217
+ spaceId: space.spaceId,
19218
+ name: space.name,
19219
+ ownerDid: this.did,
19220
+ type: "owned",
19221
+ permissions: ["*"],
19222
+ status: "active"
19223
+ });
19224
+ if (!registered.ok) {
19225
+ throw new Error(
19226
+ `Failed to seed account space ${space.spaceId}: ${registered.error.message}`
19227
+ );
19228
+ }
19229
+ }
19230
+ }
19231
+ if (step.kind === "seed-applications") {
19232
+ const registered = await this.account.applications.register(
19233
+ step.manifests.length > 0 ? [...step.manifests] : TINYCLOUD_SECRETS_BOOTSTRAP_MANIFEST
19234
+ );
19235
+ if (!registered.ok) {
19236
+ throw new Error(`Failed to seed bootstrap applications: ${registered.error.message}`);
19237
+ }
19238
+ }
19239
+ if (step.kind === "encryption-network-create") {
19240
+ await this.ensureEncryptionNetwork(step.networkId);
19241
+ }
19242
+ if (step.kind === "secret-records-schema") {
19243
+ const db = this.sqlForSpace(step.spaceId).db(step.database);
19244
+ const migrated = await db.migrations.apply({
19245
+ namespace: "tinycloud.secrets.records",
19246
+ migrations: [
19247
+ {
19248
+ id: "001_initial",
19249
+ sql: [...SECRET_RECORDS_SCHEMA]
19250
+ }
19251
+ ]
19252
+ });
19253
+ if (!migrated.ok) {
19254
+ throw new Error(
19255
+ `Failed to create secret_records schema: ${migrated.error.message}`
19256
+ );
19257
+ }
19258
+ }
19259
+ }
19260
+ }
19261
+ registerBootstrapRuntimeGrant(session, request, rawAbilities) {
19262
+ const operations = [];
19263
+ for (const resource of request.resources) {
19264
+ const service = resource.service.startsWith("tinycloud.") ? this.shortServiceName(resource.service) : resource.service;
19265
+ const spaceId = resource.space.startsWith("tinycloud:") ? resource.space : this.ownedSpaceId(resource.space);
19266
+ for (const action of resource.actions) {
19267
+ operations.push({
19268
+ spaceId,
19269
+ service,
19270
+ path: resource.path,
19271
+ action
19272
+ });
19273
+ }
19274
+ }
19275
+ for (const [resource, actions2] of Object.entries(rawAbilities ?? {})) {
19276
+ for (const action of actions2) {
19277
+ operations.push({
19278
+ resource,
19279
+ service: "encryption",
19280
+ path: resource,
19281
+ action
19282
+ });
19283
+ }
19284
+ }
19285
+ const expiresAt = extractSiweExpiration(session.siwe) ?? this.getSessionExpiry();
19286
+ const actions = [...new Set(operations.map((operation) => operation.action))];
19287
+ this.runtimePermissionGrants.push({
19288
+ session: {
19289
+ delegationHeader: session.delegationHeader,
19290
+ delegationCid: session.delegationCid,
19291
+ spaceId: session.spaceId,
19292
+ verificationMethod: session.verificationMethod,
19293
+ jwk: session.jwk
19294
+ },
19295
+ delegation: {
19296
+ cid: session.delegationCid,
19297
+ delegationHeader: session.delegationHeader,
19298
+ delegateDID: session.verificationMethod,
19299
+ delegatorDID: this.did,
19300
+ spaceId: session.spaceId,
19301
+ path: "",
19302
+ actions,
19303
+ expiry: expiresAt,
19304
+ allowSubDelegation: true,
19305
+ ownerAddress: session.address,
19306
+ chainId: session.chainId,
19307
+ host: this.config.host
19308
+ },
19309
+ operations,
19310
+ expiresAt
19311
+ });
19312
+ }
18952
19313
  async writeManifestRegistryRecords() {
18953
19314
  const request = this.capabilityRequest;
18954
19315
  if (!request || request.registryRecords.length === 0) {
@@ -18958,7 +19319,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18958
19319
  throw new Error("Manifest registry write requires wallet mode");
18959
19320
  }
18960
19321
  const accountSpaceId = this.ownedSpaceId(ACCOUNT_REGISTRY_SPACE);
18961
- await this.ensureOwnedSpaceHosted(accountSpaceId);
19322
+ await this.ensureOwnedSpaceHostedById(accountSpaceId);
18962
19323
  const result = await this.account.applications.register(request.manifests);
18963
19324
  if (!result.ok) {
18964
19325
  throw new Error(
@@ -18968,6 +19329,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18968
19329
  }
18969
19330
  scheduleAccountRegistrySync() {
18970
19331
  void this.withAccountRegistryRetry(async () => {
19332
+ void this.account.index.ensure();
18971
19333
  await this.writeManifestRegistryRecords();
18972
19334
  const spaces = await this.account.spaces.syncAccessible();
18973
19335
  if (!spaces.ok) {
@@ -19019,7 +19381,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19019
19381
  await this.ensureEncryptionNetwork(networkId);
19020
19382
  }
19021
19383
  }
19022
- async ensureOwnedSpaceHosted(spaceId) {
19384
+ async ensureOwnedSpaceHostedById(spaceId) {
19023
19385
  if (!this.auth) {
19024
19386
  throw new Error("Owned space hosting requires wallet mode");
19025
19387
  }
@@ -19062,7 +19424,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19062
19424
  * caller is the root authority of their own owned spaces, so no additional
19063
19425
  * delegation is required.
19064
19426
  *
19065
- * Unlike {@link ensureOwnedSpaceHosted}, this always submits the host
19427
+ * Unlike {@link ensureOwnedSpaceHostedById}, this always submits the host
19066
19428
  * delegation rather than inferring hosting from session activation: a space
19067
19429
  * the current session has never referenced is reported neither as
19068
19430
  * `activated` nor `skipped`, so activation-based detection would wrongly
@@ -19107,6 +19469,91 @@ var _TinyCloudNode = class _TinyCloudNode {
19107
19469
  });
19108
19470
  return spaceId;
19109
19471
  }
19472
+ /**
19473
+ * Ensure one of this user's owned spaces (e.g. `"secrets"`) is hosted on the
19474
+ * server.
19475
+ *
19476
+ * At sign-in, a full-authority session auto-hosts the owner's `secrets`
19477
+ * space, but a session created with a manifest / capabilityRequest does not.
19478
+ * Such a session can therefore hold valid `tinycloud.kv/*` capabilities for
19479
+ * the owned `secrets` space yet still fail its first scoped
19480
+ * `secrets.put(...)` with `404 Space not found`, because the space was never
19481
+ * registered on the node.
19482
+ *
19483
+ * Calling this resolves `name` to the owner's owned-space URI
19484
+ * (`tinycloud:pkh:eip155:<chain>:<addr>:<name>`). It first consults the
19485
+ * account-space spaces registry (`account/spaces/{space_id}`, the canonical
19486
+ * KV source of truth, fronted by a best-effort SQLite index): if the space is
19487
+ * already registered/hosted it returns the URI WITHOUT submitting a host
19488
+ * delegation, avoiding a redundant host-SIWE signature prompt for owners who
19489
+ * already use the space. Only when the space is absent — or the registry
19490
+ * check fails for any reason (e.g. a cold SQLite index reporting
19491
+ * `no such table: spaces`) — does it fall through to {@link hostOwnedSpace}.
19492
+ *
19493
+ * The registry check is purely an optimization: any failure falls back to
19494
+ * hosting, and the host SIWE is idempotent server-side, so re-hosting an
19495
+ * existing space remains a safe no-op. Must be called after {@link signIn}.
19496
+ *
19497
+ * @param name - The owned space name (e.g. `"secrets"`).
19498
+ * @returns The hosted owned-space URI.
19499
+ */
19500
+ async ensureOwnedSpaceHosted(name) {
19501
+ if (!this.auth || !this.auth.tinyCloudSession) {
19502
+ throw new Error("Not signed in. Call signIn() first.");
19503
+ }
19504
+ const spaceId = this.ownedSpaceId(name);
19505
+ if (await this.isOwnedSpaceRegistered(spaceId)) {
19506
+ return spaceId;
19507
+ }
19508
+ const hosted = await this.hostOwnedSpace(name);
19509
+ try {
19510
+ await this.account.spaces.register({
19511
+ spaceId,
19512
+ name,
19513
+ ownerDid: this.did,
19514
+ type: "owned",
19515
+ permissions: ["*"],
19516
+ status: "active"
19517
+ });
19518
+ } catch {
19519
+ }
19520
+ return hosted;
19521
+ }
19522
+ /**
19523
+ * Check whether an owned space is already registered/hosted by consulting the
19524
+ * account spaces registry.
19525
+ *
19526
+ * Source of truth is the canonical KV registry record
19527
+ * `account/spaces/{space_id}`, read here via `account.spaces.get(spaceId)`.
19528
+ * The KV path is used (rather than `syncAccessible()`) because it works under
19529
+ * a manifest/recap session with NO extra prompt: the composed manifest recap
19530
+ * already grants `tinycloud.kv get/list` on the account space `spaces/`
19531
+ * prefix, whereas `syncAccessible()` depends on `tinycloud.space/list`, which
19532
+ * a recap session does not hold. Before reading, it consults the fast SQLite
19533
+ * index (`account.index.spaces.list()`) as a best-effort short-circuit; on a
19534
+ * cold index (`no such table: spaces`) or any other index failure it falls
19535
+ * back to the canonical KV read.
19536
+ *
19537
+ * This is a best-effort optimization. ANY failure of the check path (missing
19538
+ * table, KV error, missing record, thrown exception) resolves to `false` so
19539
+ * the caller falls through to hosting — per the directive, "if it fails in any
19540
+ * way then create the space".
19541
+ */
19542
+ async isOwnedSpaceRegistered(spaceId) {
19543
+ try {
19544
+ const indexed = await this.account.index.spaces.list();
19545
+ if (indexed.ok && indexed.data.some((space) => space.spaceId === spaceId)) {
19546
+ return true;
19547
+ }
19548
+ } catch {
19549
+ }
19550
+ try {
19551
+ const record = await this.account.spaces.get(spaceId);
19552
+ return record.ok;
19553
+ } catch {
19554
+ return false;
19555
+ }
19556
+ }
19110
19557
  /**
19111
19558
  * Restore a previously established session from stored delegation data.
19112
19559
  *
@@ -19124,8 +19571,8 @@ var _TinyCloudNode = class _TinyCloudNode {
19124
19571
  this._hooks = void 0;
19125
19572
  this._vault = void 0;
19126
19573
  this._encryption = void 0;
19127
- this._baseSecrets = void 0;
19128
- this._secrets = void 0;
19574
+ this._baseSecrets.clear();
19575
+ this._secrets.clear();
19129
19576
  this._spaceService = void 0;
19130
19577
  this._serviceContext = void 0;
19131
19578
  this.runtimePermissionGrants = [];
@@ -19136,6 +19583,14 @@ var _TinyCloudNode = class _TinyCloudNode {
19136
19583
  if (sessionData.chainId) {
19137
19584
  this._chainId = sessionData.chainId;
19138
19585
  }
19586
+ const resolvedHost = await this.resolveRestoredHost(
19587
+ sessionData.tinycloudHosts,
19588
+ restoredAddress,
19589
+ sessionData.chainId
19590
+ );
19591
+ if (resolvedHost) {
19592
+ this.config.host = resolvedHost;
19593
+ }
19139
19594
  this._serviceContext = new ServiceContext2({
19140
19595
  invoke: this.invokeWithRuntimePermissions,
19141
19596
  invokeAny: this.invokeAnyWithRuntimePermissions,
@@ -19181,12 +19636,45 @@ var _TinyCloudNode = class _TinyCloudNode {
19181
19636
  signature: sessionData.signature ?? ""
19182
19637
  };
19183
19638
  if (this.auth) {
19184
- this.auth.setRestoredTinyCloudSession(tcSession);
19639
+ this.auth.setRestoredTinyCloudSession(
19640
+ tcSession,
19641
+ this.config.host ? [this.config.host] : void 0
19642
+ );
19185
19643
  } else {
19186
19644
  this._restoredTcSession = tcSession;
19187
19645
  }
19188
19646
  }
19189
19647
  }
19648
+ /**
19649
+ * Resolve the host a restored session should target.
19650
+ *
19651
+ * Mirrors fresh sign-in host resolution but for the restore path:
19652
+ * an explicit/pinned host always wins, then the hosts the session was
19653
+ * persisted with, then a lazy registry/fallback resolution for sessions
19654
+ * that predate the persisted `tinycloudHosts` field. Returns `undefined`
19655
+ * only when there's nothing to resolve from (no explicit host, no
19656
+ * persisted hosts, and no address/chainId) — in which case the existing
19657
+ * `config.host` (default) is left in place.
19658
+ *
19659
+ * Resolution failures are surfaced, not swallowed: a genuinely broken
19660
+ * registry lookup throws rather than silently falling back to a wrong host.
19661
+ */
19662
+ async resolveRestoredHost(persistedHosts, address, chainId) {
19663
+ if (this.explicitHost) {
19664
+ return this.explicitHost;
19665
+ }
19666
+ if (persistedHosts && persistedHosts.length > 0) {
19667
+ return persistedHosts[0];
19668
+ }
19669
+ if (address === void 0 || chainId === void 0) {
19670
+ return void 0;
19671
+ }
19672
+ const resolved = await resolveTinyCloudHosts2(pkhDid2(address, chainId), {
19673
+ registryUrl: this.config.tinycloudRegistryUrl,
19674
+ fallbackHosts: this.config.tinycloudFallbackHosts
19675
+ });
19676
+ return resolved.hosts[0];
19677
+ }
19190
19678
  /**
19191
19679
  * Resolve the currently-active TinyCloudSession, preferring the auth
19192
19680
  * layer's value (wallet mode) and falling back to the node-level
@@ -19231,9 +19719,11 @@ var _TinyCloudNode = class _TinyCloudNode {
19231
19719
  );
19232
19720
  }
19233
19721
  this.signer = _TinyCloudNode.nodeDefaults.createSigner(privateKey);
19722
+ const authConfig = { ...this.config, prefix };
19723
+ const useBootstrapSignInRequest = this.shouldUseBootstrapSignInRequest(authConfig);
19234
19724
  this.auth = new NodeUserAuthorization({
19235
19725
  signer: this.signer,
19236
- signStrategy: { type: "auto-sign" },
19726
+ signStrategy: this.config.signStrategy ?? { type: "auto-sign" },
19237
19727
  wasmBindings: this.wasmBindings,
19238
19728
  sessionStorage: options?.sessionStorage ?? this.config.sessionStorage ?? new MemorySessionStorage(),
19239
19729
  domain: this.siweDomain,
@@ -19242,14 +19732,14 @@ var _TinyCloudNode = class _TinyCloudNode {
19242
19732
  tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
19243
19733
  tinycloudRegistryUrl: this.config.tinycloudRegistryUrl,
19244
19734
  tinycloudFallbackHosts: this.config.tinycloudFallbackHosts,
19245
- autoCreateSpace: this.config.autoCreateSpace,
19735
+ autoCreateSpace: useBootstrapSignInRequest ? false : this.config.autoCreateSpace,
19246
19736
  enablePublicSpace: this.config.enablePublicSpace ?? true,
19247
- spaceCreationHandler: this.config.spaceCreationHandler,
19737
+ spaceCreationHandler: useBootstrapSignInRequest ? void 0 : this.config.spaceCreationHandler,
19248
19738
  nonce: this.config.nonce,
19249
19739
  siweConfig: this.config.siweConfig,
19250
- manifest: this.config.manifest,
19251
- capabilityRequest: this.config.capabilityRequest,
19252
- includeAccountRegistryPermissions: this.config.includeAccountRegistryPermissions
19740
+ manifest: useBootstrapSignInRequest ? void 0 : this.config.manifest,
19741
+ capabilityRequest: useBootstrapSignInRequest ? BOOTSTRAP_SESSION_REQUESTS.default : this.config.capabilityRequest,
19742
+ includeAccountRegistryPermissions: useBootstrapSignInRequest ? false : this.config.includeAccountRegistryPermissions
19253
19743
  });
19254
19744
  this.tc = new TinyCloud(this.auth, {
19255
19745
  invokeAny: this.invokeAnyWithRuntimePermissions,
@@ -19276,9 +19766,11 @@ var _TinyCloudNode = class _TinyCloudNode {
19276
19766
  }
19277
19767
  const prefix = options?.prefix ?? "default";
19278
19768
  this.signer = signer;
19769
+ const authConfig = { ...this.config, prefix };
19770
+ const useBootstrapSignInRequest = this.shouldUseBootstrapSignInRequest(authConfig);
19279
19771
  this.auth = new NodeUserAuthorization({
19280
19772
  signer: this.signer,
19281
- signStrategy: { type: "auto-sign" },
19773
+ signStrategy: this.config.signStrategy ?? { type: "auto-sign" },
19282
19774
  wasmBindings: this.wasmBindings,
19283
19775
  sessionStorage: options?.sessionStorage ?? this.config.sessionStorage ?? new MemorySessionStorage(),
19284
19776
  domain: this.siweDomain,
@@ -19287,14 +19779,14 @@ var _TinyCloudNode = class _TinyCloudNode {
19287
19779
  tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
19288
19780
  tinycloudRegistryUrl: this.config.tinycloudRegistryUrl,
19289
19781
  tinycloudFallbackHosts: this.config.tinycloudFallbackHosts,
19290
- autoCreateSpace: this.config.autoCreateSpace,
19782
+ autoCreateSpace: useBootstrapSignInRequest ? false : this.config.autoCreateSpace,
19291
19783
  enablePublicSpace: this.config.enablePublicSpace ?? true,
19292
- spaceCreationHandler: this.config.spaceCreationHandler,
19784
+ spaceCreationHandler: useBootstrapSignInRequest ? void 0 : this.config.spaceCreationHandler,
19293
19785
  nonce: this.config.nonce,
19294
19786
  siweConfig: this.config.siweConfig,
19295
- manifest: this.config.manifest,
19296
- capabilityRequest: this.config.capabilityRequest,
19297
- includeAccountRegistryPermissions: this.config.includeAccountRegistryPermissions
19787
+ manifest: useBootstrapSignInRequest ? void 0 : this.config.manifest,
19788
+ capabilityRequest: useBootstrapSignInRequest ? BOOTSTRAP_SESSION_REQUESTS.default : this.config.capabilityRequest,
19789
+ includeAccountRegistryPermissions: useBootstrapSignInRequest ? false : this.config.includeAccountRegistryPermissions
19298
19790
  });
19299
19791
  this.tc = new TinyCloud(this.auth, {
19300
19792
  invokeAny: this.invokeAnyWithRuntimePermissions,
@@ -19370,6 +19862,20 @@ var _TinyCloudNode = class _TinyCloudNode {
19370
19862
  getDefaultEncryptionNetworkId(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
19371
19863
  return `urn:tinycloud:encryption:${this.did}:${name}`;
19372
19864
  }
19865
+ getEncryptionNetworkIdForSpace(spaceId, name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
19866
+ const ownerDid = this.ownerDidFromSpaceId(spaceId) ?? this.did;
19867
+ return `urn:tinycloud:encryption:${ownerDid}:${name}`;
19868
+ }
19869
+ ownerDidFromSpaceId(spaceId) {
19870
+ if (!spaceId.startsWith("tinycloud:")) return void 0;
19871
+ const body = spaceId.slice("tinycloud:".length);
19872
+ const lastSeparator = body.lastIndexOf(":");
19873
+ if (lastSeparator <= 0) return void 0;
19874
+ const owner = body.slice(0, lastSeparator);
19875
+ if (owner.startsWith("did:")) return owner;
19876
+ if (!owner.includes(":")) return void 0;
19877
+ return `did:${owner}`;
19878
+ }
19373
19879
  requireServiceSession() {
19374
19880
  const session = this._serviceContext?.session;
19375
19881
  if (!session) {
@@ -19557,7 +20063,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19557
20063
  spaceId,
19558
20064
  crypto: vaultCrypto,
19559
20065
  encryption: {
19560
- networkId: this.getDefaultEncryptionNetworkId(),
20066
+ networkId: this.getEncryptionNetworkIdForSpace(spaceId),
19561
20067
  service: this.getEncryptionService(),
19562
20068
  decryptCapabilityProof: () => ({
19563
20069
  proofs: [this.requireServiceSession().delegationCid]
@@ -19688,6 +20194,9 @@ var _TinyCloudNode = class _TinyCloudNode {
19688
20194
  }
19689
20195
  return vaultService;
19690
20196
  },
20197
+ createSecretsService: (spaceId) => {
20198
+ return this.secretsForSpace(spaceId);
20199
+ },
19691
20200
  // Enable space.delegations.create() via SIWE-based delegation
19692
20201
  createDelegation: async (params) => {
19693
20202
  try {
@@ -19810,11 +20319,10 @@ var _TinyCloudNode = class _TinyCloudNode {
19810
20319
  try {
19811
20320
  const host = this.config.host;
19812
20321
  const now = /* @__PURE__ */ new Date();
19813
- const abilities = {
19814
- kv: {
19815
- [params.path]: params.actions
19816
- }
19817
- };
20322
+ const abilities = sharingActionsToAbilities(params.path, params.actions);
20323
+ if (!abilities) {
20324
+ return void 0;
20325
+ }
19818
20326
  const prepared = this.wasmBindings.prepareSession({
19819
20327
  abilities,
19820
20328
  address: this.wasmBindings.ensureEip55(session.address),
@@ -20070,27 +20578,49 @@ var _TinyCloudNode = class _TinyCloudNode {
20070
20578
  if (!this._spaceService) {
20071
20579
  throw new Error("Not signed in. Call signIn() first.");
20072
20580
  }
20073
- if (!this._secrets) {
20074
- this._secrets = new NodeSecretsService({
20075
- getService: () => this.getBaseSecrets(),
20581
+ return this.secretsForSpace("secrets");
20582
+ }
20583
+ /**
20584
+ * App-facing secrets API backed by the requested space's vault.
20585
+ */
20586
+ secretsForSpace(spaceId) {
20587
+ if (!this._spaceService) {
20588
+ throw new Error("Not signed in. Call signIn() first.");
20589
+ }
20590
+ const resolvedSpace = spaceId.startsWith("tinycloud:") ? spaceId : this.ownedSpaceId(spaceId);
20591
+ let secrets = this._secrets.get(resolvedSpace);
20592
+ if (!secrets) {
20593
+ secrets = new NodeSecretsService({
20594
+ getService: () => this.getBaseSecrets(resolvedSpace),
20595
+ space: resolvedSpace,
20076
20596
  getManifest: () => this.manifest,
20077
20597
  hasPermissions: (permissions) => this.hasRuntimePermissions(permissions),
20078
20598
  grantPermissions: (additional) => this.grantRuntimePermissions(additional),
20079
20599
  canEscalate: () => this.signer !== void 0 && this.tc !== void 0,
20080
- getEncryptionNetworkId: () => this.getDefaultEncryptionNetworkId(),
20600
+ getEncryptionNetworkId: () => this.getEncryptionNetworkIdForSpace(resolvedSpace),
20601
+ resolveSpace: (space) => space.startsWith("tinycloud:") ? space : this.ownedSpaceId(space),
20081
20602
  getUnlockSigner: () => this.signer ?? void 0
20082
20603
  });
20604
+ this._secrets.set(resolvedSpace, secrets);
20083
20605
  }
20084
- return this._secrets;
20606
+ return secrets;
20085
20607
  }
20086
- getBaseSecrets() {
20608
+ getBaseSecrets(spaceId) {
20087
20609
  if (!this._spaceService) {
20088
20610
  throw new Error("Not signed in. Call signIn() first.");
20089
20611
  }
20090
- if (!this._baseSecrets) {
20091
- this._baseSecrets = new SecretsService(() => this.space("secrets").vault);
20612
+ const resolvedSpace = spaceId.startsWith("tinycloud:") ? spaceId : this.ownedSpaceId(spaceId);
20613
+ let secrets = this._baseSecrets.get(resolvedSpace);
20614
+ if (!secrets) {
20615
+ const kvService = this.createSpaceScopedKVService(resolvedSpace);
20616
+ const vaultService = this.createVaultService(resolvedSpace, kvService);
20617
+ if (this._serviceContext) {
20618
+ vaultService.initialize(this._serviceContext);
20619
+ }
20620
+ secrets = new SecretsService(() => vaultService);
20621
+ this._baseSecrets.set(resolvedSpace, secrets);
20092
20622
  }
20093
- return this._baseSecrets;
20623
+ return secrets;
20094
20624
  }
20095
20625
  /**
20096
20626
  * Hooks write stream subscription API.
@@ -21504,6 +22034,7 @@ var _TinyCloudNode = class _TinyCloudNode {
21504
22034
  delegation,
21505
22035
  targetHost,
21506
22036
  this.wasmBindings.invoke,
22037
+ this.wasmBindings.invokeAny,
21507
22038
  this.config.telemetry
21508
22039
  );
21509
22040
  }
@@ -21569,6 +22100,7 @@ var _TinyCloudNode = class _TinyCloudNode {
21569
22100
  delegation,
21570
22101
  targetHost,
21571
22102
  this.wasmBindings.invoke,
22103
+ this.wasmBindings.invokeAny,
21572
22104
  this.config.telemetry
21573
22105
  );
21574
22106
  }
@@ -21700,7 +22232,14 @@ import {
21700
22232
  pkhDid as pkhDid3,
21701
22233
  principalDid,
21702
22234
  principalDidEquals as principalDidEquals3,
21703
- parseCanonicalNetworkId
22235
+ parseCanonicalNetworkId,
22236
+ TinyCloudDebugLogger,
22237
+ tinyCloudDebugLogger,
22238
+ enableTinyCloudDebug,
22239
+ disableTinyCloudDebug,
22240
+ getTinyCloudDebugLogs,
22241
+ clearTinyCloudDebugLogs,
22242
+ installTinyCloudDebugGlobals
21704
22243
  } from "@tinycloud/sdk-core";
21705
22244
 
21706
22245
  // src/storage/FileSessionStorage.ts
@@ -22034,6 +22573,7 @@ export {
22034
22573
  SpaceErrorCodes,
22035
22574
  SpaceService2 as SpaceService,
22036
22575
  TinyCloud2 as TinyCloud,
22576
+ TinyCloudDebugLogger,
22037
22577
  TinyCloudNode,
22038
22578
  UnsupportedFeatureError2 as UnsupportedFeatureError,
22039
22579
  VAULT_PERMISSION_SERVICE,
@@ -22058,8 +22598,10 @@ export {
22058
22598
  canonicalizeSecretScope,
22059
22599
  checkDecryptInvocationInput,
22060
22600
  checkNodeInfo2 as checkNodeInfo,
22601
+ clearTinyCloudDebugLogs,
22061
22602
  composeManifestRequest2 as composeManifestRequest,
22062
22603
  createCapabilityKeyRegistry,
22604
+ createOpenKeyCallbackSigningStrategy,
22063
22605
  createSharingService,
22064
22606
  createSpaceService,
22065
22607
  createVaultCrypto2 as createVaultCrypto,
@@ -22071,7 +22613,9 @@ export {
22071
22613
  deserializeDelegation,
22072
22614
  didCacheKey,
22073
22615
  didEquals,
22616
+ disableTinyCloudDebug,
22074
22617
  discoverNetwork,
22618
+ enableTinyCloudDebug,
22075
22619
  encryptToNetwork,
22076
22620
  encryptionBase64Decode,
22077
22621
  encryptionBase64Encode,
@@ -22083,9 +22627,11 @@ export {
22083
22627
  expandPermissionEntries2 as expandPermissionEntries,
22084
22628
  expandPermissionEntry,
22085
22629
  generateRandomReceiverKey,
22630
+ getTinyCloudDebugLogs,
22086
22631
  grantAuthRequest,
22087
22632
  hexDecode,
22088
22633
  hexEncode,
22634
+ installTinyCloudDebugGlobals,
22089
22635
  isCapabilitySubset2 as isCapabilitySubset,
22090
22636
  isEvmAddress,
22091
22637
  isNetworkId,
@@ -22107,6 +22653,7 @@ export {
22107
22653
  resolveSecretPath2 as resolveSecretPath,
22108
22654
  resourceCapabilitiesToSpaceAbilitiesMap2 as resourceCapabilitiesToSpaceAbilitiesMap,
22109
22655
  serializeDelegation,
22656
+ tinyCloudDebugLogger,
22110
22657
  validateEnvelope,
22111
22658
  validateManifest,
22112
22659
  verifyDecryptResponse