@tinycloud/operations 0.3.4-beta.6 → 0.4.0-beta.7

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.cjs CHANGED
@@ -6967,6 +6967,9 @@ function evaluateOperationAuthority(granted, required, resolveSpace2) {
6967
6967
  missing: canonicalizeCapabilities(missing)
6968
6968
  };
6969
6969
  }
6970
+ function delegationWithinRequest(requested, effective, resolveSpace2) {
6971
+ return requested.length > 0 && evaluateOperationAuthority(requested, canonicalizeCapabilities(effective), resolveSpace2).satisfied;
6972
+ }
6970
6973
  function isExactCapabilityMemberSubset(hinted, planned, resolveSpace2) {
6971
6974
  const canonicalHinted = canonicalizeOperationCapabilities(hinted, resolveSpace2);
6972
6975
  const canonicalPlanned = canonicalizeOperationCapabilities(planned, resolveSpace2);
@@ -7079,6 +7082,11 @@ var init_authority = __esm({
7079
7082
  });
7080
7083
 
7081
7084
  // src/state.ts
7085
+ function processWideContext(key) {
7086
+ var _a;
7087
+ const registry = globalThis;
7088
+ return registry[_a = Symbol.for(key)] ?? (registry[_a] = new import_node_async_hooks.AsyncLocalStorage());
7089
+ }
7082
7090
  function tinycloudHomePath() {
7083
7091
  const home = invocationStateRoot.getStore() ?? process.env.TC_HOME ?? process.env.HOME ?? process.env.USERPROFILE ?? (0, import_node_os.homedir)();
7084
7092
  return (0, import_node_path.join)(home, ".tinycloud");
@@ -7418,8 +7426,8 @@ var init_state = __esm({
7418
7426
  TEST_LOCK_CLAIMED_BARRIER_DIR = "TC_TEST_PROFILE_LOCK_CLAIMED_BARRIER_DIR";
7419
7427
  TEST_LOCK_FENCED_BARRIER_DIR = "TC_TEST_PROFILE_LOCK_FENCED_BARRIER_DIR";
7420
7428
  TEST_LOCK_VERIFIED_BARRIER_DIR = "TC_TEST_PROFILE_LOCK_VERIFIED_BARRIER_DIR";
7421
- invocationStateRoot = new import_node_async_hooks.AsyncLocalStorage();
7422
- heldProfileLocks = new import_node_async_hooks.AsyncLocalStorage();
7429
+ invocationStateRoot = processWideContext("tinycloud.operations.invocationStateRoot.v1");
7430
+ heldProfileLocks = processWideContext("tinycloud.operations.heldProfileLocks.v1");
7423
7431
  ProfileLockTimeoutError = class extends Error {
7424
7432
  constructor(profile, timeoutMs) {
7425
7433
  super(`Timed out waiting for the profile lock for "${profile}" after ${timeoutMs}ms.`);
@@ -7634,7 +7642,7 @@ function evaluateArtifactAuthority(granted, required, resolveSpace2) {
7634
7642
  function defaultRequestId(now) {
7635
7643
  return `req_${now.getTime().toString(36)}_${(0, import_node_crypto2.randomUUID)().replace(/-/g, "")}`;
7636
7644
  }
7637
- var import_node_crypto2, import_zod2, THIRTY_DAYS_MS, PermissionEntrySchema, CommandSchema, LegacyPermissionRequestArtifactSchema, PortableDelegationSchema, PermissionRequestArtifactSchema, DelegationImportArtifactSchema;
7645
+ var import_node_crypto2, import_zod2, THIRTY_DAYS_MS, PermissionEntrySchema, CommandSchema, LegacyPermissionRequestArtifactSchema, PortableDelegationSchema, PermissionRequestArtifactSchema, DelegationImportArtifactSchema, DelegationRequestBindingSchema;
7638
7646
  var init_artifacts = __esm({
7639
7647
  "src/artifacts.ts"() {
7640
7648
  "use strict";
@@ -7709,6 +7717,7 @@ var init_artifacts = __esm({
7709
7717
  expiry: import_zod2.z.string().datetime({ offset: true }).optional(),
7710
7718
  prompted: import_zod2.z.boolean().optional()
7711
7719
  }).strict();
7720
+ DelegationRequestBindingSchema = PermissionRequestArtifactSchema.pick({ requestId: true, requested: true }).strict();
7712
7721
  }
7713
7722
  });
7714
7723
 
@@ -7768,18 +7777,18 @@ function resolveOperationSpace(space, node, ownerSpace) {
7768
7777
  if (resolved !== void 0) return canonicalizeSpaceUri(resolved);
7769
7778
  }
7770
7779
  const did = typeof candidate.did === "string" ? candidate.did : void 0;
7771
- const pkh = did === void 0 ? null : (0, import_node_sdk.parsePkhDid)(did);
7780
+ const pkh = did === void 0 ? null : nodeSdk.parsePkhDid(did);
7772
7781
  if (pkh === null) {
7773
7782
  throw invalidSecretInput();
7774
7783
  }
7775
- return (0, import_node_sdk.makePkhSpaceId)(pkh.address, pkh.chainId, space);
7784
+ return nodeSdk.makePkhSpaceId(pkh.address, pkh.chainId, space);
7776
7785
  }
7777
7786
  function ownedSpaceForName(ownerSpace, name) {
7778
- const parsed = (0, import_node_sdk.parseSpaceUri)(ownerSpace);
7787
+ const parsed = nodeSdk.parseSpaceUri(ownerSpace);
7779
7788
  if (parsed === null) return void 0;
7780
7789
  try {
7781
- const pkh = (0, import_node_sdk.parsePkhDid)(parsed.owner);
7782
- return pkh === null ? void 0 : (0, import_node_sdk.makePkhSpaceId)(pkh.address, pkh.chainId, name);
7790
+ const pkh = nodeSdk.parsePkhDid(parsed.owner);
7791
+ return pkh === null ? void 0 : nodeSdk.makePkhSpaceId(pkh.address, pkh.chainId, name);
7783
7792
  } catch {
7784
7793
  return void 0;
7785
7794
  }
@@ -7794,7 +7803,7 @@ function canonicalizeSpaceUri(space) {
7794
7803
  /^tinycloud:pkh:eip155:(\d+):(0x[0-9a-fA-F]{40}):([^:]+)$/
7795
7804
  );
7796
7805
  if (pkh !== null) {
7797
- return `tinycloud:pkh:eip155:${pkh[1]}:${(0, import_node_sdk.canonicalizeAddress)(pkh[2]).toLowerCase()}:${pkh[3]}`;
7806
+ return `tinycloud:pkh:eip155:${pkh[1]}:${nodeSdk.canonicalizeAddress(pkh[2]).toLowerCase()}:${pkh[3]}`;
7798
7807
  }
7799
7808
  return space;
7800
7809
  }
@@ -7834,11 +7843,11 @@ function invalidSecretInput() {
7834
7843
  operationError("INPUT_INVALID", "The secret reference is invalid.")
7835
7844
  );
7836
7845
  }
7837
- var import_node_sdk, DEFAULT_SECRETS_SPACE, SECRET_MANAGER_BASE_URL, SECRET_NAME_RE2, RESERVED_SECRET_SCOPES;
7846
+ var nodeSdk, DEFAULT_SECRETS_SPACE, SECRET_MANAGER_BASE_URL, SECRET_NAME_RE2, RESERVED_SECRET_SCOPES;
7838
7847
  var init_secrets = __esm({
7839
7848
  "src/secrets.ts"() {
7840
7849
  "use strict";
7841
- import_node_sdk = require("@tinycloud/node-sdk");
7850
+ nodeSdk = __toESM(require("@tinycloud/node-sdk"), 1);
7842
7851
  init_errors();
7843
7852
  DEFAULT_SECRETS_SPACE = "secrets";
7844
7853
  SECRET_MANAGER_BASE_URL = "https://secrets.tinycloud.xyz";
@@ -7991,7 +8000,7 @@ async function importRequestBoundDelegation(context, input) {
7991
8000
  };
7992
8001
  let activated;
7993
8002
  try {
7994
- activated = await nodeSdk.activateValidatedRuntimeDelegation(
8003
+ activated = await nodeSdk2.activateValidatedRuntimeDelegation(
7995
8004
  context.runtime.node,
7996
8005
  delegation,
7997
8006
  { host }
@@ -8007,11 +8016,7 @@ async function importRequestBoundDelegation(context, input) {
8007
8016
  context.runtime.node,
8008
8017
  context.summary.space
8009
8018
  );
8010
- if (request.requested.length === 0 || !evaluateOperationAuthority(
8011
- request.requested,
8012
- effectivePermissions,
8013
- resolveSpace2
8014
- ).satisfied) {
8019
+ if (!delegationWithinRequest(request.requested, effectivePermissions, resolveSpace2)) {
8015
8020
  return operationFailure(
8016
8021
  "DELEGATION_REJECTED",
8017
8022
  "The delegation exceeds the stored authority request."
@@ -8039,12 +8044,19 @@ async function importRequestBoundDelegation(context, input) {
8039
8044
  )
8040
8045
  };
8041
8046
  }
8042
- const alreadyPresent = records.some((entry) => delegationCid(entry) === activated.cid);
8047
+ const record = {
8048
+ delegation: activated.delegation,
8049
+ permissions: activated.effectivePermissions,
8050
+ authorityRequest: {
8051
+ requestId: request.requestId,
8052
+ requested: request.requested
8053
+ }
8054
+ };
8055
+ const position = records.findIndex((entry) => delegationCid(entry) === activated.cid);
8056
+ const alreadyPresent = position !== -1;
8057
+ const others = records.filter((entry) => delegationCid(entry) !== activated.cid);
8043
8058
  return {
8044
- records: alreadyPresent ? records : [...records, {
8045
- delegation: activated.delegation,
8046
- permissions: activated.effectivePermissions
8047
- }],
8059
+ records: alreadyPresent ? [...others.slice(0, position), record, ...others.slice(position)] : [...records, record],
8048
8060
  result: {
8049
8061
  status: "ok",
8050
8062
  output: {
@@ -8173,11 +8185,11 @@ function internalImportFailure() {
8173
8185
  function operationFailure(code, message, details) {
8174
8186
  return { status: "error", error: operationError(code, message, { ...details === void 0 ? {} : { details } }) };
8175
8187
  }
8176
- var nodeSdk, import_zod3, EmptyInputSchema, CapabilitiesOutputSchema, AuthRequestInputSchema, PermissionRequestOutputSchema, AuthRequestOutputSchema, AuthImportOutputSchema, AUTH_POSTURES, AUTH_EXPOSURE, AUTH_CAPABILITIES_SENSITIVITY, AUTH_REQUEST_SENSITIVITY, AUTH_IMPORT_SENSITIVITY, authOperationDefinitions;
8188
+ var nodeSdk2, import_zod3, EmptyInputSchema, CapabilitiesOutputSchema, AuthRequestInputSchema, PermissionRequestOutputSchema, AuthRequestOutputSchema, AuthImportOutputSchema, AUTH_POSTURES, AUTH_EXPOSURE, AUTH_CAPABILITIES_SENSITIVITY, AUTH_REQUEST_SENSITIVITY, AUTH_IMPORT_SENSITIVITY, authOperationDefinitions;
8177
8189
  var init_auth = __esm({
8178
8190
  "src/operations/auth.ts"() {
8179
8191
  "use strict";
8180
- nodeSdk = __toESM(require("@tinycloud/node-sdk"), 1);
8192
+ nodeSdk2 = __toESM(require("@tinycloud/node-sdk"), 1);
8181
8193
  import_zod3 = require("zod");
8182
8194
  init_authority();
8183
8195
  init_artifacts();
@@ -9774,6 +9786,173 @@ ORDER BY type, name`;
9774
9786
  }
9775
9787
  });
9776
9788
 
9789
+ // src/delegation-binding.ts
9790
+ function bindingMigrationPath(profile) {
9791
+ return (0, import_node_path2.join)(profilePath(profile), "delegation-binding-migration.json");
9792
+ }
9793
+ function storedDelegationKind(entry) {
9794
+ if (!isRecord4(entry)) return "refused";
9795
+ const delegation = entry.delegation;
9796
+ if (!isRecord4(delegation)) return "refused";
9797
+ const header = delegation.delegationHeader;
9798
+ if (!isRecord4(header) || Object.keys(header).length !== 1 || !Object.prototype.hasOwnProperty.call(header, "Authorization") || typeof header.Authorization !== "string") {
9799
+ return "refused";
9800
+ }
9801
+ if ("siweProof" in delegation) return "signed-login";
9802
+ if (header.Authorization.includes(".")) return "compact";
9803
+ return "authorityRequest" in entry ? "refused" : "other";
9804
+ }
9805
+ function replayLimit(entry, migrated) {
9806
+ const kind = storedDelegationKind(entry);
9807
+ if (!isRecord4(entry) || kind !== "compact" && kind !== "signed-login") return void 0;
9808
+ const binding = DelegationRequestBindingSchema.safeParse(entry.authorityRequest);
9809
+ if (binding.success) return binding.data.requested;
9810
+ return !migrated && kind === "compact" && !("authorityRequest" in entry) ? "signed" : void 0;
9811
+ }
9812
+ function bindToSignedCapabilities(record, signed, source, recordedAt = (/* @__PURE__ */ new Date()).toISOString()) {
9813
+ return {
9814
+ ...record,
9815
+ authorityRequest: DelegationRequestBindingSchema.parse({
9816
+ requestId: `${REQUEST_ID_PREFIX[source]}:${record.delegation.cid}`,
9817
+ requested: structuredClone(signed)
9818
+ }),
9819
+ authorityRequestAudit: { source, recordedAt, note: BINDING_NOTES[source] }
9820
+ };
9821
+ }
9822
+ async function prepareStoredDelegationReplay(profile, node, options) {
9823
+ if (await bindingMigrationRecorded(profile)) return true;
9824
+ if (!options.migrate || !await holdsPersistedSession(profile, node)) return false;
9825
+ try {
9826
+ return await withProfileLock(profile, async () => {
9827
+ if (await bindingMigrationRecorded(profile)) return true;
9828
+ if (!await holdsPersistedSession(profile, node)) return false;
9829
+ const recordedAt = (/* @__PURE__ */ new Date()).toISOString();
9830
+ const bound = /* @__PURE__ */ new Map();
9831
+ const unbound = [];
9832
+ for (const entry of await readAdditionalDelegations(profile)) {
9833
+ if (!isRecord4(entry) || "authorityRequest" in entry || storedDelegationKind(entry) !== "compact") continue;
9834
+ const migrated = await migratedRecord(node, entry, options.host, recordedAt);
9835
+ if (migrated === void 0) unbound.push(String(storedCid(entry)));
9836
+ else bound.set(recordKey(entry), migrated);
9837
+ }
9838
+ if (bound.size > 0) {
9839
+ await updateProfileStore(profile, "additional-delegations", (records) => ({
9840
+ records: records.map((entry) => {
9841
+ const key = !isRecord4(entry) || "authorityRequest" in entry ? void 0 : recordKey(entry);
9842
+ return (key === void 0 ? void 0 : bound.get(key)) ?? entry;
9843
+ }),
9844
+ result: void 0
9845
+ }));
9846
+ }
9847
+ await writeJsonAtomic(bindingMigrationPath(profile), {
9848
+ formatVersion: 1,
9849
+ migratedAt: recordedAt,
9850
+ bound: [...bound.values()].map(storedCid),
9851
+ unbound
9852
+ });
9853
+ return true;
9854
+ }, { timeoutMs: MIGRATION_LOCK_TIMEOUT_MS });
9855
+ } catch {
9856
+ return false;
9857
+ }
9858
+ }
9859
+ async function holdsPersistedSession(profile, node) {
9860
+ try {
9861
+ const persisted = await readSession(profile);
9862
+ return typeof persisted?.verificationMethod === "string" && typeof node.sessionDid === "string" && persisted.verificationMethod.split("#", 1)[0] === node.sessionDid.split("#", 1)[0];
9863
+ } catch {
9864
+ return false;
9865
+ }
9866
+ }
9867
+ async function migratedRecord(node, entry, host, recordedAt) {
9868
+ const delegation = normalizeStoredDelegation(entry);
9869
+ if (delegation === void 0) return void 0;
9870
+ const signed = await signedCapabilities(node, delegation, host);
9871
+ if (signed === void 0) return void 0;
9872
+ try {
9873
+ return bindToSignedCapabilities({ ...entry, delegation: { ...entry.delegation, cid: delegation.cid } }, signed, "migration", recordedAt);
9874
+ } catch {
9875
+ return void 0;
9876
+ }
9877
+ }
9878
+ async function replayStoredDelegation(node, entry, options) {
9879
+ const delegation = normalizeStoredDelegation(entry);
9880
+ if (delegation === void 0 || delegation.expiry.getTime() <= Date.now()) return void 0;
9881
+ const limit = replayLimit(entry, options.migrated);
9882
+ if (limit === void 0) return void 0;
9883
+ const authorize = limit === "signed" ? () => true : (effective) => delegationWithinRequest(limit, effective, options.resolveSpace);
9884
+ try {
9885
+ return await nodeSdk3.activateValidatedRuntimeDelegation(node, delegation, { host: options.host, authorize });
9886
+ } catch {
9887
+ return void 0;
9888
+ }
9889
+ }
9890
+ async function bindingMigrationRecorded(profile) {
9891
+ try {
9892
+ return await readJson(bindingMigrationPath(profile)) !== null;
9893
+ } catch {
9894
+ return true;
9895
+ }
9896
+ }
9897
+ async function signedCapabilities(node, delegation, host) {
9898
+ let signed;
9899
+ try {
9900
+ await nodeSdk3.activateValidatedRuntimeDelegation(node, delegation, {
9901
+ // The transport host is not signed authority; check it at replay instead.
9902
+ host: delegation.host ?? host,
9903
+ authorize: (effective) => {
9904
+ signed = effective;
9905
+ return false;
9906
+ }
9907
+ });
9908
+ } catch {
9909
+ }
9910
+ return signed;
9911
+ }
9912
+ function normalizeStoredDelegation(entry) {
9913
+ if (!isRecord4(entry)) return void 0;
9914
+ const raw = entry.delegation;
9915
+ if (!isRecord4(raw) || !isRecord4(raw.delegationHeader)) return void 0;
9916
+ const expiry = raw.expiry instanceof Date ? raw.expiry : typeof raw.expiry === "string" ? new Date(raw.expiry) : void 0;
9917
+ if (expiry === void 0 || Number.isNaN(expiry.getTime()) || typeof raw.cid !== "string" || typeof raw.spaceId !== "string" || typeof raw.path !== "string" || !Array.isArray(raw.actions) || !raw.actions.every((action) => typeof action === "string") || typeof raw.delegateDID !== "string" || typeof raw.ownerAddress !== "string" || typeof raw.chainId !== "number" || typeof raw.delegationHeader.Authorization !== "string") {
9918
+ return void 0;
9919
+ }
9920
+ return { ...raw, expiry };
9921
+ }
9922
+ function storedCid(entry) {
9923
+ const cid = isRecord4(entry) && isRecord4(entry.delegation) ? entry.delegation.cid : void 0;
9924
+ return typeof cid === "string" ? cid : void 0;
9925
+ }
9926
+ function recordKey(entry) {
9927
+ const delegation = entry.delegation;
9928
+ if (!isRecord4(delegation) || !isRecord4(delegation.delegationHeader)) return void 0;
9929
+ return JSON.stringify([delegation.cid, delegation.delegationHeader.Authorization]);
9930
+ }
9931
+ function isRecord4(value) {
9932
+ return typeof value === "object" && value !== null && !Array.isArray(value);
9933
+ }
9934
+ var import_node_path2, nodeSdk3, REQUEST_ID_PREFIX, BINDING_NOTES, MIGRATION_LOCK_TIMEOUT_MS;
9935
+ var init_delegation_binding = __esm({
9936
+ "src/delegation-binding.ts"() {
9937
+ "use strict";
9938
+ import_node_path2 = require("path");
9939
+ nodeSdk3 = __toESM(require("@tinycloud/node-sdk"), 1);
9940
+ init_artifacts();
9941
+ init_authority();
9942
+ init_state();
9943
+ init_secrets();
9944
+ REQUEST_ID_PREFIX = {
9945
+ migration: "migrated",
9946
+ "unbound-import": "unbound-import"
9947
+ };
9948
+ BINDING_NOTES = {
9949
+ migration: "Stored without a request binding before bindings were required. Bound at migration to exactly its own signed capabilities, so replay refuses any delegation in this record that exceeds them.",
9950
+ "unbound-import": "Imported by `tc auth import` without a stored request. Bound to exactly its own signed capabilities, so replay refuses any delegation in this record that exceeds them."
9951
+ };
9952
+ MIGRATION_LOCK_TIMEOUT_MS = 250;
9953
+ }
9954
+ });
9955
+
9777
9956
  // src/operations/status.ts
9778
9957
  function createStatusDefinition(params) {
9779
9958
  return {
@@ -9829,7 +10008,7 @@ async function inspectPinnedProfileStatus(context, _input) {
9829
10008
  async function pinnedProfileExists(profile) {
9830
10009
  try {
9831
10010
  const storedProfile = await readProfile(profile);
9832
- return isRecord4(storedProfile);
10011
+ return isRecord5(storedProfile);
9833
10012
  } catch {
9834
10013
  return false;
9835
10014
  }
@@ -9840,16 +10019,18 @@ async function countLiveAdditionalDelegations(profile) {
9840
10019
  if (rawDelegations === null) return 0;
9841
10020
  if (!Array.isArray(rawDelegations)) throw new TypeError("Invalid additional delegation store.");
9842
10021
  const now = Date.now();
10022
+ const migrated = await bindingMigrationRecorded(profile);
9843
10023
  let count = 0;
9844
10024
  for (const entry of rawDelegations) {
10025
+ if (storedDelegationKind(entry) === "refused") continue;
9845
10026
  const expiry = storedDelegationExpiry(entry);
9846
- if (expiry.getTime() > now) count += 1;
10027
+ if (expiry.getTime() > now && replayLimit(entry, migrated) !== void 0) count += 1;
9847
10028
  }
9848
10029
  return count;
9849
10030
  }
9850
10031
  function summarizeSession(session) {
9851
10032
  if (session === null) return { present: false, expired: null, expiresAt: null };
9852
- if (!isRecord4(session)) throw new TypeError("Invalid session store.");
10033
+ if (!isRecord5(session)) throw new TypeError("Invalid session store.");
9853
10034
  const expiry = extractSessionExpiry(session);
9854
10035
  if (expiry === null) return { present: true, expired: null, expiresAt: null };
9855
10036
  return {
@@ -9859,7 +10040,7 @@ function summarizeSession(session) {
9859
10040
  };
9860
10041
  }
9861
10042
  function storedDelegationExpiry(entry) {
9862
- if (!isRecord4(entry) || !isRecord4(entry.delegation) || !("expiry" in entry.delegation)) {
10043
+ if (!isRecord5(entry) || !isRecord5(entry.delegation) || !("expiry" in entry.delegation)) {
9863
10044
  throw new TypeError("Invalid stored delegation.");
9864
10045
  }
9865
10046
  return parseExpiry(entry.delegation.expiry);
@@ -9888,7 +10069,7 @@ function profileNotFound(profile) {
9888
10069
  )
9889
10070
  };
9890
10071
  }
9891
- function isRecord4(value) {
10072
+ function isRecord5(value) {
9892
10073
  return typeof value === "object" && value !== null && !Array.isArray(value);
9893
10074
  }
9894
10075
  var import_zod7, StatusInputSchema, StatusOutputSchema, STATUS_POSTURES, STATUS_EXPOSURE, STATUS_SENSITIVITY, statusOperationDefinitions;
@@ -9896,6 +10077,7 @@ var init_status = __esm({
9896
10077
  "src/operations/status.ts"() {
9897
10078
  "use strict";
9898
10079
  import_zod7 = require("zod");
10080
+ init_delegation_binding();
9899
10081
  init_errors();
9900
10082
  init_state();
9901
10083
  StatusInputSchema = import_zod7.z.object({}).strict();
@@ -9918,7 +10100,9 @@ var init_status = __esm({
9918
10100
  expired: import_zod7.z.boolean().nullable(),
9919
10101
  expiresAt: import_zod7.z.string().datetime({ offset: true }).nullable()
9920
10102
  }).strict(),
9921
- liveAdditionalDelegationCount: import_zod7.z.number().int().nonnegative()
10103
+ liveAdditionalDelegationCount: import_zod7.z.number().int().nonnegative().describe(
10104
+ "Stored, unexpired delegations the replay rule accepts: compact-UCAN or signed-login records with a request binding, and unbound compact-UCAN records before the profile's one-time binding migration. Each is still validated, and checked against its binding, when a runtime installs it."
10105
+ )
9922
10106
  }).strict();
9923
10107
  STATUS_POSTURES = [
9924
10108
  "owner-openkey",
@@ -10072,9 +10256,9 @@ function isOperatorType(value) {
10072
10256
  return value === "human" || value === "agent";
10073
10257
  }
10074
10258
  function isStoredProfile(value) {
10075
- return isRecord5(value) && isNonEmptyString(value.name) && isNonEmptyString(value.host) && typeof value.chainId === "number" && Number.isFinite(value.chainId) && isNonEmptyString(value.spaceName) && isNonEmptyString(value.did) && isNonEmptyString(value.createdAt) && isOptionalNonEmptyString(value.sessionDid) && isOptionalNonEmptyString(value.ownerDid) && isOptionalNonEmptyString(value.spaceId) && isOptionalPosture(value.posture) && isOptionalOperatorType(value.operatorType) && isOptionalAuthMethod(value.authMethod) && isOptionalNonEmptyString(value.defaultSpace) && isOptionalNonEmptyString(value.privateKey) && isOptionalNonEmptyString(value.address) && isOptionalNonEmptyString(value.openkeyHost);
10259
+ return isRecord6(value) && isNonEmptyString(value.name) && isNonEmptyString(value.host) && typeof value.chainId === "number" && Number.isFinite(value.chainId) && isNonEmptyString(value.spaceName) && isNonEmptyString(value.did) && isNonEmptyString(value.createdAt) && isOptionalNonEmptyString(value.sessionDid) && isOptionalNonEmptyString(value.ownerDid) && isOptionalNonEmptyString(value.spaceId) && isOptionalPosture(value.posture) && isOptionalOperatorType(value.operatorType) && isOptionalAuthMethod(value.authMethod) && isOptionalNonEmptyString(value.defaultSpace) && isOptionalNonEmptyString(value.privateKey) && isOptionalNonEmptyString(value.address) && isOptionalNonEmptyString(value.openkeyHost);
10076
10260
  }
10077
- function isRecord5(value) {
10261
+ function isRecord6(value) {
10078
10262
  return typeof value === "object" && value !== null && !Array.isArray(value);
10079
10263
  }
10080
10264
  function isNonEmptyString(value) {
@@ -10131,14 +10315,12 @@ async function createInvocationRuntime(target, requirement = "authenticated") {
10131
10315
  const profileName = summary.profile;
10132
10316
  try {
10133
10317
  const {
10134
- activateValidatedRuntimeDelegation: activateValidatedRuntimeDelegation2,
10135
10318
  TinyCloudNode: TinyCloudNodeConstructor
10136
- } = nodeSdk2;
10319
+ } = nodeSdk4;
10137
10320
  const explicitPrivateKeyOverride = typeof target.privateKey === "string";
10138
- const [session, key, additionalDelegations] = explicitPrivateKeyOverride ? [null, null, []] : await Promise.all([
10321
+ const [session, key] = explicitPrivateKeyOverride ? [null, null] : await Promise.all([
10139
10322
  readSession(profileName),
10140
- readJson(`${profilePath(profileName)}/key.json`),
10141
- readAdditionalDelegations(profileName)
10323
+ readJson(`${profilePath(profileName)}/key.json`)
10142
10324
  ]);
10143
10325
  const profile = resolved.profile;
10144
10326
  const actualPosture = resolvePosture(profile);
@@ -10185,19 +10367,26 @@ async function createInvocationRuntime(target, requirement = "authenticated") {
10185
10367
  const livePrincipalDid = normalizeDid(node.did);
10186
10368
  const authenticatedSpace = explicitPrivateKeyOverride ? spaceForAuthenticatedPrincipal(livePrincipalDid) : void 0;
10187
10369
  const livePermissions = [...node.getVerifiedSessionCapabilities()];
10370
+ const resolveSpace2 = operationSpaceResolver(node, authenticatedSpace ?? summary.space);
10188
10371
  const seenCids = /* @__PURE__ */ new Set();
10189
- for (const entry of additionalDelegations) {
10190
- const delegation = normalizeStoredDelegation(entry);
10191
- if (delegation === void 0 || delegation.expiry.getTime() <= Date.now()) continue;
10192
- try {
10193
- const activated = await activateValidatedRuntimeDelegation2(node, delegation, {
10194
- host: summary.host
10372
+ if (!explicitPrivateKeyOverride) {
10373
+ const activator = node;
10374
+ const migrated = await prepareStoredDelegationReplay(profileName, activator, {
10375
+ host: summary.host,
10376
+ // Only the profile's own restored session; a local sign-in without
10377
+ // one uses a fresh session key the stored records do not address.
10378
+ migrate: activeSession !== void 0
10379
+ });
10380
+ for (const entry of await readAdditionalDelegations(profileName)) {
10381
+ const activated = await replayStoredDelegation(activator, entry, {
10382
+ host: summary.host,
10383
+ migrated,
10384
+ resolveSpace: resolveSpace2
10195
10385
  });
10196
- if (!seenCids.has(activated.cid)) {
10386
+ if (activated !== void 0 && !seenCids.has(activated.cid)) {
10197
10387
  seenCids.add(activated.cid);
10198
10388
  livePermissions.push(...activated.effectivePermissions);
10199
10389
  }
10200
- } catch {
10201
10390
  }
10202
10391
  }
10203
10392
  const runtime = {
@@ -10229,8 +10418,8 @@ async function createInvocationRuntime(target, requirement = "authenticated") {
10229
10418
  function spaceForAuthenticatedPrincipal(principal2) {
10230
10419
  if (principal2 === void 0) return void 0;
10231
10420
  try {
10232
- const pkh = nodeSdk2.parsePkhDid(principal2);
10233
- return pkh === null ? void 0 : nodeSdk2.makePkhSpaceId(pkh.address, pkh.chainId, "secrets");
10421
+ const pkh = nodeSdk4.parsePkhDid(principal2);
10422
+ return pkh === null ? void 0 : nodeSdk4.makePkhSpaceId(pkh.address, pkh.chainId, "secrets");
10234
10423
  } catch {
10235
10424
  return void 0;
10236
10425
  }
@@ -10238,7 +10427,7 @@ function spaceForAuthenticatedPrincipal(principal2) {
10238
10427
  function normalizeSession(session, key) {
10239
10428
  const header = session.delegationHeader;
10240
10429
  const jwk = hasPrivateParameter(session.jwk) ? session.jwk : key;
10241
- if (!isRecord6(header) || typeof header.Authorization !== "string" || typeof session.delegationCid !== "string" || typeof session.spaceId !== "string" || !hasPrivateParameter(jwk) || typeof session.verificationMethod !== "string") {
10430
+ if (!isRecord7(header) || typeof header.Authorization !== "string" || typeof session.delegationCid !== "string" || typeof session.spaceId !== "string" || !hasPrivateParameter(jwk) || typeof session.verificationMethod !== "string") {
10242
10431
  return void 0;
10243
10432
  }
10244
10433
  return {
@@ -10254,27 +10443,14 @@ function normalizeSession(session, key) {
10254
10443
  ...Array.isArray(session.tinycloudHosts) && session.tinycloudHosts.every((host) => typeof host === "string") ? { tinycloudHosts: session.tinycloudHosts } : {}
10255
10444
  };
10256
10445
  }
10257
- function normalizeStoredDelegation(entry) {
10258
- const raw = entry.delegation;
10259
- if (!isRecord6(raw) || !isRecord6(raw.delegationHeader)) return void 0;
10260
- const expiry = parseExpiry2(raw.expiry);
10261
- if (expiry === void 0 || typeof raw.cid !== "string" || typeof raw.spaceId !== "string" || typeof raw.path !== "string" || !Array.isArray(raw.actions) || !raw.actions.every((action) => typeof action === "string") || typeof raw.delegateDID !== "string" || typeof raw.ownerAddress !== "string" || typeof raw.chainId !== "number" || typeof raw.delegationHeader.Authorization !== "string") {
10262
- return void 0;
10263
- }
10264
- return { ...raw, expiry };
10265
- }
10266
- function parseExpiry2(value) {
10267
- const expiry = value instanceof Date ? value : typeof value === "string" ? new Date(value) : void 0;
10268
- return expiry !== void 0 && !Number.isNaN(expiry.getTime()) ? expiry : void 0;
10269
- }
10270
10446
  function hasPrivateParameter(value) {
10271
- return isRecord6(value) && typeof value.d === "string" && value.d.length > 0;
10447
+ return isRecord7(value) && typeof value.d === "string" && value.d.length > 0;
10272
10448
  }
10273
10449
  function normalizeDid(value) {
10274
10450
  if (typeof value !== "string" || value.length === 0) return void 0;
10275
10451
  return value.split("#", 1)[0];
10276
10452
  }
10277
- function isRecord6(value) {
10453
+ function isRecord7(value) {
10278
10454
  return typeof value === "object" && value !== null && !Array.isArray(value);
10279
10455
  }
10280
10456
  function failed(context, error) {
@@ -10287,15 +10463,17 @@ function unresolvedSummary(target) {
10287
10463
  posture: "unauthenticated"
10288
10464
  };
10289
10465
  }
10290
- var nodeSdk2;
10466
+ var nodeSdk4;
10291
10467
  var init_runtime = __esm({
10292
10468
  "src/runtime.ts"() {
10293
10469
  "use strict";
10294
- nodeSdk2 = __toESM(require("@tinycloud/node-sdk"), 1);
10470
+ nodeSdk4 = __toESM(require("@tinycloud/node-sdk"), 1);
10295
10471
  init_authority();
10296
10472
  init_errors();
10297
10473
  init_profile();
10474
+ init_secrets();
10298
10475
  init_state();
10476
+ init_delegation_binding();
10299
10477
  }
10300
10478
  });
10301
10479
 
@@ -10308,8 +10486,8 @@ module.exports = __toCommonJS(index_exports);
10308
10486
 
10309
10487
  // src/invoke.ts
10310
10488
  var import_node_crypto3 = require("crypto");
10311
- var import_node_path2 = require("path");
10312
- var import_node_sdk2 = require("@tinycloud/node-sdk");
10489
+ var import_node_path3 = require("path");
10490
+ var import_node_sdk = require("@tinycloud/node-sdk");
10313
10491
 
10314
10492
  // ../sdk-core/src/policy/errors.ts
10315
10493
  var SignedObjectProfileError = class extends Error {
@@ -11014,7 +11192,7 @@ function normalizeInvocationTarget(target) {
11014
11192
  const stateRoot = candidate.stateRoot;
11015
11193
  const allowOwnerProfile = candidate.allowOwnerProfile;
11016
11194
  const privateKey = candidate.privateKey;
11017
- if (profile !== void 0 && typeof profile !== "string" || host !== void 0 && typeof host !== "string" || stateRoot !== void 0 && (typeof stateRoot !== "string" || !(0, import_node_path2.isAbsolute)(stateRoot)) || allowOwnerProfile !== void 0 && typeof allowOwnerProfile !== "boolean" || privateKey !== void 0 && typeof privateKey !== "string") {
11195
+ if (profile !== void 0 && typeof profile !== "string" || host !== void 0 && typeof host !== "string" || stateRoot !== void 0 && (typeof stateRoot !== "string" || !(0, import_node_path3.isAbsolute)(stateRoot)) || allowOwnerProfile !== void 0 && typeof allowOwnerProfile !== "boolean" || privateKey !== void 0 && typeof privateKey !== "string") {
11018
11196
  return void 0;
11019
11197
  }
11020
11198
  return {