@tearleads/client-sdk 0.1.37 → 0.1.39
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/client/Tearleads.d.ts +3 -0
- package/client/Tearleads.js +20 -12
- package/client/Tearleads.js.map +1 -1
- package/client/organizations/organizationGroupHistory.js +1 -1
- package/client/organizations/organizationGroupHistory.js.map +1 -1
- package/client/organizations/organizationHistory.js +1 -1
- package/client/organizations/organizationHistory.js.map +1 -1
- package/client/session/index.js +2 -2
- package/client/session/index.js.map +1 -1
- package/client/session/sessionOrganizationCreation.js +1 -0
- package/client/session/sessionOrganizationCreation.js.map +1 -1
- package/client/session/sessionTypes.d.ts +3 -0
- package/client/session/sessionTypes.js.map +1 -1
- package/client/workflowRuntime.d.ts +2 -0
- package/client/workflowRuntime.js +34 -16
- package/client/workflowRuntime.js.map +1 -1
- package/data/keyingProjectionVerification/principalPolicyVerification.js +1 -2
- package/data/keyingProjectionVerification/principalPolicyVerification.js.map +1 -1
- package/data/keyingProjectionVerification/resolvedPrincipalPolicy.js +39 -51
- package/data/keyingProjectionVerification/resolvedPrincipalPolicy.js.map +1 -1
- package/data/keyingProjectionVerification/types.d.ts +1 -1
- package/data/keyingProjectionVerification/types.js.map +1 -1
- package/data/persistence/documents/documentDiscoveryEvidencePersistence.d.ts +16 -0
- package/data/persistence/documents/documentDiscoveryEvidencePersistence.js +23 -0
- package/data/persistence/documents/documentDiscoveryEvidencePersistence.js.map +1 -1
- package/data/persistence/organizations/organizationAccessRevocationPersistence.js +4 -0
- package/data/persistence/organizations/organizationAccessRevocationPersistence.js.map +1 -1
- package/data/persistence/organizations/organizationReadModelPersistence.js +7 -1
- package/data/persistence/organizations/organizationReadModelPersistence.js.map +1 -1
- package/data/principals/principalRecoveryEpoch.js +18 -0
- package/data/principals/principalRecoveryEpoch.js.map +1 -0
- package/data/trustedUserIdentity/index.d.ts +1 -1
- package/data/trustedUserIdentity/index.js +1 -1
- package/data/trustedUserIdentity/index.js.map +1 -1
- package/data/trustedUserIdentity/service.d.ts +12 -1
- package/data/trustedUserIdentity/service.js +27 -3
- package/data/trustedUserIdentity/service.js.map +1 -1
- package/package.json +1 -1
- package/workflows/organizations/createOrganization.d.ts +3 -0
- package/workflows/organizations/createOrganization.js +1 -0
- package/workflows/organizations/createOrganization.js.map +1 -1
- package/workflows/organizations/currentGroupPolicyMutationContext.js +5 -5
- package/workflows/organizations/currentGroupPolicyMutationContext.js.map +1 -1
- package/workflows/principals/principalCurrentRecoveryBatch.js +1 -1
- package/workflows/principals/principalCurrentRecoveryBatch.js.map +1 -1
- package/workflows/principals/principalRecoveryLocalPreference.js.map +1 -1
- package/workflows/principals/principalRecoveryMemo.d.ts +9 -4
- package/workflows/principals/principalRecoveryMemo.js +94 -34
- package/workflows/principals/principalRecoveryMemo.js.map +1 -1
- package/workflows/principals/principalRecoveryMemoValidity.js +33 -0
- package/workflows/principals/principalRecoveryMemoValidity.js.map +1 -0
- package/workflows/principals/principalRecoveryOutage.js +2 -2
- package/workflows/principals/principalRecoveryOutage.js.map +1 -1
- package/workflows/principals/recoverCurrentOrganizationPolicy.js +8 -3
- package/workflows/principals/recoverCurrentOrganizationPolicy.js.map +1 -1
- package/workflows/principals/recoverPrincipalPolicyHistory.js +42 -8
- package/workflows/principals/recoverPrincipalPolicyHistory.js.map +1 -1
- package/workflows/principals/recoverPublicPrincipalHistory.js +5 -5
- package/workflows/principals/recoverPublicPrincipalHistory.js.map +1 -1
- package/workflows/principals/recoverScopedPrincipalPolicyHistory.d.ts +9 -2
- package/workflows/principals/recoverScopedPrincipalPolicyHistory.js +8 -2
- package/workflows/principals/recoverScopedPrincipalPolicyHistory.js.map +1 -1
- package/workflows/principals/runtimePolicyRecovery.js +4 -1
- package/workflows/principals/runtimePolicyRecovery.js.map +1 -1
- package/workflows/principals/runtimePolicyWarmer.js +12 -4
- package/workflows/principals/runtimePolicyWarmer.js.map +1 -1
- package/workflows/registration/organizationProvisioningPersistence.d.ts +3 -0
- package/workflows/registration/organizationProvisioningPersistence.js +27 -1
- package/workflows/registration/organizationProvisioningPersistence.js.map +1 -1
- package/workflows/registration/persistRegistrationBootstrap.d.ts +3 -0
- package/workflows/registration/persistRegistrationBootstrap.js +7 -1
- package/workflows/registration/persistRegistrationBootstrap.js.map +1 -1
- package/workflows/registration/provisionedPrincipalHistory.d.ts +45 -0
- package/workflows/registration/provisionedPrincipalHistory.js +104 -0
- package/workflows/registration/provisionedPrincipalHistory.js.map +1 -0
- package/workflows/registration/provisioningAcknowledgement.js +18 -0
- package/workflows/registration/provisioningAcknowledgement.js.map +1 -1
- package/workflows/registration/registerIdentity.d.ts +3 -0
- package/workflows/registration/registerIdentity.js +1 -0
- package/workflows/registration/registerIdentity.js.map +1 -1
|
@@ -30,6 +30,7 @@ export async function loadCurrentGroupPolicyMutationContext(input) {
|
|
|
30
30
|
{ head: adminHead, projection: authority.admins.policy.projection },
|
|
31
31
|
],
|
|
32
32
|
};
|
|
33
|
+
// A reused citation keeps the lifetime of the read that produced it.
|
|
33
34
|
const references = new Map();
|
|
34
35
|
return {
|
|
35
36
|
adminGroupId: authority.descriptor.adminGroupId,
|
|
@@ -60,18 +61,17 @@ export async function loadCurrentGroupPolicyMutationContext(input) {
|
|
|
60
61
|
if (oldest !== undefined)
|
|
61
62
|
references.delete(oldest);
|
|
62
63
|
}
|
|
63
|
-
pending = authority
|
|
64
|
-
.readGroup(input.groupId, owned)
|
|
65
|
-
.then((selected) => selected.policy);
|
|
64
|
+
pending = authority.readGroup(input.groupId, owned);
|
|
66
65
|
references.set(key, pending);
|
|
67
66
|
}
|
|
68
|
-
const
|
|
67
|
+
const selected = await pending.catch((error) => {
|
|
69
68
|
if (references.get(key) === pending)
|
|
70
69
|
references.delete(key);
|
|
71
70
|
throw error;
|
|
72
71
|
});
|
|
73
72
|
assertProjectionVerificationCurrent(stillCurrent);
|
|
74
|
-
|
|
73
|
+
assertProjectionVerificationCurrent(selected.stillCurrent);
|
|
74
|
+
return selected.policy;
|
|
75
75
|
},
|
|
76
76
|
stillCurrent,
|
|
77
77
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"currentGroupPolicyMutationContext.js","sourceRoot":"","sources":["currentGroupPolicyMutationContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,4BAA4B,GAE7B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,kCAAkC,EAAE,MAAM,2DAA2D,CAAC;AAC/G,OAAO,EAAE,4BAA4B,EAAE,MAAM,uDAAuD,CAAC;AAGrG,OAAO,EAAE,mDAAmD,EAAE,MAAM,iCAAiC,CAAC;AACtG,OAAO,EAAE,wCAAwC,EAAE,MAAM,wCAAwC,CAAC;AAClG,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAE,2BAA2B,EAAE,MAAM,8BAA8B,CAAC;AAE3E,kFAAkF;AAClF,MAAM,CAAC,KAAK,UAAU,qCAAqC,CAAC,KAY3D;IACC,KAAK,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;IACrB,MAAM,EAAE,SAAS,EAAE,sBAAsB,EAAE,GACzC,MAAM,wCAAwC,CAAC,KAAK,CAAC,CAAC;IACxD,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACvD,MAAM,YAAY,GAAG,GAAG,EAAE,CACxB,KAAK,CAAC,YAAY,EAAE,IAAI,SAAS,CAAC,YAAY,EAAE,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;IAC3E,mCAAmC,CAAC,YAAY,CAAC,CAAC;IAClD,0BAA0B,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC;IAChE,2BAA2B,CACzB,KAAK,CAAC,OAAO,EACb,sBAAsB,EACtB,KAAK,CAAC,YAAY,CACnB,CAAC;IACF,MAAM,kCAAkC,CAAC;QACvC,MAAM,EAAE,EAAE;QACV,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,QAAQ,EAAE,CAAC,GAAG,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,MAAM,CAAC;QAC/C,YAAY;KACb,CAAC,CAAC;IACH,mCAAmC,CAAC,YAAY,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,4BAA4B,CAC5C,SAAS,CAAC,UAAU,EACpB,SAAS,CAAC,UAAU,CAAC,YAAY,CAClC,CAAC;IACF,MAAM,iBAAiB,GAAqC;QAC1D,WAAW,EAAE,SAAS;QACtB,MAAM,EAAE;YACN,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE;SACpE;KACF,CAAC;IACF,MAAM,UAAU,GAAG,IAAI,GAAG,
|
|
1
|
+
{"version":3,"file":"currentGroupPolicyMutationContext.js","sourceRoot":"","sources":["currentGroupPolicyMutationContext.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type PrincipalPolicyExternalAuthority,\n type ReferencedPrincipalHead,\n serializeKeyingCanonicalJson,\n type VerifiedPrincipalPolicyCurrent,\n} from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { advanceKeyingCheckpointsAtomically } from \"../../data/persistence/keyingCheckpointAdvancePersistence\";\nimport { requireOrganizationGroupHead } from \"../../data/principals/organizationAuthorityDescriptor\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { createRuntimePrincipalPolicyCurrentResolver } from \"../principals/runtimePolicyRecovery\";\nimport { createSelectedCurrentGroupMetadataContainerVerifier } from \"./currentGroupMetadataAuthority\";\nimport { loadCurrentOrganizationMutationAuthority } from \"./currentOrganizationMutationAuthority\";\nimport { assertGroupMetadataBinding } from \"./groupMetadataBinding\";\nimport { requireSignerCanManageGroup } from \"./groupMutationAuthorization\";\n\n/** Exact current directory, strict Admins and group evidence for one mutation. */\nexport async function loadCurrentGroupPolicyMutationContext(input: {\n readonly execSql: ExecSql;\n readonly groupId: string;\n readonly organizationId: string;\n readonly signerUserId: string;\n readonly resolveCurrentPolicy: NonNullable<\n ReturnType<typeof createRuntimePrincipalPolicyCurrentResolver>\n >;\n readonly recoverPendingPrincipalMutation: (\n organizationId: string,\n ) => Promise<void>;\n readonly stillCurrent: () => boolean;\n}) {\n input = { ...input };\n const { authority, currentOrgAdminUserIds } =\n await loadCurrentOrganizationMutationAuthority(input);\n const group = await authority.readGroup(input.groupId);\n const stillCurrent = () =>\n input.stillCurrent() && authority.stillCurrent() && group.stillCurrent();\n assertProjectionVerificationCurrent(stillCurrent);\n assertGroupMetadataBinding(group.current, authority.descriptor);\n requireSignerCanManageGroup(\n group.current,\n currentOrgAdminUserIds,\n input.signerUserId,\n );\n await advanceKeyingCheckpointsAtomically({\n access: [],\n execSql: input.execSql,\n organizationId: input.organizationId,\n policies: [...group.dependencies, group.policy],\n stillCurrent,\n });\n assertProjectionVerificationCurrent(stillCurrent);\n const adminHead = requireOrganizationGroupHead(\n authority.descriptor,\n authority.descriptor.adminGroupId,\n );\n const externalAuthority: PrincipalPolicyExternalAuthority = {\n currentHead: adminHead,\n states: [\n { head: adminHead, projection: authority.admins.policy.projection },\n ],\n };\n // A reused citation keeps the lifetime of the read that produced it.\n const references = new Map<\n string,\n Promise<{\n readonly policy: VerifiedPrincipalPolicyCurrent;\n readonly stillCurrent: () => boolean;\n }>\n >();\n return {\n adminGroupId: authority.descriptor.adminGroupId,\n adminCurrent: authority.admins,\n currentPolicy: group.current,\n verifiedCurrentPolicy: group.policy,\n verifyMetadataContainer:\n createSelectedCurrentGroupMetadataContainerVerifier({\n authority,\n organizationId: input.organizationId,\n stillCurrent,\n }),\n currentOrgAdminUserIds,\n externalAuthority,\n isOrganizationAdminsGroup:\n input.groupId === authority.descriptor.adminGroupId,\n localPolicyCheckpoint: group.policy.checkpoint,\n memberGroupId: authority.descriptor.memberGroupId,\n organizationDescriptor: authority.descriptor,\n organizationCurrent: authority.directory,\n async readPredecessorReference(reference: ReferencedPrincipalHead) {\n assertProjectionVerificationCurrent(stillCurrent);\n const owned = structuredClone(reference);\n const key = serializeKeyingCanonicalJson({ ...owned });\n let pending = references.get(key);\n if (!pending) {\n // Keep at most one bounded selection's citations, even across many paths.\n if (references.size === 128) {\n const oldest = references.keys().next().value;\n if (oldest !== undefined) references.delete(oldest);\n }\n pending = authority.readGroup(input.groupId, owned);\n references.set(key, pending);\n }\n const selected = await pending.catch((error: unknown) => {\n if (references.get(key) === pending) references.delete(key);\n throw error;\n });\n assertProjectionVerificationCurrent(stillCurrent);\n assertProjectionVerificationCurrent(selected.stillCurrent);\n return selected.policy;\n },\n stillCurrent,\n };\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { recoverCurrentOrganizationPolicy } from "./recoverCurrentOrganizationPolicy.js";
|
|
2
2
|
import { createScopedPrincipalPolicyHistoryBatch } from "./recoverScopedPrincipalPolicyHistory.js";
|
|
3
|
-
/** Discovery and selected references share one authenticated
|
|
3
|
+
/** Discovery and selected references share one authenticated batch view. */
|
|
4
4
|
export function createPrincipalCurrentRecoveryBatch() {
|
|
5
5
|
const memo = {
|
|
6
6
|
directories: new Map(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"principalCurrentRecoveryBatch.js","sourceRoot":"","sources":["principalCurrentRecoveryBatch.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gCAAgC,EAAE,MAAM,oCAAoC,CAAC;AACtF,OAAO,EAAE,uCAAuC,EAAE,MAAM,uCAAuC,CAAC;AAEhG,
|
|
1
|
+
{"version":3,"file":"principalCurrentRecoveryBatch.js","sourceRoot":"","sources":["principalCurrentRecoveryBatch.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gCAAgC,EAAE,MAAM,oCAAoC,CAAC;AACtF,OAAO,EAAE,uCAAuC,EAAE,MAAM,uCAAuC,CAAC;AAEhG,4EAA4E;AAC5E,MAAM,UAAU,mCAAmC;IACjD,MAAM,IAAI,GAA0B;QAClC,WAAW,EAAE,IAAI,GAAG,EAAE;QACtB,MAAM,EAAE,IAAI,GAAG,EAAE;KAClB,CAAC;IACF,OAAO;QACL,OAAO,EAAE,uCAAuC,CAAC,IAAI,CAAC;QACtD,QAAQ,EAAE,CAAC,KAA+B,EAAE,EAAE,CAC5C,gCAAgC,CAAC,KAAK,EAAE,IAAI,CAAC;KAChD,CAAC;AACJ,CAAC","sourcesContent":["import type { PrincipalRecoveryContext } from \"./principalRecoveryDirectory\";\nimport type { PrincipalRecoveryMemo } from \"./principalRecoveryMemo\";\nimport { recoverCurrentOrganizationPolicy } from \"./recoverCurrentOrganizationPolicy\";\nimport { createScopedPrincipalPolicyHistoryBatch } from \"./recoverScopedPrincipalPolicyHistory\";\n\n/** Discovery and selected references share one authenticated batch view. */\nexport function createPrincipalCurrentRecoveryBatch() {\n const memo: PrincipalRecoveryMemo = {\n directories: new Map(),\n admins: new Map(),\n };\n return {\n recover: createScopedPrincipalPolicyHistoryBatch(memo),\n discover: (input: PrincipalRecoveryContext) =>\n recoverCurrentOrganizationPolicy(input, memo),\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"principalRecoveryLocalPreference.js","sourceRoot":"","sources":["principalRecoveryLocalPreference.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,6BAA6B,EAAE,MAAM,uDAAuD,CAAC;AACtG,OAAO,EAAE,iCAAiC,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,kCAAkC,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"principalRecoveryLocalPreference.js","sourceRoot":"","sources":["principalRecoveryLocalPreference.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,6BAA6B,EAAE,MAAM,uDAAuD,CAAC;AACtG,OAAO,EAAE,iCAAiC,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,kCAAkC,EAAE,MAAM,2BAA2B,CAAC;AAM/E,wFAAwF;AACxF,MAAM,CAAC,KAAK,UAAU,mCAAmC,CACvD,OAA2C,EAC3C,OAAmD,EACnD,kBAA2B,EAC3B,wBAAwB,GAAG,KAAK;IAEhC,IAAI,kBAAkB,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC;gBAC1B,GAAG,OAAO;gBACV,OAAO,EAAE,IAAI;aACd,CAAC,CAAC;YACH,mCAAmC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YAC1D,IACE,wBAAwB;gBACxB,OAAO,CAAC,WAAW;gBACnB,6BAA6B,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC;gBAEpE,OAAO,KAAK,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,mCAAmC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YAC1D,IACE,CAAC,CAAC,KAAK,YAAY,iCAAiC,CAAC;gBACrD,CAAC,CACC,KAAK,YAAY,uBAAuB;oBACxC,KAAK,CAAC,IAAI,KAAK,oBAAoB,CACpC;gBAED,MAAM,KAAK,CAAC;QAChB,CAAC;IACH,CAAC;IACD,OAAO,kCAAkC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC9D,CAAC","sourcesContent":["import { KeyingVerificationError } from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { principalHeadMatchesReference } from \"../../data/principals/organizationAuthorityDescriptor\";\nimport { PrincipalHistoryRecoveryRaceError } from \"./principalHistoryRecoveryTypes\";\nimport { recoverWithPrincipalOutageFallback } from \"./principalRecoveryOutage\";\nimport type {\n RecoverScopedPrincipalPolicyHistoryOptions,\n ScopedPrincipalPolicyRecoveryBatch,\n} from \"./recoverScopedPrincipalPolicyHistory\";\n\n/** Reuse exact current artifacts, or a proved historical page within a newer prefix. */\nexport async function recoverWithPrincipalLocalPreference(\n recover: ScopedPrincipalPolicyRecoveryBatch,\n options: RecoverScopedPrincipalPolicyHistoryOptions,\n preferLocalCurrent: boolean,\n allowHistoricalReference = false,\n) {\n if (preferLocalCurrent && !options.offline) {\n try {\n const local = await recover({\n ...options,\n offline: true,\n });\n assertProjectionVerificationCurrent(options.stillCurrent);\n if (\n allowHistoricalReference ||\n options.historyPage ||\n principalHeadMatchesReference(local.policy.state, options.reference)\n )\n return local;\n } catch (error) {\n assertProjectionVerificationCurrent(options.stillCurrent);\n if (\n !(error instanceof PrincipalHistoryRecoveryRaceError) &&\n !(\n error instanceof KeyingVerificationError &&\n error.code === \"missing_dependency\"\n )\n )\n throw error;\n }\n }\n return recoverWithPrincipalOutageFallback(recover, options);\n}\n"]}
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { type ReferencedPrincipalHead } from "../../internal/crypto/index.js";
|
|
2
2
|
import type { RecoveredPrincipalPolicyHistory } from "./principalHistoryRecoveryTypes.js";
|
|
3
3
|
import { type PrincipalRecoveryContext, type RecoveredPolicyDirectory } from "./principalRecoveryDirectory.js";
|
|
4
|
-
|
|
5
|
-
value: T;
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
interface MemoEntry<T> {
|
|
5
|
+
readonly value: T;
|
|
6
|
+
/** The producing call's private lease and caller lifetime. */
|
|
7
|
+
readonly stillCurrent: () => boolean;
|
|
8
|
+
/** Organization hint epoch observed before the reads that produced value. */
|
|
9
|
+
readonly epoch: number;
|
|
10
|
+
}
|
|
11
|
+
type Memo<T> = Map<string, Promise<MemoEntry<T>>>;
|
|
8
12
|
export interface PrincipalRecoveryMemo {
|
|
9
13
|
readonly directories: Memo<RecoveredPolicyDirectory>;
|
|
10
14
|
readonly admins: Memo<RecoveredPrincipalPolicyHistory>;
|
|
11
15
|
}
|
|
12
16
|
export declare function createPrincipalRecoveryReader(input: PrincipalRecoveryContext, memo?: PrincipalRecoveryMemo): {
|
|
17
|
+
stillCurrent: () => boolean;
|
|
13
18
|
directory: (references: readonly ReferencedPrincipalHead[]) => Promise<RecoveredPolicyDirectory>;
|
|
14
19
|
admins: (expectedHead: ReferencedPrincipalHead, retainedReferences?: readonly ReferencedPrincipalHead[]) => Promise<RecoveredPrincipalPolicyHistory>;
|
|
15
20
|
};
|
|
@@ -2,23 +2,81 @@ import { KeyingVerificationError, serializeKeyingCanonicalJson, } from "../../in
|
|
|
2
2
|
import { assertProjectionVerificationCurrent } from "../../data/keyingProjectionVerification/types.js";
|
|
3
3
|
import { principalHeadMatchesReference } from "../../data/principals/organizationAuthorityDescriptor.js";
|
|
4
4
|
import { recoverPolicyDirectory, } from "./principalRecoveryDirectory.js";
|
|
5
|
+
import { adminsMemoValidity, directoryMemoValidity, } from "./principalRecoveryMemoValidity.js";
|
|
5
6
|
import { recoverPrincipalPolicyHistory } from "./recoverPrincipalPolicyHistory.js";
|
|
6
|
-
/**
|
|
7
|
-
|
|
7
|
+
/** Synchronous: no hint and no lease end since the entry's reads began. */
|
|
8
|
+
function entryLive(entry, validity) {
|
|
9
|
+
return entry.epoch === validity.epoch() && entry.stillCurrent();
|
|
10
|
+
}
|
|
11
|
+
function evict(memo, key, pending) {
|
|
12
|
+
if (memo?.get(key) === pending)
|
|
13
|
+
memo.delete(key);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Return a live entry, evicting one whose producer lease ended, that predates
|
|
17
|
+
* an organization hint, or that a durable pin has moved past. Liveness is
|
|
18
|
+
* checked again after every await, so a hint or lease end during the pin
|
|
19
|
+
* reads also evicts. The caller rechecks synchronously before use. A pin read
|
|
20
|
+
* is a snapshot; a pin that advances later is caught by the checkpoint reload
|
|
21
|
+
* in recovery, the caller's checkpoint refresh, and atomic admission.
|
|
22
|
+
*/
|
|
23
|
+
async function reusableEntry(memo, key, validity) {
|
|
24
|
+
const pending = memo?.get(key);
|
|
25
|
+
if (!pending)
|
|
26
|
+
return undefined;
|
|
27
|
+
const entry = await pending.catch(() => undefined);
|
|
28
|
+
if (entry &&
|
|
29
|
+
entryLive(entry, validity) &&
|
|
30
|
+
(await validity.covers(entry.value)) &&
|
|
31
|
+
entryLive(entry, validity))
|
|
32
|
+
return { entry, pending };
|
|
33
|
+
evict(memo, key, pending);
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Share authenticated results within one workflow operation. A later caller
|
|
38
|
+
* never receives a result whose producer lease ended, that predates an
|
|
39
|
+
* organization hint, or that a durable pin has moved past; it recovers again.
|
|
40
|
+
* A result derived from a reused source keeps the source's epoch, so a hint
|
|
41
|
+
* that arrives before it is stored still evicts it. Every value is handed out
|
|
42
|
+
* with the consumer's lifetime and its producer's, so a producer that expires
|
|
43
|
+
* after hand-out still invalidates the consumer's use.
|
|
44
|
+
*/
|
|
45
|
+
async function memoPrincipalRecovery(memo, key, stillCurrent, validity, sourceEpoch, work) {
|
|
8
46
|
assertProjectionVerificationCurrent(stillCurrent);
|
|
9
|
-
|
|
10
|
-
if (
|
|
11
|
-
|
|
12
|
-
|
|
47
|
+
const reused = await reusableEntry(memo, key, validity);
|
|
48
|
+
if (reused) {
|
|
49
|
+
const { entry } = reused;
|
|
50
|
+
if (entryLive(entry, validity)) {
|
|
51
|
+
assertProjectionVerificationCurrent(stillCurrent);
|
|
52
|
+
return {
|
|
53
|
+
value: entry.value,
|
|
54
|
+
stillCurrent: () => stillCurrent() && entry.stillCurrent(),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
evict(memo, key, reused.pending);
|
|
13
58
|
}
|
|
59
|
+
// Observed before work starts its reads; a derived result keeps its source's.
|
|
60
|
+
const epoch = sourceEpoch ?? validity.epoch();
|
|
61
|
+
const pending = work().then((value) => ({ value, stillCurrent, epoch }));
|
|
62
|
+
memo?.set(key, pending);
|
|
14
63
|
const result = await pending.catch((error) => {
|
|
15
|
-
|
|
16
|
-
memo.delete(key);
|
|
64
|
+
evict(memo, key, pending);
|
|
17
65
|
throw error;
|
|
18
66
|
});
|
|
67
|
+
// A hint during production leaves this caller's in-flight view, never a
|
|
68
|
+
// stored one: the stale epoch would evict it anyway.
|
|
69
|
+
if (result.epoch !== validity.epoch())
|
|
70
|
+
evict(memo, key, pending);
|
|
19
71
|
assertProjectionVerificationCurrent(result.stillCurrent);
|
|
20
72
|
assertProjectionVerificationCurrent(stillCurrent);
|
|
21
|
-
|
|
73
|
+
// stillCurrent already includes the lifetime of a derived value's source.
|
|
74
|
+
return { value: result.value, stillCurrent };
|
|
75
|
+
}
|
|
76
|
+
function fallsBackOnline(input, error) {
|
|
77
|
+
return (!input.offline &&
|
|
78
|
+
error instanceof KeyingVerificationError &&
|
|
79
|
+
error.code === "missing_dependency");
|
|
22
80
|
}
|
|
23
81
|
async function readMemoizedAdmins(input, memo, scope, expectedHead, retainedReferences) {
|
|
24
82
|
// Every recovered Current already retains its own exact head.
|
|
@@ -28,10 +86,11 @@ async function readMemoizedAdmins(input, memo, scope, expectedHead, retainedRefe
|
|
|
28
86
|
{ ...expectedHead },
|
|
29
87
|
selected.map((reference) => ({ ...reference })),
|
|
30
88
|
]);
|
|
31
|
-
const
|
|
32
|
-
|
|
89
|
+
const validity = adminsMemoValidity(input, expectedHead);
|
|
90
|
+
const known = (await reusableEntry(memo, key([]), validity))?.entry;
|
|
91
|
+
const sourceCurrent = known?.stillCurrent ?? (() => true);
|
|
33
92
|
const current = () => !input.signal?.aborted && input.stillCurrent() && sourceCurrent();
|
|
34
|
-
return memoPrincipalRecovery(memo, key(references), current, async () => {
|
|
93
|
+
return memoPrincipalRecovery(memo, key(references), current, validity, known?.epoch, async () => {
|
|
35
94
|
const options = {
|
|
36
95
|
...input,
|
|
37
96
|
expectedHead,
|
|
@@ -40,8 +99,6 @@ async function readMemoizedAdmins(input, memo, scope, expectedHead, retainedRefe
|
|
|
40
99
|
stillCurrent: current,
|
|
41
100
|
};
|
|
42
101
|
if (known) {
|
|
43
|
-
sourceCurrent = (await known).stillCurrent;
|
|
44
|
-
assertProjectionVerificationCurrent(current);
|
|
45
102
|
try {
|
|
46
103
|
// A freshly verified exact head only needs local proofs for additional
|
|
47
104
|
// citations; their signature and private-root checks still run.
|
|
@@ -52,9 +109,7 @@ async function readMemoizedAdmins(input, memo, scope, expectedHead, retainedRefe
|
|
|
52
109
|
}
|
|
53
110
|
catch (error) {
|
|
54
111
|
assertProjectionVerificationCurrent(current);
|
|
55
|
-
if (input
|
|
56
|
-
!(error instanceof KeyingVerificationError) ||
|
|
57
|
-
error.code !== "missing_dependency")
|
|
112
|
+
if (!fallsBackOnline(input, error))
|
|
58
113
|
throw error;
|
|
59
114
|
}
|
|
60
115
|
}
|
|
@@ -62,28 +117,26 @@ async function readMemoizedAdmins(input, memo, scope, expectedHead, retainedRefe
|
|
|
62
117
|
});
|
|
63
118
|
}
|
|
64
119
|
async function readMemoizedDirectory(input, memo, key, references) {
|
|
65
|
-
const
|
|
66
|
-
|
|
120
|
+
const validity = directoryMemoValidity(input);
|
|
121
|
+
const known = references.length > 0
|
|
122
|
+
? (await reusableEntry(memo, key([]), validity))?.entry
|
|
123
|
+
: undefined;
|
|
124
|
+
const sourceCurrent = known?.stillCurrent ?? (() => true);
|
|
67
125
|
const current = () => !input.signal?.aborted && input.stillCurrent() && sourceCurrent();
|
|
68
|
-
return memoPrincipalRecovery(memo, key(references), current, async () => {
|
|
126
|
+
return memoPrincipalRecovery(memo, key(references), current, validity, known?.epoch, async () => {
|
|
69
127
|
if (!known)
|
|
70
128
|
return recoverPolicyDirectory(input, references);
|
|
71
|
-
const source = await known;
|
|
72
|
-
sourceCurrent = source.stillCurrent;
|
|
73
|
-
assertProjectionVerificationCurrent(current);
|
|
74
129
|
try {
|
|
75
|
-
// Add authenticated historical citations to this
|
|
76
|
-
// view. A newer private prefix must not silently replace
|
|
77
|
-
return await recoverPolicyDirectory({ ...input, offline: true, stillCurrent: current }, references,
|
|
130
|
+
// Add authenticated historical citations to this operation's exact
|
|
131
|
+
// directory view. A newer private prefix must not silently replace it.
|
|
132
|
+
return await recoverPolicyDirectory({ ...input, offline: true, stillCurrent: current }, references, known.value);
|
|
78
133
|
}
|
|
79
134
|
catch (error) {
|
|
80
135
|
assertProjectionVerificationCurrent(current);
|
|
81
|
-
if (input
|
|
82
|
-
!(error instanceof KeyingVerificationError) ||
|
|
83
|
-
error.code !== "missing_dependency")
|
|
136
|
+
if (!fallsBackOnline(input, error))
|
|
84
137
|
throw error;
|
|
85
138
|
}
|
|
86
|
-
return recoverPolicyDirectory({ ...input, stillCurrent: current }, references,
|
|
139
|
+
return recoverPolicyDirectory({ ...input, stillCurrent: current }, references, known.value);
|
|
87
140
|
});
|
|
88
141
|
}
|
|
89
142
|
export function createPrincipalRecoveryReader(input, memo) {
|
|
@@ -96,9 +149,17 @@ export function createPrincipalRecoveryReader(input, memo) {
|
|
|
96
149
|
...scope,
|
|
97
150
|
references.map((reference) => ({ ...reference })),
|
|
98
151
|
]);
|
|
152
|
+
// Every lifetime behind a value this reader handed out; its caller must
|
|
153
|
+
// keep checking them at the point of use.
|
|
154
|
+
const lifetimes = [];
|
|
155
|
+
const lease = (leased) => {
|
|
156
|
+
lifetimes.push(leased.stillCurrent);
|
|
157
|
+
return leased.value;
|
|
158
|
+
};
|
|
99
159
|
return {
|
|
160
|
+
stillCurrent: () => lifetimes.every((current) => current()),
|
|
100
161
|
directory: async (references) => {
|
|
101
|
-
const value = await readMemoizedDirectory(input, memo?.directories, directoryKey, references);
|
|
162
|
+
const value = lease(await readMemoizedDirectory(input, memo?.directories, directoryKey, references));
|
|
102
163
|
// Retaining extra citations does not prevent this authenticated directory
|
|
103
164
|
// from satisfying a later read with no selected historical references.
|
|
104
165
|
const unselectedKey = directoryKey([]);
|
|
@@ -107,12 +168,11 @@ export function createPrincipalRecoveryReader(input, memo) {
|
|
|
107
168
|
selected &&
|
|
108
169
|
references.length > 0 &&
|
|
109
170
|
!memo.directories.has(unselectedKey))
|
|
110
|
-
//
|
|
111
|
-
// a later caller still has a live lease of its own.
|
|
171
|
+
// The alias keeps the original lease and validity; either ending evicts it.
|
|
112
172
|
memo.directories.set(unselectedKey, selected);
|
|
113
173
|
return value;
|
|
114
174
|
},
|
|
115
|
-
admins: (expectedHead, retainedReferences = []) => readMemoizedAdmins(input, memo?.admins, scope, expectedHead, retainedReferences),
|
|
175
|
+
admins: async (expectedHead, retainedReferences = []) => lease(await readMemoizedAdmins(input, memo?.admins, scope, expectedHead, retainedReferences)),
|
|
116
176
|
};
|
|
117
177
|
}
|
|
118
178
|
//# sourceMappingURL=principalRecoveryMemo.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"principalRecoveryMemo.js","sourceRoot":"","sources":["principalRecoveryMemo.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n type ReferencedPrincipalHead,\n serializeKeyingCanonicalJson,\n} from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { principalHeadMatchesReference } from \"../../data/principals/organizationAuthorityDescriptor\";\nimport type { RecoveredPrincipalPolicyHistory } from \"./principalHistoryRecoveryTypes\";\nimport {\n type PrincipalRecoveryContext,\n type RecoveredPolicyDirectory,\n recoverPolicyDirectory,\n} from \"./principalRecoveryDirectory\";\nimport { recoverPrincipalPolicyHistory } from \"./recoverPrincipalPolicyHistory\";\n\ntype Memo<T> = Map<string, Promise<{ value: T; stillCurrent: () => boolean }>>;\n\nexport interface PrincipalRecoveryMemo {\n readonly directories: Memo<RecoveredPolicyDirectory>;\n readonly admins: Memo<RecoveredPrincipalPolicyHistory>;\n}\n\n/** Only share authenticated results within one projection collection lifetime. */\nasync function memoPrincipalRecovery<T>(\n memo: Memo<T> | undefined,\n key: string,\n stillCurrent: () => boolean,\n work: () => Promise<T>,\n): Promise<T> {\n assertProjectionVerificationCurrent(stillCurrent);\n let pending = memo?.get(key);\n if (!pending) {\n pending = work().then((value) => ({ value, stillCurrent }));\n memo?.set(key, pending);\n }\n const result = await pending.catch((error: unknown) => {\n if (memo?.get(key) === pending) memo.delete(key);\n throw error;\n });\n assertProjectionVerificationCurrent(result.stillCurrent);\n assertProjectionVerificationCurrent(stillCurrent);\n return result.value;\n}\n\nasync function readMemoizedAdmins(\n input: PrincipalRecoveryContext,\n memo: Memo<RecoveredPrincipalPolicyHistory> | undefined,\n scope: readonly (string | boolean)[],\n expectedHead: ReferencedPrincipalHead,\n retainedReferences: readonly ReferencedPrincipalHead[],\n) {\n // Every recovered Current already retains its own exact head.\n const references = retainedReferences.filter(\n (reference) => !principalHeadMatchesReference(expectedHead, reference),\n );\n const key = (selected: readonly ReferencedPrincipalHead[]) =>\n serializeKeyingCanonicalJson([\n ...scope,\n { ...expectedHead },\n selected.map((reference) => ({ ...reference })),\n ]);\n const known = memo?.get(key([]));\n let sourceCurrent = () => true;\n const current = () =>\n !input.signal?.aborted && input.stillCurrent() && sourceCurrent();\n return memoPrincipalRecovery(memo, key(references), current, async () => {\n const options = {\n ...input,\n expectedHead,\n retainedReferences,\n historyVerification: \"direct-admins\" as const,\n stillCurrent: current,\n };\n if (known) {\n sourceCurrent = (await known).stillCurrent;\n assertProjectionVerificationCurrent(current);\n try {\n // A freshly verified exact head only needs local proofs for additional\n // citations; their signature and private-root checks still run.\n return await recoverPrincipalPolicyHistory({\n ...options,\n offline: true,\n });\n } catch (error) {\n assertProjectionVerificationCurrent(current);\n if (\n input.offline ||\n !(error instanceof KeyingVerificationError) ||\n error.code !== \"missing_dependency\"\n )\n throw error;\n }\n }\n return recoverPrincipalPolicyHistory(options);\n });\n}\n\nasync function readMemoizedDirectory(\n input: PrincipalRecoveryContext,\n memo: Memo<RecoveredPolicyDirectory> | undefined,\n key: (references: readonly ReferencedPrincipalHead[]) => string,\n references: readonly ReferencedPrincipalHead[],\n) {\n const known = references.length > 0 ? memo?.get(key([])) : undefined;\n let sourceCurrent = () => true;\n const current = () =>\n !input.signal?.aborted && input.stillCurrent() && sourceCurrent();\n return memoPrincipalRecovery(memo, key(references), current, async () => {\n if (!known) return recoverPolicyDirectory(input, references);\n const source = await known;\n sourceCurrent = source.stillCurrent;\n assertProjectionVerificationCurrent(current);\n try {\n // Add authenticated historical citations to this batch's exact directory\n // view. A newer private prefix must not silently replace that view.\n return await recoverPolicyDirectory(\n { ...input, offline: true, stillCurrent: current },\n references,\n source.value,\n );\n } catch (error) {\n assertProjectionVerificationCurrent(current);\n if (\n input.offline ||\n !(error instanceof KeyingVerificationError) ||\n error.code !== \"missing_dependency\"\n )\n throw error;\n }\n return recoverPolicyDirectory(\n { ...input, stillCurrent: current },\n references,\n source.value,\n );\n });\n}\n\nexport function createPrincipalRecoveryReader(\n input: PrincipalRecoveryContext,\n memo?: PrincipalRecoveryMemo,\n) {\n const scope = [\n input.organizationId,\n input.protection.context,\n input.offline === true,\n ];\n const directoryKey = (references: readonly ReferencedPrincipalHead[]) =>\n serializeKeyingCanonicalJson([\n ...scope,\n references.map((reference) => ({ ...reference })),\n ]);\n return {\n directory: async (references: readonly ReferencedPrincipalHead[]) => {\n const value = await readMemoizedDirectory(\n input,\n memo?.directories,\n directoryKey,\n references,\n );\n // Retaining extra citations does not prevent this authenticated directory\n // from satisfying a later read with no selected historical references.\n const unselectedKey = directoryKey([]);\n const selected = memo?.directories.get(directoryKey(references));\n if (\n memo &&\n selected &&\n references.length > 0 &&\n !memo.directories.has(unselectedKey)\n )\n // Keep the original lease: its expiry requires a new batch, even if\n // a later caller still has a live lease of its own.\n memo.directories.set(unselectedKey, selected);\n return value;\n },\n admins: (\n expectedHead: ReferencedPrincipalHead,\n retainedReferences: readonly ReferencedPrincipalHead[] = [],\n ) =>\n readMemoizedAdmins(\n input,\n memo?.admins,\n scope,\n expectedHead,\n retainedReferences,\n ),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"principalRecoveryMemo.js","sourceRoot":"","sources":["principalRecoveryMemo.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EAEvB,4BAA4B,GAC7B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,6BAA6B,EAAE,MAAM,uDAAuD,CAAC;AAEtG,OAAO,EAGL,sBAAsB,GACvB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,kBAAkB,EAClB,qBAAqB,GAEtB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AAwBhF,2EAA2E;AAC3E,SAAS,SAAS,CAChB,KAAmB,EACnB,QAA0C;IAE1C,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,CAAC,KAAK,EAAE,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;AAClE,CAAC;AAED,SAAS,KAAK,CACZ,IAAyB,EACzB,GAAW,EACX,OAA8B;IAE9B,IAAI,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,KAAK,OAAO;QAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AACnD,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,UAAU,aAAa,CAC1B,IAAyB,EACzB,GAAW,EACX,QAA0C;IAI1C,MAAM,OAAO,GAAG,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACnD,IACE,KAAK;QACL,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC;QAC1B,CAAC,MAAM,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACpC,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC;QAE1B,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IAC5B,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAC1B,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;GAQG;AACH,KAAK,UAAU,qBAAqB,CAClC,IAAyB,EACzB,GAAW,EACX,YAA2B,EAC3B,QAA0C,EAC1C,WAA+B,EAC/B,IAAsB;IAEtB,mCAAmC,CAAC,YAAY,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;IACxD,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;QACzB,IAAI,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC;YAC/B,mCAAmC,CAAC,YAAY,CAAC,CAAC;YAClD,OAAO;gBACL,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,YAAY,EAAE,GAAG,EAAE,CAAC,YAAY,EAAE,IAAI,KAAK,CAAC,YAAY,EAAE;aAC3D,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC;IACD,8EAA8E;IAC9E,MAAM,KAAK,GAAG,WAAW,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;IAC9C,MAAM,OAAO,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IACzE,IAAI,EAAE,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACxB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;QACpD,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;QAC1B,MAAM,KAAK,CAAC;IACd,CAAC,CAAC,CAAC;IACH,wEAAwE;IACxE,qDAAqD;IACrD,IAAI,MAAM,CAAC,KAAK,KAAK,QAAQ,CAAC,KAAK,EAAE;QAAE,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IACjE,mCAAmC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IACzD,mCAAmC,CAAC,YAAY,CAAC,CAAC;IAClD,0EAA0E;IAC1E,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,YAAY,EAAE,CAAC;AAC/C,CAAC;AAED,SAAS,eAAe,CACtB,KAA+B,EAC/B,KAAc;IAEd,OAAO,CACL,CAAC,KAAK,CAAC,OAAO;QACd,KAAK,YAAY,uBAAuB;QACxC,KAAK,CAAC,IAAI,KAAK,oBAAoB,CACpC,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,kBAAkB,CAC/B,KAA+B,EAC/B,IAAuD,EACvD,KAAoC,EACpC,YAAqC,EACrC,kBAAsD;IAEtD,8DAA8D;IAC9D,MAAM,UAAU,GAAG,kBAAkB,CAAC,MAAM,CAC1C,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,6BAA6B,CAAC,YAAY,EAAE,SAAS,CAAC,CACvE,CAAC;IACF,MAAM,GAAG,GAAG,CAAC,QAA4C,EAAE,EAAE,CAC3D,4BAA4B,CAAC;QAC3B,GAAG,KAAK;QACR,EAAE,GAAG,YAAY,EAAE;QACnB,QAAQ,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,SAAS,EAAE,CAAC,CAAC;KAChD,CAAC,CAAC;IACL,MAAM,QAAQ,GAAG,kBAAkB,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IACzD,MAAM,KAAK,GAAG,CAAC,MAAM,aAAa,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC;IACpE,MAAM,aAAa,GAAG,KAAK,EAAE,YAAY,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAC1D,MAAM,OAAO,GAAG,GAAG,EAAE,CACnB,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,IAAI,KAAK,CAAC,YAAY,EAAE,IAAI,aAAa,EAAE,CAAC;IACpE,OAAO,qBAAqB,CAC1B,IAAI,EACJ,GAAG,CAAC,UAAU,CAAC,EACf,OAAO,EACP,QAAQ,EACR,KAAK,EAAE,KAAK,EACZ,KAAK,IAAI,EAAE;QACT,MAAM,OAAO,GAAG;YACd,GAAG,KAAK;YACR,YAAY;YACZ,kBAAkB;YAClB,mBAAmB,EAAE,eAAwB;YAC7C,YAAY,EAAE,OAAO;SACtB,CAAC;QACF,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,CAAC;gBACH,uEAAuE;gBACvE,gEAAgE;gBAChE,OAAO,MAAM,6BAA6B,CAAC;oBACzC,GAAG,OAAO;oBACV,OAAO,EAAE,IAAI;iBACd,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,mCAAmC,CAAC,OAAO,CAAC,CAAC;gBAC7C,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC;oBAAE,MAAM,KAAK,CAAC;YAClD,CAAC;QACH,CAAC;QACD,OAAO,6BAA6B,CAAC,OAAO,CAAC,CAAC;IAChD,CAAC,CACF,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,qBAAqB,CAClC,KAA+B,EAC/B,IAAgD,EAChD,GAA+D,EAC/D,UAA8C;IAE9C,MAAM,QAAQ,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,KAAK,GACT,UAAU,CAAC,MAAM,GAAG,CAAC;QACnB,CAAC,CAAC,CAAC,MAAM,aAAa,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,KAAK;QACvD,CAAC,CAAC,SAAS,CAAC;IAChB,MAAM,aAAa,GAAG,KAAK,EAAE,YAAY,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAC1D,MAAM,OAAO,GAAG,GAAG,EAAE,CACnB,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,IAAI,KAAK,CAAC,YAAY,EAAE,IAAI,aAAa,EAAE,CAAC;IACpE,OAAO,qBAAqB,CAC1B,IAAI,EACJ,GAAG,CAAC,UAAU,CAAC,EACf,OAAO,EACP,QAAQ,EACR,KAAK,EAAE,KAAK,EACZ,KAAK,IAAI,EAAE;QACT,IAAI,CAAC,KAAK;YAAE,OAAO,sBAAsB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QAC7D,IAAI,CAAC;YACH,mEAAmE;YACnE,uEAAuE;YACvE,OAAO,MAAM,sBAAsB,CACjC,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,EAClD,UAAU,EACV,KAAK,CAAC,KAAK,CACZ,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,mCAAmC,CAAC,OAAO,CAAC,CAAC;YAC7C,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAC;QAClD,CAAC;QACD,OAAO,sBAAsB,CAC3B,EAAE,GAAG,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,EACnC,UAAU,EACV,KAAK,CAAC,KAAK,CACZ,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,6BAA6B,CAC3C,KAA+B,EAC/B,IAA4B;IAE5B,MAAM,KAAK,GAAG;QACZ,KAAK,CAAC,cAAc;QACpB,KAAK,CAAC,UAAU,CAAC,OAAO;QACxB,KAAK,CAAC,OAAO,KAAK,IAAI;KACvB,CAAC;IACF,MAAM,YAAY,GAAG,CAAC,UAA8C,EAAE,EAAE,CACtE,4BAA4B,CAAC;QAC3B,GAAG,KAAK;QACR,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,SAAS,EAAE,CAAC,CAAC;KAClD,CAAC,CAAC;IACL,wEAAwE;IACxE,0CAA0C;IAC1C,MAAM,SAAS,GAAsB,EAAE,CAAC;IACxC,MAAM,KAAK,GAAG,CAAI,MAAiB,EAAK,EAAE;QACxC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QACpC,OAAO,MAAM,CAAC,KAAK,CAAC;IACtB,CAAC,CAAC;IACF,OAAO;QACL,YAAY,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC;QAC3D,SAAS,EAAE,KAAK,EAAE,UAA8C,EAAE,EAAE;YAClE,MAAM,KAAK,GAAG,KAAK,CACjB,MAAM,qBAAqB,CACzB,KAAK,EACL,IAAI,EAAE,WAAW,EACjB,YAAY,EACZ,UAAU,CACX,CACF,CAAC;YACF,0EAA0E;YAC1E,uEAAuE;YACvE,MAAM,aAAa,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;YACvC,MAAM,QAAQ,GAAG,IAAI,EAAE,WAAW,CAAC,GAAG,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC;YACjE,IACE,IAAI;gBACJ,QAAQ;gBACR,UAAU,CAAC,MAAM,GAAG,CAAC;gBACrB,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,aAAa,CAAC;gBAEpC,4EAA4E;gBAC5E,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;YAChD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,EAAE,KAAK,EACX,YAAqC,EACrC,qBAAyD,EAAE,EAC3D,EAAE,CACF,KAAK,CACH,MAAM,kBAAkB,CACtB,KAAK,EACL,IAAI,EAAE,MAAM,EACZ,KAAK,EACL,YAAY,EACZ,kBAAkB,CACnB,CACF;KACJ,CAAC;AACJ,CAAC","sourcesContent":["import {\n KeyingVerificationError,\n type ReferencedPrincipalHead,\n serializeKeyingCanonicalJson,\n} from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { principalHeadMatchesReference } from \"../../data/principals/organizationAuthorityDescriptor\";\nimport type { RecoveredPrincipalPolicyHistory } from \"./principalHistoryRecoveryTypes\";\nimport {\n type PrincipalRecoveryContext,\n type RecoveredPolicyDirectory,\n recoverPolicyDirectory,\n} from \"./principalRecoveryDirectory\";\nimport {\n adminsMemoValidity,\n directoryMemoValidity,\n type PrincipalRecoveryMemoValidity,\n} from \"./principalRecoveryMemoValidity\";\nimport { recoverPrincipalPolicyHistory } from \"./recoverPrincipalPolicyHistory\";\n\ninterface MemoEntry<T> {\n readonly value: T;\n /** The producing call's private lease and caller lifetime. */\n readonly stillCurrent: () => boolean;\n /** Organization hint epoch observed before the reads that produced value. */\n readonly epoch: number;\n}\n\ntype Memo<T> = Map<string, Promise<MemoEntry<T>>>;\n\n/** A value handed out with every lifetime it depends on. */\ninterface Leased<T> {\n readonly value: T;\n /** The consumer's lifetime and every producer lifetime behind value. */\n readonly stillCurrent: () => boolean;\n}\n\nexport interface PrincipalRecoveryMemo {\n readonly directories: Memo<RecoveredPolicyDirectory>;\n readonly admins: Memo<RecoveredPrincipalPolicyHistory>;\n}\n\n/** Synchronous: no hint and no lease end since the entry's reads began. */\nfunction entryLive<T>(\n entry: MemoEntry<T>,\n validity: PrincipalRecoveryMemoValidity<T>,\n): boolean {\n return entry.epoch === validity.epoch() && entry.stillCurrent();\n}\n\nfunction evict<T>(\n memo: Memo<T> | undefined,\n key: string,\n pending: Promise<MemoEntry<T>>,\n): void {\n if (memo?.get(key) === pending) memo.delete(key);\n}\n\n/**\n * Return a live entry, evicting one whose producer lease ended, that predates\n * an organization hint, or that a durable pin has moved past. Liveness is\n * checked again after every await, so a hint or lease end during the pin\n * reads also evicts. The caller rechecks synchronously before use. A pin read\n * is a snapshot; a pin that advances later is caught by the checkpoint reload\n * in recovery, the caller's checkpoint refresh, and atomic admission.\n */\nasync function reusableEntry<T>(\n memo: Memo<T> | undefined,\n key: string,\n validity: PrincipalRecoveryMemoValidity<T>,\n): Promise<\n { entry: MemoEntry<T>; pending: Promise<MemoEntry<T>> } | undefined\n> {\n const pending = memo?.get(key);\n if (!pending) return undefined;\n const entry = await pending.catch(() => undefined);\n if (\n entry &&\n entryLive(entry, validity) &&\n (await validity.covers(entry.value)) &&\n entryLive(entry, validity)\n )\n return { entry, pending };\n evict(memo, key, pending);\n return undefined;\n}\n\n/**\n * Share authenticated results within one workflow operation. A later caller\n * never receives a result whose producer lease ended, that predates an\n * organization hint, or that a durable pin has moved past; it recovers again.\n * A result derived from a reused source keeps the source's epoch, so a hint\n * that arrives before it is stored still evicts it. Every value is handed out\n * with the consumer's lifetime and its producer's, so a producer that expires\n * after hand-out still invalidates the consumer's use.\n */\nasync function memoPrincipalRecovery<T>(\n memo: Memo<T> | undefined,\n key: string,\n stillCurrent: () => boolean,\n validity: PrincipalRecoveryMemoValidity<T>,\n sourceEpoch: number | undefined,\n work: () => Promise<T>,\n): Promise<Leased<T>> {\n assertProjectionVerificationCurrent(stillCurrent);\n const reused = await reusableEntry(memo, key, validity);\n if (reused) {\n const { entry } = reused;\n if (entryLive(entry, validity)) {\n assertProjectionVerificationCurrent(stillCurrent);\n return {\n value: entry.value,\n stillCurrent: () => stillCurrent() && entry.stillCurrent(),\n };\n }\n evict(memo, key, reused.pending);\n }\n // Observed before work starts its reads; a derived result keeps its source's.\n const epoch = sourceEpoch ?? validity.epoch();\n const pending = work().then((value) => ({ value, stillCurrent, epoch }));\n memo?.set(key, pending);\n const result = await pending.catch((error: unknown) => {\n evict(memo, key, pending);\n throw error;\n });\n // A hint during production leaves this caller's in-flight view, never a\n // stored one: the stale epoch would evict it anyway.\n if (result.epoch !== validity.epoch()) evict(memo, key, pending);\n assertProjectionVerificationCurrent(result.stillCurrent);\n assertProjectionVerificationCurrent(stillCurrent);\n // stillCurrent already includes the lifetime of a derived value's source.\n return { value: result.value, stillCurrent };\n}\n\nfunction fallsBackOnline(\n input: PrincipalRecoveryContext,\n error: unknown,\n): boolean {\n return (\n !input.offline &&\n error instanceof KeyingVerificationError &&\n error.code === \"missing_dependency\"\n );\n}\n\nasync function readMemoizedAdmins(\n input: PrincipalRecoveryContext,\n memo: Memo<RecoveredPrincipalPolicyHistory> | undefined,\n scope: readonly (string | boolean)[],\n expectedHead: ReferencedPrincipalHead,\n retainedReferences: readonly ReferencedPrincipalHead[],\n) {\n // Every recovered Current already retains its own exact head.\n const references = retainedReferences.filter(\n (reference) => !principalHeadMatchesReference(expectedHead, reference),\n );\n const key = (selected: readonly ReferencedPrincipalHead[]) =>\n serializeKeyingCanonicalJson([\n ...scope,\n { ...expectedHead },\n selected.map((reference) => ({ ...reference })),\n ]);\n const validity = adminsMemoValidity(input, expectedHead);\n const known = (await reusableEntry(memo, key([]), validity))?.entry;\n const sourceCurrent = known?.stillCurrent ?? (() => true);\n const current = () =>\n !input.signal?.aborted && input.stillCurrent() && sourceCurrent();\n return memoPrincipalRecovery(\n memo,\n key(references),\n current,\n validity,\n known?.epoch,\n async () => {\n const options = {\n ...input,\n expectedHead,\n retainedReferences,\n historyVerification: \"direct-admins\" as const,\n stillCurrent: current,\n };\n if (known) {\n try {\n // A freshly verified exact head only needs local proofs for additional\n // citations; their signature and private-root checks still run.\n return await recoverPrincipalPolicyHistory({\n ...options,\n offline: true,\n });\n } catch (error) {\n assertProjectionVerificationCurrent(current);\n if (!fallsBackOnline(input, error)) throw error;\n }\n }\n return recoverPrincipalPolicyHistory(options);\n },\n );\n}\n\nasync function readMemoizedDirectory(\n input: PrincipalRecoveryContext,\n memo: Memo<RecoveredPolicyDirectory> | undefined,\n key: (references: readonly ReferencedPrincipalHead[]) => string,\n references: readonly ReferencedPrincipalHead[],\n) {\n const validity = directoryMemoValidity(input);\n const known =\n references.length > 0\n ? (await reusableEntry(memo, key([]), validity))?.entry\n : undefined;\n const sourceCurrent = known?.stillCurrent ?? (() => true);\n const current = () =>\n !input.signal?.aborted && input.stillCurrent() && sourceCurrent();\n return memoPrincipalRecovery(\n memo,\n key(references),\n current,\n validity,\n known?.epoch,\n async () => {\n if (!known) return recoverPolicyDirectory(input, references);\n try {\n // Add authenticated historical citations to this operation's exact\n // directory view. A newer private prefix must not silently replace it.\n return await recoverPolicyDirectory(\n { ...input, offline: true, stillCurrent: current },\n references,\n known.value,\n );\n } catch (error) {\n assertProjectionVerificationCurrent(current);\n if (!fallsBackOnline(input, error)) throw error;\n }\n return recoverPolicyDirectory(\n { ...input, stillCurrent: current },\n references,\n known.value,\n );\n },\n );\n}\n\nexport function createPrincipalRecoveryReader(\n input: PrincipalRecoveryContext,\n memo?: PrincipalRecoveryMemo,\n) {\n const scope = [\n input.organizationId,\n input.protection.context,\n input.offline === true,\n ];\n const directoryKey = (references: readonly ReferencedPrincipalHead[]) =>\n serializeKeyingCanonicalJson([\n ...scope,\n references.map((reference) => ({ ...reference })),\n ]);\n // Every lifetime behind a value this reader handed out; its caller must\n // keep checking them at the point of use.\n const lifetimes: (() => boolean)[] = [];\n const lease = <T>(leased: Leased<T>): T => {\n lifetimes.push(leased.stillCurrent);\n return leased.value;\n };\n return {\n stillCurrent: () => lifetimes.every((current) => current()),\n directory: async (references: readonly ReferencedPrincipalHead[]) => {\n const value = lease(\n await readMemoizedDirectory(\n input,\n memo?.directories,\n directoryKey,\n references,\n ),\n );\n // Retaining extra citations does not prevent this authenticated directory\n // from satisfying a later read with no selected historical references.\n const unselectedKey = directoryKey([]);\n const selected = memo?.directories.get(directoryKey(references));\n if (\n memo &&\n selected &&\n references.length > 0 &&\n !memo.directories.has(unselectedKey)\n )\n // The alias keeps the original lease and validity; either ending evicts it.\n memo.directories.set(unselectedKey, selected);\n return value;\n },\n admins: async (\n expectedHead: ReferencedPrincipalHead,\n retainedReferences: readonly ReferencedPrincipalHead[] = [],\n ) =>\n lease(\n await readMemoizedAdmins(\n input,\n memo?.admins,\n scope,\n expectedHead,\n retainedReferences,\n ),\n ),\n };\n}\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { loadPrincipalPolicyCheckpoint } from "../../data/persistence/keyingCheckpointPersistence.js";
|
|
2
|
+
import { principalRecoveryEpoch } from "../../data/principals/principalRecoveryEpoch.js";
|
|
3
|
+
async function pinCovers(input, head) {
|
|
4
|
+
const pin = await loadPrincipalPolicyCheckpoint(input.execSql, head.principalType, head.principalId);
|
|
5
|
+
return (!pin ||
|
|
6
|
+
pin.version < head.version ||
|
|
7
|
+
(pin.version === head.version && pin.stateHash === head.stateHash));
|
|
8
|
+
}
|
|
9
|
+
function organizationEpoch(input) {
|
|
10
|
+
return () => principalRecoveryEpoch(input.execSql, input.organizationId);
|
|
11
|
+
}
|
|
12
|
+
/** A directory names every group head; a newer pin on any of them evicts it. */
|
|
13
|
+
export function directoryMemoValidity(input) {
|
|
14
|
+
return {
|
|
15
|
+
epoch: organizationEpoch(input),
|
|
16
|
+
covers: async (directory) => {
|
|
17
|
+
for (const head of [
|
|
18
|
+
directory.policy.state,
|
|
19
|
+
...directory.descriptor.groupHeads,
|
|
20
|
+
])
|
|
21
|
+
if (!(await pinCovers(input, head)))
|
|
22
|
+
return false;
|
|
23
|
+
return true;
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function adminsMemoValidity(input, expectedHead) {
|
|
28
|
+
return {
|
|
29
|
+
epoch: organizationEpoch(input),
|
|
30
|
+
covers: () => pinCovers(input, expectedHead),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=principalRecoveryMemoValidity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"principalRecoveryMemoValidity.js","sourceRoot":"","sources":["principalRecoveryMemoValidity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EAAE,sBAAsB,EAAE,MAAM,8CAA8C,CAAC;AAoBtF,KAAK,UAAU,SAAS,CACtB,KAA+B,EAC/B,IAAgB;IAEhB,MAAM,GAAG,GAAG,MAAM,6BAA6B,CAC7C,KAAK,CAAC,OAAO,EACb,IAAI,CAAC,aAAa,EAClB,IAAI,CAAC,WAAW,CACjB,CAAC;IACF,OAAO,CACL,CAAC,GAAG;QACJ,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO;QAC1B,CAAC,GAAG,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,IAAI,GAAG,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS,CAAC,CACnE,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,KAA+B;IACxD,OAAO,GAAG,EAAE,CAAC,sBAAsB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;AAC3E,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,qBAAqB,CACnC,KAA+B;IAE/B,OAAO;QACL,KAAK,EAAE,iBAAiB,CAAC,KAAK,CAAC;QAC/B,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE;YAC1B,KAAK,MAAM,IAAI,IAAI;gBACjB,SAAS,CAAC,MAAM,CAAC,KAAK;gBACtB,GAAG,SAAS,CAAC,UAAU,CAAC,UAAU;aACnC;gBACC,IAAI,CAAC,CAAC,MAAM,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;oBAAE,OAAO,KAAK,CAAC;YACpD,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,KAA+B,EAC/B,YAAwB;IAExB,OAAO;QACL,KAAK,EAAE,iBAAiB,CAAC,KAAK,CAAC;QAC/B,MAAM,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,YAAY,CAAC;KAC7C,CAAC;AACJ,CAAC","sourcesContent":["import type { ReferencedPrincipalHead } from \"@tearleads/crypto\";\nimport { loadPrincipalPolicyCheckpoint } from \"../../data/persistence/keyingCheckpointPersistence\";\nimport { principalRecoveryEpoch } from \"../../data/principals/principalRecoveryEpoch\";\nimport type { RecoveredPrincipalPolicyHistory } from \"./principalHistoryRecoveryTypes\";\nimport type {\n PrincipalRecoveryContext,\n RecoveredPolicyDirectory,\n} from \"./principalRecoveryDirectory\";\n\n/** What keeps a memo entry valid beyond its producer's lease. */\nexport interface PrincipalRecoveryMemoValidity<T> {\n /** Organization hint epoch; production records it before its reads. */\n readonly epoch: () => number;\n /** No durable pin has moved past or forked from a memoized head. */\n readonly covers: (value: T) => Promise<boolean>;\n}\n\ntype PinnedHead = Pick<\n ReferencedPrincipalHead,\n \"principalType\" | \"principalId\" | \"version\" | \"stateHash\"\n>;\n\nasync function pinCovers(\n input: PrincipalRecoveryContext,\n head: PinnedHead,\n): Promise<boolean> {\n const pin = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n head.principalType,\n head.principalId,\n );\n return (\n !pin ||\n pin.version < head.version ||\n (pin.version === head.version && pin.stateHash === head.stateHash)\n );\n}\n\nfunction organizationEpoch(input: PrincipalRecoveryContext) {\n return () => principalRecoveryEpoch(input.execSql, input.organizationId);\n}\n\n/** A directory names every group head; a newer pin on any of them evicts it. */\nexport function directoryMemoValidity(\n input: PrincipalRecoveryContext,\n): PrincipalRecoveryMemoValidity<RecoveredPolicyDirectory> {\n return {\n epoch: organizationEpoch(input),\n covers: async (directory) => {\n for (const head of [\n directory.policy.state,\n ...directory.descriptor.groupHeads,\n ])\n if (!(await pinCovers(input, head))) return false;\n return true;\n },\n };\n}\n\nexport function adminsMemoValidity(\n input: PrincipalRecoveryContext,\n expectedHead: PinnedHead,\n): PrincipalRecoveryMemoValidity<RecoveredPrincipalPolicyHistory> {\n return {\n epoch: organizationEpoch(input),\n covers: () => pinCovers(input, expectedHead),\n };\n}\n"]}
|
|
@@ -2,7 +2,7 @@ import { KeyingVerificationError } from "../../internal/crypto/index.js";
|
|
|
2
2
|
import { ProjectionDependencyUnavailableError } from "../../data/keyingProjectionVerification/dependencyUnavailable.js";
|
|
3
3
|
import { assertProjectionVerificationCurrent } from "../../data/keyingProjectionVerification/types.js";
|
|
4
4
|
import { PrincipalPolicyHistoryReadError } from "./principalHistoryRecoveryTypes.js";
|
|
5
|
-
import {
|
|
5
|
+
import { recoverLeasedScopedPrincipalPolicyHistory, } from "./recoverScopedPrincipalPolicyHistory.js";
|
|
6
6
|
/** Refusals and invalid evidence must never silently select a cached policy. */
|
|
7
7
|
export function isPrincipalRecoveryOutage(error) {
|
|
8
8
|
if (!(error instanceof PrincipalPolicyHistoryReadError))
|
|
@@ -24,7 +24,7 @@ export async function recoverWithPrincipalOutageFallback(recover, options) {
|
|
|
24
24
|
if (options.offline || !isPrincipalRecoveryOutage(error))
|
|
25
25
|
throw error;
|
|
26
26
|
try {
|
|
27
|
-
return await
|
|
27
|
+
return await recoverLeasedScopedPrincipalPolicyHistory({
|
|
28
28
|
...options,
|
|
29
29
|
offline: true,
|
|
30
30
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"principalRecoveryOutage.js","sourceRoot":"","sources":["principalRecoveryOutage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,oCAAoC,EAAE,MAAM,+DAA+D,CAAC;AACrH,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,+BAA+B,EAAE,MAAM,iCAAiC,CAAC;AAClF,OAAO,
|
|
1
|
+
{"version":3,"file":"principalRecoveryOutage.js","sourceRoot":"","sources":["principalRecoveryOutage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,oCAAoC,EAAE,MAAM,+DAA+D,CAAC;AACrH,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,+BAA+B,EAAE,MAAM,iCAAiC,CAAC;AAClF,OAAO,EAEL,yCAAyC,GAE1C,MAAM,uCAAuC,CAAC;AAE/C,gFAAgF;AAChF,MAAM,UAAU,yBAAyB,CAAC,KAAc;IACtD,IAAI,CAAC,CAAC,KAAK,YAAY,+BAA+B,CAAC;QAAE,OAAO,KAAK,CAAC;IACtE,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAC1B,OAAO,CACL,OAAO,CAAC,IAAI,KAAK,SAAS;QAC1B,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM;YACtB,OAAO,CAAC,MAAM,KAAK,IAAI;YACvB,OAAO,CAAC,MAAM,IAAI,GAAG,CAAC;QACxB,CAAC,OAAO,CAAC,IAAI,KAAK,WAAW;YAC3B,OAAO,CAAC,IAAI,KAAK,qCAAqC,CAAC,CAC1D,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kCAAkC,CACtD,OAA2C,EAC3C,OAAmD;IAEnD,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,mCAAmC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAC1D,IAAI,OAAO,CAAC,OAAO,IAAI,CAAC,yBAAyB,CAAC,KAAK,CAAC;YAAE,MAAM,KAAK,CAAC;QACtE,IAAI,CAAC;YACH,OAAO,MAAM,yCAAyC,CAAC;gBACrD,GAAG,OAAO;gBACV,OAAO,EAAE,IAAI;aACd,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,UAAU,EAAE,CAAC;YACpB,IACE,UAAU,YAAY,uBAAuB;gBAC7C,UAAU,CAAC,IAAI,KAAK,oBAAoB;gBAExC,MAAM,IAAI,oCAAoC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YACrE,MAAM,UAAU,CAAC;QACnB,CAAC;IACH,CAAC;AACH,CAAC","sourcesContent":["import { KeyingVerificationError } from \"@tearleads/crypto\";\nimport { ProjectionDependencyUnavailableError } from \"../../data/keyingProjectionVerification/dependencyUnavailable\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { PrincipalPolicyHistoryReadError } from \"./principalHistoryRecoveryTypes\";\nimport {\n type RecoverScopedPrincipalPolicyHistoryOptions,\n recoverLeasedScopedPrincipalPolicyHistory,\n type ScopedPrincipalPolicyRecoveryBatch,\n} from \"./recoverScopedPrincipalPolicyHistory\";\n\n/** Refusals and invalid evidence must never silently select a cached policy. */\nexport function isPrincipalRecoveryOutage(error: unknown): boolean {\n if (!(error instanceof PrincipalPolicyHistoryReadError)) return false;\n const { failure } = error;\n return (\n failure.kind === \"network\" ||\n (failure.kind === \"http\" &&\n failure.status !== null &&\n failure.status >= 500) ||\n (failure.kind === \"cancelled\" &&\n failure.code === \"principal_history_request_timed_out\")\n );\n}\n\nexport async function recoverWithPrincipalOutageFallback(\n recover: ScopedPrincipalPolicyRecoveryBatch,\n options: RecoverScopedPrincipalPolicyHistoryOptions,\n) {\n try {\n return await recover(options);\n } catch (error) {\n assertProjectionVerificationCurrent(options.stillCurrent);\n if (options.offline || !isPrincipalRecoveryOutage(error)) throw error;\n try {\n return await recoverLeasedScopedPrincipalPolicyHistory({\n ...options,\n offline: true,\n });\n } catch (localError) {\n if (\n localError instanceof KeyingVerificationError &&\n localError.code === \"missing_dependency\"\n )\n throw new ProjectionDependencyUnavailableError(localError.message);\n throw localError;\n }\n }\n}\n"]}
|
|
@@ -10,20 +10,25 @@ export async function recoverCurrentOrganizationPolicy(options, memo) {
|
|
|
10
10
|
};
|
|
11
11
|
try {
|
|
12
12
|
let directory;
|
|
13
|
+
let reader = createPrincipalRecoveryReader(input, memo);
|
|
13
14
|
try {
|
|
14
|
-
directory = await
|
|
15
|
+
directory = await reader.directory([]);
|
|
15
16
|
}
|
|
16
17
|
catch (error) {
|
|
17
18
|
assertProjectionVerificationCurrent(input.stillCurrent);
|
|
18
19
|
if (input.offline || !isPrincipalRecoveryOutage(error))
|
|
19
20
|
throw error;
|
|
20
|
-
|
|
21
|
+
reader = createPrincipalRecoveryReader({ ...input, offline: true }, memo);
|
|
22
|
+
directory = await reader.directory([]);
|
|
21
23
|
}
|
|
22
|
-
|
|
24
|
+
// A reused directory keeps its producer's lifetime to the point of use.
|
|
25
|
+
const stillCurrent = () => input.stillCurrent() && reader.stillCurrent();
|
|
26
|
+
assertProjectionVerificationCurrent(stillCurrent);
|
|
23
27
|
return {
|
|
24
28
|
current: directory.current,
|
|
25
29
|
policy: directory.policy,
|
|
26
30
|
dependencies: [],
|
|
31
|
+
stillCurrent,
|
|
27
32
|
};
|
|
28
33
|
}
|
|
29
34
|
finally {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recoverCurrentOrganizationPolicy.js","sourceRoot":"","sources":["recoverCurrentOrganizationPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,6BAA6B,EAAE,MAAM,kDAAkD,CAAC;AAKjG,OAAO,EACL,6BAA6B,GAE9B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAEtE,6FAA6F;AAC7F,MAAM,CAAC,KAAK,UAAU,gCAAgC,CACpD,OAAiC,EACjC,IAA4B;IAE5B,MAAM,KAAK,GAAG;QACZ,GAAG,OAAO;QACV,UAAU,EAAE,6BAA6B,CAAC,OAAO,CAAC,UAAU,CAAC;KAC9D,CAAC;IACF,IAAI,CAAC;QACH,IAAI,SAAmC,CAAC;QACxC,IAAI,
|
|
1
|
+
{"version":3,"file":"recoverCurrentOrganizationPolicy.js","sourceRoot":"","sources":["recoverCurrentOrganizationPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,6BAA6B,EAAE,MAAM,kDAAkD,CAAC;AAKjG,OAAO,EACL,6BAA6B,GAE9B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAEtE,6FAA6F;AAC7F,MAAM,CAAC,KAAK,UAAU,gCAAgC,CACpD,OAAiC,EACjC,IAA4B;IAE5B,MAAM,KAAK,GAAG;QACZ,GAAG,OAAO;QACV,UAAU,EAAE,6BAA6B,CAAC,OAAO,CAAC,UAAU,CAAC;KAC9D,CAAC;IACF,IAAI,CAAC;QACH,IAAI,SAAmC,CAAC;QACxC,IAAI,MAAM,GAAG,6BAA6B,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACxD,IAAI,CAAC;YACH,SAAS,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACzC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,mCAAmC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YACxD,IAAI,KAAK,CAAC,OAAO,IAAI,CAAC,yBAAyB,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAC;YACpE,MAAM,GAAG,6BAA6B,CAAC,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC;YAC1E,SAAS,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACzC,CAAC;QACD,wEAAwE;QACxE,MAAM,YAAY,GAAG,GAAG,EAAE,CAAC,KAAK,CAAC,YAAY,EAAE,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QACzE,mCAAmC,CAAC,YAAY,CAAC,CAAC;QAClD,OAAO;YACL,OAAO,EAAE,SAAS,CAAC,OAAO;YAC1B,MAAM,EAAE,SAAS,CAAC,MAAM;YACxB,YAAY,EAAE,EAAE;YAChB,YAAY;SACb,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpC,CAAC;AACH,CAAC","sourcesContent":["import { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { ownPrincipalHistoryProtection } from \"../../data/principals/principalHistoryProtection\";\nimport type {\n PrincipalRecoveryContext,\n RecoveredPolicyDirectory,\n} from \"./principalRecoveryDirectory\";\nimport {\n createPrincipalRecoveryReader,\n type PrincipalRecoveryMemo,\n} from \"./principalRecoveryMemo\";\nimport { isPrincipalRecoveryOutage } from \"./principalRecoveryOutage\";\n\n/** Discover and verify current directory artifacts, including protected offline recovery. */\nexport async function recoverCurrentOrganizationPolicy(\n options: PrincipalRecoveryContext,\n memo?: PrincipalRecoveryMemo,\n) {\n const input = {\n ...options,\n protection: ownPrincipalHistoryProtection(options.protection),\n };\n try {\n let directory: RecoveredPolicyDirectory;\n let reader = createPrincipalRecoveryReader(input, memo);\n try {\n directory = await reader.directory([]);\n } catch (error) {\n assertProjectionVerificationCurrent(input.stillCurrent);\n if (input.offline || !isPrincipalRecoveryOutage(error)) throw error;\n reader = createPrincipalRecoveryReader({ ...input, offline: true }, memo);\n directory = await reader.directory([]);\n }\n // A reused directory keeps its producer's lifetime to the point of use.\n const stillCurrent = () => input.stillCurrent() && reader.stillCurrent();\n assertProjectionVerificationCurrent(stillCurrent);\n return {\n current: directory.current,\n policy: directory.policy,\n dependencies: [],\n stillCurrent,\n };\n } finally {\n input.protection.localKey.fill(0);\n }\n}\n"]}
|