@peerbit/trusted-network 6.0.128 → 6.0.130

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.
@@ -18,6 +18,7 @@ export type CrashSafePolicyAnchorStoreV2 = CrashSafeAtomicReplaceStore;
18
18
  type DurableReducerOptionsV2 = {
19
19
  descriptor: NetworkDescriptorV2;
20
20
  resolvePolicyEntry: PolicySnapshotResolverV2;
21
+ resolvePolicyEntryByCid?: PolicyEntryCidResolverV2;
21
22
  resolveTimeoutMs?: number;
22
23
  signal?: AbortSignal;
23
24
  maxPending?: number;
@@ -26,6 +27,21 @@ type DurableReducerOptionsV2 = {
26
27
  export type TrustedNetworkV2DurablePolicyReducerOptions = DurableReducerOptionsV2 & {
27
28
  store: CrashSafePolicyAnchorStoreV2;
28
29
  };
30
+ export type PolicyEntryCidResolverV2 = (policyEntryCid: string, options: {
31
+ signal: AbortSignal;
32
+ }) => Uint8Array | undefined | Promise<Uint8Array | undefined>;
33
+ export type ExactPolicyHeadRequirementV2 = {
34
+ policyEntryCid: string;
35
+ maxAncestrySteps?: number;
36
+ /** Queue-inclusive relative deadline; callers may only shorten the 10s cap. */
37
+ timeoutMs?: number;
38
+ deadline?: number;
39
+ signal?: AbortSignal;
40
+ };
41
+ export type ExactPolicyHeadLeaseV2 = {
42
+ policyEntryCid: string;
43
+ policy: PolicyHeadProjectionV2;
44
+ };
29
45
  export type PolicyLeaseReferenceV2 = {
30
46
  sequence: bigint;
31
47
  digest: Uint8Array;
@@ -62,6 +78,11 @@ export declare class TrustedNetworkV2DurablePolicyReducer {
62
78
  private checkpoint?;
63
79
  private core;
64
80
  private published;
81
+ private readonly descriptor;
82
+ private readonly resolvePolicyEntryByCid?;
83
+ private readonly leaseLifecycleController;
84
+ private readonly disposeLifecycleObservation;
85
+ private exactAuthenticationInFlight?;
65
86
  private durableCoreIdentityBytes?;
66
87
  private operationTail;
67
88
  private authorizationFences;
@@ -106,6 +127,18 @@ export declare class TrustedNetworkV2DurablePolicyReducer {
106
127
  * throw/rejection is propagated unchanged, but never halts the policy anchor.
107
128
  */
108
129
  withAcceptedPolicyLease<T>(reference: PolicyLeaseReferenceV2, use: (lease: AcceptedPolicyLeaseV2) => T | Promise<T>): Promise<PolicyLeaseResultV2<T>>;
130
+ /**
131
+ * Run one callback while a CID-addressed policy wrapper is authenticated and
132
+ * its policy-body identity is the stable durable current head.
133
+ *
134
+ * Exact fetch, prepared admission, crash-safe publication, and callback share
135
+ * one bounded budget and serialized queue slot. No digest-resolver fallback is
136
+ * allowed when the CID resolver is absent or unavailable.
137
+ */
138
+ withExactPolicyHead<T>(requirement: ExactPolicyHeadRequirementV2, use: (lease: ExactPolicyHeadLeaseV2) => T | Promise<T>): Promise<PolicyLeaseResultV2<T>>;
139
+ private withSerializedPolicyLease;
140
+ private resolveExactPolicyEntry;
141
+ private authenticateExactPolicyEntry;
109
142
  private forkFailStopResult;
110
143
  private lifecycleHaltedResult;
111
144
  private immediateLeaseHaltedResult;
@@ -1 +1 @@
1
- {"version":3,"file":"v2-policy-anchor.d.ts","sourceRoot":"","sources":["../../src/v2-policy-anchor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"v2-policy-anchor.d.ts","sourceRoot":"","sources":["../../src/v2-policy-anchor.ts"],"names":[],"mappings":"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"}
@@ -36,7 +36,7 @@ import { deserialize, field, fixedArray, serialize, variant, vec, } from "@dao-x
36
36
  import { CrashSafeTwoSlotCheckpoint } from "@peerbit/any-store/checkpoint";
37
37
  import { PublicSignKey, sha256Sync } from "@peerbit/crypto";
38
38
  import { compare, concat, equals } from "uint8arrays";
39
- import { TRUSTED_NETWORK_V2_MAX_PENDING_POLICIES, TrustedNetworkV2PolicyReducer, authenticatePolicySnapshotEntryV2, } from "./v2-policy-engine.js";
39
+ import { TRUSTED_NETWORK_V2_MAX_PENDING_POLICIES, TrustedNetworkV2PolicyReducer, authenticatePolicySnapshotEntryV2, captureCanonicalPolicyEntryCidV2, observeAbortSignalV2, } from "./v2-policy-engine.js";
40
40
  import { NetworkDescriptorV2, PolicySubjectBindingV2, TRUSTED_NETWORK_V2_KNOWN_ROLE_BITS, TRUSTED_NETWORK_V2_MAX_POLICY_ENTRY_BYTES, assertNetworkDescriptorV2, } from "./v2.js";
41
41
  /**
42
42
  * Internal crash-safe policy-anchor storage format.
@@ -62,6 +62,8 @@ export const TRUSTED_NETWORK_V2_MAX_POLICY_ANCHOR_CHECKPOINT_PAYLOAD_BYTES = (MA
62
62
  MAX_CHECKPOINT_PAYLOAD_FRAMING_BYTES_V2;
63
63
  const MAX_QUEUED_POLICY_INPUT_BYTES_V2 = TRUSTED_NETWORK_V2_MAX_POLICY_ENTRY_BYTES * 2;
64
64
  const MAX_TIMER_DELAY_MS_V2 = 0x7fffffff;
65
+ const DEFAULT_EXACT_POLICY_HEAD_TIMEOUT_MS_V2 = 10 * 1000;
66
+ const DEFAULT_EXACT_POLICY_HEAD_MAX_ANCESTRY_STEPS_V2 = TRUSTED_NETWORK_V2_MAX_PENDING_POLICIES;
65
67
  const ZERO_DIGEST = new Uint8Array(32);
66
68
  const textEncoder = new TextEncoder();
67
69
  const CHECKPOINT_SCOPE_DOMAIN = textEncoder.encode("peerbit/trusted-network/v2/policy-anchor/checkpoint/v1\0");
@@ -83,6 +85,7 @@ const isAbortSignalV2 = (value) => {
83
85
  return false;
84
86
  }
85
87
  };
88
+ const boundedDependencyReasonV2 = (error) => (error instanceof Error ? error.message : String(error)).slice(0, MAX_UNAVAILABLE_REASON_LENGTH);
86
89
  /**
87
90
  * One self-contained application snapshot. The enclosing generic checkpoint
88
91
  * supplies generation, scope, predecessor, and checksum authentication.
@@ -543,6 +546,11 @@ export class TrustedNetworkV2DurablePolicyReducer {
543
546
  checkpoint;
544
547
  core;
545
548
  published;
549
+ descriptor;
550
+ resolvePolicyEntryByCid;
551
+ leaseLifecycleController;
552
+ disposeLifecycleObservation;
553
+ exactAuthenticationInFlight;
546
554
  durableCoreIdentityBytes;
547
555
  operationTail = Promise.resolve();
548
556
  authorizationFences = 0;
@@ -553,6 +561,10 @@ export class TrustedNetworkV2DurablePolicyReducer {
553
561
  this.checkpoint = properties.checkpoint;
554
562
  this.core = properties.core;
555
563
  this.published = publishedFromReducer(this.core);
564
+ this.descriptor = properties.descriptor;
565
+ this.resolvePolicyEntryByCid = properties.resolvePolicyEntryByCid;
566
+ this.leaseLifecycleController = properties.lifecycle[0];
567
+ this.disposeLifecycleObservation = properties.lifecycle[1];
556
568
  this.durableCoreIdentityBytes =
557
569
  properties.durableCoreIdentityBytes === undefined
558
570
  ? undefined
@@ -560,8 +572,13 @@ export class TrustedNetworkV2DurablePolicyReducer {
560
572
  }
561
573
  static async open(options) {
562
574
  assertNetworkDescriptorV2(options.descriptor);
575
+ if (options.resolvePolicyEntryByCid !== undefined &&
576
+ typeof options.resolvePolicyEntryByCid !== "function") {
577
+ throw new TypeError("Policy CID resolver must be a function");
578
+ }
563
579
  const descriptor = deserialize(serialize(options.descriptor), NetworkDescriptorV2);
564
- const signal = options.signal;
580
+ const lifecycle = observeAbortSignalV2(options.signal);
581
+ const signal = lifecycle[0].signal;
565
582
  const store = options.store;
566
583
  const coreProperties = {
567
584
  descriptor,
@@ -571,32 +588,35 @@ export class TrustedNetworkV2DurablePolicyReducer {
571
588
  maxPending: options.maxPending,
572
589
  maxPendingPolicyBytes: options.maxPendingPolicyBytes,
573
590
  };
574
- assertOpenNotAborted(signal);
575
- const checkpoint = await CrashSafeTwoSlotCheckpoint.open({
576
- store,
577
- scope: checkpointScope(descriptor),
578
- maxPayloadBytes: TRUSTED_NETWORK_V2_MAX_POLICY_ANCHOR_CHECKPOINT_PAYLOAD_BYTES,
579
- });
580
- assertOpenNotAborted(signal);
581
- // Never let the old append-only format look like an empty or newer
582
- // checkpoint. It was internal and never activated, so migration or fallback
583
- // would add rollback surface without preserving a public compatibility need.
584
- for await (const [key] of store.iterator()) {
585
- assertOpenNotAborted(signal);
586
- if (key === TRUSTED_NETWORK_V2_POLICY_ANCHOR_STORE_OWNER ||
587
- key.startsWith(`${TRUSTED_NETWORK_V2_POLICY_ANCHOR_STORE_OWNER}/`)) {
588
- throw new Error("Legacy append-only policy-anchor records are not supported; reset the dedicated store");
589
- }
590
- }
591
- assertOpenNotAborted(signal);
592
- const current = checkpoint.current;
593
591
  let core;
594
592
  try {
593
+ assertOpenNotAborted(signal);
594
+ const checkpoint = await CrashSafeTwoSlotCheckpoint.open({
595
+ store,
596
+ scope: checkpointScope(descriptor),
597
+ maxPayloadBytes: TRUSTED_NETWORK_V2_MAX_POLICY_ANCHOR_CHECKPOINT_PAYLOAD_BYTES,
598
+ });
599
+ assertOpenNotAborted(signal);
600
+ // Never let the old append-only format look like an empty or newer
601
+ // checkpoint. It was internal and never activated, so migration or fallback
602
+ // would add rollback surface without preserving a public compatibility need.
603
+ for await (const [key] of store.iterator()) {
604
+ assertOpenNotAborted(signal);
605
+ if (key === TRUSTED_NETWORK_V2_POLICY_ANCHOR_STORE_OWNER ||
606
+ key.startsWith(`${TRUSTED_NETWORK_V2_POLICY_ANCHOR_STORE_OWNER}/`)) {
607
+ throw new Error("Legacy append-only policy-anchor records are not supported; reset the dedicated store");
608
+ }
609
+ }
610
+ assertOpenNotAborted(signal);
611
+ const current = checkpoint.current;
595
612
  if (current === undefined) {
596
613
  core = new TrustedNetworkV2PolicyReducer(coreProperties);
597
614
  return new TrustedNetworkV2DurablePolicyReducer({
598
615
  checkpoint,
599
616
  core,
617
+ descriptor,
618
+ resolvePolicyEntryByCid: options.resolvePolicyEntryByCid,
619
+ lifecycle,
600
620
  });
601
621
  }
602
622
  const payload = decodeCanonicalCheckpointPayload(current.payload);
@@ -614,11 +634,15 @@ export class TrustedNetworkV2DurablePolicyReducer {
614
634
  // storage, so release that potentially 8.78 MiB snapshot immediately.
615
635
  checkpoint: forked ? undefined : checkpoint,
616
636
  core,
637
+ descriptor,
638
+ resolvePolicyEntryByCid: options.resolvePolicyEntryByCid,
639
+ lifecycle,
617
640
  durableCoreIdentityBytes: decoded.coreIdentityBytes,
618
641
  });
619
642
  }
620
643
  catch (error) {
621
644
  core?.abort();
645
+ lifecycle[1]();
622
646
  throw error;
623
647
  }
624
648
  }
@@ -685,6 +709,7 @@ export class TrustedNetworkV2DurablePolicyReducer {
685
709
  }
686
710
  abort() {
687
711
  this.core.abort();
712
+ this.disposeLifecycleObservation();
688
713
  }
689
714
  ingest(entryBytes) {
690
715
  if (this.terminalError !== undefined) {
@@ -842,37 +867,190 @@ export class TrustedNetworkV2DurablePolicyReducer {
842
867
  reason: "Policy lease callback must be a function",
843
868
  });
844
869
  }
845
- const retainedReferenceBytes = 32;
846
- if (!this.reserveOperation(retainedReferenceBytes)) {
870
+ let digest;
871
+ try {
872
+ digest = copyBytes(suppliedDigest);
873
+ }
874
+ catch {
875
+ return Promise.resolve({
876
+ status: "rejected",
877
+ reason: "Policy reference digest must contain exactly 32 bytes",
878
+ });
879
+ }
880
+ return this.withSerializedPolicyLease(32, deadline, signal, async (leaseSignal) => {
881
+ const resolution = await this.core.resolveAcceptedPolicyPrefix({ sequence, digest }, { maxSteps, deadline, signal: leaseSignal });
882
+ return resolution.status === "resolved"
883
+ ? {
884
+ status: "resolved",
885
+ lease: {
886
+ policy: resolution.policy,
887
+ acceptedHead: resolution.acceptedHead,
888
+ },
889
+ }
890
+ : resolution;
891
+ }, use);
892
+ }
893
+ /**
894
+ * Run one callback while a CID-addressed policy wrapper is authenticated and
895
+ * its policy-body identity is the stable durable current head.
896
+ *
897
+ * Exact fetch, prepared admission, crash-safe publication, and callback share
898
+ * one bounded budget and serialized queue slot. No digest-resolver fallback is
899
+ * allowed when the CID resolver is absent or unavailable.
900
+ */
901
+ withExactPolicyHead(requirement, use) {
902
+ const halted = this.immediateLeaseHaltedResult();
903
+ if (halted !== undefined)
904
+ return Promise.resolve(halted);
905
+ let policyEntryCid;
906
+ let maxAncestrySteps;
907
+ let deadline;
908
+ let signal;
909
+ try {
910
+ policyEntryCid = captureCanonicalPolicyEntryCidV2(requirement.policyEntryCid);
911
+ maxAncestrySteps =
912
+ requirement.maxAncestrySteps ??
913
+ DEFAULT_EXACT_POLICY_HEAD_MAX_ANCESTRY_STEPS_V2;
914
+ if (!Number.isSafeInteger(maxAncestrySteps) ||
915
+ maxAncestrySteps < 0 ||
916
+ maxAncestrySteps > TRUSTED_NETWORK_V2_MAX_PENDING_POLICIES) {
917
+ return Promise.resolve({
918
+ status: "rejected",
919
+ reason: `Exact policy maxAncestrySteps must be between 0 and ${TRUSTED_NETWORK_V2_MAX_PENDING_POLICIES}`,
920
+ });
921
+ }
922
+ const suppliedDeadline = requirement.deadline;
923
+ if (suppliedDeadline !== undefined &&
924
+ (!Number.isSafeInteger(suppliedDeadline) || suppliedDeadline < 0)) {
925
+ return Promise.resolve({
926
+ status: "rejected",
927
+ reason: "Exact policy deadline must be a non-negative safe integer",
928
+ });
929
+ }
930
+ const timeoutMs = requirement.timeoutMs ?? DEFAULT_EXACT_POLICY_HEAD_TIMEOUT_MS_V2;
931
+ if (!Number.isSafeInteger(timeoutMs) ||
932
+ timeoutMs < 0 ||
933
+ timeoutMs > DEFAULT_EXACT_POLICY_HEAD_TIMEOUT_MS_V2) {
934
+ return Promise.resolve({
935
+ status: "rejected",
936
+ reason: `Exact policy timeoutMs must be between 0 and ${DEFAULT_EXACT_POLICY_HEAD_TIMEOUT_MS_V2}`,
937
+ });
938
+ }
939
+ const relativeDeadline = Math.min(Number.MAX_SAFE_INTEGER, Date.now() + timeoutMs);
940
+ deadline =
941
+ suppliedDeadline === undefined
942
+ ? relativeDeadline
943
+ : Math.min(suppliedDeadline, relativeDeadline);
944
+ signal = requirement.signal;
945
+ if (signal !== undefined && !isAbortSignalV2(signal)) {
946
+ return Promise.resolve({
947
+ status: "rejected",
948
+ reason: "Exact policy signal must be an AbortSignal",
949
+ });
950
+ }
951
+ }
952
+ catch {
953
+ return Promise.resolve({
954
+ status: "rejected",
955
+ reason: "Exact policy-head requirement is invalid",
956
+ });
957
+ }
958
+ if (typeof use !== "function") {
959
+ return Promise.resolve({
960
+ status: "rejected",
961
+ reason: "Exact policy-head callback must be a function",
962
+ });
963
+ }
964
+ return this.withSerializedPolicyLease(textEncoder.encode(policyEntryCid).byteLength, deadline, signal, async (leaseSignal, mutationCompleted) => {
965
+ const exact = await this.resolveExactPolicyEntry(policyEntryCid, deadline, leaseSignal);
966
+ if (exact.status !== "resolved")
967
+ return exact;
968
+ this.authorizationFences += 1;
969
+ try {
970
+ const admission = await this.core.ingestPreparedExactPolicy(exact.lease, {
971
+ maxParentEdges: maxAncestrySteps,
972
+ deadline,
973
+ signal: leaseSignal,
974
+ });
975
+ // Core admission may now be mutated. From here through checkpoint
976
+ // settlement, cancellation may settle outward but cannot release the slot.
977
+ mutationCompleted();
978
+ await this.persistCorePublication(admission);
979
+ const halted = this.immediateLeaseHaltedResult();
980
+ if (halted !== undefined)
981
+ return halted;
982
+ if (admission.status !== "accepted" &&
983
+ admission.status !== "duplicate") {
984
+ return {
985
+ status: admission.status === "rejected" ||
986
+ admission.status === "capacity"
987
+ ? admission.status
988
+ : "unavailable",
989
+ reason: admission.reason ?? "Exact policy admission is unavailable",
990
+ };
991
+ }
992
+ const head = this.published.head;
993
+ if (this.published.state !== "ACTIVE" ||
994
+ head === undefined ||
995
+ head.sequence !== exact.lease.policy.sequence ||
996
+ !equals(head.digest, exact.lease.policy.digest)) {
997
+ return {
998
+ status: "unavailable",
999
+ reason: "Exact policy entry is not the durable current policy head",
1000
+ };
1001
+ }
1002
+ return {
1003
+ status: "resolved",
1004
+ lease: { policyEntryCid, policy: copyHead(head) },
1005
+ };
1006
+ }
1007
+ catch (error) {
1008
+ throw this.halt(error);
1009
+ }
1010
+ finally {
1011
+ this.authorizationFences -= 1;
1012
+ }
1013
+ }, use);
1014
+ }
1015
+ withSerializedPolicyLease(retainedInputBytes, deadline, signal, acquire, use) {
1016
+ if (!this.reserveOperation(retainedInputBytes)) {
847
1017
  return Promise.resolve({
848
1018
  status: "capacity",
849
1019
  reason: "Durable policy operation queue is at its fixed capacity",
850
1020
  });
851
1021
  }
852
- let digest;
1022
+ let observedSignal;
853
1023
  try {
854
- digest = copyBytes(suppliedDigest);
1024
+ observedSignal = observeAbortSignalV2(signal);
855
1025
  }
856
1026
  catch {
857
- this.releaseOperation(retainedReferenceBytes);
1027
+ this.releaseOperation(retainedInputBytes);
858
1028
  return Promise.resolve({
859
1029
  status: "rejected",
860
- reason: "Policy reference digest must contain exactly 32 bytes",
1030
+ reason: "Policy lease signal could not be observed",
861
1031
  });
862
1032
  }
863
1033
  let queueAcquired = false;
864
1034
  let callbackAcquired = false;
1035
+ let mutationCompleted = false;
1036
+ const acquisitionController = observedSignal[0];
1037
+ const interruptionSignal = AbortSignal.any([
1038
+ acquisitionController.signal,
1039
+ this.leaseLifecycleController.signal,
1040
+ ]);
865
1041
  let preAcquisitionResult;
866
1042
  let resolveEarly;
867
1043
  const earlyResult = new Promise((resolve) => {
868
1044
  resolveEarly = resolve;
869
1045
  });
870
1046
  let deadlineTimer;
871
- let signalListenerInstalled = false;
872
- const abortBeforeAcquisition = () => {
1047
+ const interruptBeforeAcquisition = () => {
1048
+ const halted = this.leaseLifecycleController.signal.aborted;
873
1049
  settleBeforeAcquisition({
874
- status: "unavailable",
875
- reason: "Policy lease acquisition was aborted by the caller",
1050
+ status: halted ? "halted" : "unavailable",
1051
+ reason: halted
1052
+ ? "Policy reducer lifecycle is aborted"
1053
+ : "Policy lease acquisition was aborted by the caller",
876
1054
  });
877
1055
  };
878
1056
  const cleanupWakeups = () => {
@@ -880,29 +1058,26 @@ export class TrustedNetworkV2DurablePolicyReducer {
880
1058
  clearTimeout(deadlineTimer);
881
1059
  deadlineTimer = undefined;
882
1060
  }
883
- if (signalListenerInstalled) {
884
- signalListenerInstalled = false;
885
- try {
886
- signal?.removeEventListener("abort", abortBeforeAcquisition);
887
- }
888
- catch {
889
- // A structurally valid hostile signal must not break queue cleanup.
890
- }
891
- }
1061
+ interruptionSignal.removeEventListener("abort", interruptBeforeAcquisition);
1062
+ observedSignal[1]();
892
1063
  };
893
1064
  function settleBeforeAcquisition(result) {
894
- if (queueAcquired ||
895
- callbackAcquired ||
896
- preAcquisitionResult !== undefined) {
1065
+ if (callbackAcquired || preAcquisitionResult !== undefined) {
897
1066
  return;
898
1067
  }
899
1068
  preAcquisitionResult = result;
1069
+ acquisitionController.abort();
900
1070
  cleanupWakeups();
901
- resolveEarly(result);
1071
+ if (!queueAcquired || mutationCompleted)
1072
+ resolveEarly(result);
902
1073
  }
1074
+ const completeMutation = () => {
1075
+ mutationCompleted = true;
1076
+ if (preAcquisitionResult !== undefined)
1077
+ resolveEarly(preAcquisitionResult);
1078
+ };
903
1079
  const armDeadlineWakeup = () => {
904
1080
  if (deadline === undefined ||
905
- queueAcquired ||
906
1081
  callbackAcquired ||
907
1082
  preAcquisitionResult !== undefined) {
908
1083
  return;
@@ -933,26 +1108,22 @@ export class TrustedNetworkV2DurablePolicyReducer {
933
1108
  return preAcquisitionResult;
934
1109
  }
935
1110
  queueAcquired = true;
936
- cleanupWakeups();
937
1111
  const queuedHalt = this.immediateLeaseHaltedResult();
938
1112
  if (queuedHalt !== undefined)
939
1113
  return queuedHalt;
940
- const resolution = await this.core.resolveAcceptedPolicyPrefix({
941
- sequence,
942
- digest,
943
- }, { maxSteps, deadline, signal });
1114
+ const acquisition = await acquire(acquisitionController.signal, completeMutation);
944
1115
  if (preAcquisitionResult !== undefined) {
945
1116
  return preAcquisitionResult;
946
1117
  }
947
- if (resolution.status !== "resolved")
948
- return resolution;
1118
+ if (acquisition.status !== "resolved")
1119
+ return acquisition;
949
1120
  // Lifecycle abort before callback invocation loses acquisition. There is
950
1121
  // no await between this check and invoking user code, which is the lease's
951
1122
  // linearization point.
952
1123
  const resolvedHalt = this.immediateLeaseHaltedResult();
953
1124
  if (resolvedHalt !== undefined)
954
1125
  return resolvedHalt;
955
- if (signal?.aborted) {
1126
+ if (acquisitionController.signal.aborted) {
956
1127
  return {
957
1128
  status: "unavailable",
958
1129
  reason: "Policy lease acquisition was aborted by the caller",
@@ -966,37 +1137,138 @@ export class TrustedNetworkV2DurablePolicyReducer {
966
1137
  }
967
1138
  callbackAcquired = true;
968
1139
  cleanupWakeups();
969
- const value = await use({
970
- // The core created these two projections independently and retains
971
- // neither, including when the requested policy is the current head.
972
- policy: resolution.policy,
973
- acceptedHead: resolution.acceptedHead,
974
- });
1140
+ const value = await use(acquisition.lease);
975
1141
  return { status: "completed", value };
976
1142
  });
977
1143
  const retainedResult = queuedResult.finally(() => {
978
1144
  cleanupWakeups();
979
- this.releaseOperation(retainedReferenceBytes);
1145
+ this.releaseOperation(retainedInputBytes);
980
1146
  });
981
1147
  this.operationTail = retainedResult.then(() => { }, () => { });
982
- if (signal !== undefined) {
983
- signalListenerInstalled = true;
1148
+ interruptionSignal.addEventListener("abort", interruptBeforeAcquisition, {
1149
+ once: true,
1150
+ });
1151
+ if (interruptionSignal.aborted)
1152
+ interruptBeforeAcquisition();
1153
+ armDeadlineWakeup();
1154
+ return Promise.race([retainedResult, earlyResult]);
1155
+ }
1156
+ async resolveExactPolicyEntry(policyEntryCid, deadline, callerSignal) {
1157
+ const resolver = this.resolvePolicyEntryByCid;
1158
+ if (resolver === undefined) {
1159
+ return {
1160
+ status: "unavailable",
1161
+ reason: "Exact policy-head readiness has no policy CID resolver",
1162
+ };
1163
+ }
1164
+ if (this.core.state === "HALTED") {
1165
+ return {
1166
+ status: "halted",
1167
+ reason: "Policy reducer lifecycle is aborted",
1168
+ };
1169
+ }
1170
+ if (callerSignal.aborted) {
1171
+ return {
1172
+ status: "unavailable",
1173
+ reason: "Exact policy-head acquisition was aborted by the caller",
1174
+ };
1175
+ }
1176
+ if (Date.now() >= deadline) {
1177
+ return {
1178
+ status: "unavailable",
1179
+ reason: "Exact policy-head acquisition deadline elapsed",
1180
+ };
1181
+ }
1182
+ const interrupted = new Promise((_resolve, reject) => {
1183
+ callerSignal.addEventListener("abort", () => reject(new Error("Exact policy-head resolution interrupted")), { once: true });
1184
+ });
1185
+ const interruptionFailure = () => {
1186
+ const halted = this.immediateLeaseHaltedResult();
1187
+ if (halted !== undefined)
1188
+ return halted;
1189
+ if (callerSignal.aborted) {
1190
+ return {
1191
+ status: "unavailable",
1192
+ reason: "Exact policy-head acquisition was aborted by the caller",
1193
+ };
1194
+ }
1195
+ if (Date.now() >= deadline) {
1196
+ return {
1197
+ status: "unavailable",
1198
+ reason: "Exact policy-head acquisition deadline elapsed",
1199
+ };
1200
+ }
1201
+ return undefined;
1202
+ };
1203
+ const priorAuthentication = this.exactAuthenticationInFlight;
1204
+ if (priorAuthentication !== undefined) {
984
1205
  try {
985
- signal.addEventListener("abort", abortBeforeAcquisition, {
986
- once: true,
987
- });
1206
+ await Promise.race([
1207
+ priorAuthentication.catch(() => { }),
1208
+ interrupted,
1209
+ ]);
988
1210
  }
989
1211
  catch {
990
- settleBeforeAcquisition({
991
- status: "rejected",
992
- reason: "Policy lease signal could not be observed",
1212
+ return (interruptionFailure() ?? {
1213
+ status: "unavailable",
1214
+ reason: "Exact policy authentication slot is unavailable",
993
1215
  });
994
1216
  }
995
- if (signal.aborted)
996
- abortBeforeAcquisition();
1217
+ const interruptedResult = interruptionFailure();
1218
+ if (interruptedResult !== undefined)
1219
+ return interruptedResult;
997
1220
  }
998
- armDeadlineWakeup();
999
- return Promise.race([retainedResult, earlyResult]);
1221
+ let entryBytes;
1222
+ try {
1223
+ entryBytes = await Promise.race([
1224
+ Promise.resolve().then(() => resolver(policyEntryCid, { signal: callerSignal })),
1225
+ interrupted,
1226
+ ]);
1227
+ }
1228
+ catch (error) {
1229
+ const interruptedResult = interruptionFailure();
1230
+ if (interruptedResult !== undefined)
1231
+ return interruptedResult;
1232
+ return {
1233
+ status: "unavailable",
1234
+ reason: `Policy CID resolver dependency is unavailable: ${boundedDependencyReasonV2(error)}`,
1235
+ };
1236
+ }
1237
+ if (entryBytes === undefined) {
1238
+ return {
1239
+ status: "unavailable",
1240
+ reason: "Exact policy entry is unavailable from its CID resolver",
1241
+ };
1242
+ }
1243
+ const authentication = Promise.resolve().then(() => this.authenticateExactPolicyEntry({
1244
+ policyEntryCid,
1245
+ entryBytes,
1246
+ descriptor: this.descriptor,
1247
+ }));
1248
+ this.exactAuthenticationInFlight = authentication;
1249
+ const clearAuthentication = () => {
1250
+ if (this.exactAuthenticationInFlight === authentication) {
1251
+ this.exactAuthenticationInFlight = undefined;
1252
+ }
1253
+ };
1254
+ void authentication.then(clearAuthentication, clearAuthentication);
1255
+ try {
1256
+ const authenticated = await Promise.race([authentication, interrupted]);
1257
+ const completedInterruption = interruptionFailure();
1258
+ return (completedInterruption ?? { status: "resolved", lease: authenticated });
1259
+ }
1260
+ catch (error) {
1261
+ const interruptedResult = interruptionFailure();
1262
+ if (interruptedResult !== undefined)
1263
+ return interruptedResult;
1264
+ return {
1265
+ status: "rejected",
1266
+ reason: `Exact policy entry is invalid: ${boundedDependencyReasonV2(error)}`,
1267
+ };
1268
+ }
1269
+ }
1270
+ authenticateExactPolicyEntry(properties) {
1271
+ return this.core.prepareExactPolicyEntry(properties.policyEntryCid, properties.entryBytes);
1000
1272
  }
1001
1273
  forkFailStopResult() {
1002
1274
  return {
@@ -1125,6 +1397,11 @@ export class TrustedNetworkV2DurablePolicyReducer {
1125
1397
  throw new Error("Policy-anchor checkpoint is unavailable");
1126
1398
  }
1127
1399
  await checkpoint.commit(payloadBytes);
1400
+ // Atomic replacement is not cancellable. A lifecycle that ended while it
1401
+ // was in flight may leave durable recovery state, but cannot publish it into
1402
+ // the stale in-memory instance.
1403
+ if (this.core.state === "HALTED")
1404
+ return;
1128
1405
  this.durableCoreIdentityBytes = nextDurableCoreIdentityBytes;
1129
1406
  this.published = nextPublished;
1130
1407
  if (durableState.state === "FORKED") {