@tinycloud/node-sdk 2.11.0 → 3.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/core.cjs CHANGED
@@ -78,6 +78,7 @@ __export(core_exports, {
78
78
  VaultPublicSpaceKVActions: () => import_sdk_core16.VaultPublicSpaceKVActions,
79
79
  VersionCheckError: () => import_sdk_core20.VersionCheckError,
80
80
  WasmKeyProvider: () => WasmKeyProvider,
81
+ activateCompactRuntimeDelegation: () => activateCompactRuntimeDelegation,
81
82
  addressStorageKey: () => import_sdk_core11.addressStorageKey,
82
83
  buildSpaceUri: () => import_sdk_core19.buildSpaceUri,
83
84
  canonicalizeAddress: () => import_sdk_core11.canonicalizeAddress,
@@ -2179,6 +2180,18 @@ var ROOT_DELEGATION_ACTIONS = [
2179
2180
  import_sdk_core8.DUCKDB.ALL
2180
2181
  ];
2181
2182
  var DEFAULT_SESSION_EXPIRATION_MS = import_sdk_core8.EXPIRY.SESSION_MS;
2183
+ function unifiedRootAttenuation(capabilities) {
2184
+ const attenuation = {};
2185
+ for (const capability of capabilities) {
2186
+ if (attenuation[capability.resource] !== void 0) throw new Error("unified owner root contains a duplicate resource");
2187
+ attenuation[capability.resource] = capability.kind === "encryption" ? { [capability.action]: [{}] } : Object.fromEntries(capability.actions.map((action) => [action, [{
2188
+ type: "xyz.tinycloud.resource/selector",
2189
+ kind: capability.selector,
2190
+ value: capability.resource
2191
+ }]]));
2192
+ }
2193
+ return attenuation;
2194
+ }
2182
2195
  function decodeAuthorizationBytes(authorization) {
2183
2196
  const encoded = authorization.replace(/^Bearer /i, "");
2184
2197
  const match = /^([A-Za-z0-9_-]+)(={1,2})?$/.exec(encoded);
@@ -2558,6 +2571,8 @@ var _TinyCloudNode = class _TinyCloudNode {
2558
2571
  * hosting is confirmed against the session that was active at the time.
2559
2572
  */
2560
2573
  this.confirmedHostedSpaceIds = /* @__PURE__ */ new Set();
2574
+ /** Serializes account-registry writes for the active node instance. */
2575
+ this.accountRegistryTail = Promise.resolve();
2561
2576
  /**
2562
2577
  * True when the last signIn() detected an interactive signer and skipped
2563
2578
  * client-side bootstrap. Apps can read this to know whether bootstrap was
@@ -2954,6 +2969,7 @@ var _TinyCloudNode = class _TinyCloudNode {
2954
2969
  "Cannot signIn() in session-only mode. Provide a privateKey in config to create your own space."
2955
2970
  );
2956
2971
  }
2972
+ await this.accountRegistryTail;
2957
2973
  await this.wasmBindings.ensureInitialized?.();
2958
2974
  this._address = (0, import_sdk_core8.canonicalizeAddress)(await this.signer.getAddress());
2959
2975
  this._chainId = await this.signer.getChainId();
@@ -3435,16 +3451,48 @@ var _TinyCloudNode = class _TinyCloudNode {
3435
3451
  }
3436
3452
  }
3437
3453
  scheduleAccountRegistrySync() {
3438
- void this.withAccountRegistryRetry(async () => {
3439
- void this.account.index.ensure();
3440
- await this.writeManifestRegistryRecords();
3441
- if (this.currentSessionCanListSpaces()) {
3442
- const spaces = await this.account.spaces.syncAccessible();
3443
- if (!spaces.ok) {
3444
- throw new Error(`Failed to sync account spaces: ${spaces.error.message}`);
3454
+ const session = this.currentTinyCloudSession();
3455
+ if (!session || this.pendingAccountRegistrySync?.session === session) {
3456
+ return;
3457
+ }
3458
+ const promise = this.enqueueAccountRegistryOperation(session, async () => {
3459
+ await this.withAccountRegistryRetry(async () => {
3460
+ if (this.currentTinyCloudSession() !== session) {
3461
+ return;
3462
+ }
3463
+ await this.account.index.ensure();
3464
+ if (this.currentTinyCloudSession() !== session) {
3465
+ return;
3466
+ }
3467
+ await this.writeManifestRegistryRecords();
3468
+ if (this.currentTinyCloudSession() !== session) {
3469
+ return;
3470
+ }
3471
+ if (this.currentSessionCanListSpaces()) {
3472
+ const spaces = await this.account.spaces.syncAccessible();
3473
+ if (!spaces.ok) {
3474
+ throw new Error(`Failed to sync account spaces: ${spaces.error.message}`);
3475
+ }
3445
3476
  }
3477
+ });
3478
+ });
3479
+ this.pendingAccountRegistrySync = { session, promise };
3480
+ const clearPendingSync = () => {
3481
+ if (this.pendingAccountRegistrySync?.promise === promise) {
3482
+ this.pendingAccountRegistrySync = void 0;
3483
+ }
3484
+ };
3485
+ void promise.then(clearPendingSync, clearPendingSync);
3486
+ }
3487
+ enqueueAccountRegistryOperation(session, task) {
3488
+ const operation = this.accountRegistryTail.then(async () => {
3489
+ if (this.currentTinyCloudSession() === session) {
3490
+ await task();
3446
3491
  }
3447
3492
  });
3493
+ this.accountRegistryTail = operation.catch(() => {
3494
+ });
3495
+ return operation;
3448
3496
  }
3449
3497
  /**
3450
3498
  * Whether the current primary session may invoke `tinycloud.space/list`.
@@ -3597,7 +3645,8 @@ var _TinyCloudNode = class _TinyCloudNode {
3597
3645
  * @returns The hosted space URI.
3598
3646
  */
3599
3647
  async hostOwnedSpace(name) {
3600
- if (!this.auth || !this.auth.tinyCloudSession) {
3648
+ const session = this.auth?.tinyCloudSession;
3649
+ if (!this.auth || !session) {
3601
3650
  throw new Error("Not signed in. Call signIn() first.");
3602
3651
  }
3603
3652
  const spaceId = this.ownedSpaceId(name);
@@ -3613,22 +3662,26 @@ var _TinyCloudNode = class _TinyCloudNode {
3613
3662
  }
3614
3663
  const activation = await (0, import_sdk_core8.activateSessionWithHost)(
3615
3664
  host,
3616
- this.auth.tinyCloudSession.delegationHeader
3665
+ session.delegationHeader
3617
3666
  );
3618
3667
  if (!activation.success || activation.skipped?.includes(spaceId)) {
3619
3668
  throw new Error(
3620
3669
  `Failed to activate session for owned space ${spaceId}: ${activation.error ?? "space was skipped"}`
3621
3670
  );
3622
3671
  }
3623
- void this.account.spaces.register({
3624
- spaceId,
3625
- name,
3626
- ownerDid: this.did,
3627
- type: "owned",
3628
- permissions: ["*"],
3629
- status: "active"
3630
- }).catch(() => {
3631
- });
3672
+ try {
3673
+ await this.enqueueAccountRegistryOperation(session, async () => {
3674
+ await this.account.spaces.register({
3675
+ spaceId,
3676
+ name,
3677
+ ownerDid: this.did,
3678
+ type: "owned",
3679
+ permissions: ["*"],
3680
+ status: "active"
3681
+ });
3682
+ });
3683
+ } catch {
3684
+ }
3632
3685
  return spaceId;
3633
3686
  }
3634
3687
  /**
@@ -3663,23 +3716,12 @@ var _TinyCloudNode = class _TinyCloudNode {
3663
3716
  if (!this.auth || !this.auth.tinyCloudSession) {
3664
3717
  throw new Error("Not signed in. Call signIn() first.");
3665
3718
  }
3719
+ await this.accountRegistryTail;
3666
3720
  const spaceId = this.ownedSpaceId(name);
3667
3721
  if (await this.isOwnedSpaceRegistered(spaceId)) {
3668
3722
  return spaceId;
3669
3723
  }
3670
- const hosted = await this.hostOwnedSpace(name);
3671
- try {
3672
- await this.account.spaces.register({
3673
- spaceId,
3674
- name,
3675
- ownerDid: this.did,
3676
- type: "owned",
3677
- permissions: ["*"],
3678
- status: "active"
3679
- });
3680
- } catch {
3681
- }
3682
- return hosted;
3724
+ return this.hostOwnedSpace(name);
3683
3725
  }
3684
3726
  /** Resolve and authenticate the owner encoded by an owned-space URI. */
3685
3727
  credentialSpaceOwnerDid(spaceId) {
@@ -4232,6 +4274,46 @@ var _TinyCloudNode = class _TinyCloudNode {
4232
4274
  if (session === void 0) throw new Error("Not signed in. Call signIn() first.");
4233
4275
  return signBytesWithJwk(bytes, session.jwk);
4234
4276
  }
4277
+ /** Author a proofless Policy/v3 owner root with the authenticated holder key. */
4278
+ async createUnifiedOwnerRoot(input) {
4279
+ const ownerDid = this.credentialHolderDid;
4280
+ if (input.ownerDid !== ownerDid) throw new Error("unified owner root signer does not match owner DID");
4281
+ const facts = {
4282
+ role: input.role,
4283
+ mode: input.role === "policy-authority" ? "policy-source" : "conditional-mint",
4284
+ ownerDid,
4285
+ policyId: input.policyId,
4286
+ policyDigestHex: input.policyDigestHex,
4287
+ policyCid: input.policyCid,
4288
+ contentSourceDigestHex: input.contentSourceDigestHex,
4289
+ capabilityCeilingHashHex: input.capabilityCeilingHashHex,
4290
+ nativeProjectionHashHex: input.nativeProjectionHashHex,
4291
+ nodeAudience: input.nodeAudience,
4292
+ ...input.role === "policy-enforcement" ? { enforcerDid: input.audienceDid } : {}
4293
+ };
4294
+ const root = await (0, import_sdk_core8.signCompactUcanRootAuthorization)({
4295
+ issuerDid: ownerDid,
4296
+ audienceDid: input.audienceDid,
4297
+ attenuation: unifiedRootAttenuation(input.capabilities),
4298
+ facts: [facts],
4299
+ notBefore: Math.floor(input.notBefore.getTime() / 1e3),
4300
+ expiresAt: Math.floor(input.expiresAt.getTime() / 1e3),
4301
+ nonce: base64UrlEncode(crypto.getRandomValues(new Uint8Array(16))),
4302
+ sign: (bytes) => this.signSessionBytes(bytes)
4303
+ });
4304
+ const kv = input.capabilities.find((capability) => capability.kind === "kv");
4305
+ const resource = kv?.resource ?? "";
4306
+ const marker = resource.indexOf("/kv/");
4307
+ return {
4308
+ cid: root.cid,
4309
+ delegationHeader: { Authorization: root.authorization },
4310
+ delegateDID: input.audienceDid,
4311
+ spaceId: marker === -1 ? "" : resource.slice("tinycloud://".length, marker),
4312
+ path: marker === -1 ? resource : resource.slice(marker + 4),
4313
+ actions: kv?.actions ?? [],
4314
+ expiry: input.expiresAt
4315
+ };
4316
+ }
4235
4317
  /** Apply only an already-enabled signing policy to the exact credential request. */
4236
4318
  async autoSignCredentialBytes(bytes) {
4237
4319
  const strategy = this.config.signStrategy;
@@ -7264,6 +7346,338 @@ var import_sdk_core12 = require("@tinycloud/sdk-core");
7264
7346
 
7265
7347
  // src/delegation.ts
7266
7348
  var import_sdk_core9 = require("@tinycloud/sdk-core");
7349
+ var SERVICE_BY_SHORT_NAME = {
7350
+ kv: "tinycloud.kv",
7351
+ sql: "tinycloud.sql",
7352
+ duckdb: "tinycloud.duckdb",
7353
+ hooks: "tinycloud.hooks"
7354
+ };
7355
+ function authorizationWithoutBearer(authorization) {
7356
+ return authorization.replace(/^Bearer /i, "");
7357
+ }
7358
+ function normalizedHost(host) {
7359
+ if (typeof host !== "string" || host.length === 0) {
7360
+ throw new Error("Validated runtime delegation requires a non-empty host.");
7361
+ }
7362
+ return host.replace(/\/+$/, "");
7363
+ }
7364
+ function didPrincipalsMatch(left, right) {
7365
+ try {
7366
+ return (0, import_sdk_core9.principalDidEquals)(left, right);
7367
+ } catch {
7368
+ return left === right;
7369
+ }
7370
+ }
7371
+ function compactUcanPayload(authorization) {
7372
+ const compact = authorizationWithoutBearer(authorization);
7373
+ const parts = compact.split(".");
7374
+ if (parts.length !== 3 || parts.some((part) => part.length === 0)) {
7375
+ throw new Error(
7376
+ "Validated runtime delegation requires a compact UCAN authorization so its signed authority can be derived."
7377
+ );
7378
+ }
7379
+ try {
7380
+ const payload = JSON.parse(
7381
+ new TextDecoder("utf-8", { fatal: true }).decode((0, import_sdk_core9.decodeBase64Url)(parts[1]))
7382
+ );
7383
+ if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
7384
+ throw new Error("payload is not an object");
7385
+ }
7386
+ return payload;
7387
+ } catch {
7388
+ throw new Error(
7389
+ "Validated runtime delegation authorization has an invalid compact UCAN payload."
7390
+ );
7391
+ }
7392
+ }
7393
+ function canonicalPermissionEntries(permissions) {
7394
+ return permissions.map((permission) => ({
7395
+ ...permission,
7396
+ actions: [...permission.actions].sort()
7397
+ })).sort(
7398
+ (left, right) => left.service.localeCompare(right.service) || (left.space ?? "").localeCompare(right.space ?? "") || left.path.localeCompare(right.path) || left.actions.join("\0").localeCompare(right.actions.join("\0")) || (0, import_sdk_core9.canonicalizeRecapCaveats)(left.caveats).localeCompare((0, import_sdk_core9.canonicalizeRecapCaveats)(right.caveats))
7399
+ );
7400
+ }
7401
+ function signedCaveats(value) {
7402
+ if (!Array.isArray(value) || value.some(
7403
+ (caveat) => caveat === null || typeof caveat !== "object" || Array.isArray(caveat)
7404
+ )) {
7405
+ throw new Error("Validated runtime delegation has malformed signed ReCap caveats.");
7406
+ }
7407
+ const cloned = JSON.parse(JSON.stringify(value));
7408
+ return (0, import_sdk_core9.canonicalizeRecapCaveats)(cloned) === (0, import_sdk_core9.canonicalizeRecapCaveats)(void 0) ? void 0 : cloned;
7409
+ }
7410
+ function groupedSignedEntries(service, space, path, abilities) {
7411
+ const groups = /* @__PURE__ */ new Map();
7412
+ for (const [action, rawCaveats] of Object.entries(abilities)) {
7413
+ const caveats = signedCaveats(rawCaveats);
7414
+ const identity = (0, import_sdk_core9.canonicalizeRecapCaveats)(caveats);
7415
+ const group = groups.get(identity) ?? { actions: [], caveats };
7416
+ group.actions.push(action);
7417
+ groups.set(identity, group);
7418
+ }
7419
+ return [...groups.values()].map((group) => ({
7420
+ service,
7421
+ ...space === void 0 ? {} : { space },
7422
+ path,
7423
+ actions: group.actions.sort(),
7424
+ ...group.caveats === void 0 ? {} : { caveats: group.caveats }
7425
+ }));
7426
+ }
7427
+ function signedAuthorityFromCompactUcan(authorization) {
7428
+ const payload = compactUcanPayload(authorization);
7429
+ if (typeof payload.aud !== "string" || payload.aud.length === 0) {
7430
+ throw new Error("Validated runtime delegation is missing a signed audience.");
7431
+ }
7432
+ if (typeof payload.iss !== "string" || payload.iss.length === 0) {
7433
+ throw new Error("Validated runtime delegation is missing a signed issuer.");
7434
+ }
7435
+ if (!Array.isArray(payload.prf) || payload.prf.some(
7436
+ (proof) => typeof proof !== "string" || proof.length === 0
7437
+ )) {
7438
+ throw new Error("Validated runtime delegation has malformed signed proofs.");
7439
+ }
7440
+ if (typeof payload.exp !== "number" || !Number.isFinite(payload.exp)) {
7441
+ throw new Error("Validated runtime delegation is missing a signed expiry.");
7442
+ }
7443
+ const expiry = new Date(payload.exp * 1e3);
7444
+ if (Number.isNaN(expiry.getTime())) {
7445
+ throw new Error("Validated runtime delegation has an invalid signed expiry.");
7446
+ }
7447
+ if (payload.att === null || typeof payload.att !== "object" || Array.isArray(payload.att)) {
7448
+ throw new Error("Validated runtime delegation is missing a signed attenuation.");
7449
+ }
7450
+ const fact = Array.isArray(payload.fct) && payload.fct.length > 0 && payload.fct[0] !== null && typeof payload.fct[0] === "object" && !Array.isArray(payload.fct[0]) ? payload.fct[0] : void 0;
7451
+ const remainingRedelegationDepth = fact?.remainingRedelegationDepth;
7452
+ if (remainingRedelegationDepth !== void 0 && (!Number.isInteger(remainingRedelegationDepth) || remainingRedelegationDepth < 0 || remainingRedelegationDepth > 8)) {
7453
+ throw new Error("Validated runtime delegation has invalid signed redelegation depth.");
7454
+ }
7455
+ const permissions = [];
7456
+ for (const [resource, abilities] of Object.entries(payload.att)) {
7457
+ if (abilities === null || typeof abilities !== "object" || Array.isArray(abilities)) {
7458
+ throw new Error("Validated runtime delegation has an invalid signed attenuation.");
7459
+ }
7460
+ const abilityEntries = Object.entries(abilities);
7461
+ if (abilityEntries.length === 0) {
7462
+ throw new Error("Validated runtime delegation has an empty signed capability.");
7463
+ }
7464
+ if (resource.startsWith("urn:tinycloud:encryption:")) {
7465
+ permissions.push(...groupedSignedEntries(
7466
+ "tinycloud.encryption",
7467
+ void 0,
7468
+ resource,
7469
+ abilities
7470
+ ));
7471
+ continue;
7472
+ }
7473
+ const nativeMatch = /^(tinycloud:[^/]+)\/(kv|sql|duckdb|hooks)\/(.*)$/.exec(resource);
7474
+ const policyMatch = /^tinycloud:\/\/([^/]+)\/(kv|sql|duckdb|hooks)\/(.*)$/.exec(resource);
7475
+ const match = nativeMatch ?? policyMatch;
7476
+ if (!match) {
7477
+ throw new Error(
7478
+ `Validated runtime delegation has an unsupported signed resource '${resource}'.`
7479
+ );
7480
+ }
7481
+ const [, space, shortService, path] = match;
7482
+ const service = SERVICE_BY_SHORT_NAME[shortService];
7483
+ if (service === void 0) {
7484
+ throw new Error(
7485
+ `Validated runtime delegation has an unsupported signed service '${shortService}'.`
7486
+ );
7487
+ }
7488
+ permissions.push(...groupedSignedEntries(
7489
+ service,
7490
+ space,
7491
+ path,
7492
+ abilities
7493
+ ));
7494
+ }
7495
+ if (permissions.length === 0) {
7496
+ throw new Error("Validated runtime delegation has no signed capabilities.");
7497
+ }
7498
+ const canonicalPermissions = canonicalPermissionEntries(permissions);
7499
+ const canonicalResources = canonicalPermissions.map((permission) => ({
7500
+ service: permission.service === "tinycloud.encryption" ? "encryption" : permission.service.slice("tinycloud.".length),
7501
+ space: permission.service === "tinycloud.encryption" ? "encryption" : permission.space,
7502
+ path: permission.path,
7503
+ actions: [...permission.actions],
7504
+ ...permission.caveats === void 0 ? {} : { caveats: permission.caveats }
7505
+ }));
7506
+ return {
7507
+ issuer: payload.iss,
7508
+ audience: payload.aud,
7509
+ expiry,
7510
+ proofs: [...payload.prf],
7511
+ ...remainingRedelegationDepth === void 0 ? {} : { remainingRedelegationDepth },
7512
+ permissions: canonicalPermissions,
7513
+ resources: canonicalResources
7514
+ };
7515
+ }
7516
+ function canonicalResourcesFromPortableDelegation(delegation) {
7517
+ if (delegation.resources !== void 0 && delegation.resources.length > 0) {
7518
+ return delegation.resources.map((resource) => ({ ...resource, actions: [...resource.actions].sort() })).sort(
7519
+ (left, right) => left.service.localeCompare(right.service) || left.space.localeCompare(right.space) || left.path.localeCompare(right.path) || left.actions.join("\0").localeCompare(right.actions.join("\0")) || (0, import_sdk_core9.canonicalizeRecapCaveats)(left.caveats).localeCompare((0, import_sdk_core9.canonicalizeRecapCaveats)(right.caveats))
7520
+ );
7521
+ }
7522
+ const byService = /* @__PURE__ */ new Map();
7523
+ for (const action of delegation.actions) {
7524
+ const [service] = action.split("/", 1);
7525
+ const shortService = service.replace(/^tinycloud\./, "");
7526
+ const values = byService.get(shortService) ?? [];
7527
+ values.push(action);
7528
+ byService.set(shortService, values);
7529
+ }
7530
+ return [...byService.entries()].map(([service, actions]) => ({
7531
+ service,
7532
+ space: service === "encryption" ? "encryption" : delegation.spaceId,
7533
+ path: delegation.path,
7534
+ actions: [...new Set(actions)].sort(),
7535
+ ...delegation.caveats === void 0 ? {} : { caveats: delegation.caveats }
7536
+ })).sort(
7537
+ (left, right) => left.service.localeCompare(right.service) || left.space.localeCompare(right.space) || left.path.localeCompare(right.path)
7538
+ );
7539
+ }
7540
+ function resourcesMatch(actual, expected) {
7541
+ const comparable = (resource) => ({
7542
+ ...resource,
7543
+ caveats: (0, import_sdk_core9.canonicalizeRecapCaveats)(resource.caveats)
7544
+ });
7545
+ return JSON.stringify(actual.map(comparable)) === JSON.stringify(expected.map(comparable));
7546
+ }
7547
+ async function activateValidatedRuntimeDelegation(node, delegation, options) {
7548
+ const host = normalizedHost(options.host);
7549
+ if (delegation.host !== void 0 && normalizedHost(delegation.host) !== host) {
7550
+ throw new Error(
7551
+ `Runtime delegation host '${delegation.host}' does not match expected host '${options.host}'.`
7552
+ );
7553
+ }
7554
+ if (!(delegation.expiry instanceof Date) || Number.isNaN(delegation.expiry.getTime())) {
7555
+ throw new Error("Runtime delegation has an invalid expiry.");
7556
+ }
7557
+ if (delegation.expiry.getTime() <= Date.now()) {
7558
+ throw new Error("Runtime delegation is expired.");
7559
+ }
7560
+ const authorization = delegation.delegationHeader?.Authorization;
7561
+ if (typeof authorization !== "string" || authorization.length === 0) {
7562
+ throw new Error("Runtime delegation is missing authorization bytes.");
7563
+ }
7564
+ const cid = node.computeDelegationCid(authorization);
7565
+ if (cid !== delegation.cid) {
7566
+ throw new Error("Runtime delegation CID does not match authorization bytes.");
7567
+ }
7568
+ const signed = signedAuthorityFromCompactUcan(authorization);
7569
+ if (signed.expiry.getTime() <= Date.now()) {
7570
+ throw new Error("Runtime delegation is expired.");
7571
+ }
7572
+ if (delegation.expiry.getTime() !== signed.expiry.getTime()) {
7573
+ throw new Error("Runtime delegation expiry does not match signed authority.");
7574
+ }
7575
+ if (!didPrincipalsMatch(signed.audience, node.sessionDid)) {
7576
+ throw new Error(
7577
+ `Runtime delegation targets ${signed.audience} but this session is ${node.sessionDid}.`
7578
+ );
7579
+ }
7580
+ if (!didPrincipalsMatch(delegation.delegateDID, signed.audience)) {
7581
+ throw new Error("Runtime delegation audience does not match signed authority.");
7582
+ }
7583
+ const declaredResources = canonicalResourcesFromPortableDelegation(delegation);
7584
+ if (!resourcesMatch(declaredResources, signed.resources)) {
7585
+ throw new Error("Runtime delegation resources do not match signed authority.");
7586
+ }
7587
+ const previouslyInstalled = node.getRuntimePermissionDelegations().find((candidate) => candidate.cid === cid);
7588
+ if (previouslyInstalled !== void 0 && previouslyInstalled.delegationHeader.Authorization !== authorization) {
7589
+ throw new Error(
7590
+ "A different authorization is already installed for this runtime delegation CID."
7591
+ );
7592
+ }
7593
+ const primary = signed.resources[0];
7594
+ const signedSpace = signed.permissions.find(
7595
+ (permission) => permission.service !== "tinycloud.encryption"
7596
+ )?.space;
7597
+ const installedCandidate = {
7598
+ cid,
7599
+ delegationHeader: { Authorization: authorization },
7600
+ ownerAddress: delegation.ownerAddress,
7601
+ chainId: delegation.chainId,
7602
+ delegatorDID: signed.issuer,
7603
+ ...signed.proofs.length === 1 ? { parentCid: signed.proofs[0] } : {},
7604
+ ...signed.remainingRedelegationDepth === void 0 ? {} : {
7605
+ allowSubDelegation: signed.remainingRedelegationDepth > 0,
7606
+ disableSubDelegation: signed.remainingRedelegationDepth === 0
7607
+ },
7608
+ authHeader: authorization,
7609
+ spaceId: signedSpace ?? delegation.spaceId,
7610
+ path: primary.path,
7611
+ actions: [...primary.actions],
7612
+ ...primary.caveats === void 0 ? {} : { caveats: primary.caveats },
7613
+ resources: signed.resources.map((resource) => ({
7614
+ ...resource,
7615
+ actions: [...resource.actions]
7616
+ })),
7617
+ expiry: new Date(signed.expiry),
7618
+ delegateDID: signed.audience,
7619
+ host
7620
+ };
7621
+ if (node.isSessionOnly === true && typeof node.did === "string" && (0, import_sdk_core9.principalDidEquals)(node.did, node.sessionDid) && typeof node.useDelegation === "function") {
7622
+ await node.useDelegation(installedCandidate);
7623
+ } else {
7624
+ await node.useRuntimeDelegation(installedCandidate);
7625
+ }
7626
+ const installed = node.getRuntimePermissionDelegations().find((candidate) => candidate.cid === cid);
7627
+ if (!installed) {
7628
+ throw new Error("Runtime delegation activation did not install the validated authority.");
7629
+ }
7630
+ if (installed.delegationHeader.Authorization !== authorization || !didPrincipalsMatch(installed.delegateDID, signed.audience) || installed.expiry.getTime() !== signed.expiry.getTime() || !resourcesMatch(
7631
+ canonicalResourcesFromPortableDelegation(installed),
7632
+ signed.resources
7633
+ ) || normalizedHost(installed.host ?? "") !== host) {
7634
+ throw new Error(
7635
+ "Runtime delegation activation installed authority that differs from the validated authority."
7636
+ );
7637
+ }
7638
+ return {
7639
+ cid,
7640
+ delegation: installed,
7641
+ effectivePermissions: signed.permissions,
7642
+ expiry: new Date(signed.expiry),
7643
+ audience: signed.audience,
7644
+ host: normalizedHost(installed.host ?? host)
7645
+ };
7646
+ }
7647
+ async function activateCompactRuntimeDelegation(node, input) {
7648
+ const authorization = input.authorization;
7649
+ const cid = node.computeDelegationCid(authorization);
7650
+ if (cid !== input.cid) {
7651
+ throw new Error("Compact runtime delegation CID does not match authorization bytes.");
7652
+ }
7653
+ const signed = signedAuthorityFromCompactUcan(authorization);
7654
+ const primary = signed.resources[0];
7655
+ const signedSpace = signed.permissions.find(
7656
+ (permission) => permission.service !== "tinycloud.encryption"
7657
+ )?.space;
7658
+ const delegation = {
7659
+ cid,
7660
+ delegationHeader: { Authorization: authorization },
7661
+ ownerAddress: input.ownerAddress,
7662
+ chainId: input.chainId,
7663
+ delegatorDID: signed.issuer,
7664
+ authHeader: authorization,
7665
+ spaceId: signedSpace ?? primary.space,
7666
+ path: primary.path,
7667
+ actions: [...primary.actions],
7668
+ ...primary.caveats === void 0 ? {} : { caveats: primary.caveats },
7669
+ resources: signed.resources.map((resource) => ({
7670
+ ...resource,
7671
+ actions: [...resource.actions]
7672
+ })),
7673
+ expiry: new Date(signed.expiry),
7674
+ delegateDID: signed.audience,
7675
+ host: normalizedHost(input.host)
7676
+ };
7677
+ return activateValidatedRuntimeDelegation(node, delegation, {
7678
+ host: input.host
7679
+ });
7680
+ }
7267
7681
  function serializeDelegation(delegation) {
7268
7682
  return JSON.stringify({
7269
7683
  ...delegation,
@@ -7339,6 +7753,7 @@ var import_sdk_core21 = require("@tinycloud/sdk-core");
7339
7753
  VaultPublicSpaceKVActions,
7340
7754
  VersionCheckError,
7341
7755
  WasmKeyProvider,
7756
+ activateCompactRuntimeDelegation,
7342
7757
  addressStorageKey,
7343
7758
  buildSpaceUri,
7344
7759
  canonicalizeAddress,