@tinycloud/node-sdk 2.2.0-beta.9 → 2.2.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
@@ -17031,6 +17031,7 @@ var require_utils2 = __commonJS({
17031
17031
  import {
17032
17032
  invoke,
17033
17033
  invokeAny,
17034
+ computeCid,
17034
17035
  prepareSession,
17035
17036
  completeSessionSetup,
17036
17037
  ensureEip55,
@@ -17054,6 +17055,7 @@ var _NodeWasmBindings = class _NodeWasmBindings {
17054
17055
  constructor() {
17055
17056
  this.invoke = invoke;
17056
17057
  this.invokeAny = invokeAny;
17058
+ this.computeCid = computeCid;
17057
17059
  this.prepareSession = prepareSession;
17058
17060
  this.completeSessionSetup = completeSessionSetup;
17059
17061
  this.ensureEip55 = ensureEip55;
@@ -17159,6 +17161,7 @@ import {
17159
17161
  DuckDbService as DuckDbService2,
17160
17162
  HooksService as HooksService2,
17161
17163
  DataVaultService,
17164
+ EncryptionService,
17162
17165
  SecretsService,
17163
17166
  createVaultCrypto,
17164
17167
  ServiceContext as ServiceContext2,
@@ -17170,12 +17173,17 @@ import {
17170
17173
  UnsupportedFeatureError,
17171
17174
  makePublicSpaceId,
17172
17175
  ACCOUNT_REGISTRY_SPACE,
17176
+ ENCRYPTION_PERMISSION_SERVICE as ENCRYPTION_PERMISSION_SERVICE2,
17173
17177
  PermissionNotInManifestError,
17174
17178
  SessionExpiredError,
17175
17179
  expandPermissionEntries as expandPermissionEntriesCore,
17176
17180
  isCapabilitySubset,
17177
17181
  parseRecapCapabilities,
17178
- SERVICE_LONG_TO_SHORT
17182
+ SERVICE_LONG_TO_SHORT,
17183
+ EXPIRY as EXPIRY3,
17184
+ canonicalHashHex,
17185
+ canonicalizeEncryptionJson,
17186
+ verifyDidKeyEd25519Signature
17179
17187
  } from "@tinycloud/sdk-core";
17180
17188
 
17181
17189
  // src/authorization/NodeUserAuthorization.ts
@@ -17186,10 +17194,12 @@ import {
17186
17194
  checkNodeInfo,
17187
17195
  AutoApproveSpaceCreationHandler,
17188
17196
  DEFAULT_MANIFEST_SPACE,
17197
+ ENCRYPTION_PERMISSION_SERVICE,
17189
17198
  composeManifestRequest,
17190
17199
  resourceCapabilitiesToAbilitiesMap,
17191
17200
  resourceCapabilitiesToSpaceAbilitiesMap,
17192
- resolveTinyCloudHosts
17201
+ resolveTinyCloudHosts,
17202
+ EXPIRY
17193
17203
  } from "@tinycloud/sdk-core";
17194
17204
 
17195
17205
  // src/authorization/strategies.ts
@@ -17320,7 +17330,7 @@ var NodeUserAuthorization = class {
17320
17330
  ]
17321
17331
  }
17322
17332
  };
17323
- this.sessionExpirationMs = config.sessionExpirationMs ?? 60 * 60 * 1e3;
17333
+ this.sessionExpirationMs = config.sessionExpirationMs ?? EXPIRY.SESSION_MS;
17324
17334
  this.autoCreateSpace = config.autoCreateSpace ?? false;
17325
17335
  this.spaceCreationHandler = config.spaceCreationHandler;
17326
17336
  this.tinycloudHosts = config.tinycloudHosts;
@@ -17373,6 +17383,23 @@ var NodeUserAuthorization = class {
17373
17383
  get tinyCloudSession() {
17374
17384
  return this._tinyCloudSession;
17375
17385
  }
17386
+ /**
17387
+ * Rehydrate the auth-layer session from previously-persisted delegation
17388
+ * data. Used by {@link TinyCloudNode.restoreSession} so that downstream
17389
+ * surfaces that read from `tinyCloudSession` (notably
17390
+ * `grantRuntimePermissions`, which extracts the SIWE expiry from it) work
17391
+ * without re-running the full sign-in flow.
17392
+ *
17393
+ * Caller must supply the same fields that `signIn` would have written —
17394
+ * `siwe` is the load-bearing one because `extractSiweExpiration` returns
17395
+ * undefined for missing SIWEs and the SDK then treats the session as
17396
+ * expired-at-epoch-zero.
17397
+ */
17398
+ setRestoredTinyCloudSession(session) {
17399
+ this._tinyCloudSession = session;
17400
+ this._address = session.address;
17401
+ this._chainId = session.chainId;
17402
+ }
17376
17403
  async resolveTinyCloudHostsForSignIn(address, chainId) {
17377
17404
  if (this.tinycloudHosts && this.tinycloudHosts.length > 0) {
17378
17405
  return;
@@ -17443,21 +17470,64 @@ var NodeUserAuthorization = class {
17443
17470
  }
17444
17471
  return this.wasm.makeSpaceId(address, chainId, space);
17445
17472
  }
17473
+ defaultEncryptionNetworkId(address, chainId) {
17474
+ return `urn:tinycloud:encryption:did:pkh:eip155:${chainId}:${address}:default`;
17475
+ }
17446
17476
  resolveSignInCapabilities(address, chainId) {
17447
17477
  const request = this.getCapabilityRequest();
17448
17478
  if (request === void 0) {
17479
+ const defaultNetworkId = this.defaultEncryptionNetworkId(address, chainId);
17480
+ const secretsSpaceId = this.wasm.makeSpaceId(address, chainId, "secrets");
17449
17481
  return {
17450
17482
  abilities: this.defaultActions,
17451
- spaceId: this.wasm.makeSpaceId(address, chainId, this.spacePrefix)
17483
+ spaceId: this.wasm.makeSpaceId(address, chainId, this.spacePrefix),
17484
+ spaceAbilities: {
17485
+ [secretsSpaceId]: {
17486
+ kv: {
17487
+ "vault/secrets/": [
17488
+ "tinycloud.kv/get",
17489
+ "tinycloud.kv/put",
17490
+ "tinycloud.kv/del",
17491
+ "tinycloud.kv/list",
17492
+ "tinycloud.kv/metadata"
17493
+ ]
17494
+ }
17495
+ }
17496
+ },
17497
+ rawAbilities: {
17498
+ [defaultNetworkId]: [
17499
+ "tinycloud.encryption/decrypt",
17500
+ "tinycloud.encryption/network.create"
17501
+ ]
17502
+ }
17452
17503
  };
17453
17504
  }
17454
- const primarySpaceName = request.resources.find((entry) => entry.space !== "account")?.space ?? DEFAULT_MANIFEST_SPACE;
17505
+ const rawAbilities = {};
17506
+ const spaceResources = request.resources.filter((entry) => {
17507
+ if (entry.service !== ENCRYPTION_PERMISSION_SERVICE) {
17508
+ return true;
17509
+ }
17510
+ const existing = rawAbilities[entry.path];
17511
+ if (existing === void 0) {
17512
+ rawAbilities[entry.path] = [...entry.actions];
17513
+ } else {
17514
+ const seen = new Set(existing);
17515
+ for (const action of entry.actions) {
17516
+ if (!seen.has(action)) {
17517
+ existing.push(action);
17518
+ seen.add(action);
17519
+ }
17520
+ }
17521
+ }
17522
+ return false;
17523
+ });
17524
+ const primarySpaceName = spaceResources.find((entry) => entry.space !== "account")?.space ?? DEFAULT_MANIFEST_SPACE;
17455
17525
  const primarySpaceId = this.resolveSpaceName(
17456
17526
  primarySpaceName,
17457
17527
  address,
17458
17528
  chainId
17459
17529
  );
17460
- const bySpace = resourceCapabilitiesToSpaceAbilitiesMap(request.resources);
17530
+ const bySpace = resourceCapabilitiesToSpaceAbilitiesMap(spaceResources);
17461
17531
  const spaceAbilities = {};
17462
17532
  for (const [space, abilities] of Object.entries(bySpace)) {
17463
17533
  spaceAbilities[this.resolveSpaceName(space, address, chainId)] = abilities;
@@ -17465,7 +17535,8 @@ var NodeUserAuthorization = class {
17465
17535
  return {
17466
17536
  abilities: spaceAbilities[primarySpaceId] ?? resourceCapabilitiesToAbilitiesMap([]),
17467
17537
  spaceId: primarySpaceId,
17468
- spaceAbilities
17538
+ spaceAbilities,
17539
+ rawAbilities: Object.keys(rawAbilities).length > 0 ? rawAbilities : void 0
17469
17540
  };
17470
17541
  }
17471
17542
  /**
@@ -17690,6 +17761,7 @@ var NodeUserAuthorization = class {
17690
17761
  const prepared = this.wasm.prepareSession({
17691
17762
  abilities: capabilityPlan.abilities,
17692
17763
  ...capabilityPlan.spaceAbilities !== void 0 ? { spaceAbilities: capabilityPlan.spaceAbilities } : {},
17764
+ ...capabilityPlan.rawAbilities !== void 0 ? { rawAbilities: capabilityPlan.rawAbilities } : {},
17693
17765
  address,
17694
17766
  chainId,
17695
17767
  domain: this.domain,
@@ -17835,6 +17907,7 @@ var NodeUserAuthorization = class {
17835
17907
  const prepared = this.wasm.prepareSession({
17836
17908
  abilities: capabilityPlan.abilities,
17837
17909
  ...capabilityPlan.spaceAbilities !== void 0 ? { spaceAbilities: capabilityPlan.spaceAbilities } : {},
17910
+ ...capabilityPlan.rawAbilities !== void 0 ? { rawAbilities: capabilityPlan.rawAbilities } : {},
17838
17911
  address,
17839
17912
  chainId,
17840
17913
  domain: this.domain,
@@ -18182,7 +18255,8 @@ function createWasmKeyProvider(sessionManager) {
18182
18255
  // src/delegateToHelpers.ts
18183
18256
  import {
18184
18257
  parseExpiry,
18185
- SiweMessage
18258
+ SiweMessage,
18259
+ EXPIRY as EXPIRY2
18186
18260
  } from "@tinycloud/sdk-core";
18187
18261
  function legacyParamsToPermissionEntries(actions, path, spaceIdOverride) {
18188
18262
  const byService = /* @__PURE__ */ new Map();
@@ -18214,9 +18288,10 @@ function legacyParamsToPermissionEntries(actions, path, spaceIdOverride) {
18214
18288
  }
18215
18289
  return entries;
18216
18290
  }
18291
+ var DEFAULT_DELEGATION_EXPIRY_MS = EXPIRY2.SESSION_MS;
18217
18292
  function resolveExpiryMs(expiry) {
18218
18293
  if (expiry === void 0) {
18219
- return 60 * 60 * 1e3;
18294
+ return DEFAULT_DELEGATION_EXPIRY_MS;
18220
18295
  }
18221
18296
  if (typeof expiry === "number") {
18222
18297
  if (!Number.isFinite(expiry) || expiry <= 0) {
@@ -18245,10 +18320,11 @@ function extractSiweExpiration(siwe) {
18245
18320
  // src/NodeSecretsService.ts
18246
18321
  import {
18247
18322
  ErrorCodes,
18323
+ resolveSecretListPrefix,
18324
+ resolveSecretPath,
18325
+ expandPermissionEntries,
18248
18326
  resolveManifest
18249
18327
  } from "@tinycloud/sdk-core";
18250
- var SECRET_NAME_RE = /^[A-Z][A-Z0-9_]*$/;
18251
- var SECRET_PREFIX = "secrets/";
18252
18328
  var SECRETS_SPACE = "secrets";
18253
18329
  function ok() {
18254
18330
  return { ok: true, data: void 0 };
@@ -18265,27 +18341,39 @@ function secretsError(code, message, cause) {
18265
18341
  };
18266
18342
  }
18267
18343
  function displayActionUrn(action) {
18268
- return action === "put" ? "tinycloud.vault/write" : "tinycloud.vault/delete";
18344
+ switch (action) {
18345
+ case "get":
18346
+ return "tinycloud.kv/get";
18347
+ case "put":
18348
+ return "tinycloud.kv/put";
18349
+ case "del":
18350
+ return "tinycloud.kv/del";
18351
+ case "list":
18352
+ return "tinycloud.kv/list";
18353
+ }
18269
18354
  }
18270
- function kvActionUrn(action) {
18271
- return `tinycloud.kv/${action}`;
18355
+ function secretActionName(action) {
18356
+ return action;
18272
18357
  }
18273
- function secretPermissionEntries(name, action) {
18274
- return [
18275
- {
18276
- service: "tinycloud.vault",
18277
- space: SECRETS_SPACE,
18278
- path: `${SECRET_PREFIX}${name}`,
18279
- actions: [action === "put" ? "write" : "delete"],
18358
+ function secretPermissionEntries(name, options, action, encryptionNetworkId) {
18359
+ const entries = [];
18360
+ const path = action === "list" ? resolveSecretListPrefix(options) : resolveSecretPath(name, options).permissionPaths.vault;
18361
+ entries.push({
18362
+ service: "tinycloud.kv",
18363
+ space: SECRETS_SPACE,
18364
+ path,
18365
+ actions: [secretActionName(action)],
18366
+ skipPrefix: true
18367
+ });
18368
+ if (action === "get" && encryptionNetworkId !== void 0) {
18369
+ entries.push({
18370
+ service: "tinycloud.encryption",
18371
+ path: encryptionNetworkId,
18372
+ actions: ["decrypt"],
18280
18373
  skipPrefix: true
18281
- }
18282
- ];
18283
- }
18284
- function secretResourcePath(base2, name) {
18285
- return `${base2}/${SECRET_PREFIX}${name}`;
18286
- }
18287
- function isSecretsSpace(space) {
18288
- return space === SECRETS_SPACE || space.endsWith(`:${SECRETS_SPACE}`);
18374
+ });
18375
+ }
18376
+ return entries;
18289
18377
  }
18290
18378
  var NodeSecretsService = class {
18291
18379
  constructor(config) {
@@ -18313,48 +18401,62 @@ var NodeSecretsService = class {
18313
18401
  this.shouldRestoreUnlock = false;
18314
18402
  this.service.lock();
18315
18403
  }
18316
- get(name) {
18317
- return this.service.get(name);
18404
+ async get(name, options) {
18405
+ const permission = await this.ensurePermission(name, options, "get");
18406
+ if (!permission.ok) return permission;
18407
+ return options === void 0 ? this.service.get(name) : this.service.get(name, options);
18318
18408
  }
18319
- async put(name, value) {
18320
- const permission = await this.ensureMutationPermission(name, "put");
18409
+ async put(name, value, options) {
18410
+ const permission = await this.ensurePermission(name, options, "put");
18321
18411
  if (!permission.ok) return permission;
18322
- return this.service.put(name, value);
18412
+ return options === void 0 ? this.service.put(name, value) : this.service.put(name, value, options);
18323
18413
  }
18324
- async delete(name) {
18325
- const permission = await this.ensureMutationPermission(name, "del");
18414
+ async delete(name, options) {
18415
+ const permission = await this.ensurePermission(name, options, "del");
18326
18416
  if (!permission.ok) return permission;
18327
- return this.service.delete(name);
18417
+ return options === void 0 ? this.service.delete(name) : this.service.delete(name, options);
18328
18418
  }
18329
- list() {
18330
- return this.service.list();
18419
+ async list(options) {
18420
+ const permission = await this.ensurePermission("", options, "list");
18421
+ if (!permission.ok) return permission;
18422
+ return options === void 0 ? this.service.list() : this.service.list(options);
18331
18423
  }
18332
18424
  get service() {
18333
18425
  return this.config.getService();
18334
18426
  }
18335
- async ensureMutationPermission(name, action) {
18336
- if (!SECRET_NAME_RE.test(name)) {
18427
+ async ensurePermission(name, options, action) {
18428
+ const target = name || "secrets";
18429
+ let permissionEntries;
18430
+ try {
18431
+ permissionEntries = secretPermissionEntries(
18432
+ name,
18433
+ options,
18434
+ action,
18435
+ action === "get" ? this.config.getEncryptionNetworkId?.() : void 0
18436
+ );
18437
+ } catch (error) {
18337
18438
  return secretsError(
18338
18439
  ErrorCodes.INVALID_INPUT,
18339
- `Invalid secret name ${JSON.stringify(name)}. Secret names must match ${SECRET_NAME_RE.source}.`
18440
+ error instanceof Error ? error.message : String(error),
18441
+ error instanceof Error ? error : void 0
18340
18442
  );
18341
18443
  }
18342
- if (this.hasMutationPermission(name, action)) {
18444
+ if (this.hasPermission(permissionEntries)) {
18343
18445
  return ok();
18344
18446
  }
18345
18447
  if (!this.config.canEscalate()) {
18346
18448
  return secretsError(
18347
18449
  ErrorCodes.PERMISSION_DENIED,
18348
- `Cannot autosign ${displayActionUrn(action)} for ${name}; TinyCloudNode needs wallet mode with a signer or privateKey.`
18450
+ `Cannot autosign ${displayActionUrn(action)} for ${target}; TinyCloudNode needs wallet mode with a signer or privateKey.`
18349
18451
  );
18350
18452
  }
18351
18453
  try {
18352
- await this.config.grantPermissions(secretPermissionEntries(name, action));
18454
+ await this.config.grantPermissions(permissionEntries);
18353
18455
  return this.restoreUnlockAfterEscalation();
18354
18456
  } catch (error) {
18355
18457
  return secretsError(
18356
18458
  ErrorCodes.PERMISSION_DENIED,
18357
- error instanceof Error ? error.message : `Autosign escalation for ${displayActionUrn(action)} on ${name} failed.`,
18459
+ error instanceof Error ? error.message : `Autosign escalation for ${displayActionUrn(action)} on ${target} failed.`,
18358
18460
  error instanceof Error ? error : void 0
18359
18461
  );
18360
18462
  }
@@ -18365,26 +18467,117 @@ var NodeSecretsService = class {
18365
18467
  }
18366
18468
  return this.service.unlock(this.unlockSigner);
18367
18469
  }
18368
- hasMutationPermission(name, action) {
18470
+ hasPermission(permissionEntries) {
18471
+ if (this.config.hasPermissions?.(permissionEntries)) {
18472
+ return true;
18473
+ }
18369
18474
  const manifest = this.config.getManifest();
18370
18475
  if (manifest === void 0) {
18371
18476
  return false;
18372
18477
  }
18373
18478
  const manifests = Array.isArray(manifest) ? manifest : [manifest];
18374
- const requiredAction = kvActionUrn(action);
18375
- return manifests.some((entry) => {
18376
- const resolved = resolveManifest(entry);
18377
- return ["keys", "vault"].every(
18378
- (base2) => resolved.resources.some(
18379
- (resource) => resource.service === "tinycloud.kv" && isSecretsSpace(resource.space) && resource.path === secretResourcePath(base2, name) && resource.actions.includes(requiredAction)
18380
- )
18381
- );
18382
- });
18479
+ const requestedEntries = expandPermissionEntries(permissionEntries);
18480
+ return requestedEntries.every(
18481
+ (entry) => manifests.some((candidate) => {
18482
+ const resolved = resolveManifest(candidate);
18483
+ return resolved.resources.some(
18484
+ (resource) => resource.service === entry.service && resource.space === entry.space && resource.path === entry.path && entry.actions.every((action) => resource.actions.includes(action))
18485
+ );
18486
+ })
18487
+ );
18383
18488
  }
18384
18489
  };
18385
18490
 
18386
18491
  // src/TinyCloudNode.ts
18387
18492
  var DEFAULT_HOST = "https://node.tinycloud.xyz";
18493
+ var DEFAULT_ENCRYPTION_NETWORK_NAME = "default";
18494
+ var NETWORK_CREATE_ACTION = "tinycloud.encryption/network.create";
18495
+ var DECRYPT_ACTION = "tinycloud.encryption/decrypt";
18496
+ var NETWORK_ADMIN_TYPE = "tinycloud.encryption.network-admin/v1";
18497
+ var DEFAULT_SESSION_EXPIRATION_MS = EXPIRY3.SESSION_MS;
18498
+ function base64UrlEncode(bytes) {
18499
+ const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
18500
+ let output = "";
18501
+ for (let i = 0; i < bytes.length; i += 3) {
18502
+ const a = bytes[i];
18503
+ const b = bytes[i + 1];
18504
+ const c = bytes[i + 2];
18505
+ const triplet = a << 16 | (b ?? 0) << 8 | (c ?? 0);
18506
+ output += alphabet[triplet >> 18 & 63];
18507
+ output += alphabet[triplet >> 12 & 63];
18508
+ if (i + 1 < bytes.length) output += alphabet[triplet >> 6 & 63];
18509
+ if (i + 2 < bytes.length) output += alphabet[triplet & 63];
18510
+ }
18511
+ return output;
18512
+ }
18513
+ function base64UrlDecode(value) {
18514
+ const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
18515
+ const bytes = [];
18516
+ let buffer = 0;
18517
+ let bits = 0;
18518
+ for (const char of value) {
18519
+ const index = alphabet.indexOf(char);
18520
+ if (index < 0) {
18521
+ throw new Error("invalid base64url input");
18522
+ }
18523
+ buffer = buffer << 6 | index;
18524
+ bits += 6;
18525
+ if (bits >= 8) {
18526
+ bits -= 8;
18527
+ bytes.push(buffer >> bits & 255);
18528
+ }
18529
+ }
18530
+ return new Uint8Array(bytes);
18531
+ }
18532
+ async function signJwtInputWithJwk(signingInput, jwk) {
18533
+ const bytes = new TextEncoder().encode(signingInput);
18534
+ try {
18535
+ const subtle = globalThis.crypto?.subtle;
18536
+ if (!subtle) {
18537
+ throw new Error("WebCrypto subtle API is unavailable");
18538
+ }
18539
+ const key2 = await subtle.importKey(
18540
+ "jwk",
18541
+ jwk,
18542
+ { name: "Ed25519" },
18543
+ false,
18544
+ ["sign"]
18545
+ );
18546
+ return new Uint8Array(await subtle.sign({ name: "Ed25519" }, key2, bytes));
18547
+ } catch {
18548
+ const nodeCrypto = await import("crypto");
18549
+ const key2 = nodeCrypto.createPrivateKey({ key: jwk, format: "jwk" });
18550
+ return new Uint8Array(nodeCrypto.sign(null, Buffer.from(bytes), key2));
18551
+ }
18552
+ }
18553
+ async function rewriteInvocationAudience(authorization, audience, jwk) {
18554
+ const [headerPart, payloadPart] = authorization.split(".");
18555
+ if (!headerPart || !payloadPart) {
18556
+ throw new Error("invalid invocation authorization");
18557
+ }
18558
+ const header = JSON.parse(new TextDecoder().decode(base64UrlDecode(headerPart)));
18559
+ const payload = JSON.parse(new TextDecoder().decode(base64UrlDecode(payloadPart)));
18560
+ payload.aud = audience;
18561
+ const signingInput = `${base64UrlEncode(
18562
+ new TextEncoder().encode(JSON.stringify(header))
18563
+ )}.${base64UrlEncode(new TextEncoder().encode(JSON.stringify(payload)))}`;
18564
+ const signature2 = await signJwtInputWithJwk(signingInput, jwk);
18565
+ return `${signingInput}.${base64UrlEncode(signature2)}`;
18566
+ }
18567
+ function authorizationHeader(headers) {
18568
+ if (Array.isArray(headers)) {
18569
+ const entry = headers.find(([name]) => name.toLowerCase() === "authorization");
18570
+ if (!entry) {
18571
+ throw new Error("network invocation did not include an Authorization header");
18572
+ }
18573
+ return entry[1];
18574
+ }
18575
+ const value = headers.Authorization ?? headers.authorization;
18576
+ if (!value) {
18577
+ throw new Error("network invocation did not include an Authorization header");
18578
+ }
18579
+ return value;
18580
+ }
18388
18581
  var _TinyCloudNode = class _TinyCloudNode {
18389
18582
  /**
18390
18583
  * Create a new TinyCloudNode instance.
@@ -18429,12 +18622,10 @@ var _TinyCloudNode = class _TinyCloudNode {
18429
18622
  throw new Error("WASM binding does not support invokeAny");
18430
18623
  }
18431
18624
  const grant = this.findGrantForOperations(
18432
- entries.map((entry) => ({
18433
- spaceId: entry.spaceId,
18434
- service: this.invocationServiceName(entry.service),
18435
- path: entry.path,
18436
- action: entry.action
18437
- }))
18625
+ entries.flatMap((entry) => {
18626
+ const operation = this.operationFromInvokeAnyEntry(entry);
18627
+ return operation ? [operation] : [];
18628
+ })
18438
18629
  );
18439
18630
  return this.wasmBindings.invokeAny(grant?.session ?? session, entries, facts);
18440
18631
  };
@@ -18527,7 +18718,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18527
18718
  sessionStorage: config.sessionStorage ?? new MemorySessionStorage(),
18528
18719
  domain: this.siweDomain,
18529
18720
  spacePrefix: config.prefix,
18530
- sessionExpirationMs: config.sessionExpirationMs ?? 60 * 60 * 1e3,
18721
+ sessionExpirationMs: config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS,
18531
18722
  tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
18532
18723
  tinycloudRegistryUrl: config.tinycloudRegistryUrl,
18533
18724
  tinycloudFallbackHosts: config.tinycloudFallbackHosts,
@@ -18662,6 +18853,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18662
18853
  this._duckdb = void 0;
18663
18854
  this._hooks = void 0;
18664
18855
  this._vault = void 0;
18856
+ this._encryption = void 0;
18665
18857
  this._baseSecrets = void 0;
18666
18858
  this._secrets = void 0;
18667
18859
  this._spaceService = void 0;
@@ -18670,6 +18862,9 @@ var _TinyCloudNode = class _TinyCloudNode {
18670
18862
  await this.tc.signIn(options);
18671
18863
  this.syncResolvedHostFromAuth();
18672
18864
  this.initializeServices();
18865
+ if (this.config.manifest === void 0 && this.config.capabilityRequest === void 0) {
18866
+ await this.ensureOwnedSpaceHosted(this.ownedSpaceId("secrets"));
18867
+ }
18673
18868
  await this.writeManifestRegistryRecords();
18674
18869
  this.notificationHandler.success("Successfully signed in");
18675
18870
  }
@@ -18752,6 +18947,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18752
18947
  this._duckdb = void 0;
18753
18948
  this._hooks = void 0;
18754
18949
  this._vault = void 0;
18950
+ this._encryption = void 0;
18755
18951
  this._baseSecrets = void 0;
18756
18952
  this._secrets = void 0;
18757
18953
  this._spaceService = void 0;
@@ -18793,6 +18989,33 @@ var _TinyCloudNode = class _TinyCloudNode {
18793
18989
  this._vault.initialize(this._serviceContext);
18794
18990
  this._serviceContext.registerService("vault", this._vault);
18795
18991
  this.initializeV2Services(serviceSession);
18992
+ if (sessionData.siwe && sessionData.address && sessionData.chainId) {
18993
+ const tcSession = {
18994
+ address: sessionData.address,
18995
+ chainId: sessionData.chainId,
18996
+ sessionKey: JSON.stringify(sessionData.jwk),
18997
+ spaceId: sessionData.spaceId,
18998
+ delegationCid: sessionData.delegationCid,
18999
+ delegationHeader: sessionData.delegationHeader,
19000
+ verificationMethod: sessionData.verificationMethod,
19001
+ jwk: sessionData.jwk,
19002
+ siwe: sessionData.siwe,
19003
+ signature: sessionData.signature ?? ""
19004
+ };
19005
+ if (this.auth) {
19006
+ this.auth.setRestoredTinyCloudSession(tcSession);
19007
+ } else {
19008
+ this._restoredTcSession = tcSession;
19009
+ }
19010
+ }
19011
+ }
19012
+ /**
19013
+ * Resolve the currently-active TinyCloudSession, preferring the auth
19014
+ * layer's value (wallet mode) and falling back to the node-level
19015
+ * rehydration set by {@link restoreSession} (session-only mode).
19016
+ */
19017
+ currentTinyCloudSession() {
19018
+ return this.auth?.tinyCloudSession ?? this._restoredTcSession;
18796
19019
  }
18797
19020
  /**
18798
19021
  * Connect a wallet to upgrade from session-only mode to wallet mode.
@@ -18837,7 +19060,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18837
19060
  sessionStorage: options?.sessionStorage ?? this.config.sessionStorage ?? new MemorySessionStorage(),
18838
19061
  domain: this.siweDomain,
18839
19062
  spacePrefix: prefix,
18840
- sessionExpirationMs: this.config.sessionExpirationMs ?? 60 * 60 * 1e3,
19063
+ sessionExpirationMs: this.config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS,
18841
19064
  tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
18842
19065
  tinycloudRegistryUrl: this.config.tinycloudRegistryUrl,
18843
19066
  tinycloudFallbackHosts: this.config.tinycloudFallbackHosts,
@@ -18881,7 +19104,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18881
19104
  sessionStorage: options?.sessionStorage ?? this.config.sessionStorage ?? new MemorySessionStorage(),
18882
19105
  domain: this.siweDomain,
18883
19106
  spacePrefix: prefix,
18884
- sessionExpirationMs: this.config.sessionExpirationMs ?? 60 * 60 * 1e3,
19107
+ sessionExpirationMs: this.config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS,
18885
19108
  tinycloudHosts: this.explicitHost ? [this.explicitHost] : void 0,
18886
19109
  tinycloudRegistryUrl: this.config.tinycloudRegistryUrl,
18887
19110
  tinycloudFallbackHosts: this.config.tinycloudFallbackHosts,
@@ -18904,7 +19127,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18904
19127
  * @internal
18905
19128
  */
18906
19129
  initializeServices() {
18907
- const session = this.auth?.tinyCloudSession;
19130
+ const session = this.currentTinyCloudSession();
18908
19131
  if (!session) {
18909
19132
  return;
18910
19133
  }
@@ -18962,6 +19185,163 @@ var _TinyCloudNode = class _TinyCloudNode {
18962
19185
  }
18963
19186
  return kvService;
18964
19187
  }
19188
+ getDefaultEncryptionNetworkId(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
19189
+ return `urn:tinycloud:encryption:${this.did}:${name}`;
19190
+ }
19191
+ requireServiceSession() {
19192
+ const session = this._serviceContext?.session;
19193
+ if (!session) {
19194
+ throw new Error("Not signed in. Call signIn() first.");
19195
+ }
19196
+ return session;
19197
+ }
19198
+ createEncryptionCrypto() {
19199
+ const wasm = this.wasmBindings;
19200
+ const columnEncrypt = (key2, plaintext) => {
19201
+ const encrypted = wasm.vault_encrypt(key2, plaintext);
19202
+ const out = new Uint8Array(1 + encrypted.length);
19203
+ out[0] = 1;
19204
+ out.set(encrypted, 1);
19205
+ return out;
19206
+ };
19207
+ const columnDecrypt = (key2, blob) => {
19208
+ if (blob[0] !== 1) {
19209
+ return blob;
19210
+ }
19211
+ return wasm.vault_decrypt(key2, blob.slice(1));
19212
+ };
19213
+ return {
19214
+ sha256: (data) => wasm.vault_sha256(data),
19215
+ randomBytes: (length) => wasm.vault_random_bytes(length),
19216
+ x25519FromSeed: (seed) => wasm.vault_x25519_from_seed(seed),
19217
+ x25519Dh: (privateKey, publicKey) => wasm.vault_x25519_dh(privateKey, publicKey),
19218
+ authEncrypt: (key2, plaintext) => wasm.vault_encrypt(key2, plaintext),
19219
+ authDecrypt: (key2, ciphertext) => wasm.vault_decrypt(key2, ciphertext),
19220
+ sealToNetworkKey: (networkPublicKey, symmetricKey) => {
19221
+ const seed = wasm.vault_random_bytes(32);
19222
+ const ephemeral = wasm.vault_x25519_from_seed(seed);
19223
+ const shared = wasm.vault_x25519_dh(
19224
+ ephemeral.privateKey,
19225
+ networkPublicKey
19226
+ );
19227
+ const encrypted = columnEncrypt(shared, symmetricKey);
19228
+ const out = new Uint8Array(ephemeral.publicKey.length + encrypted.length);
19229
+ out.set(ephemeral.publicKey, 0);
19230
+ out.set(encrypted, ephemeral.publicKey.length);
19231
+ return out;
19232
+ },
19233
+ openWithReceiverKey: (receiverPrivateKey, wrappedKey) => {
19234
+ const peerPublic = wrappedKey.slice(0, 32);
19235
+ const ciphertext = wrappedKey.slice(32);
19236
+ const shared = wasm.vault_x25519_dh(receiverPrivateKey, peerPublic);
19237
+ return columnDecrypt(shared, ciphertext);
19238
+ },
19239
+ verifyNodeSignature: (nodeId, message, signature2) => verifyDidKeyEd25519Signature(nodeId, message, signature2)
19240
+ };
19241
+ }
19242
+ async fetchNodeId() {
19243
+ const response = await fetch(`${this.config.host}/info`);
19244
+ if (!response.ok) {
19245
+ throw new Error(`Failed to fetch node info: HTTP ${response.status}`);
19246
+ }
19247
+ const info = await response.json();
19248
+ if (typeof info.nodeId !== "string" || info.nodeId.length === 0) {
19249
+ throw new Error("Node /info response did not include nodeId");
19250
+ }
19251
+ return info.nodeId;
19252
+ }
19253
+ async signRawNetworkAuthorization(input) {
19254
+ if (!this.wasmBindings.invokeAny) {
19255
+ throw new Error("WASM binding does not support raw-resource invokeAny");
19256
+ }
19257
+ if (!this.wasmBindings.computeCid) {
19258
+ throw new Error("WASM binding does not support invocation CID computation");
19259
+ }
19260
+ const session = this.requireServiceSession();
19261
+ const headers = this.invokeAnyWithRuntimePermissions(
19262
+ session,
19263
+ [
19264
+ {
19265
+ resource: input.networkId,
19266
+ service: "encryption",
19267
+ path: input.networkId,
19268
+ action: input.action
19269
+ }
19270
+ ],
19271
+ [input.facts]
19272
+ );
19273
+ const authorization = authorizationHeader(headers);
19274
+ const audienceBound = await rewriteInvocationAudience(
19275
+ authorization,
19276
+ input.targetNode,
19277
+ session.jwk
19278
+ );
19279
+ return {
19280
+ authorization: audienceBound,
19281
+ invocationCid: this.wasmBindings.computeCid(
19282
+ new TextEncoder().encode(audienceBound),
19283
+ 0x55n
19284
+ )
19285
+ };
19286
+ }
19287
+ createEncryptionService() {
19288
+ const crypto2 = this.createEncryptionCrypto();
19289
+ const transport = {
19290
+ postDecrypt: async ({ networkId, authorization, canonicalBody }) => {
19291
+ const response = await fetch(
19292
+ `${this.config.host}/encryption/networks/${encodeURIComponent(networkId)}/decrypt`,
19293
+ {
19294
+ method: "POST",
19295
+ headers: {
19296
+ Authorization: authorization,
19297
+ "Content-Type": "application/json"
19298
+ },
19299
+ body: canonicalBody
19300
+ }
19301
+ );
19302
+ if (!response.ok) {
19303
+ throw new Error(
19304
+ `decrypt failed ${response.status}: ${await response.text()}`
19305
+ );
19306
+ }
19307
+ return await response.json();
19308
+ }
19309
+ };
19310
+ return new EncryptionService({
19311
+ crypto: crypto2,
19312
+ signer: {
19313
+ signDecryptInvocation: async (input) => {
19314
+ const signed2 = await this.signRawNetworkAuthorization({
19315
+ targetNode: input.targetNode,
19316
+ networkId: input.networkId,
19317
+ action: DECRYPT_ACTION,
19318
+ facts: input.facts
19319
+ });
19320
+ return {
19321
+ ...signed2,
19322
+ canonicalBody: canonicalizeEncryptionJson(
19323
+ input.body
19324
+ )
19325
+ };
19326
+ }
19327
+ },
19328
+ transport,
19329
+ node: {
19330
+ fetchByNetworkId: (networkId) => this.getEncryptionNetwork(networkId)
19331
+ }
19332
+ });
19333
+ }
19334
+ getEncryptionService() {
19335
+ if (!this._serviceContext) {
19336
+ throw new Error("Not signed in. Call signIn() first.");
19337
+ }
19338
+ if (!this._encryption) {
19339
+ this._encryption = this.createEncryptionService();
19340
+ this._encryption.initialize(this._serviceContext);
19341
+ this._serviceContext.registerService("encryption", this._encryption);
19342
+ }
19343
+ return this._encryption;
19344
+ }
18965
19345
  createVaultService(spaceId, kv) {
18966
19346
  const wasm = this.wasmBindings;
18967
19347
  const vaultCrypto = createVaultCrypto({
@@ -18977,6 +19357,13 @@ var _TinyCloudNode = class _TinyCloudNode {
18977
19357
  return new DataVaultService({
18978
19358
  spaceId,
18979
19359
  crypto: vaultCrypto,
19360
+ encryption: {
19361
+ networkId: this.getDefaultEncryptionNetworkId(),
19362
+ service: this.getEncryptionService(),
19363
+ decryptCapabilityProof: () => ({
19364
+ proofs: [this.requireServiceSession().delegationCid]
19365
+ })
19366
+ },
18980
19367
  tc: {
18981
19368
  kv,
18982
19369
  ensurePublicSpace: async () => {
@@ -19157,7 +19544,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19157
19544
  * @internal
19158
19545
  */
19159
19546
  getSessionExpiry() {
19160
- const expirationMs = this.config.sessionExpirationMs ?? 60 * 60 * 1e3;
19547
+ const expirationMs = this.config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS;
19161
19548
  return new Date(Date.now() + expirationMs);
19162
19549
  }
19163
19550
  /**
@@ -19214,7 +19601,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19214
19601
  if (!this.signer) {
19215
19602
  return void 0;
19216
19603
  }
19217
- const session = this.auth?.tinyCloudSession;
19604
+ const session = this.currentTinyCloudSession();
19218
19605
  if (!session) {
19219
19606
  return void 0;
19220
19607
  }
@@ -19314,6 +19701,34 @@ var _TinyCloudNode = class _TinyCloudNode {
19314
19701
  }
19315
19702
  return this._sql;
19316
19703
  }
19704
+ /**
19705
+ * Get an SQL service scoped to a specific space.
19706
+ *
19707
+ * Mirrors {@link SpaceService}'s per-space KV factory: clones the active
19708
+ * service context and overrides its session's spaceId so that subsequent
19709
+ * `sql/<dbName>/<action>` invocations route to that space. Useful when
19710
+ * the caller already holds a delegation covering the target space (e.g.
19711
+ * via {@link grantRuntimePermissions} or {@link useRuntimeDelegation})
19712
+ * but the SDK's per-space SQL surface isn't otherwise exposed.
19713
+ *
19714
+ * Does NOT auto-create the space.
19715
+ *
19716
+ * @param spaceId - Full space URI (`tinycloud:pkh:eip155:<chain>:<addr>:<name>`).
19717
+ */
19718
+ sqlForSpace(spaceId) {
19719
+ if (!this._serviceContext || !this._serviceContext.session) {
19720
+ throw new Error("Not signed in. Call signIn() first.");
19721
+ }
19722
+ const sql = new SQLService2({});
19723
+ const spaceScopedContext = new ServiceContext2({
19724
+ invoke: this._serviceContext.invoke,
19725
+ fetch: this._serviceContext.fetch,
19726
+ hosts: this._serviceContext.hosts
19727
+ });
19728
+ spaceScopedContext.setSession({ ...this._serviceContext.session, spaceId });
19729
+ sql.initialize(spaceScopedContext);
19730
+ return sql;
19731
+ }
19317
19732
  /**
19318
19733
  * DuckDB database operations on this user's space.
19319
19734
  */
@@ -19337,6 +19752,79 @@ var _TinyCloudNode = class _TinyCloudNode {
19337
19752
  }
19338
19753
  return this._vault;
19339
19754
  }
19755
+ /**
19756
+ * Network-scoped encryption/decrypt service.
19757
+ */
19758
+ get encryption() {
19759
+ return this.getEncryptionService();
19760
+ }
19761
+ async getEncryptionNetwork(nameOrNetworkId = this.getDefaultEncryptionNetworkId()) {
19762
+ const networkId = nameOrNetworkId.startsWith("urn:tinycloud:encryption:") ? nameOrNetworkId : this.getDefaultEncryptionNetworkId(nameOrNetworkId);
19763
+ const response = await fetch(
19764
+ `${this.config.host}/encryption/networks/${encodeURIComponent(networkId)}`
19765
+ );
19766
+ if (response.status === 404) {
19767
+ return null;
19768
+ }
19769
+ if (!response.ok) {
19770
+ throw new Error(
19771
+ `Failed to fetch encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
19772
+ );
19773
+ }
19774
+ const body = await response.json();
19775
+ return "descriptor" in body && body.descriptor ? body.descriptor : body;
19776
+ }
19777
+ async createEncryptionNetwork(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
19778
+ const targetNode = await this.fetchNodeId();
19779
+ const principal = this.did;
19780
+ const networkId = this.getDefaultEncryptionNetworkId(name);
19781
+ const body = {
19782
+ name,
19783
+ principal,
19784
+ threshold: { n: 1, t: 1 }
19785
+ };
19786
+ const crypto2 = this.createEncryptionCrypto();
19787
+ const facts = {
19788
+ type: NETWORK_ADMIN_TYPE,
19789
+ targetNode,
19790
+ networkId,
19791
+ bodyHash: canonicalHashHex(
19792
+ crypto2.sha256,
19793
+ body
19794
+ ),
19795
+ action: NETWORK_CREATE_ACTION
19796
+ };
19797
+ const signed2 = await this.signRawNetworkAuthorization({
19798
+ targetNode,
19799
+ networkId,
19800
+ action: NETWORK_CREATE_ACTION,
19801
+ facts
19802
+ });
19803
+ const response = await fetch(`${this.config.host}/encryption/networks`, {
19804
+ method: "POST",
19805
+ headers: {
19806
+ Authorization: signed2.authorization,
19807
+ "Content-Type": "application/json"
19808
+ },
19809
+ body: canonicalizeEncryptionJson(
19810
+ body
19811
+ )
19812
+ });
19813
+ if (!response.ok) {
19814
+ throw new Error(
19815
+ `Failed to create encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
19816
+ );
19817
+ }
19818
+ const created = await response.json();
19819
+ return created.descriptor;
19820
+ }
19821
+ async ensureEncryptionNetwork(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
19822
+ const existing = await this.getEncryptionNetwork(name);
19823
+ if (existing) {
19824
+ return existing;
19825
+ }
19826
+ return this.createEncryptionNetwork(name);
19827
+ }
19340
19828
  /**
19341
19829
  * App-facing secrets API backed by the `secrets` space vault.
19342
19830
  */
@@ -19348,8 +19836,10 @@ var _TinyCloudNode = class _TinyCloudNode {
19348
19836
  this._secrets = new NodeSecretsService({
19349
19837
  getService: () => this.getBaseSecrets(),
19350
19838
  getManifest: () => this.manifest,
19839
+ hasPermissions: (permissions) => this.hasRuntimePermissions(permissions),
19351
19840
  grantPermissions: (additional) => this.grantRuntimePermissions(additional),
19352
19841
  canEscalate: () => this.signer !== void 0 && this.tc !== void 0,
19842
+ getEncryptionNetworkId: () => this.getDefaultEncryptionNetworkId(),
19353
19843
  getUnlockSigner: () => this.signer ?? void 0
19354
19844
  });
19355
19845
  }
@@ -19439,7 +19929,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19439
19929
  * every requested permission.
19440
19930
  */
19441
19931
  hasRuntimePermissions(permissions) {
19442
- const session = this.auth?.tinyCloudSession;
19932
+ const session = this.currentTinyCloudSession();
19443
19933
  if (!session || !Array.isArray(permissions) || permissions.length === 0) {
19444
19934
  return false;
19445
19935
  }
@@ -19459,7 +19949,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19459
19949
  if (permissions === void 0) {
19460
19950
  return this.runtimePermissionGrants.map((grant) => grant.delegation);
19461
19951
  }
19462
- const session = this.auth?.tinyCloudSession;
19952
+ const session = this.currentTinyCloudSession();
19463
19953
  if (!session || !Array.isArray(permissions) || permissions.length === 0) {
19464
19954
  return [];
19465
19955
  }
@@ -19473,7 +19963,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19473
19963
  * matching service calls and downstream `delegateTo()` calls can use it.
19474
19964
  */
19475
19965
  async useRuntimeDelegation(delegation) {
19476
- const session = this.auth?.tinyCloudSession;
19966
+ const session = this.currentTinyCloudSession();
19477
19967
  if (!session) {
19478
19968
  throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
19479
19969
  }
@@ -19512,7 +20002,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19512
20002
  if (!Array.isArray(permissions) || permissions.length === 0) {
19513
20003
  throw new Error("grantRuntimePermissions requires a non-empty permissions array");
19514
20004
  }
19515
- const session = this.auth?.tinyCloudSession;
20005
+ const session = this.currentTinyCloudSession();
19516
20006
  if (!session) {
19517
20007
  throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
19518
20008
  }
@@ -19536,13 +20026,22 @@ var _TinyCloudNode = class _TinyCloudNode {
19536
20026
  "grantRuntimePermissions requires wallet mode with a signer or privateKey."
19537
20027
  );
19538
20028
  }
20029
+ const rawEntries = expanded.filter(
20030
+ (entry) => this.isEncryptionPermissionEntry(entry)
20031
+ );
20032
+ const spaceEntries = expanded.filter(
20033
+ (entry) => !this.isEncryptionPermissionEntry(entry)
20034
+ );
19539
20035
  const bySpace = /* @__PURE__ */ new Map();
19540
- for (const entry of expanded) {
20036
+ for (const entry of spaceEntries) {
19541
20037
  const spaceId = this.resolvePermissionSpace(entry.space, session);
19542
20038
  const current = bySpace.get(spaceId) ?? [];
19543
20039
  current.push(entry);
19544
20040
  bySpace.set(spaceId, current);
19545
20041
  }
20042
+ if (bySpace.size === 0 && rawEntries.length > 0) {
20043
+ bySpace.set(session.spaceId, []);
20044
+ }
19546
20045
  const now = /* @__PURE__ */ new Date();
19547
20046
  const requestedExpiryMs = resolveExpiryMs(options?.expiry);
19548
20047
  let expiresAt = new Date(now.getTime() + requestedExpiryMs);
@@ -19550,10 +20049,17 @@ var _TinyCloudNode = class _TinyCloudNode {
19550
20049
  expiresAt = sessionExpiry;
19551
20050
  }
19552
20051
  const delegations = [];
20052
+ let rawEntriesAttached = false;
19553
20053
  for (const [spaceId, entries] of bySpace) {
20054
+ const rawForDelegation = !rawEntriesAttached ? rawEntries : [];
20055
+ if (rawForDelegation.length > 0) {
20056
+ rawEntriesAttached = true;
20057
+ }
20058
+ const delegatedEntries = [...entries, ...rawForDelegation];
19554
20059
  const abilities = this.permissionsToAbilities(entries);
19555
20060
  const prepared = this.wasmBindings.prepareSession({
19556
20061
  abilities,
20062
+ ...rawForDelegation.length > 0 ? { rawAbilities: this.permissionsToRawAbilities(rawForDelegation) } : {},
19557
20063
  address: this.wasmBindings.ensureEip55(session.address),
19558
20064
  chainId: session.chainId,
19559
20065
  domain: this.siweDomain,
@@ -19578,7 +20084,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19578
20084
  }
19579
20085
  const delegation = this.runtimeDelegationFromSession(
19580
20086
  delegatedSession,
19581
- entries,
20087
+ delegatedEntries,
19582
20088
  spaceId,
19583
20089
  session,
19584
20090
  expiresAt
@@ -19592,7 +20098,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19592
20098
  jwk: session.jwk
19593
20099
  },
19594
20100
  delegation,
19595
- operations: this.permissionOperations(entries, spaceId),
20101
+ operations: this.permissionOperations(delegatedEntries, spaceId),
19596
20102
  expiresAt
19597
20103
  });
19598
20104
  delegations.push(delegation);
@@ -19740,7 +20246,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19740
20246
  ];
19741
20247
  const abilities = { kv: { "": kvActions } };
19742
20248
  const now = /* @__PURE__ */ new Date();
19743
- const expiryMs = 60 * 60 * 1e3;
20249
+ const expiryMs = EXPIRY3.EPHEMERAL_MS;
19744
20250
  const expirationTime = new Date(now.getTime() + expiryMs);
19745
20251
  const prepared = this.wasmBindings.prepareSession({
19746
20252
  abilities,
@@ -19910,7 +20416,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19910
20416
  * `forceWalletSign` is not set.
19911
20417
  */
19912
20418
  async delegateTo(did, permissions, options) {
19913
- const session = this.auth?.tinyCloudSession;
20419
+ const session = this.currentTinyCloudSession();
19914
20420
  if (!session) {
19915
20421
  throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
19916
20422
  }
@@ -20024,11 +20530,8 @@ var _TinyCloudNode = class _TinyCloudNode {
20024
20530
  * the current session; we build one multi-resource abilities map
20025
20531
  * and emit one signed UCAN covering them all.
20026
20532
  *
20027
- * All entries must share the same target space (the UCAN is
20028
- * scoped to a single space). If they don't, this throws mixing
20029
- * spaces in a single delegation is not supported by the underlying
20030
- * Rust create_delegation call and the resulting UCAN would be
20031
- * under-specified.
20533
+ * Non-encryption entries must share the same target space. Encryption
20534
+ * entries are raw network URNs and do not participate in space grouping.
20032
20535
  *
20033
20536
  * @internal
20034
20537
  */
@@ -20040,15 +20543,18 @@ var _TinyCloudNode = class _TinyCloudNode {
20040
20543
  }
20041
20544
  const resolvedSpaces = /* @__PURE__ */ new Set();
20042
20545
  for (const entry of entries) {
20546
+ if (this.isEncryptionPermissionEntry(entry)) {
20547
+ continue;
20548
+ }
20043
20549
  const spaceId2 = this.resolvePermissionSpace(entry.space, session);
20044
20550
  resolvedSpaces.add(spaceId2);
20045
20551
  }
20046
- if (resolvedSpaces.size !== 1) {
20552
+ if (resolvedSpaces.size > 1) {
20047
20553
  throw new Error(
20048
20554
  `delegateTo: all permission entries must target the same space, got ${resolvedSpaces.size}: ${JSON.stringify([...resolvedSpaces])}`
20049
20555
  );
20050
20556
  }
20051
- const spaceId = [...resolvedSpaces][0];
20557
+ const spaceId = resolvedSpaces.size === 1 ? [...resolvedSpaces][0] : session.spaceId;
20052
20558
  const abilities = {};
20053
20559
  for (const entry of entries) {
20054
20560
  const shortService = SERVICE_LONG_TO_SHORT[entry.service];
@@ -20195,11 +20701,32 @@ var _TinyCloudNode = class _TinyCloudNode {
20195
20701
  }
20196
20702
  return abilities;
20197
20703
  }
20704
+ isEncryptionPermissionEntry(entry) {
20705
+ return entry.service === ENCRYPTION_PERMISSION_SERVICE2 && entry.path.startsWith("urn:tinycloud:encryption:");
20706
+ }
20707
+ permissionsToRawAbilities(entries) {
20708
+ const rawAbilities = {};
20709
+ for (const entry of entries) {
20710
+ if (!this.isEncryptionPermissionEntry(entry)) {
20711
+ continue;
20712
+ }
20713
+ const existing = rawAbilities[entry.path] ?? [];
20714
+ const seen = new Set(existing);
20715
+ for (const action of entry.actions) {
20716
+ if (!seen.has(action)) {
20717
+ existing.push(action);
20718
+ seen.add(action);
20719
+ }
20720
+ }
20721
+ rawAbilities[entry.path] = existing;
20722
+ }
20723
+ return rawAbilities;
20724
+ }
20198
20725
  permissionOperations(entries, spaceId) {
20199
20726
  return entries.flatMap((entry) => {
20200
20727
  const service = this.shortServiceName(entry.service);
20201
20728
  return entry.actions.map((action) => ({
20202
- spaceId,
20729
+ ...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
20203
20730
  service,
20204
20731
  path: entry.path,
20205
20732
  action
@@ -20222,7 +20749,7 @@ var _TinyCloudNode = class _TinyCloudNode {
20222
20749
  const spaceId = this.resolvePermissionSpace(entry.space, session);
20223
20750
  const service = this.shortServiceName(entry.service);
20224
20751
  return entry.actions.map((action) => ({
20225
- spaceId,
20752
+ ...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
20226
20753
  service,
20227
20754
  path: entry.path,
20228
20755
  action
@@ -20279,24 +20806,40 @@ var _TinyCloudNode = class _TinyCloudNode {
20279
20806
  expiresAt: delegation.expiry
20280
20807
  };
20281
20808
  }
20809
+ installRuntimeGrantFromServiceSession(delegation, session, expiresAt) {
20810
+ const operations = this.operationsFromDelegation(delegation);
20811
+ if (operations.length === 0) {
20812
+ return;
20813
+ }
20814
+ this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
20815
+ (grant) => grant.delegation.cid !== delegation.cid && grant.session.delegationCid !== session.delegationCid
20816
+ );
20817
+ this.runtimePermissionGrants.push({
20818
+ session,
20819
+ delegation,
20820
+ operations,
20821
+ expiresAt
20822
+ });
20823
+ }
20282
20824
  delegatedResourcesForEntries(entries, spaceId) {
20283
20825
  return entries.map((entry) => ({
20284
20826
  service: this.shortServiceName(entry.service),
20285
- space: spaceId,
20827
+ space: this.isEncryptionPermissionEntry(entry) ? "encryption" : spaceId,
20286
20828
  path: entry.path,
20287
20829
  actions: [...entry.actions]
20288
20830
  }));
20289
20831
  }
20290
20832
  operationsFromDelegation(delegation) {
20291
20833
  const resources = delegation.resources !== void 0 && delegation.resources.length > 0 ? delegation.resources : this.flatDelegationResources(delegation);
20292
- return resources.flatMap(
20293
- (resource) => resource.actions.map((action) => ({
20294
- spaceId: resource.space,
20295
- service: this.invocationServiceName(resource.service),
20834
+ return resources.flatMap((resource) => {
20835
+ const service = this.invocationServiceName(resource.service);
20836
+ return resource.actions.map((action) => ({
20837
+ ...this.isEncryptionNetworkOperation(service, resource.path) ? { resource: resource.path } : { spaceId: resource.space },
20838
+ service,
20296
20839
  path: resource.path,
20297
20840
  action
20298
- }))
20299
- );
20841
+ }));
20842
+ });
20300
20843
  }
20301
20844
  flatDelegationResources(delegation) {
20302
20845
  const byService = /* @__PURE__ */ new Map();
@@ -20345,7 +20888,13 @@ var _TinyCloudNode = class _TinyCloudNode {
20345
20888
  );
20346
20889
  }
20347
20890
  operationCovers(granted, requested) {
20348
- return granted.spaceId === requested.spaceId && granted.service === requested.service && this.actionContains(granted.action, requested.action) && this.pathContains(granted.path, requested.path);
20891
+ if (granted.service !== requested.service || !this.actionContains(granted.action, requested.action)) {
20892
+ return false;
20893
+ }
20894
+ if (granted.resource !== void 0 || requested.resource !== void 0) {
20895
+ return granted.resource !== void 0 && requested.resource !== void 0 && granted.resource === requested.resource && this.pathContains(granted.path, requested.path);
20896
+ }
20897
+ return granted.spaceId !== void 0 && requested.spaceId !== void 0 && granted.spaceId === requested.spaceId && this.pathContains(granted.path, requested.path);
20349
20898
  }
20350
20899
  actionContains(grantedAction, requestedAction) {
20351
20900
  if (grantedAction === requestedAction) {
@@ -20360,6 +20909,37 @@ var _TinyCloudNode = class _TinyCloudNode {
20360
20909
  invocationServiceName(service) {
20361
20910
  return service.startsWith("tinycloud.") ? this.shortServiceName(service) : service;
20362
20911
  }
20912
+ isEncryptionNetworkOperation(service, path) {
20913
+ return service === "encryption" && path.startsWith("urn:tinycloud:encryption:");
20914
+ }
20915
+ operationFromInvokeAnyEntry(entry) {
20916
+ const service = this.invocationServiceName(entry.service);
20917
+ if (typeof entry.resource === "string") {
20918
+ return {
20919
+ resource: entry.resource,
20920
+ service,
20921
+ path: entry.path,
20922
+ action: entry.action
20923
+ };
20924
+ }
20925
+ if (this.isEncryptionNetworkOperation(service, entry.path)) {
20926
+ return {
20927
+ resource: entry.path,
20928
+ service,
20929
+ path: entry.path,
20930
+ action: entry.action
20931
+ };
20932
+ }
20933
+ if (typeof entry.spaceId === "string") {
20934
+ return {
20935
+ spaceId: entry.spaceId,
20936
+ service,
20937
+ path: entry.path,
20938
+ action: entry.action
20939
+ };
20940
+ }
20941
+ return void 0;
20942
+ }
20363
20943
  pathContains(grantedPath, requestedPath) {
20364
20944
  if (grantedPath === "" || grantedPath === "/") {
20365
20945
  return true;
@@ -20598,6 +21178,17 @@ var _TinyCloudNode = class _TinyCloudNode {
20598
21178
  // Not used in session-only mode
20599
21179
  };
20600
21180
  this.trackReceivedDelegation(delegation, this.sessionKeyJwk);
21181
+ this.installRuntimeGrantFromServiceSession(
21182
+ delegation,
21183
+ {
21184
+ delegationHeader: session2.delegationHeader,
21185
+ delegationCid: session2.delegationCid,
21186
+ spaceId: session2.spaceId,
21187
+ verificationMethod: session2.verificationMethod,
21188
+ jwk: session2.jwk
21189
+ },
21190
+ delegation.expiry
21191
+ );
20601
21192
  return new DelegatedAccess(session2, delegation, targetHost, this.wasmBindings.invoke);
20602
21193
  }
20603
21194
  const mySession = this.auth?.tinyCloudSession;
@@ -20609,6 +21200,10 @@ var _TinyCloudNode = class _TinyCloudNode {
20609
21200
  const kvActions = delegation.actions.filter((a) => a.startsWith("tinycloud.kv/"));
20610
21201
  const sqlActions = delegation.actions.filter((a) => a.startsWith("tinycloud.sql/"));
20611
21202
  const duckdbActions = delegation.actions.filter((a) => a.startsWith("tinycloud.duckdb/"));
21203
+ const encryptionActions = delegation.actions.filter(
21204
+ (a) => a.startsWith("tinycloud.encryption/")
21205
+ );
21206
+ const rawAbilities = {};
20612
21207
  if (kvActions.length > 0) {
20613
21208
  abilities.kv = { [delegation.path]: kvActions };
20614
21209
  }
@@ -20618,6 +21213,9 @@ var _TinyCloudNode = class _TinyCloudNode {
20618
21213
  if (duckdbActions.length > 0) {
20619
21214
  abilities.duckdb = { [delegation.path]: duckdbActions };
20620
21215
  }
21216
+ if (encryptionActions.length > 0 && delegation.path.startsWith("urn:tinycloud:encryption:")) {
21217
+ rawAbilities[delegation.path] = encryptionActions;
21218
+ }
20621
21219
  const now = /* @__PURE__ */ new Date();
20622
21220
  const maxExpiry = new Date(now.getTime() + 60 * 60 * 1e3);
20623
21221
  const expirationTime = delegation.expiry < maxExpiry ? delegation.expiry : maxExpiry;
@@ -20630,7 +21228,8 @@ var _TinyCloudNode = class _TinyCloudNode {
20630
21228
  expirationTime: expirationTime.toISOString(),
20631
21229
  spaceId: delegation.spaceId,
20632
21230
  jwk,
20633
- parents: [delegation.cid]
21231
+ parents: [delegation.cid],
21232
+ ...Object.keys(rawAbilities).length > 0 ? { rawAbilities } : {}
20634
21233
  });
20635
21234
  const signature2 = await this.signer.signMessage(prepared.siwe);
20636
21235
  const invokerSession = this.wasmBindings.completeSessionSetup({
@@ -20657,6 +21256,17 @@ var _TinyCloudNode = class _TinyCloudNode {
20657
21256
  signature: signature2
20658
21257
  };
20659
21258
  this.trackReceivedDelegation(delegation, jwk);
21259
+ this.installRuntimeGrantFromServiceSession(
21260
+ delegation,
21261
+ {
21262
+ delegationHeader: session.delegationHeader,
21263
+ delegationCid: session.delegationCid,
21264
+ spaceId: session.spaceId,
21265
+ verificationMethod: session.verificationMethod,
21266
+ jwk: session.jwk
21267
+ },
21268
+ expirationTime
21269
+ );
20660
21270
  return new DelegatedAccess(session, delegation, targetHost, this.wasmBindings.invoke);
20661
21271
  }
20662
21272
  /**
@@ -20911,7 +21521,7 @@ import {
20911
21521
  loadManifest,
20912
21522
  isCapabilitySubset as isCapabilitySubset2,
20913
21523
  expandActionShortNames,
20914
- expandPermissionEntries,
21524
+ expandPermissionEntries as expandPermissionEntries2,
20915
21525
  expandPermissionEntry,
20916
21526
  parseExpiry as parseExpiry2,
20917
21527
  resourceCapabilitiesToSpaceAbilitiesMap as resourceCapabilitiesToSpaceAbilitiesMap2
@@ -20934,7 +21544,11 @@ function deserializeDelegation(data) {
20934
21544
  }
20935
21545
 
20936
21546
  // src/index.ts
20937
- import { KVService as KVService3, PrefixedKVService } from "@tinycloud/sdk-core";
21547
+ import {
21548
+ DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
21549
+ KVService as KVService3,
21550
+ PrefixedKVService
21551
+ } from "@tinycloud/sdk-core";
20938
21552
  import { SQLService as SQLService3, SQLAction, DatabaseHandle } from "@tinycloud/sdk-core";
20939
21553
  import {
20940
21554
  DuckDbService as DuckDbService3,
@@ -20946,7 +21560,53 @@ import {
20946
21560
  VaultHeaders,
20947
21561
  VaultPublicSpaceKVActions,
20948
21562
  createVaultCrypto as createVaultCrypto2,
20949
- SecretsService as SecretsService2
21563
+ SecretsService as SecretsService2,
21564
+ SECRET_NAME_RE,
21565
+ canonicalizeSecretScope,
21566
+ resolveSecretListPrefix as resolveSecretListPrefix2,
21567
+ resolveSecretPath as resolveSecretPath2
21568
+ } from "@tinycloud/sdk-core";
21569
+ import {
21570
+ EncryptionService as EncryptionService2,
21571
+ parseNetworkId,
21572
+ buildNetworkId,
21573
+ isNetworkId,
21574
+ networkDiscoveryKey,
21575
+ NetworkIdError,
21576
+ ENCRYPTION_NETWORK_URN_PREFIX,
21577
+ NETWORK_NAME_PATTERN,
21578
+ canonicalizeEncryptionJson as canonicalizeEncryptionJson2,
21579
+ canonicalHashHex as canonicalHashHex2,
21580
+ hexEncode,
21581
+ hexDecode,
21582
+ encryptionBase64Encode,
21583
+ encryptionBase64Decode,
21584
+ encryptionUtf8Encode,
21585
+ encryptionUtf8Decode,
21586
+ encryptToNetwork,
21587
+ decryptEnvelopeWithKey,
21588
+ validateEnvelope,
21589
+ generateRandomReceiverKey,
21590
+ deriveSignedReceiverKey,
21591
+ buildCanonicalDecryptRequest,
21592
+ buildDecryptFacts,
21593
+ buildDecryptAttenuation,
21594
+ buildDecryptInvocation,
21595
+ checkDecryptInvocationInput,
21596
+ verifyDecryptResponse,
21597
+ canonicalSignedResponse,
21598
+ openWrappedKey,
21599
+ discoverNetwork,
21600
+ ensureNetworkUsableForDecrypt,
21601
+ DEFAULT_ENCRYPTION_ALG,
21602
+ ENVELOPE_VERSION,
21603
+ DEFAULT_KEY_VERSION,
21604
+ DECRYPT_FACT_TYPE,
21605
+ DECRYPT_RESULT_TYPE,
21606
+ DECRYPT_ACTION as DECRYPT_ACTION2,
21607
+ ENCRYPTION_SERVICE,
21608
+ ENCRYPTION_SERVICE_SHORT,
21609
+ encryptionError
20950
21610
  } from "@tinycloud/sdk-core";
20951
21611
  import { HooksService as HooksService3 } from "@tinycloud/sdk-core";
20952
21612
  import {
@@ -20982,8 +21642,14 @@ export {
20982
21642
  AutoApproveSpaceCreationHandler2 as AutoApproveSpaceCreationHandler,
20983
21643
  CapabilityKeyRegistry2 as CapabilityKeyRegistry,
20984
21644
  CapabilityKeyRegistryErrorCodes,
21645
+ DECRYPT_ACTION2 as DECRYPT_ACTION,
21646
+ DECRYPT_FACT_TYPE,
21647
+ DECRYPT_RESULT_TYPE,
21648
+ DEFAULT_ENCRYPTION_ALG,
21649
+ DEFAULT_KEY_VERSION,
20985
21650
  DEFAULT_MANIFEST_SPACE2 as DEFAULT_MANIFEST_SPACE,
20986
21651
  DEFAULT_MANIFEST_VERSION,
21652
+ DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
20987
21653
  DataVaultService2 as DataVaultService,
20988
21654
  DatabaseHandle,
20989
21655
  DelegatedAccess,
@@ -20992,17 +21658,25 @@ export {
20992
21658
  DuckDbAction,
20993
21659
  DuckDbDatabaseHandle,
20994
21660
  DuckDbService3 as DuckDbService,
21661
+ ENCRYPTION_NETWORK_URN_PREFIX,
21662
+ ENCRYPTION_SERVICE,
21663
+ ENCRYPTION_SERVICE_SHORT,
21664
+ ENVELOPE_VERSION,
21665
+ EncryptionService2 as EncryptionService,
20995
21666
  FileSessionStorage,
20996
21667
  HooksService3 as HooksService,
20997
21668
  KVService3 as KVService,
20998
21669
  ManifestValidationError,
20999
21670
  MemorySessionStorage,
21671
+ NETWORK_NAME_PATTERN,
21672
+ NetworkIdError,
21000
21673
  NodeUserAuthorization,
21001
21674
  NodeWasmBindings,
21002
21675
  PermissionNotInManifestError2 as PermissionNotInManifestError,
21003
21676
  PrefixedKVService,
21004
21677
  PrivateKeySigner,
21005
21678
  ProtocolMismatchError,
21679
+ SECRET_NAME_RE,
21006
21680
  SQLAction,
21007
21681
  SQLService3 as SQLService,
21008
21682
  SecretsService2 as SecretsService,
@@ -21021,7 +21695,17 @@ export {
21021
21695
  VaultPublicSpaceKVActions,
21022
21696
  VersionCheckError,
21023
21697
  WasmKeyProvider,
21698
+ buildCanonicalDecryptRequest,
21699
+ buildDecryptAttenuation,
21700
+ buildDecryptFacts,
21701
+ buildDecryptInvocation,
21702
+ buildNetworkId,
21024
21703
  buildSpaceUri,
21704
+ canonicalHashHex2 as canonicalHashHex,
21705
+ canonicalSignedResponse,
21706
+ canonicalizeEncryptionJson2 as canonicalizeEncryptionJson,
21707
+ canonicalizeSecretScope,
21708
+ checkDecryptInvocationInput,
21025
21709
  checkNodeInfo2 as checkNodeInfo,
21026
21710
  composeManifestRequest2 as composeManifestRequest,
21027
21711
  createCapabilityKeyRegistry,
@@ -21029,21 +21713,42 @@ export {
21029
21713
  createSpaceService,
21030
21714
  createVaultCrypto2 as createVaultCrypto,
21031
21715
  createWasmKeyProvider,
21716
+ decryptEnvelopeWithKey,
21032
21717
  defaultSignStrategy,
21033
21718
  defaultSpaceCreationHandler,
21719
+ deriveSignedReceiverKey,
21034
21720
  deserializeDelegation,
21721
+ discoverNetwork,
21722
+ encryptToNetwork,
21723
+ encryptionBase64Decode,
21724
+ encryptionBase64Encode,
21725
+ encryptionError,
21726
+ encryptionUtf8Decode,
21727
+ encryptionUtf8Encode,
21728
+ ensureNetworkUsableForDecrypt,
21035
21729
  expandActionShortNames,
21036
- expandPermissionEntries,
21730
+ expandPermissionEntries2 as expandPermissionEntries,
21037
21731
  expandPermissionEntry,
21732
+ generateRandomReceiverKey,
21733
+ hexDecode,
21734
+ hexEncode,
21038
21735
  isCapabilitySubset2 as isCapabilitySubset,
21736
+ isNetworkId,
21039
21737
  loadManifest,
21040
21738
  makePublicSpaceId2 as makePublicSpaceId,
21739
+ networkDiscoveryKey,
21740
+ openWrappedKey,
21041
21741
  parseExpiry2 as parseExpiry,
21742
+ parseNetworkId,
21042
21743
  parseSpaceUri,
21043
21744
  resolveManifest2 as resolveManifest,
21745
+ resolveSecretListPrefix2 as resolveSecretListPrefix,
21746
+ resolveSecretPath2 as resolveSecretPath,
21044
21747
  resourceCapabilitiesToSpaceAbilitiesMap2 as resourceCapabilitiesToSpaceAbilitiesMap,
21045
21748
  serializeDelegation,
21046
- validateManifest
21749
+ validateEnvelope,
21750
+ validateManifest,
21751
+ verifyDecryptResponse
21047
21752
  };
21048
21753
  /*! Bundled license information:
21049
21754