@tinycloud/node-sdk 2.2.0-beta.9 → 2.2.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -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,180 @@ 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
+ wellKnown: {
19333
+ fetchWellKnown: async (principal, discoveryKey) => {
19334
+ if (!this._address || principal !== this.did) {
19335
+ return null;
19336
+ }
19337
+ if (!this.config.host) {
19338
+ return null;
19339
+ }
19340
+ const publicSpaceId = makePublicSpaceId(this._address, this._chainId);
19341
+ const result = await TinyCloud.readPublicSpace(this.config.host, publicSpaceId, discoveryKey);
19342
+ if (!result.ok) {
19343
+ return null;
19344
+ }
19345
+ const body = result.data;
19346
+ return "descriptor" in body && body.descriptor ? body.descriptor : body;
19347
+ }
19348
+ }
19349
+ });
19350
+ }
19351
+ getEncryptionService() {
19352
+ if (!this._serviceContext) {
19353
+ throw new Error("Not signed in. Call signIn() first.");
19354
+ }
19355
+ if (!this._encryption) {
19356
+ this._encryption = this.createEncryptionService();
19357
+ this._encryption.initialize(this._serviceContext);
19358
+ this._serviceContext.registerService("encryption", this._encryption);
19359
+ }
19360
+ return this._encryption;
19361
+ }
18965
19362
  createVaultService(spaceId, kv) {
18966
19363
  const wasm = this.wasmBindings;
18967
19364
  const vaultCrypto = createVaultCrypto({
@@ -18977,6 +19374,13 @@ var _TinyCloudNode = class _TinyCloudNode {
18977
19374
  return new DataVaultService({
18978
19375
  spaceId,
18979
19376
  crypto: vaultCrypto,
19377
+ encryption: {
19378
+ networkId: this.getDefaultEncryptionNetworkId(),
19379
+ service: this.getEncryptionService(),
19380
+ decryptCapabilityProof: () => ({
19381
+ proofs: [this.requireServiceSession().delegationCid]
19382
+ })
19383
+ },
18980
19384
  tc: {
18981
19385
  kv,
18982
19386
  ensurePublicSpace: async () => {
@@ -19157,7 +19561,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19157
19561
  * @internal
19158
19562
  */
19159
19563
  getSessionExpiry() {
19160
- const expirationMs = this.config.sessionExpirationMs ?? 60 * 60 * 1e3;
19564
+ const expirationMs = this.config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS;
19161
19565
  return new Date(Date.now() + expirationMs);
19162
19566
  }
19163
19567
  /**
@@ -19214,7 +19618,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19214
19618
  if (!this.signer) {
19215
19619
  return void 0;
19216
19620
  }
19217
- const session = this.auth?.tinyCloudSession;
19621
+ const session = this.currentTinyCloudSession();
19218
19622
  if (!session) {
19219
19623
  return void 0;
19220
19624
  }
@@ -19314,6 +19718,34 @@ var _TinyCloudNode = class _TinyCloudNode {
19314
19718
  }
19315
19719
  return this._sql;
19316
19720
  }
19721
+ /**
19722
+ * Get an SQL service scoped to a specific space.
19723
+ *
19724
+ * Mirrors {@link SpaceService}'s per-space KV factory: clones the active
19725
+ * service context and overrides its session's spaceId so that subsequent
19726
+ * `sql/<dbName>/<action>` invocations route to that space. Useful when
19727
+ * the caller already holds a delegation covering the target space (e.g.
19728
+ * via {@link grantRuntimePermissions} or {@link useRuntimeDelegation})
19729
+ * but the SDK's per-space SQL surface isn't otherwise exposed.
19730
+ *
19731
+ * Does NOT auto-create the space.
19732
+ *
19733
+ * @param spaceId - Full space URI (`tinycloud:pkh:eip155:<chain>:<addr>:<name>`).
19734
+ */
19735
+ sqlForSpace(spaceId) {
19736
+ if (!this._serviceContext || !this._serviceContext.session) {
19737
+ throw new Error("Not signed in. Call signIn() first.");
19738
+ }
19739
+ const sql = new SQLService2({});
19740
+ const spaceScopedContext = new ServiceContext2({
19741
+ invoke: this._serviceContext.invoke,
19742
+ fetch: this._serviceContext.fetch,
19743
+ hosts: this._serviceContext.hosts
19744
+ });
19745
+ spaceScopedContext.setSession({ ...this._serviceContext.session, spaceId });
19746
+ sql.initialize(spaceScopedContext);
19747
+ return sql;
19748
+ }
19317
19749
  /**
19318
19750
  * DuckDB database operations on this user's space.
19319
19751
  */
@@ -19337,6 +19769,79 @@ var _TinyCloudNode = class _TinyCloudNode {
19337
19769
  }
19338
19770
  return this._vault;
19339
19771
  }
19772
+ /**
19773
+ * Network-scoped encryption/decrypt service.
19774
+ */
19775
+ get encryption() {
19776
+ return this.getEncryptionService();
19777
+ }
19778
+ async getEncryptionNetwork(nameOrNetworkId = this.getDefaultEncryptionNetworkId()) {
19779
+ const networkId = nameOrNetworkId.startsWith("urn:tinycloud:encryption:") ? nameOrNetworkId : this.getDefaultEncryptionNetworkId(nameOrNetworkId);
19780
+ const response = await fetch(
19781
+ `${this.config.host}/encryption/networks/${encodeURIComponent(networkId)}`
19782
+ );
19783
+ if (response.status === 404) {
19784
+ return null;
19785
+ }
19786
+ if (!response.ok) {
19787
+ throw new Error(
19788
+ `Failed to fetch encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
19789
+ );
19790
+ }
19791
+ const body = await response.json();
19792
+ return "descriptor" in body && body.descriptor ? body.descriptor : body;
19793
+ }
19794
+ async createEncryptionNetwork(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
19795
+ const targetNode = await this.fetchNodeId();
19796
+ const principal = this.did;
19797
+ const networkId = this.getDefaultEncryptionNetworkId(name);
19798
+ const body = {
19799
+ name,
19800
+ principal,
19801
+ threshold: { n: 1, t: 1 }
19802
+ };
19803
+ const crypto2 = this.createEncryptionCrypto();
19804
+ const facts = {
19805
+ type: NETWORK_ADMIN_TYPE,
19806
+ targetNode,
19807
+ networkId,
19808
+ bodyHash: canonicalHashHex(
19809
+ crypto2.sha256,
19810
+ body
19811
+ ),
19812
+ action: NETWORK_CREATE_ACTION
19813
+ };
19814
+ const signed2 = await this.signRawNetworkAuthorization({
19815
+ targetNode,
19816
+ networkId,
19817
+ action: NETWORK_CREATE_ACTION,
19818
+ facts
19819
+ });
19820
+ const response = await fetch(`${this.config.host}/encryption/networks`, {
19821
+ method: "POST",
19822
+ headers: {
19823
+ Authorization: signed2.authorization,
19824
+ "Content-Type": "application/json"
19825
+ },
19826
+ body: canonicalizeEncryptionJson(
19827
+ body
19828
+ )
19829
+ });
19830
+ if (!response.ok) {
19831
+ throw new Error(
19832
+ `Failed to create encryption network ${networkId}: HTTP ${response.status} ${await response.text()}`
19833
+ );
19834
+ }
19835
+ const created = await response.json();
19836
+ return created.descriptor;
19837
+ }
19838
+ async ensureEncryptionNetwork(name = DEFAULT_ENCRYPTION_NETWORK_NAME) {
19839
+ const existing = await this.getEncryptionNetwork(name);
19840
+ if (existing) {
19841
+ return existing;
19842
+ }
19843
+ return this.createEncryptionNetwork(name);
19844
+ }
19340
19845
  /**
19341
19846
  * App-facing secrets API backed by the `secrets` space vault.
19342
19847
  */
@@ -19348,8 +19853,10 @@ var _TinyCloudNode = class _TinyCloudNode {
19348
19853
  this._secrets = new NodeSecretsService({
19349
19854
  getService: () => this.getBaseSecrets(),
19350
19855
  getManifest: () => this.manifest,
19856
+ hasPermissions: (permissions) => this.hasRuntimePermissions(permissions),
19351
19857
  grantPermissions: (additional) => this.grantRuntimePermissions(additional),
19352
19858
  canEscalate: () => this.signer !== void 0 && this.tc !== void 0,
19859
+ getEncryptionNetworkId: () => this.getDefaultEncryptionNetworkId(),
19353
19860
  getUnlockSigner: () => this.signer ?? void 0
19354
19861
  });
19355
19862
  }
@@ -19439,7 +19946,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19439
19946
  * every requested permission.
19440
19947
  */
19441
19948
  hasRuntimePermissions(permissions) {
19442
- const session = this.auth?.tinyCloudSession;
19949
+ const session = this.currentTinyCloudSession();
19443
19950
  if (!session || !Array.isArray(permissions) || permissions.length === 0) {
19444
19951
  return false;
19445
19952
  }
@@ -19459,7 +19966,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19459
19966
  if (permissions === void 0) {
19460
19967
  return this.runtimePermissionGrants.map((grant) => grant.delegation);
19461
19968
  }
19462
- const session = this.auth?.tinyCloudSession;
19969
+ const session = this.currentTinyCloudSession();
19463
19970
  if (!session || !Array.isArray(permissions) || permissions.length === 0) {
19464
19971
  return [];
19465
19972
  }
@@ -19473,7 +19980,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19473
19980
  * matching service calls and downstream `delegateTo()` calls can use it.
19474
19981
  */
19475
19982
  async useRuntimeDelegation(delegation) {
19476
- const session = this.auth?.tinyCloudSession;
19983
+ const session = this.currentTinyCloudSession();
19477
19984
  if (!session) {
19478
19985
  throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
19479
19986
  }
@@ -19512,7 +20019,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19512
20019
  if (!Array.isArray(permissions) || permissions.length === 0) {
19513
20020
  throw new Error("grantRuntimePermissions requires a non-empty permissions array");
19514
20021
  }
19515
- const session = this.auth?.tinyCloudSession;
20022
+ const session = this.currentTinyCloudSession();
19516
20023
  if (!session) {
19517
20024
  throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
19518
20025
  }
@@ -19536,13 +20043,22 @@ var _TinyCloudNode = class _TinyCloudNode {
19536
20043
  "grantRuntimePermissions requires wallet mode with a signer or privateKey."
19537
20044
  );
19538
20045
  }
20046
+ const rawEntries = expanded.filter(
20047
+ (entry) => this.isEncryptionPermissionEntry(entry)
20048
+ );
20049
+ const spaceEntries = expanded.filter(
20050
+ (entry) => !this.isEncryptionPermissionEntry(entry)
20051
+ );
19539
20052
  const bySpace = /* @__PURE__ */ new Map();
19540
- for (const entry of expanded) {
20053
+ for (const entry of spaceEntries) {
19541
20054
  const spaceId = this.resolvePermissionSpace(entry.space, session);
19542
20055
  const current = bySpace.get(spaceId) ?? [];
19543
20056
  current.push(entry);
19544
20057
  bySpace.set(spaceId, current);
19545
20058
  }
20059
+ if (bySpace.size === 0 && rawEntries.length > 0) {
20060
+ bySpace.set(session.spaceId, []);
20061
+ }
19546
20062
  const now = /* @__PURE__ */ new Date();
19547
20063
  const requestedExpiryMs = resolveExpiryMs(options?.expiry);
19548
20064
  let expiresAt = new Date(now.getTime() + requestedExpiryMs);
@@ -19550,10 +20066,17 @@ var _TinyCloudNode = class _TinyCloudNode {
19550
20066
  expiresAt = sessionExpiry;
19551
20067
  }
19552
20068
  const delegations = [];
20069
+ let rawEntriesAttached = false;
19553
20070
  for (const [spaceId, entries] of bySpace) {
20071
+ const rawForDelegation = !rawEntriesAttached ? rawEntries : [];
20072
+ if (rawForDelegation.length > 0) {
20073
+ rawEntriesAttached = true;
20074
+ }
20075
+ const delegatedEntries = [...entries, ...rawForDelegation];
19554
20076
  const abilities = this.permissionsToAbilities(entries);
19555
20077
  const prepared = this.wasmBindings.prepareSession({
19556
20078
  abilities,
20079
+ ...rawForDelegation.length > 0 ? { rawAbilities: this.permissionsToRawAbilities(rawForDelegation) } : {},
19557
20080
  address: this.wasmBindings.ensureEip55(session.address),
19558
20081
  chainId: session.chainId,
19559
20082
  domain: this.siweDomain,
@@ -19578,7 +20101,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19578
20101
  }
19579
20102
  const delegation = this.runtimeDelegationFromSession(
19580
20103
  delegatedSession,
19581
- entries,
20104
+ delegatedEntries,
19582
20105
  spaceId,
19583
20106
  session,
19584
20107
  expiresAt
@@ -19592,7 +20115,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19592
20115
  jwk: session.jwk
19593
20116
  },
19594
20117
  delegation,
19595
- operations: this.permissionOperations(entries, spaceId),
20118
+ operations: this.permissionOperations(delegatedEntries, spaceId),
19596
20119
  expiresAt
19597
20120
  });
19598
20121
  delegations.push(delegation);
@@ -19740,7 +20263,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19740
20263
  ];
19741
20264
  const abilities = { kv: { "": kvActions } };
19742
20265
  const now = /* @__PURE__ */ new Date();
19743
- const expiryMs = 60 * 60 * 1e3;
20266
+ const expiryMs = EXPIRY3.EPHEMERAL_MS;
19744
20267
  const expirationTime = new Date(now.getTime() + expiryMs);
19745
20268
  const prepared = this.wasmBindings.prepareSession({
19746
20269
  abilities,
@@ -19910,7 +20433,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19910
20433
  * `forceWalletSign` is not set.
19911
20434
  */
19912
20435
  async delegateTo(did, permissions, options) {
19913
- const session = this.auth?.tinyCloudSession;
20436
+ const session = this.currentTinyCloudSession();
19914
20437
  if (!session) {
19915
20438
  throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
19916
20439
  }
@@ -20024,11 +20547,8 @@ var _TinyCloudNode = class _TinyCloudNode {
20024
20547
  * the current session; we build one multi-resource abilities map
20025
20548
  * and emit one signed UCAN covering them all.
20026
20549
  *
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.
20550
+ * Non-encryption entries must share the same target space. Encryption
20551
+ * entries are raw network URNs and do not participate in space grouping.
20032
20552
  *
20033
20553
  * @internal
20034
20554
  */
@@ -20040,15 +20560,18 @@ var _TinyCloudNode = class _TinyCloudNode {
20040
20560
  }
20041
20561
  const resolvedSpaces = /* @__PURE__ */ new Set();
20042
20562
  for (const entry of entries) {
20563
+ if (this.isEncryptionPermissionEntry(entry)) {
20564
+ continue;
20565
+ }
20043
20566
  const spaceId2 = this.resolvePermissionSpace(entry.space, session);
20044
20567
  resolvedSpaces.add(spaceId2);
20045
20568
  }
20046
- if (resolvedSpaces.size !== 1) {
20569
+ if (resolvedSpaces.size > 1) {
20047
20570
  throw new Error(
20048
20571
  `delegateTo: all permission entries must target the same space, got ${resolvedSpaces.size}: ${JSON.stringify([...resolvedSpaces])}`
20049
20572
  );
20050
20573
  }
20051
- const spaceId = [...resolvedSpaces][0];
20574
+ const spaceId = resolvedSpaces.size === 1 ? [...resolvedSpaces][0] : session.spaceId;
20052
20575
  const abilities = {};
20053
20576
  for (const entry of entries) {
20054
20577
  const shortService = SERVICE_LONG_TO_SHORT[entry.service];
@@ -20195,11 +20718,32 @@ var _TinyCloudNode = class _TinyCloudNode {
20195
20718
  }
20196
20719
  return abilities;
20197
20720
  }
20721
+ isEncryptionPermissionEntry(entry) {
20722
+ return entry.service === ENCRYPTION_PERMISSION_SERVICE2 && entry.path.startsWith("urn:tinycloud:encryption:");
20723
+ }
20724
+ permissionsToRawAbilities(entries) {
20725
+ const rawAbilities = {};
20726
+ for (const entry of entries) {
20727
+ if (!this.isEncryptionPermissionEntry(entry)) {
20728
+ continue;
20729
+ }
20730
+ const existing = rawAbilities[entry.path] ?? [];
20731
+ const seen = new Set(existing);
20732
+ for (const action of entry.actions) {
20733
+ if (!seen.has(action)) {
20734
+ existing.push(action);
20735
+ seen.add(action);
20736
+ }
20737
+ }
20738
+ rawAbilities[entry.path] = existing;
20739
+ }
20740
+ return rawAbilities;
20741
+ }
20198
20742
  permissionOperations(entries, spaceId) {
20199
20743
  return entries.flatMap((entry) => {
20200
20744
  const service = this.shortServiceName(entry.service);
20201
20745
  return entry.actions.map((action) => ({
20202
- spaceId,
20746
+ ...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
20203
20747
  service,
20204
20748
  path: entry.path,
20205
20749
  action
@@ -20222,7 +20766,7 @@ var _TinyCloudNode = class _TinyCloudNode {
20222
20766
  const spaceId = this.resolvePermissionSpace(entry.space, session);
20223
20767
  const service = this.shortServiceName(entry.service);
20224
20768
  return entry.actions.map((action) => ({
20225
- spaceId,
20769
+ ...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
20226
20770
  service,
20227
20771
  path: entry.path,
20228
20772
  action
@@ -20279,24 +20823,40 @@ var _TinyCloudNode = class _TinyCloudNode {
20279
20823
  expiresAt: delegation.expiry
20280
20824
  };
20281
20825
  }
20826
+ installRuntimeGrantFromServiceSession(delegation, session, expiresAt) {
20827
+ const operations = this.operationsFromDelegation(delegation);
20828
+ if (operations.length === 0) {
20829
+ return;
20830
+ }
20831
+ this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
20832
+ (grant) => grant.delegation.cid !== delegation.cid && grant.session.delegationCid !== session.delegationCid
20833
+ );
20834
+ this.runtimePermissionGrants.push({
20835
+ session,
20836
+ delegation,
20837
+ operations,
20838
+ expiresAt
20839
+ });
20840
+ }
20282
20841
  delegatedResourcesForEntries(entries, spaceId) {
20283
20842
  return entries.map((entry) => ({
20284
20843
  service: this.shortServiceName(entry.service),
20285
- space: spaceId,
20844
+ space: this.isEncryptionPermissionEntry(entry) ? "encryption" : spaceId,
20286
20845
  path: entry.path,
20287
20846
  actions: [...entry.actions]
20288
20847
  }));
20289
20848
  }
20290
20849
  operationsFromDelegation(delegation) {
20291
20850
  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),
20851
+ return resources.flatMap((resource) => {
20852
+ const service = this.invocationServiceName(resource.service);
20853
+ return resource.actions.map((action) => ({
20854
+ ...this.isEncryptionNetworkOperation(service, resource.path) ? { resource: resource.path } : { spaceId: resource.space },
20855
+ service,
20296
20856
  path: resource.path,
20297
20857
  action
20298
- }))
20299
- );
20858
+ }));
20859
+ });
20300
20860
  }
20301
20861
  flatDelegationResources(delegation) {
20302
20862
  const byService = /* @__PURE__ */ new Map();
@@ -20345,7 +20905,13 @@ var _TinyCloudNode = class _TinyCloudNode {
20345
20905
  );
20346
20906
  }
20347
20907
  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);
20908
+ if (granted.service !== requested.service || !this.actionContains(granted.action, requested.action)) {
20909
+ return false;
20910
+ }
20911
+ if (granted.resource !== void 0 || requested.resource !== void 0) {
20912
+ return granted.resource !== void 0 && requested.resource !== void 0 && granted.resource === requested.resource && this.pathContains(granted.path, requested.path);
20913
+ }
20914
+ return granted.spaceId !== void 0 && requested.spaceId !== void 0 && granted.spaceId === requested.spaceId && this.pathContains(granted.path, requested.path);
20349
20915
  }
20350
20916
  actionContains(grantedAction, requestedAction) {
20351
20917
  if (grantedAction === requestedAction) {
@@ -20360,6 +20926,37 @@ var _TinyCloudNode = class _TinyCloudNode {
20360
20926
  invocationServiceName(service) {
20361
20927
  return service.startsWith("tinycloud.") ? this.shortServiceName(service) : service;
20362
20928
  }
20929
+ isEncryptionNetworkOperation(service, path) {
20930
+ return service === "encryption" && path.startsWith("urn:tinycloud:encryption:");
20931
+ }
20932
+ operationFromInvokeAnyEntry(entry) {
20933
+ const service = this.invocationServiceName(entry.service);
20934
+ if (typeof entry.resource === "string") {
20935
+ return {
20936
+ resource: entry.resource,
20937
+ service,
20938
+ path: entry.path,
20939
+ action: entry.action
20940
+ };
20941
+ }
20942
+ if (this.isEncryptionNetworkOperation(service, entry.path)) {
20943
+ return {
20944
+ resource: entry.path,
20945
+ service,
20946
+ path: entry.path,
20947
+ action: entry.action
20948
+ };
20949
+ }
20950
+ if (typeof entry.spaceId === "string") {
20951
+ return {
20952
+ spaceId: entry.spaceId,
20953
+ service,
20954
+ path: entry.path,
20955
+ action: entry.action
20956
+ };
20957
+ }
20958
+ return void 0;
20959
+ }
20363
20960
  pathContains(grantedPath, requestedPath) {
20364
20961
  if (grantedPath === "" || grantedPath === "/") {
20365
20962
  return true;
@@ -20598,6 +21195,17 @@ var _TinyCloudNode = class _TinyCloudNode {
20598
21195
  // Not used in session-only mode
20599
21196
  };
20600
21197
  this.trackReceivedDelegation(delegation, this.sessionKeyJwk);
21198
+ this.installRuntimeGrantFromServiceSession(
21199
+ delegation,
21200
+ {
21201
+ delegationHeader: session2.delegationHeader,
21202
+ delegationCid: session2.delegationCid,
21203
+ spaceId: session2.spaceId,
21204
+ verificationMethod: session2.verificationMethod,
21205
+ jwk: session2.jwk
21206
+ },
21207
+ delegation.expiry
21208
+ );
20601
21209
  return new DelegatedAccess(session2, delegation, targetHost, this.wasmBindings.invoke);
20602
21210
  }
20603
21211
  const mySession = this.auth?.tinyCloudSession;
@@ -20609,6 +21217,10 @@ var _TinyCloudNode = class _TinyCloudNode {
20609
21217
  const kvActions = delegation.actions.filter((a) => a.startsWith("tinycloud.kv/"));
20610
21218
  const sqlActions = delegation.actions.filter((a) => a.startsWith("tinycloud.sql/"));
20611
21219
  const duckdbActions = delegation.actions.filter((a) => a.startsWith("tinycloud.duckdb/"));
21220
+ const encryptionActions = delegation.actions.filter(
21221
+ (a) => a.startsWith("tinycloud.encryption/")
21222
+ );
21223
+ const rawAbilities = {};
20612
21224
  if (kvActions.length > 0) {
20613
21225
  abilities.kv = { [delegation.path]: kvActions };
20614
21226
  }
@@ -20618,6 +21230,9 @@ var _TinyCloudNode = class _TinyCloudNode {
20618
21230
  if (duckdbActions.length > 0) {
20619
21231
  abilities.duckdb = { [delegation.path]: duckdbActions };
20620
21232
  }
21233
+ if (encryptionActions.length > 0 && delegation.path.startsWith("urn:tinycloud:encryption:")) {
21234
+ rawAbilities[delegation.path] = encryptionActions;
21235
+ }
20621
21236
  const now = /* @__PURE__ */ new Date();
20622
21237
  const maxExpiry = new Date(now.getTime() + 60 * 60 * 1e3);
20623
21238
  const expirationTime = delegation.expiry < maxExpiry ? delegation.expiry : maxExpiry;
@@ -20630,7 +21245,8 @@ var _TinyCloudNode = class _TinyCloudNode {
20630
21245
  expirationTime: expirationTime.toISOString(),
20631
21246
  spaceId: delegation.spaceId,
20632
21247
  jwk,
20633
- parents: [delegation.cid]
21248
+ parents: [delegation.cid],
21249
+ ...Object.keys(rawAbilities).length > 0 ? { rawAbilities } : {}
20634
21250
  });
20635
21251
  const signature2 = await this.signer.signMessage(prepared.siwe);
20636
21252
  const invokerSession = this.wasmBindings.completeSessionSetup({
@@ -20657,6 +21273,17 @@ var _TinyCloudNode = class _TinyCloudNode {
20657
21273
  signature: signature2
20658
21274
  };
20659
21275
  this.trackReceivedDelegation(delegation, jwk);
21276
+ this.installRuntimeGrantFromServiceSession(
21277
+ delegation,
21278
+ {
21279
+ delegationHeader: session.delegationHeader,
21280
+ delegationCid: session.delegationCid,
21281
+ spaceId: session.spaceId,
21282
+ verificationMethod: session.verificationMethod,
21283
+ jwk: session.jwk
21284
+ },
21285
+ expirationTime
21286
+ );
20660
21287
  return new DelegatedAccess(session, delegation, targetHost, this.wasmBindings.invoke);
20661
21288
  }
20662
21289
  /**
@@ -20911,7 +21538,7 @@ import {
20911
21538
  loadManifest,
20912
21539
  isCapabilitySubset as isCapabilitySubset2,
20913
21540
  expandActionShortNames,
20914
- expandPermissionEntries,
21541
+ expandPermissionEntries as expandPermissionEntries2,
20915
21542
  expandPermissionEntry,
20916
21543
  parseExpiry as parseExpiry2,
20917
21544
  resourceCapabilitiesToSpaceAbilitiesMap as resourceCapabilitiesToSpaceAbilitiesMap2
@@ -20934,7 +21561,11 @@ function deserializeDelegation(data) {
20934
21561
  }
20935
21562
 
20936
21563
  // src/index.ts
20937
- import { KVService as KVService3, PrefixedKVService } from "@tinycloud/sdk-core";
21564
+ import {
21565
+ DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
21566
+ KVService as KVService3,
21567
+ PrefixedKVService
21568
+ } from "@tinycloud/sdk-core";
20938
21569
  import { SQLService as SQLService3, SQLAction, DatabaseHandle } from "@tinycloud/sdk-core";
20939
21570
  import {
20940
21571
  DuckDbService as DuckDbService3,
@@ -20946,7 +21577,53 @@ import {
20946
21577
  VaultHeaders,
20947
21578
  VaultPublicSpaceKVActions,
20948
21579
  createVaultCrypto as createVaultCrypto2,
20949
- SecretsService as SecretsService2
21580
+ SecretsService as SecretsService2,
21581
+ SECRET_NAME_RE,
21582
+ canonicalizeSecretScope,
21583
+ resolveSecretListPrefix as resolveSecretListPrefix2,
21584
+ resolveSecretPath as resolveSecretPath2
21585
+ } from "@tinycloud/sdk-core";
21586
+ import {
21587
+ EncryptionService as EncryptionService2,
21588
+ parseNetworkId,
21589
+ buildNetworkId,
21590
+ isNetworkId,
21591
+ networkDiscoveryKey,
21592
+ NetworkIdError,
21593
+ ENCRYPTION_NETWORK_URN_PREFIX,
21594
+ NETWORK_NAME_PATTERN,
21595
+ canonicalizeEncryptionJson as canonicalizeEncryptionJson2,
21596
+ canonicalHashHex as canonicalHashHex2,
21597
+ hexEncode,
21598
+ hexDecode,
21599
+ encryptionBase64Encode,
21600
+ encryptionBase64Decode,
21601
+ encryptionUtf8Encode,
21602
+ encryptionUtf8Decode,
21603
+ encryptToNetwork,
21604
+ decryptEnvelopeWithKey,
21605
+ validateEnvelope,
21606
+ generateRandomReceiverKey,
21607
+ deriveSignedReceiverKey,
21608
+ buildCanonicalDecryptRequest,
21609
+ buildDecryptFacts,
21610
+ buildDecryptAttenuation,
21611
+ buildDecryptInvocation,
21612
+ checkDecryptInvocationInput,
21613
+ verifyDecryptResponse,
21614
+ canonicalSignedResponse,
21615
+ openWrappedKey,
21616
+ discoverNetwork,
21617
+ ensureNetworkUsableForDecrypt,
21618
+ DEFAULT_ENCRYPTION_ALG,
21619
+ ENVELOPE_VERSION,
21620
+ DEFAULT_KEY_VERSION,
21621
+ DECRYPT_FACT_TYPE,
21622
+ DECRYPT_RESULT_TYPE,
21623
+ DECRYPT_ACTION as DECRYPT_ACTION2,
21624
+ ENCRYPTION_SERVICE,
21625
+ ENCRYPTION_SERVICE_SHORT,
21626
+ encryptionError
20950
21627
  } from "@tinycloud/sdk-core";
20951
21628
  import { HooksService as HooksService3 } from "@tinycloud/sdk-core";
20952
21629
  import {
@@ -20982,8 +21659,14 @@ export {
20982
21659
  AutoApproveSpaceCreationHandler2 as AutoApproveSpaceCreationHandler,
20983
21660
  CapabilityKeyRegistry2 as CapabilityKeyRegistry,
20984
21661
  CapabilityKeyRegistryErrorCodes,
21662
+ DECRYPT_ACTION2 as DECRYPT_ACTION,
21663
+ DECRYPT_FACT_TYPE,
21664
+ DECRYPT_RESULT_TYPE,
21665
+ DEFAULT_ENCRYPTION_ALG,
21666
+ DEFAULT_KEY_VERSION,
20985
21667
  DEFAULT_MANIFEST_SPACE2 as DEFAULT_MANIFEST_SPACE,
20986
21668
  DEFAULT_MANIFEST_VERSION,
21669
+ DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
20987
21670
  DataVaultService2 as DataVaultService,
20988
21671
  DatabaseHandle,
20989
21672
  DelegatedAccess,
@@ -20992,17 +21675,25 @@ export {
20992
21675
  DuckDbAction,
20993
21676
  DuckDbDatabaseHandle,
20994
21677
  DuckDbService3 as DuckDbService,
21678
+ ENCRYPTION_NETWORK_URN_PREFIX,
21679
+ ENCRYPTION_SERVICE,
21680
+ ENCRYPTION_SERVICE_SHORT,
21681
+ ENVELOPE_VERSION,
21682
+ EncryptionService2 as EncryptionService,
20995
21683
  FileSessionStorage,
20996
21684
  HooksService3 as HooksService,
20997
21685
  KVService3 as KVService,
20998
21686
  ManifestValidationError,
20999
21687
  MemorySessionStorage,
21688
+ NETWORK_NAME_PATTERN,
21689
+ NetworkIdError,
21000
21690
  NodeUserAuthorization,
21001
21691
  NodeWasmBindings,
21002
21692
  PermissionNotInManifestError2 as PermissionNotInManifestError,
21003
21693
  PrefixedKVService,
21004
21694
  PrivateKeySigner,
21005
21695
  ProtocolMismatchError,
21696
+ SECRET_NAME_RE,
21006
21697
  SQLAction,
21007
21698
  SQLService3 as SQLService,
21008
21699
  SecretsService2 as SecretsService,
@@ -21021,7 +21712,17 @@ export {
21021
21712
  VaultPublicSpaceKVActions,
21022
21713
  VersionCheckError,
21023
21714
  WasmKeyProvider,
21715
+ buildCanonicalDecryptRequest,
21716
+ buildDecryptAttenuation,
21717
+ buildDecryptFacts,
21718
+ buildDecryptInvocation,
21719
+ buildNetworkId,
21024
21720
  buildSpaceUri,
21721
+ canonicalHashHex2 as canonicalHashHex,
21722
+ canonicalSignedResponse,
21723
+ canonicalizeEncryptionJson2 as canonicalizeEncryptionJson,
21724
+ canonicalizeSecretScope,
21725
+ checkDecryptInvocationInput,
21025
21726
  checkNodeInfo2 as checkNodeInfo,
21026
21727
  composeManifestRequest2 as composeManifestRequest,
21027
21728
  createCapabilityKeyRegistry,
@@ -21029,21 +21730,42 @@ export {
21029
21730
  createSpaceService,
21030
21731
  createVaultCrypto2 as createVaultCrypto,
21031
21732
  createWasmKeyProvider,
21733
+ decryptEnvelopeWithKey,
21032
21734
  defaultSignStrategy,
21033
21735
  defaultSpaceCreationHandler,
21736
+ deriveSignedReceiverKey,
21034
21737
  deserializeDelegation,
21738
+ discoverNetwork,
21739
+ encryptToNetwork,
21740
+ encryptionBase64Decode,
21741
+ encryptionBase64Encode,
21742
+ encryptionError,
21743
+ encryptionUtf8Decode,
21744
+ encryptionUtf8Encode,
21745
+ ensureNetworkUsableForDecrypt,
21035
21746
  expandActionShortNames,
21036
- expandPermissionEntries,
21747
+ expandPermissionEntries2 as expandPermissionEntries,
21037
21748
  expandPermissionEntry,
21749
+ generateRandomReceiverKey,
21750
+ hexDecode,
21751
+ hexEncode,
21038
21752
  isCapabilitySubset2 as isCapabilitySubset,
21753
+ isNetworkId,
21039
21754
  loadManifest,
21040
21755
  makePublicSpaceId2 as makePublicSpaceId,
21756
+ networkDiscoveryKey,
21757
+ openWrappedKey,
21041
21758
  parseExpiry2 as parseExpiry,
21759
+ parseNetworkId,
21042
21760
  parseSpaceUri,
21043
21761
  resolveManifest2 as resolveManifest,
21762
+ resolveSecretListPrefix2 as resolveSecretListPrefix,
21763
+ resolveSecretPath2 as resolveSecretPath,
21044
21764
  resourceCapabilitiesToSpaceAbilitiesMap2 as resourceCapabilitiesToSpaceAbilitiesMap,
21045
21765
  serializeDelegation,
21046
- validateManifest
21766
+ validateEnvelope,
21767
+ validateManifest,
21768
+ verifyDecryptResponse
21047
21769
  };
21048
21770
  /*! Bundled license information:
21049
21771