@tinycloud/sdk-core 3.8.0-beta.1 → 3.8.0-beta.15

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
@@ -6177,7 +6177,11 @@ var DelegationErrorCodes = {
6177
6177
  KV_SERVICE_UNAVAILABLE: "KV_SERVICE_UNAVAILABLE",
6178
6178
  DATA_FETCH_FAILED: "DATA_FETCH_FAILED",
6179
6179
  VALIDATION_ERROR: "VALIDATION_ERROR",
6180
- UNSUPPORTED_OPERATION: "UNSUPPORTED_OPERATION"
6180
+ UNSUPPORTED_OPERATION: "UNSUPPORTED_OPERATION",
6181
+ /** The node answered 503: the delegation chain is too deep to evaluate right now. Retry later. */
6182
+ CHAIN_TOO_DEEP: "CHAIN_TOO_DEEP",
6183
+ /** The node answered 400 "invalid delegation query cursor": malformed, foreign or from another filter. Start the listing again without it. */
6184
+ INVALID_CURSOR: "INVALID_CURSOR"
6181
6185
  };
6182
6186
  var DelegationSchema = z4.object({
6183
6187
  /** Content identifier (CID) of the delegation */
@@ -6254,7 +6258,7 @@ var AccountDelegationPageSchema = z4.object({
6254
6258
  nextCursor: z4.string().optional()
6255
6259
  }).strict();
6256
6260
  var AccountDelegationQueryOptionsSchema = z4.object({
6257
- direction: z4.enum(["granted", "received", "all"]).optional(),
6261
+ direction: z4.enum(["granted", "received", "all", "issued"]).optional(),
6258
6262
  status: z4.enum(["active", "pending", "expired", "revoked", "ancestor_revoked"]).optional(),
6259
6263
  space: z4.string().trim().min(1).optional(),
6260
6264
  limit: z4.number().int().min(1).max(100).optional(),
@@ -6440,6 +6444,8 @@ var DelegationManagerConfigSchema = z4.object({
6440
6444
  ).optional(),
6441
6445
  /** Optional owner-signed authorization for direct CID revocation */
6442
6446
  signOwnerRevocation: z4.custom().optional(),
6447
+ /** Optional signer for proofless invocations by the session's own did:key (issued-delegation list and revoke) */
6448
+ sessionInvocationSigner: z4.custom().optional(),
6443
6449
  /** Optional custom fetch implementation */
6444
6450
  fetch: z4.unknown().refine(
6445
6451
  (val) => val === void 0 || typeof val === "function",
@@ -6535,7 +6541,7 @@ var CreateDelegationWasmResultSchema = z4.object({
6535
6541
  resources: z4.array(DelegatedResourceSchema)
6536
6542
  });
6537
6543
 
6538
- // src/delegations/DelegationManager.ts
6544
+ // src/delegations/shared.ts
6539
6545
  var DelegationAction = {
6540
6546
  REVOKE: "tinycloud.delegation/revoke",
6541
6547
  STATUS: "tinycloud.delegation/status",
@@ -6552,6 +6558,8 @@ function createError(code3, message, cause, meta) {
6552
6558
  meta
6553
6559
  };
6554
6560
  }
6561
+
6562
+ // src/delegations/DelegationManager.ts
6555
6563
  async function readInvokedJson(response, action) {
6556
6564
  const text3 = await response.text();
6557
6565
  if (text3.trim() === "") {
@@ -6574,11 +6582,13 @@ var DelegationManager = class {
6574
6582
  * @param config - Configuration including hosts, session, and invoke function
6575
6583
  */
6576
6584
  constructor(config) {
6585
+ this.issuedState = { issuedQuerySupported: false };
6577
6586
  this.hosts = config.hosts;
6578
6587
  this.session = config.session;
6579
6588
  this.invoke = config.invoke;
6580
6589
  this.invokeAny = config.invokeAny;
6581
6590
  this.signOwnerRevocation = config.signOwnerRevocation;
6591
+ this.sessionInvocationSigner = config.sessionInvocationSigner;
6582
6592
  this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
6583
6593
  this.accountSpaceId = config.accountSpaceId;
6584
6594
  }
@@ -6801,8 +6811,14 @@ var DelegationManager = class {
6801
6811
  };
6802
6812
  }
6803
6813
  }
6804
- /** Query lifecycle-complete delegation history for the authenticated account. */
6805
- async query(options2 = {}) {
6814
+ /**
6815
+ * Query lifecycle-complete delegation history for the authenticated account.
6816
+ *
6817
+ * `scope.spaces` lists the spaces to sign `tinycloud.delegation/list` for, in order. Omitted, the
6818
+ * account space is tried, then the session's own space. A caller that knows which of them the session
6819
+ * holds passes only those; an empty list sends nothing and answers PERMISSION_DENIED.
6820
+ */
6821
+ async query(options2 = {}, scope = {}) {
6806
6822
  const validated = AccountDelegationQueryOptionsSchema.safeParse(options2);
6807
6823
  if (!validated.success) {
6808
6824
  return {
@@ -6815,6 +6831,7 @@ var DelegationManager = class {
6815
6831
  )
6816
6832
  };
6817
6833
  }
6834
+ if (validated.data.direction === "issued") return this.queryIssued(validated.data);
6818
6835
  if (!this.invokeAny) {
6819
6836
  return {
6820
6837
  ok: false,
@@ -6836,7 +6853,7 @@ var DelegationManager = class {
6836
6853
  }
6837
6854
  try {
6838
6855
  let response;
6839
- for (const spaceId of /* @__PURE__ */ new Set([accountSpaceId, this.session.spaceId])) {
6856
+ for (const spaceId of new Set(scope.spaces ?? [accountSpaceId, this.session.spaceId])) {
6840
6857
  const headers = Object.fromEntries(new Headers(this.invokeAny(this.session, [{
6841
6858
  spaceId,
6842
6859
  service: "delegation",
@@ -6896,6 +6913,45 @@ var DelegationManager = class {
6896
6913
  };
6897
6914
  }
6898
6915
  }
6916
+ /** The state the lazily loaded issued-delegation module reads and updates. */
6917
+ issuedContext() {
6918
+ return {
6919
+ host: this.host,
6920
+ fetchFn: this.fetchFn,
6921
+ sessionInvocationSigner: this.sessionInvocationSigner,
6922
+ state: this.issuedState
6923
+ };
6924
+ }
6925
+ /**
6926
+ * Lists the delegations this session's own key issued (`direction: "issued"`).
6927
+ * The implementation loads on first use.
6928
+ */
6929
+ async queryIssued(options2) {
6930
+ const issued = await import("@tinycloud/sdk-core/delegations/issued");
6931
+ return issued.queryIssued(this.issuedContext(), options2);
6932
+ }
6933
+ /**
6934
+ * Every delegation this session's key issued, following `nextCursor` until
6935
+ * it is absent. `maxItems` stops early once that many are collected;
6936
+ * `untilCid` stops after the page that contains that delegation. Items come
6937
+ * in CID order (each carries `issuedAt`). The implementation loads on first use.
6938
+ */
6939
+ async queryAllIssued(options2 = {}) {
6940
+ const issued = await import("@tinycloud/sdk-core/delegations/issued");
6941
+ return issued.queryAllIssued((page) => this.query(page), options2);
6942
+ }
6943
+ /**
6944
+ * Revokes a delegation this session's key issued, with no proof: a UCAN
6945
+ * signed by the session's own did:key carrying `urn:cid:<cid>` and
6946
+ * `tinycloud.delegation/revoke`. Revoking a delegation that is already revoked
6947
+ * succeeds. A refusal (the key is not the delegator) carries the node's
6948
+ * message; 503 means the chain is too deep right now, retry later.
6949
+ * The implementation loads on first use.
6950
+ */
6951
+ async revokeIssued(cid2) {
6952
+ const issued = await import("@tinycloud/sdk-core/delegations/issued");
6953
+ return issued.revokeIssued(this.issuedContext(), cid2);
6954
+ }
6899
6955
  /**
6900
6956
  * Gets the full delegation chain for a given delegation.
6901
6957
  *
@@ -7202,7 +7258,8 @@ function validateEncodedShareData(data) {
7202
7258
  // src/manifest.ts
7203
7259
  import ms from "ms";
7204
7260
  import { ACCOUNT_MANIFEST_PERMISSIONS } from "@tinycloud/bootstrap";
7205
- import { resolveSecretPath, SECRET_NAME_RE } from "@tinycloud/sdk-services";
7261
+ import { resolveSecretPath, SECRET_NAME_RE, parseNetworkId } from "@tinycloud/sdk-services";
7262
+ import { parseEncryptionResource as parseEncryptionResource2 } from "@tinycloud/sdk-services/encryption/resource";
7206
7263
  var DEFAULT_KNOWLEDGE_ROOT = "knowledge/index.md";
7207
7264
  var ManifestValidationError = class extends Error {
7208
7265
  constructor(message) {
@@ -7325,6 +7382,14 @@ function expandEncryptionPermissionEntry(entry) {
7325
7382
  `tinycloud.encryption entries require path to be a networkId URN (got ${JSON.stringify(entry.path)})`
7326
7383
  );
7327
7384
  }
7385
+ if (entry.path.includes("/")) {
7386
+ throw new ManifestValidationError("named keys go in manifest.keys");
7387
+ }
7388
+ try {
7389
+ parseNetworkId(entry.path);
7390
+ } catch {
7391
+ throw new ManifestValidationError("tinycloud.encryption entries require a valid networkId URN");
7392
+ }
7328
7393
  const normalizedActions = [];
7329
7394
  for (const action of entry.actions) {
7330
7395
  if (action === "decrypt" || action === "tinycloud.encryption/decrypt") {
@@ -7371,6 +7436,29 @@ function expandEncryptionPermissionEntry(entry) {
7371
7436
  function expandPermissionEntries(entries) {
7372
7437
  return entries.flatMap(expandPermissionEntry);
7373
7438
  }
7439
+ function expandDelegationPermissionEntries(entries) {
7440
+ return entries.flatMap((entry) => {
7441
+ if (entry.service !== ENCRYPTION_PERMISSION_SERVICE || typeof entry.path !== "string" || !entry.path.includes("/")) {
7442
+ return expandPermissionEntry(entry);
7443
+ }
7444
+ let resource;
7445
+ try {
7446
+ resource = parseEncryptionResource2(entry.path);
7447
+ } catch {
7448
+ throw new ManifestValidationError("delegation requires a canonical exact named-key resource");
7449
+ }
7450
+ if (resource.kind !== "key" || !Array.isArray(entry.actions) || entry.actions.length === 0 || entry.actions.some((action) => action !== "decrypt" && action !== "tinycloud.encryption/decrypt")) {
7451
+ throw new ManifestValidationError("named-key delegation requires an exact key and decrypt only");
7452
+ }
7453
+ return [{
7454
+ ...entry,
7455
+ space: ENCRYPTION_MANIFEST_SPACE,
7456
+ path: `${resource.networkId}/${resource.path}`,
7457
+ actions: ["tinycloud.encryption/decrypt"],
7458
+ skipPrefix: true
7459
+ }];
7460
+ });
7461
+ }
7374
7462
  function applyPrefix(prefix, path, skipPrefix) {
7375
7463
  if (skipPrefix) {
7376
7464
  return path;
@@ -7449,8 +7537,56 @@ function validateManifest(input) {
7449
7537
  if (m.secrets !== void 0) {
7450
7538
  validateManifestSecrets(m.secrets);
7451
7539
  }
7540
+ if (Object.prototype.hasOwnProperty.call(m, "keys_app_id") && (typeof m.keys_app_id !== "string" || !KEY_APP_ID.test(m.keys_app_id))) {
7541
+ throw new ManifestValidationError("manifest.keys_app_id is invalid");
7542
+ }
7543
+ validateManifestKeys(m.keys_app_id ?? m.app_id, m.keys);
7452
7544
  return m;
7453
7545
  }
7546
+ var KEY_SEGMENT = "[a-z0-9][a-z0-9._-]{0,63}";
7547
+ var KEY_APP_ID = new RegExp(`^${KEY_SEGMENT}$`);
7548
+ var KEY_PATH = new RegExp(`^${KEY_SEGMENT}(?:/${KEY_SEGMENT}){2,7}$`);
7549
+ var KEY_ABILITIES = ["key.create", "decrypt", "key.block", "key.unblock"];
7550
+ function validateManifestKeys(appId, keys) {
7551
+ if (keys === void 0) return;
7552
+ if (!Array.isArray(keys) || keys.length > 32) {
7553
+ throw new ManifestValidationError("manifest.keys must contain at most 32 entries");
7554
+ }
7555
+ if (keys.length === 0) return;
7556
+ if (!KEY_APP_ID.test(appId)) {
7557
+ throw new ManifestValidationError("manifest.keys requires a valid app_id");
7558
+ }
7559
+ const paths = /* @__PURE__ */ new Set();
7560
+ let prefixSeen = false;
7561
+ for (const [index, value] of keys.entries()) {
7562
+ const label = `manifest.keys[${index}]`;
7563
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
7564
+ throw new ManifestValidationError(`${label} must be an object`);
7565
+ }
7566
+ const entry = value;
7567
+ const fields = Object.keys(entry);
7568
+ const hasPath = Object.prototype.hasOwnProperty.call(entry, "path");
7569
+ const hasPrefix = Object.prototype.hasOwnProperty.call(entry, "prefix");
7570
+ if (hasPath === hasPrefix || fields.some((field) => !["path", "prefix", "abilities"].includes(field))) {
7571
+ throw new ManifestValidationError(`${label} must have one path or prefix and abilities`);
7572
+ }
7573
+ if (!Array.isArray(entry.abilities) || entry.abilities.length === 0 || new Set(entry.abilities).size !== entry.abilities.length || entry.abilities.some((ability) => typeof ability !== "string" || !KEY_ABILITIES.includes(ability))) {
7574
+ throw new ManifestValidationError(`${label}.abilities is invalid`);
7575
+ }
7576
+ if (hasPath) {
7577
+ const path = entry.path;
7578
+ if (typeof path !== "string" || path.length > 255 || !KEY_PATH.test(path) || !path.startsWith(`apps/${appId}/`) || paths.has(path)) {
7579
+ throw new ManifestValidationError(`${label}.path is invalid`);
7580
+ }
7581
+ paths.add(path);
7582
+ } else {
7583
+ if (entry.prefix !== `apps/${appId}/` || prefixSeen) {
7584
+ throw new ManifestValidationError(`${label}.prefix is invalid`);
7585
+ }
7586
+ prefixSeen = true;
7587
+ }
7588
+ }
7589
+ }
7454
7590
  function resolveManifestKnowledgeRoot(knowledge) {
7455
7591
  if (knowledge === void 0) {
7456
7592
  return void 0;
@@ -7968,6 +8104,7 @@ function manifestAbilitiesUnion(resolved) {
7968
8104
  }
7969
8105
 
7970
8106
  // src/capabilities.ts
8107
+ import { encryptionResourceContains } from "@tinycloud/sdk-services/encryption/resource";
7971
8108
  var PermissionNotInManifestError = class extends Error {
7972
8109
  constructor(missing, granted) {
7973
8110
  super(
@@ -8019,10 +8156,9 @@ function isCapabilitySubset(requested, granted) {
8019
8156
  const grantedActions = matchingScope.flatMap(
8020
8157
  (entry) => expandActionShortNames(entry.service, entry.actions)
8021
8158
  );
8159
+ const namedKeyRequest = req.service === "tinycloud.encryption" && req.path.startsWith("urn:tinycloud:encryption:") && req.path.includes("/");
8022
8160
  const covered = requestedActions.every(
8023
- (requestedAction) => grantedActions.some(
8024
- (grantedAction) => actionContains(grantedAction, requestedAction)
8025
- )
8161
+ (requestedAction) => grantedActions.some((grantedAction) => namedKeyRequest ? grantedAction === requestedAction : actionContains(grantedAction, requestedAction))
8026
8162
  );
8027
8163
  if (!covered) {
8028
8164
  missing.push(cloneEntry(req));
@@ -8037,7 +8173,9 @@ function canonicalizeEntryScopeMatches(requested, granted) {
8037
8173
  if (normalizeSpace(requested.space ?? DEFAULT_MANIFEST_SPACE) !== normalizeSpace(granted.space ?? DEFAULT_MANIFEST_SPACE)) {
8038
8174
  return false;
8039
8175
  }
8040
- if (!pathContains2(granted.path, requested.path)) {
8176
+ const namedKeyRequest = requested.service === "tinycloud.encryption" && requested.path.startsWith("urn:tinycloud:encryption:") && requested.path.includes("/");
8177
+ const coveredPath = namedKeyRequest ? encryptionResourceContains(granted.path, requested.path) : pathContains2(granted.path, requested.path);
8178
+ if (!coveredPath) {
8041
8179
  return false;
8042
8180
  }
8043
8181
  if (!recapCaveatsEqual(requested.caveats, granted.caveats)) {
@@ -12242,15 +12380,15 @@ function markDelegationTerminal(token, key) {
12242
12380
  // src/networkId.ts
12243
12381
  import {
12244
12382
  buildNetworkId,
12245
- parseNetworkId
12383
+ parseNetworkId as parseNetworkId2
12246
12384
  } from "@tinycloud/sdk-services";
12247
12385
  function canonicalizeNetworkId(networkId) {
12248
- const parsed = parseNetworkId(networkId);
12386
+ const parsed = parseNetworkId2(networkId);
12249
12387
  return buildNetworkId(canonicalizeDid(parsed.ownerDid), parsed.name);
12250
12388
  }
12251
12389
  function parseCanonicalNetworkId(networkId) {
12252
12390
  const canonical = canonicalizeNetworkId(networkId);
12253
- return parseNetworkId(canonical);
12391
+ return parseNetworkId2(canonical);
12254
12392
  }
12255
12393
 
12256
12394
  // src/storage.schema.ts
@@ -15063,7 +15201,7 @@ import {
15063
15201
  resolveSecretPath as resolveSecretPath2,
15064
15202
  EncryptionService,
15065
15203
  DecryptTransportResponseError,
15066
- parseNetworkId as parseNetworkId2,
15204
+ parseNetworkId as parseNetworkId3,
15067
15205
  buildNetworkId as buildNetworkId2,
15068
15206
  isNetworkId,
15069
15207
  networkDiscoveryKey,
@@ -19833,6 +19971,7 @@ export {
19833
19971
  ensureNetworkUsableForDecrypt,
19834
19972
  err5 as err,
19835
19973
  expandActionShortNames,
19974
+ expandDelegationPermissionEntries,
19836
19975
  expandPermissionEntries,
19837
19976
  expandPermissionEntry,
19838
19977
  extractImmutableSiweFields,
@@ -19888,7 +20027,7 @@ export {
19888
20027
  parseCapabilityResource,
19889
20028
  parseCompactUcanAuthorization,
19890
20029
  parseExpiry,
19891
- parseNetworkId2 as parseNetworkId,
20030
+ parseNetworkId3 as parseNetworkId,
19892
20031
  parseNodeNativePortableDelegation,
19893
20032
  parsePkhDid,
19894
20033
  parsePolicySessionUcan,