@tinycloud/node-sdk 2.2.0-beta.0 → 2.2.0-beta.10

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
@@ -17159,6 +17159,7 @@ import {
17159
17159
  DuckDbService as DuckDbService2,
17160
17160
  HooksService as HooksService2,
17161
17161
  DataVaultService,
17162
+ SecretsService,
17162
17163
  createVaultCrypto,
17163
17164
  ServiceContext as ServiceContext2,
17164
17165
  SilentNotificationHandler,
@@ -17171,7 +17172,7 @@ import {
17171
17172
  ACCOUNT_REGISTRY_SPACE,
17172
17173
  PermissionNotInManifestError,
17173
17174
  SessionExpiredError,
17174
- expandActionShortNames,
17175
+ expandPermissionEntries as expandPermissionEntriesCore,
17175
17176
  isCapabilitySubset,
17176
17177
  parseRecapCapabilities,
17177
17178
  SERVICE_LONG_TO_SHORT
@@ -17187,7 +17188,8 @@ import {
17187
17188
  DEFAULT_MANIFEST_SPACE,
17188
17189
  composeManifestRequest,
17189
17190
  resourceCapabilitiesToAbilitiesMap,
17190
- resourceCapabilitiesToSpaceAbilitiesMap
17191
+ resourceCapabilitiesToSpaceAbilitiesMap,
17192
+ resolveTinyCloudHosts
17191
17193
  } from "@tinycloud/sdk-core";
17192
17194
 
17193
17195
  // src/authorization/strategies.ts
@@ -17321,9 +17323,9 @@ var NodeUserAuthorization = class {
17321
17323
  this.sessionExpirationMs = config.sessionExpirationMs ?? 60 * 60 * 1e3;
17322
17324
  this.autoCreateSpace = config.autoCreateSpace ?? false;
17323
17325
  this.spaceCreationHandler = config.spaceCreationHandler;
17324
- this.tinycloudHosts = config.tinycloudHosts ?? [
17325
- "https://node.tinycloud.xyz"
17326
- ];
17326
+ this.tinycloudHosts = config.tinycloudHosts;
17327
+ this.tinycloudRegistryUrl = config.tinycloudRegistryUrl;
17328
+ this.tinycloudFallbackHosts = config.tinycloudFallbackHosts;
17327
17329
  this.enablePublicSpace = config.enablePublicSpace ?? true;
17328
17330
  this.nonce = config.nonce;
17329
17331
  this.siweConfig = config.siweConfig;
@@ -17344,6 +17346,9 @@ var NodeUserAuthorization = class {
17344
17346
  get capabilityRequest() {
17345
17347
  return this.getCapabilityRequest();
17346
17348
  }
17349
+ get hosts() {
17350
+ return this.tinycloudHosts ? [...this.tinycloudHosts] : [];
17351
+ }
17347
17352
  /**
17348
17353
  * Install or replace the stored manifest. Takes effect on the next
17349
17354
  * `signIn()` call — the current session (if any) is not touched.
@@ -17368,6 +17373,26 @@ var NodeUserAuthorization = class {
17368
17373
  get tinyCloudSession() {
17369
17374
  return this._tinyCloudSession;
17370
17375
  }
17376
+ async resolveTinyCloudHostsForSignIn(address, chainId) {
17377
+ if (this.tinycloudHosts && this.tinycloudHosts.length > 0) {
17378
+ return;
17379
+ }
17380
+ const subject = `did:pkh:eip155:${chainId}:${address}`;
17381
+ const resolved = await resolveTinyCloudHosts(subject, {
17382
+ registryUrl: this.tinycloudRegistryUrl,
17383
+ fallbackHosts: this.tinycloudFallbackHosts
17384
+ });
17385
+ this.tinycloudHosts = resolved.hosts;
17386
+ }
17387
+ requireTinyCloudHosts() {
17388
+ if (!this.tinycloudHosts || this.tinycloudHosts.length === 0) {
17389
+ throw new Error("TinyCloud hosts have not been resolved. Call signIn() first.");
17390
+ }
17391
+ return this.tinycloudHosts;
17392
+ }
17393
+ get primaryTinyCloudHost() {
17394
+ return this.requireTinyCloudHosts()[0];
17395
+ }
17371
17396
  get nodeFeatures() {
17372
17397
  return this._nodeFeatures;
17373
17398
  }
@@ -17499,7 +17524,7 @@ var NodeUserAuthorization = class {
17499
17524
  if (!this._tinyCloudSession || !this._address || !this._chainId) {
17500
17525
  throw new Error("Must be signed in to host space");
17501
17526
  }
17502
- const host = this.tinycloudHosts[0];
17527
+ const host = this.primaryTinyCloudHost;
17503
17528
  const spaceId = targetSpaceId ?? this._tinyCloudSession.spaceId;
17504
17529
  const peerId = await fetchPeerId(host, spaceId);
17505
17530
  const siwe = this.wasm.generateHostSIWEMessage({
@@ -17541,7 +17566,7 @@ var NodeUserAuthorization = class {
17541
17566
  if (!this._tinyCloudSession) {
17542
17567
  throw new Error("Must be signed in to ensure space exists");
17543
17568
  }
17544
- const host = this.tinycloudHosts[0];
17569
+ const host = this.primaryTinyCloudHost;
17545
17570
  const primarySpaceId = this._tinyCloudSession.spaceId;
17546
17571
  const result = await activateSessionWithHost(
17547
17572
  host,
@@ -17650,6 +17675,7 @@ var NodeUserAuthorization = class {
17650
17675
  this._chainId = await this.signer.getChainId();
17651
17676
  const address = this.wasm.ensureEip55(this._address);
17652
17677
  const chainId = this._chainId;
17678
+ await this.resolveTinyCloudHostsForSignIn(address, chainId);
17653
17679
  const keyId = `session-${Date.now()}`;
17654
17680
  this.sessionManager.renameSessionKeyId("default", keyId);
17655
17681
  const jwkString = this.sessionManager.jwk(keyId);
@@ -17728,7 +17754,7 @@ var NodeUserAuthorization = class {
17728
17754
  this._address = address;
17729
17755
  this._chainId = chainId;
17730
17756
  const nodeInfo = await checkNodeInfo(
17731
- this.tinycloudHosts[0],
17757
+ this.primaryTinyCloudHost,
17732
17758
  this.wasm.protocolVersion()
17733
17759
  );
17734
17760
  this._nodeFeatures = nodeInfo.features;
@@ -17844,6 +17870,7 @@ var NodeUserAuthorization = class {
17844
17870
  });
17845
17871
  const address = this.wasm.ensureEip55(await this.signer.getAddress());
17846
17872
  const chainId = await this.signer.getChainId();
17873
+ await this.resolveTinyCloudHostsForSignIn(address, chainId);
17847
17874
  const clientSession = {
17848
17875
  address,
17849
17876
  walletAddress: address,
@@ -17893,7 +17920,7 @@ var NodeUserAuthorization = class {
17893
17920
  this._address = address;
17894
17921
  this._chainId = chainId;
17895
17922
  const nodeInfo = await checkNodeInfo(
17896
- this.tinycloudHosts[0],
17923
+ this.primaryTinyCloudHost,
17897
17924
  this.wasm.protocolVersion()
17898
17925
  );
17899
17926
  this._nodeFeatures = nodeInfo.features;
@@ -18057,6 +18084,24 @@ var DelegatedAccess = class {
18057
18084
  get hooks() {
18058
18085
  return this._hooks;
18059
18086
  }
18087
+ /**
18088
+ * Export the handles needed to rehydrate this activated delegation via
18089
+ * `TinyCloudNode.restoreSession(...)` in another process or after a
18090
+ * restart.
18091
+ *
18092
+ * See `RestorableSession` for lifetime caveats.
18093
+ */
18094
+ get restorable() {
18095
+ return {
18096
+ delegationHeader: this.session.delegationHeader,
18097
+ delegationCid: this.session.delegationCid,
18098
+ spaceId: this.session.spaceId,
18099
+ jwk: this.session.jwk,
18100
+ verificationMethod: this.session.verificationMethod,
18101
+ address: this.session.address,
18102
+ chainId: this.session.chainId
18103
+ };
18104
+ }
18060
18105
  };
18061
18106
 
18062
18107
  // src/keys/WasmKeyProvider.ts
@@ -18197,6 +18242,152 @@ function extractSiweExpiration(siwe) {
18197
18242
  return d;
18198
18243
  }
18199
18244
 
18245
+ // src/NodeSecretsService.ts
18246
+ import {
18247
+ ErrorCodes,
18248
+ resolveSecretPath,
18249
+ resolveManifest
18250
+ } from "@tinycloud/sdk-core";
18251
+ var SECRETS_SPACE = "secrets";
18252
+ function ok() {
18253
+ return { ok: true, data: void 0 };
18254
+ }
18255
+ function secretsError(code, message, cause) {
18256
+ return {
18257
+ ok: false,
18258
+ error: {
18259
+ code,
18260
+ service: "secrets",
18261
+ message,
18262
+ ...cause ? { cause } : {}
18263
+ }
18264
+ };
18265
+ }
18266
+ function displayActionUrn(action) {
18267
+ return action === "put" ? "tinycloud.vault/write" : "tinycloud.vault/delete";
18268
+ }
18269
+ function kvActionUrn(action) {
18270
+ return `tinycloud.kv/${action}`;
18271
+ }
18272
+ function vaultMutationAction(action) {
18273
+ return action === "put" ? "write" : "delete";
18274
+ }
18275
+ function secretPermissionEntries(name, options, action) {
18276
+ const secretPath = resolveSecretPath(name, options);
18277
+ return [
18278
+ {
18279
+ service: "tinycloud.vault",
18280
+ space: SECRETS_SPACE,
18281
+ path: secretPath.vaultKey,
18282
+ actions: [vaultMutationAction(action)],
18283
+ skipPrefix: true
18284
+ }
18285
+ ];
18286
+ }
18287
+ function isSecretsSpace(space) {
18288
+ return space === SECRETS_SPACE || space.endsWith(`:${SECRETS_SPACE}`);
18289
+ }
18290
+ var NodeSecretsService = class {
18291
+ constructor(config) {
18292
+ this.config = config;
18293
+ this.shouldRestoreUnlock = false;
18294
+ }
18295
+ get vault() {
18296
+ return this.service.vault;
18297
+ }
18298
+ get isUnlocked() {
18299
+ return this.service.isUnlocked;
18300
+ }
18301
+ async unlock(signer) {
18302
+ const effectiveSigner = signer ?? this.config.getUnlockSigner?.();
18303
+ if (effectiveSigner !== void 0) {
18304
+ this.unlockSigner = effectiveSigner;
18305
+ }
18306
+ const result = await this.service.unlock(effectiveSigner);
18307
+ if (result.ok) {
18308
+ this.shouldRestoreUnlock = true;
18309
+ }
18310
+ return result;
18311
+ }
18312
+ lock() {
18313
+ this.shouldRestoreUnlock = false;
18314
+ this.service.lock();
18315
+ }
18316
+ get(name, options) {
18317
+ return options === void 0 ? this.service.get(name) : this.service.get(name, options);
18318
+ }
18319
+ async put(name, value, options) {
18320
+ const permission = await this.ensureMutationPermission(name, options, "put");
18321
+ if (!permission.ok) return permission;
18322
+ return options === void 0 ? this.service.put(name, value) : this.service.put(name, value, options);
18323
+ }
18324
+ async delete(name, options) {
18325
+ const permission = await this.ensureMutationPermission(name, options, "del");
18326
+ if (!permission.ok) return permission;
18327
+ return options === void 0 ? this.service.delete(name) : this.service.delete(name, options);
18328
+ }
18329
+ list(options) {
18330
+ return options === void 0 ? this.service.list() : this.service.list(options);
18331
+ }
18332
+ get service() {
18333
+ return this.config.getService();
18334
+ }
18335
+ async ensureMutationPermission(name, options, action) {
18336
+ let permissionEntries;
18337
+ try {
18338
+ permissionEntries = secretPermissionEntries(name, options, action);
18339
+ } catch (error) {
18340
+ return secretsError(
18341
+ ErrorCodes.INVALID_INPUT,
18342
+ error instanceof Error ? error.message : String(error),
18343
+ error instanceof Error ? error : void 0
18344
+ );
18345
+ }
18346
+ if (this.hasMutationPermission(name, options, action)) {
18347
+ return ok();
18348
+ }
18349
+ if (!this.config.canEscalate()) {
18350
+ return secretsError(
18351
+ ErrorCodes.PERMISSION_DENIED,
18352
+ `Cannot autosign ${displayActionUrn(action)} for ${name}; TinyCloudNode needs wallet mode with a signer or privateKey.`
18353
+ );
18354
+ }
18355
+ try {
18356
+ await this.config.grantPermissions(permissionEntries);
18357
+ return this.restoreUnlockAfterEscalation();
18358
+ } catch (error) {
18359
+ return secretsError(
18360
+ ErrorCodes.PERMISSION_DENIED,
18361
+ error instanceof Error ? error.message : `Autosign escalation for ${displayActionUrn(action)} on ${name} failed.`,
18362
+ error instanceof Error ? error : void 0
18363
+ );
18364
+ }
18365
+ }
18366
+ async restoreUnlockAfterEscalation() {
18367
+ if (!this.shouldRestoreUnlock) {
18368
+ return ok();
18369
+ }
18370
+ return this.service.unlock(this.unlockSigner);
18371
+ }
18372
+ hasMutationPermission(name, options, action) {
18373
+ const manifest = this.config.getManifest();
18374
+ if (manifest === void 0) {
18375
+ return false;
18376
+ }
18377
+ const manifests = Array.isArray(manifest) ? manifest : [manifest];
18378
+ const requiredAction = kvActionUrn(action);
18379
+ const secretPath = resolveSecretPath(name, options);
18380
+ return manifests.some((entry) => {
18381
+ const resolved = resolveManifest(entry);
18382
+ return ["keys", "vault"].every(
18383
+ (base2) => resolved.resources.some(
18384
+ (resource) => resource.service === "tinycloud.kv" && isSecretsSpace(resource.space) && resource.path === secretPath.permissionPaths[base2] && resource.actions.includes(requiredAction)
18385
+ )
18386
+ );
18387
+ });
18388
+ }
18389
+ };
18390
+
18200
18391
  // src/TinyCloudNode.ts
18201
18392
  var DEFAULT_HOST = "https://node.tinycloud.xyz";
18202
18393
  var _TinyCloudNode = class _TinyCloudNode {
@@ -18228,6 +18419,31 @@ var _TinyCloudNode = class _TinyCloudNode {
18228
18419
  this.auth = null;
18229
18420
  this.tc = null;
18230
18421
  this._chainId = 1;
18422
+ this.runtimePermissionGrants = [];
18423
+ this.invokeWithRuntimePermissions = (session, service, path, action, facts) => {
18424
+ return this.wasmBindings.invoke(
18425
+ this.selectInvocationSession(session, service, path, action),
18426
+ service,
18427
+ path,
18428
+ action,
18429
+ facts
18430
+ );
18431
+ };
18432
+ this.invokeAnyWithRuntimePermissions = (session, entries, facts) => {
18433
+ if (!this.wasmBindings.invokeAny) {
18434
+ throw new Error("WASM binding does not support invokeAny");
18435
+ }
18436
+ const grant = this.findGrantForOperations(
18437
+ entries.map((entry) => ({
18438
+ spaceId: entry.spaceId,
18439
+ service: this.invocationServiceName(entry.service),
18440
+ path: entry.path,
18441
+ action: entry.action
18442
+ }))
18443
+ );
18444
+ return this.wasmBindings.invokeAny(grant?.session ?? session, entries, facts);
18445
+ };
18446
+ this.explicitHost = config.host;
18231
18447
  this.config = {
18232
18448
  ...config,
18233
18449
  host: config.host ?? DEFAULT_HOST
@@ -18262,7 +18478,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18262
18478
  this._sharingService = new SharingService({
18263
18479
  hosts: [this.config.host],
18264
18480
  // session: undefined - not needed for receive()
18265
- invoke: this.wasmBindings.invoke,
18481
+ invoke: this.invokeWithRuntimePermissions,
18266
18482
  fetch: globalThis.fetch.bind(globalThis),
18267
18483
  keyProvider: this._keyProvider,
18268
18484
  registry: this._capabilityRegistry,
@@ -18309,7 +18525,6 @@ var _TinyCloudNode = class _TinyCloudNode {
18309
18525
  * @internal
18310
18526
  */
18311
18527
  setupAuth(config) {
18312
- const host = this.config.host;
18313
18528
  this.auth = new NodeUserAuthorization({
18314
18529
  signer: this.signer,
18315
18530
  signStrategy: { type: "auto-sign" },
@@ -18318,7 +18533,9 @@ var _TinyCloudNode = class _TinyCloudNode {
18318
18533
  domain: this.siweDomain,
18319
18534
  spacePrefix: config.prefix,
18320
18535
  sessionExpirationMs: config.sessionExpirationMs ?? 60 * 60 * 1e3,
18321
- tinycloudHosts: [host],
18536
+ tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
18537
+ tinycloudRegistryUrl: config.tinycloudRegistryUrl,
18538
+ tinycloudFallbackHosts: config.tinycloudFallbackHosts,
18322
18539
  autoCreateSpace: config.autoCreateSpace,
18323
18540
  enablePublicSpace: config.enablePublicSpace ?? true,
18324
18541
  spaceCreationHandler: config.spaceCreationHandler,
@@ -18329,9 +18546,15 @@ var _TinyCloudNode = class _TinyCloudNode {
18329
18546
  includeAccountRegistryPermissions: config.includeAccountRegistryPermissions
18330
18547
  });
18331
18548
  this.tc = new TinyCloud(this.auth, {
18332
- invokeAny: this.wasmBindings.invokeAny
18549
+ invokeAny: this.invokeAnyWithRuntimePermissions
18333
18550
  });
18334
18551
  }
18552
+ syncResolvedHostFromAuth() {
18553
+ const host = this.auth?.hosts[0];
18554
+ if (host) {
18555
+ this.config.host = host;
18556
+ }
18557
+ }
18335
18558
  /**
18336
18559
  * Install or replace the manifest that drives the SIWE recap at
18337
18560
  * sign-in. Takes effect on the next `signIn()` call — the current
@@ -18369,6 +18592,10 @@ var _TinyCloudNode = class _TinyCloudNode {
18369
18592
  get capabilityRequest() {
18370
18593
  return this.auth?.capabilityRequest;
18371
18594
  }
18595
+ get hosts() {
18596
+ const authHosts = this.auth?.hosts ?? [];
18597
+ return authHosts.length > 0 ? authHosts : [this.config.host];
18598
+ }
18372
18599
  /**
18373
18600
  * Get the primary identity DID for this user.
18374
18601
  * - If wallet connected and signed in: returns PKH DID (did:pkh:eip155:{chainId}:{address})
@@ -18439,8 +18666,14 @@ var _TinyCloudNode = class _TinyCloudNode {
18439
18666
  this._sql = void 0;
18440
18667
  this._duckdb = void 0;
18441
18668
  this._hooks = void 0;
18669
+ this._vault = void 0;
18670
+ this._baseSecrets = void 0;
18671
+ this._secrets = void 0;
18672
+ this._spaceService = void 0;
18442
18673
  this._serviceContext = void 0;
18674
+ this.runtimePermissionGrants = [];
18443
18675
  await this.tc.signIn(options);
18676
+ this.syncResolvedHostFromAuth();
18444
18677
  this.initializeServices();
18445
18678
  await this.writeManifestRegistryRecords();
18446
18679
  this.notificationHandler.success("Successfully signed in");
@@ -18460,7 +18693,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18460
18693
  throw new Error("Manifest registry write requires wallet mode");
18461
18694
  }
18462
18695
  const accountSpaceId = this.ownedSpaceId(ACCOUNT_REGISTRY_SPACE);
18463
- await this.auth.hostOwnedSpace(accountSpaceId);
18696
+ await this.ensureOwnedSpaceHosted(accountSpaceId);
18464
18697
  const accountKV = this.spaces.get(accountSpaceId).kv;
18465
18698
  for (const record of request.registryRecords) {
18466
18699
  const result = await accountKV.put(record.key, {
@@ -18475,6 +18708,39 @@ var _TinyCloudNode = class _TinyCloudNode {
18475
18708
  }
18476
18709
  }
18477
18710
  }
18711
+ async ensureOwnedSpaceHosted(spaceId) {
18712
+ if (!this.auth) {
18713
+ throw new Error("Owned space hosting requires wallet mode");
18714
+ }
18715
+ const session = this.auth.tinyCloudSession;
18716
+ if (!session) {
18717
+ throw new Error("Owned space hosting requires an active session");
18718
+ }
18719
+ const host = this.hosts[0] ?? this.config.host;
18720
+ if (!host) {
18721
+ throw new Error("Owned space hosting requires a TinyCloud host");
18722
+ }
18723
+ const activation = await activateSessionWithHost2(host, session.delegationHeader);
18724
+ if (activation.success && !activation.skipped?.includes(spaceId)) {
18725
+ return;
18726
+ }
18727
+ if (!activation.success && activation.status !== 404) {
18728
+ throw new Error(
18729
+ `Failed to check owned space ${spaceId}: ${activation.error ?? activation.status}`
18730
+ );
18731
+ }
18732
+ const created = await this.auth.hostOwnedSpace(spaceId);
18733
+ if (!created) {
18734
+ throw new Error(`Failed to create owned space: ${spaceId}`);
18735
+ }
18736
+ await new Promise((resolve) => setTimeout(resolve, 100));
18737
+ const retry = await activateSessionWithHost2(host, session.delegationHeader);
18738
+ if (!retry.success || retry.skipped?.includes(spaceId)) {
18739
+ throw new Error(
18740
+ `Failed to activate session after creating owned space ${spaceId}: ${retry.error ?? "space was skipped"}`
18741
+ );
18742
+ }
18743
+ }
18478
18744
  /**
18479
18745
  * Restore a previously established session from stored delegation data.
18480
18746
  *
@@ -18490,7 +18756,12 @@ var _TinyCloudNode = class _TinyCloudNode {
18490
18756
  this._sql = void 0;
18491
18757
  this._duckdb = void 0;
18492
18758
  this._hooks = void 0;
18759
+ this._vault = void 0;
18760
+ this._baseSecrets = void 0;
18761
+ this._secrets = void 0;
18762
+ this._spaceService = void 0;
18493
18763
  this._serviceContext = void 0;
18764
+ this.runtimePermissionGrants = [];
18494
18765
  if (sessionData.address) {
18495
18766
  this._address = sessionData.address;
18496
18767
  }
@@ -18498,8 +18769,8 @@ var _TinyCloudNode = class _TinyCloudNode {
18498
18769
  this._chainId = sessionData.chainId;
18499
18770
  }
18500
18771
  this._serviceContext = new ServiceContext2({
18501
- invoke: this.wasmBindings.invoke,
18502
- invokeAny: this.wasmBindings.invokeAny,
18772
+ invoke: this.invokeWithRuntimePermissions,
18773
+ invokeAny: this.invokeAnyWithRuntimePermissions,
18503
18774
  fetch: globalThis.fetch.bind(globalThis),
18504
18775
  hosts: [this.config.host]
18505
18776
  });
@@ -18523,41 +18794,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18523
18794
  jwk: sessionData.jwk
18524
18795
  };
18525
18796
  this._serviceContext.setSession(serviceSession);
18526
- const wasm = this.wasmBindings;
18527
- const vaultCrypto = createVaultCrypto({
18528
- vault_encrypt: wasm.vault_encrypt,
18529
- vault_decrypt: wasm.vault_decrypt,
18530
- vault_derive_key: wasm.vault_derive_key,
18531
- vault_x25519_from_seed: wasm.vault_x25519_from_seed,
18532
- vault_x25519_dh: wasm.vault_x25519_dh,
18533
- vault_random_bytes: wasm.vault_random_bytes,
18534
- vault_sha256: wasm.vault_sha256
18535
- });
18536
- const self2 = this;
18537
- this._vault = new DataVaultService({
18538
- spaceId: sessionData.spaceId,
18539
- crypto: vaultCrypto,
18540
- tc: {
18541
- kv: this._kv,
18542
- ensurePublicSpace: async () => {
18543
- try {
18544
- await self2.ensurePublicSpace();
18545
- return { ok: true, data: void 0 };
18546
- } catch (error) {
18547
- return { ok: false, error: { code: "STORAGE_ERROR", message: error instanceof Error ? error.message : String(error), service: "vault" } };
18548
- }
18549
- },
18550
- get publicKV() {
18551
- return self2._publicKV ?? self2.tc.publicKV;
18552
- },
18553
- readPublicSpace: (host, spaceId, key2) => TinyCloud.readPublicSpace(host, spaceId, key2),
18554
- makePublicSpaceId: TinyCloud.makePublicSpaceId,
18555
- did: this.did,
18556
- address: sessionData.address ?? this._address ?? "",
18557
- chainId: sessionData.chainId ?? this._chainId,
18558
- hosts: [this.config.host]
18559
- }
18560
- });
18797
+ this._vault = this.createVaultService(sessionData.spaceId, this._kv);
18561
18798
  this._vault.initialize(this._serviceContext);
18562
18799
  this._serviceContext.registerService("vault", this._vault);
18563
18800
  this.initializeV2Services(serviceSession);
@@ -18592,7 +18829,6 @@ var _TinyCloudNode = class _TinyCloudNode {
18592
18829
  throw new Error("Wallet already connected. Cannot connect another wallet.");
18593
18830
  }
18594
18831
  const prefix = options?.prefix ?? "default";
18595
- const host = this.config.host;
18596
18832
  if (!_TinyCloudNode.nodeDefaults) {
18597
18833
  throw new Error(
18598
18834
  "connectWallet() requires PrivateKeySigner. Use connectSigner() instead, or import from '@tinycloud/node-sdk' (not '/core') for automatic Node.js defaults."
@@ -18607,7 +18843,9 @@ var _TinyCloudNode = class _TinyCloudNode {
18607
18843
  domain: this.siweDomain,
18608
18844
  spacePrefix: prefix,
18609
18845
  sessionExpirationMs: this.config.sessionExpirationMs ?? 60 * 60 * 1e3,
18610
- tinycloudHosts: [host],
18846
+ tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
18847
+ tinycloudRegistryUrl: this.config.tinycloudRegistryUrl,
18848
+ tinycloudFallbackHosts: this.config.tinycloudFallbackHosts,
18611
18849
  autoCreateSpace: this.config.autoCreateSpace,
18612
18850
  enablePublicSpace: this.config.enablePublicSpace ?? true,
18613
18851
  spaceCreationHandler: this.config.spaceCreationHandler,
@@ -18618,7 +18856,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18618
18856
  includeAccountRegistryPermissions: this.config.includeAccountRegistryPermissions
18619
18857
  });
18620
18858
  this.tc = new TinyCloud(this.auth, {
18621
- invokeAny: this.wasmBindings.invokeAny
18859
+ invokeAny: this.invokeAnyWithRuntimePermissions
18622
18860
  });
18623
18861
  this.config.prefix = prefix;
18624
18862
  }
@@ -18640,7 +18878,6 @@ var _TinyCloudNode = class _TinyCloudNode {
18640
18878
  throw new Error("Signer already connected. Cannot connect another signer.");
18641
18879
  }
18642
18880
  const prefix = options?.prefix ?? "default";
18643
- const host = this.config.host;
18644
18881
  this.signer = signer;
18645
18882
  this.auth = new NodeUserAuthorization({
18646
18883
  signer: this.signer,
@@ -18650,7 +18887,9 @@ var _TinyCloudNode = class _TinyCloudNode {
18650
18887
  domain: this.siweDomain,
18651
18888
  spacePrefix: prefix,
18652
18889
  sessionExpirationMs: this.config.sessionExpirationMs ?? 60 * 60 * 1e3,
18653
- tinycloudHosts: [host],
18890
+ tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
18891
+ tinycloudRegistryUrl: this.config.tinycloudRegistryUrl,
18892
+ tinycloudFallbackHosts: this.config.tinycloudFallbackHosts,
18654
18893
  autoCreateSpace: this.config.autoCreateSpace,
18655
18894
  enablePublicSpace: this.config.enablePublicSpace ?? true,
18656
18895
  spaceCreationHandler: this.config.spaceCreationHandler,
@@ -18661,7 +18900,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18661
18900
  includeAccountRegistryPermissions: this.config.includeAccountRegistryPermissions
18662
18901
  });
18663
18902
  this.tc = new TinyCloud(this.auth, {
18664
- invokeAny: this.wasmBindings.invokeAny
18903
+ invokeAny: this.invokeAnyWithRuntimePermissions
18665
18904
  });
18666
18905
  this.config.prefix = prefix;
18667
18906
  }
@@ -18674,10 +18913,10 @@ var _TinyCloudNode = class _TinyCloudNode {
18674
18913
  if (!session) {
18675
18914
  return;
18676
18915
  }
18677
- this.tc.initializeServices(this.wasmBindings.invoke, [this.config.host]);
18916
+ this.tc.initializeServices(this.invokeWithRuntimePermissions, [this.config.host]);
18678
18917
  this._serviceContext = new ServiceContext2({
18679
- invoke: this.wasmBindings.invoke,
18680
- invokeAny: this.wasmBindings.invokeAny,
18918
+ invoke: this.invokeWithRuntimePermissions,
18919
+ invokeAny: this.invokeAnyWithRuntimePermissions,
18681
18920
  fetch: globalThis.fetch.bind(globalThis),
18682
18921
  hosts: [this.config.host]
18683
18922
  });
@@ -18707,6 +18946,28 @@ var _TinyCloudNode = class _TinyCloudNode {
18707
18946
  };
18708
18947
  this._serviceContext.setSession(serviceSession);
18709
18948
  this.tc.serviceContext.setSession(serviceSession);
18949
+ this._vault = this.createVaultService(session.spaceId, this._kv);
18950
+ this._vault.initialize(this._serviceContext);
18951
+ this._serviceContext.registerService("vault", this._vault);
18952
+ this.initializeV2Services(serviceSession);
18953
+ }
18954
+ createSpaceScopedKVService(spaceId) {
18955
+ const kvService = new KVService2({});
18956
+ if (this._serviceContext) {
18957
+ const spaceScopedContext = new ServiceContext2({
18958
+ invoke: this._serviceContext.invoke,
18959
+ fetch: this._serviceContext.fetch,
18960
+ hosts: this._serviceContext.hosts
18961
+ });
18962
+ const session = this._serviceContext.session;
18963
+ if (session) {
18964
+ spaceScopedContext.setSession({ ...session, spaceId });
18965
+ }
18966
+ kvService.initialize(spaceScopedContext);
18967
+ }
18968
+ return kvService;
18969
+ }
18970
+ createVaultService(spaceId, kv) {
18710
18971
  const wasm = this.wasmBindings;
18711
18972
  const vaultCrypto = createVaultCrypto({
18712
18973
  vault_encrypt: wasm.vault_encrypt,
@@ -18718,11 +18979,11 @@ var _TinyCloudNode = class _TinyCloudNode {
18718
18979
  vault_sha256: wasm.vault_sha256
18719
18980
  });
18720
18981
  const self2 = this;
18721
- this._vault = new DataVaultService({
18722
- spaceId: session.spaceId,
18982
+ return new DataVaultService({
18983
+ spaceId,
18723
18984
  crypto: vaultCrypto,
18724
18985
  tc: {
18725
- kv: this._kv,
18986
+ kv,
18726
18987
  ensurePublicSpace: async () => {
18727
18988
  try {
18728
18989
  await self2.ensurePublicSpace();
@@ -18734,17 +18995,14 @@ var _TinyCloudNode = class _TinyCloudNode {
18734
18995
  get publicKV() {
18735
18996
  return self2._publicKV ?? self2.tc.publicKV;
18736
18997
  },
18737
- readPublicSpace: (host, spaceId, key2) => TinyCloud.readPublicSpace(host, spaceId, key2),
18998
+ readPublicSpace: (host, targetSpaceId, key2) => TinyCloud.readPublicSpace(host, targetSpaceId, key2),
18738
18999
  makePublicSpaceId: TinyCloud.makePublicSpaceId,
18739
19000
  did: this.did,
18740
- address: this._address,
19001
+ address: this._address ?? "",
18741
19002
  chainId: this._chainId,
18742
19003
  hosts: [this.config.host]
18743
19004
  }
18744
19005
  });
18745
- this._vault.initialize(this._serviceContext);
18746
- this._serviceContext.registerService("vault", this._vault);
18747
- this.initializeV2Services(serviceSession);
18748
19006
  }
18749
19007
  /**
18750
19008
  * Initialize the v2 delegation system services.
@@ -18828,7 +19086,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18828
19086
  this._delegationManager = new DelegationManager({
18829
19087
  hosts: [this.config.host],
18830
19088
  session: serviceSession,
18831
- invoke: this.wasmBindings.invoke,
19089
+ invoke: this.invokeWithRuntimePermissions,
18832
19090
  fetch: globalThis.fetch.bind(globalThis)
18833
19091
  });
18834
19092
  this._spaceService = new SpaceService({
@@ -18839,20 +19097,15 @@ var _TinyCloudNode = class _TinyCloudNode {
18839
19097
  capabilityRegistry: this._capabilityRegistry,
18840
19098
  userDid: this.did,
18841
19099
  createKVService: (spaceId) => {
18842
- const kvService = new KVService2({});
19100
+ return this.createSpaceScopedKVService(spaceId);
19101
+ },
19102
+ createVaultService: (spaceId) => {
19103
+ const kvService = this.createSpaceScopedKVService(spaceId);
19104
+ const vaultService = this.createVaultService(spaceId, kvService);
18843
19105
  if (this._serviceContext) {
18844
- const spaceScopedContext = new ServiceContext2({
18845
- invoke: this._serviceContext.invoke,
18846
- fetch: this._serviceContext.fetch,
18847
- hosts: this._serviceContext.hosts
18848
- });
18849
- const session = this._serviceContext.session;
18850
- if (session) {
18851
- spaceScopedContext.setSession({ ...session, spaceId });
18852
- }
18853
- kvService.initialize(spaceScopedContext);
19106
+ vaultService.initialize(this._serviceContext);
18854
19107
  }
18855
- return kvService;
19108
+ return vaultService;
18856
19109
  },
18857
19110
  // Enable space.delegations.create() via SIWE-based delegation
18858
19111
  createDelegation: async (params) => {
@@ -19089,6 +19342,33 @@ var _TinyCloudNode = class _TinyCloudNode {
19089
19342
  }
19090
19343
  return this._vault;
19091
19344
  }
19345
+ /**
19346
+ * App-facing secrets API backed by the `secrets` space vault.
19347
+ */
19348
+ get secrets() {
19349
+ if (!this._spaceService) {
19350
+ throw new Error("Not signed in. Call signIn() first.");
19351
+ }
19352
+ if (!this._secrets) {
19353
+ this._secrets = new NodeSecretsService({
19354
+ getService: () => this.getBaseSecrets(),
19355
+ getManifest: () => this.manifest,
19356
+ grantPermissions: (additional) => this.grantRuntimePermissions(additional),
19357
+ canEscalate: () => this.signer !== void 0 && this.tc !== void 0,
19358
+ getUnlockSigner: () => this.signer ?? void 0
19359
+ });
19360
+ }
19361
+ return this._secrets;
19362
+ }
19363
+ getBaseSecrets() {
19364
+ if (!this._spaceService) {
19365
+ throw new Error("Not signed in. Call signIn() first.");
19366
+ }
19367
+ if (!this._baseSecrets) {
19368
+ this._baseSecrets = new SecretsService(() => this.space("secrets").vault);
19369
+ }
19370
+ return this._baseSecrets;
19371
+ }
19092
19372
  /**
19093
19373
  * Hooks write stream subscription API.
19094
19374
  */
@@ -19159,6 +19439,171 @@ var _TinyCloudNode = class _TinyCloudNode {
19159
19439
  }
19160
19440
  };
19161
19441
  }
19442
+ /**
19443
+ * Check whether the current session or an approved runtime delegation covers
19444
+ * every requested permission.
19445
+ */
19446
+ hasRuntimePermissions(permissions) {
19447
+ const session = this.auth?.tinyCloudSession;
19448
+ if (!session || !Array.isArray(permissions) || permissions.length === 0) {
19449
+ return false;
19450
+ }
19451
+ const expanded = this.expandPermissionEntries(permissions);
19452
+ if (this.sessionCoversPermissionEntries(session, expanded)) {
19453
+ return true;
19454
+ }
19455
+ return this.findRuntimeGrantsForPermissionEntries(expanded, session).length > 0;
19456
+ }
19457
+ /**
19458
+ * Return installed runtime permission delegations. When `permissions` is
19459
+ * provided, only delegations currently covering those permissions are
19460
+ * returned. Base-session manifest permissions are not represented here.
19461
+ */
19462
+ getRuntimePermissionDelegations(permissions) {
19463
+ this.pruneExpiredRuntimePermissionGrants();
19464
+ if (permissions === void 0) {
19465
+ return this.runtimePermissionGrants.map((grant) => grant.delegation);
19466
+ }
19467
+ const session = this.auth?.tinyCloudSession;
19468
+ if (!session || !Array.isArray(permissions) || permissions.length === 0) {
19469
+ return [];
19470
+ }
19471
+ const expanded = this.expandPermissionEntries(permissions);
19472
+ return this.findRuntimeGrantsForPermissionEntries(expanded, session).map(
19473
+ (grant) => grant.delegation
19474
+ );
19475
+ }
19476
+ /**
19477
+ * Install a portable runtime permission delegation into this SDK instance so
19478
+ * matching service calls and downstream `delegateTo()` calls can use it.
19479
+ */
19480
+ async useRuntimeDelegation(delegation) {
19481
+ const session = this.auth?.tinyCloudSession;
19482
+ if (!session) {
19483
+ throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
19484
+ }
19485
+ if (delegation.expiry.getTime() <= Date.now()) {
19486
+ throw new SessionExpiredError(delegation.expiry);
19487
+ }
19488
+ const expectedDids = /* @__PURE__ */ new Set([session.verificationMethod, this.sessionDid]);
19489
+ if (!expectedDids.has(delegation.delegateDID)) {
19490
+ throw new Error(
19491
+ `Runtime delegation targets ${delegation.delegateDID} but this session key is ${session.verificationMethod}.`
19492
+ );
19493
+ }
19494
+ const targetHost = delegation.host ?? this.config.host;
19495
+ const activateResult = await activateSessionWithHost2(
19496
+ targetHost,
19497
+ delegation.delegationHeader
19498
+ );
19499
+ if (!activateResult.success) {
19500
+ throw new Error(
19501
+ `Failed to activate runtime permission delegation: ${activateResult.error}`
19502
+ );
19503
+ }
19504
+ this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
19505
+ (grant) => grant.delegation.cid !== delegation.cid
19506
+ );
19507
+ this.runtimePermissionGrants.push(
19508
+ this.runtimeGrantFromDelegation(delegation, session)
19509
+ );
19510
+ }
19511
+ /**
19512
+ * Store additional permissions as narrow delegations to the current session
19513
+ * key. Future service invocations automatically use a stored delegation when
19514
+ * its `(space, service, path, action)` covers the request.
19515
+ */
19516
+ async grantRuntimePermissions(permissions, options) {
19517
+ if (!Array.isArray(permissions) || permissions.length === 0) {
19518
+ throw new Error("grantRuntimePermissions requires a non-empty permissions array");
19519
+ }
19520
+ const session = this.auth?.tinyCloudSession;
19521
+ if (!session) {
19522
+ throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
19523
+ }
19524
+ const sessionExpiry = extractSiweExpiration(session.siwe);
19525
+ if (sessionExpiry !== void 0) {
19526
+ const marginMs = _TinyCloudNode.SESSION_EXPIRY_SAFETY_MARGIN_MS;
19527
+ if (sessionExpiry.getTime() <= Date.now() + marginMs) {
19528
+ throw new SessionExpiredError(sessionExpiry);
19529
+ }
19530
+ }
19531
+ const expanded = this.expandPermissionEntries(permissions);
19532
+ if (this.sessionCoversPermissionEntries(session, expanded)) {
19533
+ return [];
19534
+ }
19535
+ const existingGrants = this.findRuntimeGrantsForPermissionEntries(expanded, session);
19536
+ if (existingGrants.length > 0) {
19537
+ return existingGrants.map((grant) => grant.delegation);
19538
+ }
19539
+ if (!this.signer) {
19540
+ throw new Error(
19541
+ "grantRuntimePermissions requires wallet mode with a signer or privateKey."
19542
+ );
19543
+ }
19544
+ const bySpace = /* @__PURE__ */ new Map();
19545
+ for (const entry of expanded) {
19546
+ const spaceId = this.resolvePermissionSpace(entry.space, session);
19547
+ const current = bySpace.get(spaceId) ?? [];
19548
+ current.push(entry);
19549
+ bySpace.set(spaceId, current);
19550
+ }
19551
+ const now = /* @__PURE__ */ new Date();
19552
+ const requestedExpiryMs = resolveExpiryMs(options?.expiry);
19553
+ let expiresAt = new Date(now.getTime() + requestedExpiryMs);
19554
+ if (sessionExpiry !== void 0 && sessionExpiry < expiresAt) {
19555
+ expiresAt = sessionExpiry;
19556
+ }
19557
+ const delegations = [];
19558
+ for (const [spaceId, entries] of bySpace) {
19559
+ const abilities = this.permissionsToAbilities(entries);
19560
+ const prepared = this.wasmBindings.prepareSession({
19561
+ abilities,
19562
+ address: this.wasmBindings.ensureEip55(session.address),
19563
+ chainId: session.chainId,
19564
+ domain: this.siweDomain,
19565
+ issuedAt: now.toISOString(),
19566
+ expirationTime: expiresAt.toISOString(),
19567
+ spaceId,
19568
+ jwk: session.jwk
19569
+ });
19570
+ const signature2 = await this.signer.signMessage(prepared.siwe);
19571
+ const delegatedSession = this.wasmBindings.completeSessionSetup({
19572
+ ...prepared,
19573
+ signature: signature2
19574
+ });
19575
+ const activateResult = await activateSessionWithHost2(
19576
+ this.config.host,
19577
+ delegatedSession.delegationHeader
19578
+ );
19579
+ if (!activateResult.success) {
19580
+ throw new Error(
19581
+ `Failed to activate runtime permission delegation: ${activateResult.error}`
19582
+ );
19583
+ }
19584
+ const delegation = this.runtimeDelegationFromSession(
19585
+ delegatedSession,
19586
+ entries,
19587
+ spaceId,
19588
+ session,
19589
+ expiresAt
19590
+ );
19591
+ this.runtimePermissionGrants.push({
19592
+ session: {
19593
+ delegationHeader: delegatedSession.delegationHeader,
19594
+ delegationCid: delegatedSession.delegationCid,
19595
+ spaceId,
19596
+ verificationMethod: session.verificationMethod,
19597
+ jwk: session.jwk
19598
+ },
19599
+ delegation,
19600
+ operations: this.permissionOperations(entries, spaceId),
19601
+ expiresAt
19602
+ });
19603
+ delegations.push(delegation);
19604
+ }
19605
+ return delegations;
19606
+ }
19162
19607
  /**
19163
19608
  * Get the DelegationManager for delegation CRUD operations.
19164
19609
  *
@@ -19227,6 +19672,12 @@ var _TinyCloudNode = class _TinyCloudNode {
19227
19672
  get spaceService() {
19228
19673
  return this.spaces;
19229
19674
  }
19675
+ /**
19676
+ * Get a Space object by short name or full URI.
19677
+ */
19678
+ space(nameOrUri) {
19679
+ return this.spaces.get(nameOrUri);
19680
+ }
19230
19681
  /**
19231
19682
  * Get the SharingService for creating and receiving v2 sharing links.
19232
19683
  *
@@ -19341,7 +19792,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19341
19792
  if (this._serviceContext) {
19342
19793
  const publicKV = new KVService2({ prefix: "" });
19343
19794
  const publicContext = new ServiceContext2({
19344
- invoke: this.wasmBindings.invoke,
19795
+ invoke: this.invokeWithRuntimePermissions,
19345
19796
  fetch: this._serviceContext.fetch,
19346
19797
  hosts: this._serviceContext.hosts
19347
19798
  });
@@ -19429,8 +19880,9 @@ var _TinyCloudNode = class _TinyCloudNode {
19429
19880
  * Issue a delegation using the capability-chain flow.
19430
19881
  *
19431
19882
  * When every requested permission is a subset of the current
19432
- * session's recap, the delegation is signed by the session key via
19433
- * WASM no wallet prompt. When at least one is NOT derivable, a
19883
+ * session's recap, or of one installed runtime permission delegation,
19884
+ * the delegation is signed by the session key via WASM no wallet
19885
+ * prompt. When at least one is NOT derivable, a
19434
19886
  * {@link PermissionNotInManifestError} is raised (carrying the
19435
19887
  * missing entries) so the caller can trigger an escalation flow
19436
19888
  * (e.g. `TinyCloudWeb.requestPermissions`). Passing
@@ -19480,10 +19932,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19480
19932
  "delegateTo requires a non-empty permissions array"
19481
19933
  );
19482
19934
  }
19483
- const expandedEntries = permissions.map((entry) => ({
19484
- ...entry,
19485
- actions: expandActionShortNames(entry.service, entry.actions)
19486
- }));
19935
+ const expandedEntries = this.expandPermissionEntries(permissions);
19487
19936
  const now = /* @__PURE__ */ new Date();
19488
19937
  const expiryMs = resolveExpiryMs(options?.expiry);
19489
19938
  const expirationTime = new Date(now.getTime() + expiryMs);
@@ -19510,6 +19959,23 @@ var _TinyCloudNode = class _TinyCloudNode {
19510
19959
  );
19511
19960
  const { subset, missing } = isCapabilitySubset(expandedEntries, granted);
19512
19961
  if (!subset) {
19962
+ const runtimeGrant = this.findGrantForOperations(
19963
+ this.permissionEntriesToOperations(expandedEntries, session)
19964
+ );
19965
+ if (runtimeGrant) {
19966
+ const marginMs = _TinyCloudNode.SESSION_EXPIRY_SAFETY_MARGIN_MS;
19967
+ if (runtimeGrant.expiresAt.getTime() <= Date.now() + marginMs) {
19968
+ throw new SessionExpiredError(runtimeGrant.expiresAt);
19969
+ }
19970
+ const runtimeExpiration = runtimeGrant.expiresAt < effectiveExpiration ? runtimeGrant.expiresAt : effectiveExpiration;
19971
+ const delegation2 = await this.createDelegationViaRuntimeGrant(
19972
+ did,
19973
+ expandedEntries,
19974
+ runtimeExpiration,
19975
+ runtimeGrant
19976
+ );
19977
+ return { delegation: delegation2, prompted: false };
19978
+ }
19513
19979
  throw new PermissionNotInManifestError(missing, granted);
19514
19980
  }
19515
19981
  const delegation = await this.createDelegationViaWasmPath(
@@ -19654,6 +20120,41 @@ var _TinyCloudNode = class _TinyCloudNode {
19654
20120
  host: this.config.host
19655
20121
  };
19656
20122
  }
20123
+ async createDelegationViaRuntimeGrant(did, entries, expirationTime, grant) {
20124
+ const result = this.createDelegationWrapper({
20125
+ session: grant.session,
20126
+ delegateDID: did,
20127
+ spaceId: grant.session.spaceId,
20128
+ abilities: this.permissionsToAbilities(entries),
20129
+ expirationSecs: Math.floor(expirationTime.getTime() / 1e3)
20130
+ });
20131
+ const primary = result.resources[0];
20132
+ const delegationHeader = { Authorization: result.delegation };
20133
+ const targetHost = grant.delegation.host ?? this.config.host;
20134
+ const activateResult = await activateSessionWithHost2(
20135
+ targetHost,
20136
+ delegationHeader
20137
+ );
20138
+ if (!activateResult.success) {
20139
+ throw new Error(
20140
+ `Failed to activate delegation with host: ${activateResult.error}`
20141
+ );
20142
+ }
20143
+ return {
20144
+ cid: result.cid,
20145
+ delegationHeader,
20146
+ spaceId: grant.session.spaceId,
20147
+ path: primary.path,
20148
+ actions: primary.actions,
20149
+ resources: result.resources,
20150
+ disableSubDelegation: false,
20151
+ expiry: result.expiry,
20152
+ delegateDID: did,
20153
+ ownerAddress: grant.delegation.ownerAddress,
20154
+ chainId: grant.delegation.chainId,
20155
+ host: targetHost
20156
+ };
20157
+ }
19657
20158
  resolvePermissionSpace(space, session) {
19658
20159
  if (space === void 0) {
19659
20160
  return this.wasmBindings.makeSpaceId(
@@ -19670,6 +20171,220 @@ var _TinyCloudNode = class _TinyCloudNode {
19670
20171
  }
19671
20172
  return this.wasmBindings.makeSpaceId(session.address, session.chainId, space);
19672
20173
  }
20174
+ expandPermissionEntries(permissions) {
20175
+ return expandPermissionEntriesCore(permissions);
20176
+ }
20177
+ shortServiceName(service) {
20178
+ const short = SERVICE_LONG_TO_SHORT[service];
20179
+ if (short === void 0) {
20180
+ throw new Error(
20181
+ `unknown service '${service}' \u2014 no short-form mapping`
20182
+ );
20183
+ }
20184
+ return short;
20185
+ }
20186
+ permissionsToAbilities(entries) {
20187
+ const abilities = {};
20188
+ for (const entry of entries) {
20189
+ const service = this.shortServiceName(entry.service);
20190
+ abilities[service] ?? (abilities[service] = {});
20191
+ const existing = abilities[service][entry.path] ?? [];
20192
+ const seen = new Set(existing);
20193
+ for (const action of entry.actions) {
20194
+ if (!seen.has(action)) {
20195
+ existing.push(action);
20196
+ seen.add(action);
20197
+ }
20198
+ }
20199
+ abilities[service][entry.path] = existing;
20200
+ }
20201
+ return abilities;
20202
+ }
20203
+ permissionOperations(entries, spaceId) {
20204
+ return entries.flatMap((entry) => {
20205
+ const service = this.shortServiceName(entry.service);
20206
+ return entry.actions.map((action) => ({
20207
+ spaceId,
20208
+ service,
20209
+ path: entry.path,
20210
+ action
20211
+ }));
20212
+ });
20213
+ }
20214
+ sessionCoversPermissionEntries(session, entries) {
20215
+ try {
20216
+ const granted = parseRecapCapabilities(
20217
+ (siwe) => this.wasmBindings.parseRecapFromSiwe(siwe),
20218
+ session.siwe
20219
+ );
20220
+ return isCapabilitySubset(entries, granted).subset;
20221
+ } catch {
20222
+ return false;
20223
+ }
20224
+ }
20225
+ permissionEntriesToOperations(entries, session) {
20226
+ return entries.flatMap((entry) => {
20227
+ const spaceId = this.resolvePermissionSpace(entry.space, session);
20228
+ const service = this.shortServiceName(entry.service);
20229
+ return entry.actions.map((action) => ({
20230
+ spaceId,
20231
+ service,
20232
+ path: entry.path,
20233
+ action
20234
+ }));
20235
+ });
20236
+ }
20237
+ findRuntimeGrantsForPermissionEntries(entries, session) {
20238
+ const grants = [];
20239
+ const operations = this.permissionEntriesToOperations(entries, session);
20240
+ if (operations.length === 0) {
20241
+ return grants;
20242
+ }
20243
+ for (const operation of operations) {
20244
+ const grant = this.findGrantForOperation(operation);
20245
+ if (!grant) {
20246
+ return [];
20247
+ }
20248
+ if (!grants.includes(grant)) {
20249
+ grants.push(grant);
20250
+ }
20251
+ }
20252
+ return grants;
20253
+ }
20254
+ runtimeDelegationFromSession(delegatedSession, entries, spaceId, session, expiresAt) {
20255
+ const resources = this.delegatedResourcesForEntries(entries, spaceId);
20256
+ const primary = resources[0];
20257
+ return {
20258
+ cid: delegatedSession.delegationCid,
20259
+ delegationHeader: delegatedSession.delegationHeader,
20260
+ spaceId,
20261
+ path: primary.path,
20262
+ actions: primary.actions,
20263
+ resources,
20264
+ disableSubDelegation: false,
20265
+ expiry: expiresAt,
20266
+ delegateDID: session.verificationMethod,
20267
+ ownerAddress: session.address,
20268
+ chainId: session.chainId,
20269
+ host: this.config.host
20270
+ };
20271
+ }
20272
+ runtimeGrantFromDelegation(delegation, session) {
20273
+ const operations = this.operationsFromDelegation(delegation);
20274
+ return {
20275
+ session: {
20276
+ delegationHeader: delegation.delegationHeader,
20277
+ delegationCid: delegation.cid,
20278
+ spaceId: delegation.spaceId,
20279
+ verificationMethod: session.verificationMethod,
20280
+ jwk: session.jwk
20281
+ },
20282
+ delegation,
20283
+ operations,
20284
+ expiresAt: delegation.expiry
20285
+ };
20286
+ }
20287
+ delegatedResourcesForEntries(entries, spaceId) {
20288
+ return entries.map((entry) => ({
20289
+ service: this.shortServiceName(entry.service),
20290
+ space: spaceId,
20291
+ path: entry.path,
20292
+ actions: [...entry.actions]
20293
+ }));
20294
+ }
20295
+ operationsFromDelegation(delegation) {
20296
+ const resources = delegation.resources !== void 0 && delegation.resources.length > 0 ? delegation.resources : this.flatDelegationResources(delegation);
20297
+ return resources.flatMap(
20298
+ (resource) => resource.actions.map((action) => ({
20299
+ spaceId: resource.space,
20300
+ service: this.invocationServiceName(resource.service),
20301
+ path: resource.path,
20302
+ action
20303
+ }))
20304
+ );
20305
+ }
20306
+ flatDelegationResources(delegation) {
20307
+ const byService = /* @__PURE__ */ new Map();
20308
+ for (const action of delegation.actions) {
20309
+ const service = this.shortServiceName(action.split("/")[0]);
20310
+ const actions = byService.get(service) ?? [];
20311
+ actions.push(action);
20312
+ byService.set(service, actions);
20313
+ }
20314
+ return [...byService.entries()].map(([service, actions]) => ({
20315
+ service,
20316
+ space: delegation.spaceId,
20317
+ path: delegation.path,
20318
+ actions
20319
+ }));
20320
+ }
20321
+ selectInvocationSession(fallback, service, path, action) {
20322
+ const grant = this.findGrantForOperation({
20323
+ spaceId: fallback.spaceId,
20324
+ service: this.invocationServiceName(service),
20325
+ path,
20326
+ action
20327
+ });
20328
+ return grant?.session ?? fallback;
20329
+ }
20330
+ findGrantForOperations(operations) {
20331
+ if (operations.length === 0) {
20332
+ return void 0;
20333
+ }
20334
+ this.pruneExpiredRuntimePermissionGrants();
20335
+ return this.runtimePermissionGrants.find((grant) => {
20336
+ return operations.every(
20337
+ (operation) => grant.operations.some(
20338
+ (granted) => this.operationCovers(granted, operation)
20339
+ )
20340
+ );
20341
+ });
20342
+ }
20343
+ findGrantForOperation(operation) {
20344
+ return this.findGrantForOperations([operation]);
20345
+ }
20346
+ pruneExpiredRuntimePermissionGrants() {
20347
+ const now = Date.now();
20348
+ this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
20349
+ (grant) => grant.expiresAt.getTime() > now
20350
+ );
20351
+ }
20352
+ operationCovers(granted, requested) {
20353
+ return granted.spaceId === requested.spaceId && granted.service === requested.service && this.actionContains(granted.action, requested.action) && this.pathContains(granted.path, requested.path);
20354
+ }
20355
+ actionContains(grantedAction, requestedAction) {
20356
+ if (grantedAction === requestedAction) {
20357
+ return true;
20358
+ }
20359
+ if (grantedAction.endsWith("/*")) {
20360
+ const prefix = grantedAction.slice(0, -2);
20361
+ return requestedAction.startsWith(`${prefix}/`);
20362
+ }
20363
+ return false;
20364
+ }
20365
+ invocationServiceName(service) {
20366
+ return service.startsWith("tinycloud.") ? this.shortServiceName(service) : service;
20367
+ }
20368
+ pathContains(grantedPath, requestedPath) {
20369
+ if (grantedPath === "" || grantedPath === "/") {
20370
+ return true;
20371
+ }
20372
+ if (grantedPath.endsWith("/**")) {
20373
+ return requestedPath.startsWith(grantedPath.slice(0, -3));
20374
+ }
20375
+ if (grantedPath.endsWith("/*")) {
20376
+ const prefix = grantedPath.slice(0, -2);
20377
+ if (!requestedPath.startsWith(prefix)) {
20378
+ return false;
20379
+ }
20380
+ const remainder = requestedPath.slice(prefix.length);
20381
+ return !remainder.includes("/") || remainder === "/";
20382
+ }
20383
+ if (grantedPath.endsWith("/")) {
20384
+ return requestedPath.startsWith(grantedPath);
20385
+ }
20386
+ return grantedPath === requestedPath;
20387
+ }
19673
20388
  /**
19674
20389
  * Issue a delegation via the legacy wallet-signed SIWE path for a single
19675
20390
  * {@link PermissionEntry}. Shares the implementation with the public
@@ -20191,15 +20906,18 @@ import {
20191
20906
  ACCOUNT_REGISTRY_SPACE as ACCOUNT_REGISTRY_SPACE2,
20192
20907
  DEFAULT_MANIFEST_SPACE as DEFAULT_MANIFEST_SPACE2,
20193
20908
  DEFAULT_MANIFEST_VERSION,
20909
+ VAULT_PERMISSION_SERVICE,
20194
20910
  PermissionNotInManifestError as PermissionNotInManifestError2,
20195
20911
  SessionExpiredError as SessionExpiredError2,
20196
20912
  ManifestValidationError,
20197
20913
  composeManifestRequest as composeManifestRequest2,
20198
- resolveManifest,
20914
+ resolveManifest as resolveManifest2,
20199
20915
  validateManifest,
20200
20916
  loadManifest,
20201
20917
  isCapabilitySubset as isCapabilitySubset2,
20202
- expandActionShortNames as expandActionShortNames2,
20918
+ expandActionShortNames,
20919
+ expandPermissionEntries,
20920
+ expandPermissionEntry,
20203
20921
  parseExpiry as parseExpiry2,
20204
20922
  resourceCapabilitiesToSpaceAbilitiesMap as resourceCapabilitiesToSpaceAbilitiesMap2
20205
20923
  } from "@tinycloud/sdk-core";
@@ -20232,7 +20950,11 @@ import {
20232
20950
  DataVaultService as DataVaultService2,
20233
20951
  VaultHeaders,
20234
20952
  VaultPublicSpaceKVActions,
20235
- createVaultCrypto as createVaultCrypto2
20953
+ createVaultCrypto as createVaultCrypto2,
20954
+ SecretsService as SecretsService2,
20955
+ SECRET_NAME_RE,
20956
+ canonicalizeSecretScope,
20957
+ resolveSecretPath as resolveSecretPath2
20236
20958
  } from "@tinycloud/sdk-core";
20237
20959
  import { HooksService as HooksService3 } from "@tinycloud/sdk-core";
20238
20960
  import {
@@ -20289,8 +21011,10 @@ export {
20289
21011
  PrefixedKVService,
20290
21012
  PrivateKeySigner,
20291
21013
  ProtocolMismatchError,
21014
+ SECRET_NAME_RE,
20292
21015
  SQLAction,
20293
21016
  SQLService3 as SQLService,
21017
+ SecretsService2 as SecretsService,
20294
21018
  ServiceContext3 as ServiceContext,
20295
21019
  SessionExpiredError2 as SessionExpiredError,
20296
21020
  SharingService2 as SharingService,
@@ -20301,11 +21025,13 @@ export {
20301
21025
  TinyCloud2 as TinyCloud,
20302
21026
  TinyCloudNode,
20303
21027
  UnsupportedFeatureError2 as UnsupportedFeatureError,
21028
+ VAULT_PERMISSION_SERVICE,
20304
21029
  VaultHeaders,
20305
21030
  VaultPublicSpaceKVActions,
20306
21031
  VersionCheckError,
20307
21032
  WasmKeyProvider,
20308
21033
  buildSpaceUri,
21034
+ canonicalizeSecretScope,
20309
21035
  checkNodeInfo2 as checkNodeInfo,
20310
21036
  composeManifestRequest2 as composeManifestRequest,
20311
21037
  createCapabilityKeyRegistry,
@@ -20316,13 +21042,16 @@ export {
20316
21042
  defaultSignStrategy,
20317
21043
  defaultSpaceCreationHandler,
20318
21044
  deserializeDelegation,
20319
- expandActionShortNames2 as expandActionShortNames,
21045
+ expandActionShortNames,
21046
+ expandPermissionEntries,
21047
+ expandPermissionEntry,
20320
21048
  isCapabilitySubset2 as isCapabilitySubset,
20321
21049
  loadManifest,
20322
21050
  makePublicSpaceId2 as makePublicSpaceId,
20323
21051
  parseExpiry2 as parseExpiry,
20324
21052
  parseSpaceUri,
20325
- resolveManifest,
21053
+ resolveManifest2 as resolveManifest,
21054
+ resolveSecretPath2 as resolveSecretPath,
20326
21055
  resourceCapabilitiesToSpaceAbilitiesMap2 as resourceCapabilitiesToSpaceAbilitiesMap,
20327
21056
  serializeDelegation,
20328
21057
  validateManifest