@tearleads/client-sdk 0.1.19 → 0.1.20
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/organizations/index.js +2 -2
- package/client/organizations/index.js.map +1 -1
- package/client/organizations/organizationHistory.js +66 -0
- package/client/organizations/organizationHistory.js.map +1 -0
- package/client/organizations/organizationReadModels.d.ts +1 -1
- package/client/organizations/organizationReadModels.js +7 -27
- package/client/organizations/organizationReadModels.js.map +1 -1
- package/client/organizations/organizationsTypes.d.ts +1 -1
- package/client/organizations/organizationsTypes.js.map +1 -1
- package/data/containers/shared/types.d.ts +2 -2
- package/data/containers/shared/types.js.map +1 -1
- package/data/documents/shared/syncSubmitFailure.d.ts +2 -2
- package/data/documents/shared/syncSubmitFailure.js.map +1 -1
- package/data/keyingProjectionVerification/documentPurgeCheckpointCurrency.js +34 -8
- package/data/keyingProjectionVerification/documentPurgeCheckpointCurrency.js.map +1 -1
- package/data/keyingProjectionVerification/documentPurgeDocumentChainVerification.js.map +1 -1
- package/data/keyingProjectionVerification/documentPurgePolicyCheckpoints.js +17 -0
- package/data/keyingProjectionVerification/documentPurgePolicyCheckpoints.js.map +1 -0
- package/data/keyingProjectionVerification/documentPurgePolicyCurrency.js +55 -0
- package/data/keyingProjectionVerification/documentPurgePolicyCurrency.js.map +1 -0
- package/data/keyingProjectionVerification/documentPurgePrincipalEvidence.js +25 -13
- package/data/keyingProjectionVerification/documentPurgePrincipalEvidence.js.map +1 -1
- package/data/keyingProjectionVerification/documentPurgeProofVerification.d.ts +5 -3
- package/data/keyingProjectionVerification/documentPurgeProofVerification.js +52 -8
- package/data/keyingProjectionVerification/documentPurgeProofVerification.js.map +1 -1
- package/data/keyingProjectionVerification/projectionPolicyEvidence.js.map +1 -1
- package/data/keyingProjectionVerification/types.d.ts +4 -0
- package/data/keyingProjectionVerification/types.js.map +1 -1
- package/data/persistence/principalAuthorizationCheckpoints.js +1 -1
- package/data/persistence/principalAuthorizationCheckpoints.js.map +1 -1
- package/data/persistence/principalCurrentAcknowledgementPersistence.d.ts +1 -0
- package/data/persistence/principalCurrentAcknowledgementPersistence.js +58 -12
- package/data/persistence/principalCurrentAcknowledgementPersistence.js.map +1 -1
- package/data/persistence/principalHistoryEvidencePersistence.d.ts +2 -0
- package/data/persistence/principalHistoryEvidencePersistence.js +2 -0
- package/data/persistence/principalHistoryEvidencePersistence.js.map +1 -1
- package/data/persistence/principalHistoryIncompleteRetention.js +32 -0
- package/data/persistence/principalHistoryIncompleteRetention.js.map +1 -0
- package/data/persistence/principalHistoryNodeRetention.js +76 -0
- package/data/persistence/principalHistoryNodeRetention.js.map +1 -0
- package/data/persistence/principalHistoryPrefixPersistence.d.ts +1 -0
- package/data/persistence/principalHistoryPrefixPersistence.js +30 -1
- package/data/persistence/principalHistoryPrefixPersistence.js.map +1 -1
- package/data/persistence/principalHistoryRootOwnership.js +71 -0
- package/data/persistence/principalHistoryRootOwnership.js.map +1 -0
- package/data/persistence/principalHistoryStagePersistence.js +45 -0
- package/data/persistence/principalHistoryStagePersistence.js.map +1 -1
- package/data/persistence/principalHistoryStageRetention.js +44 -0
- package/data/persistence/principalHistoryStageRetention.js.map +1 -0
- package/data/persistence/principalHistoryUnpublishedRetention.js +71 -0
- package/data/persistence/principalHistoryUnpublishedRetention.js.map +1 -0
- package/data/persistence/principalKeyEnvelopeArchivePersistence.js +63 -0
- package/data/persistence/principalKeyEnvelopeArchivePersistence.js.map +1 -0
- package/data/persistence/principalKeyEnvelopeCandidates.js +10 -1
- package/data/persistence/principalKeyEnvelopeCandidates.js.map +1 -1
- package/data/sqlite/principalHistoryEvidenceSchema.js +2 -0
- package/data/sqlite/principalHistoryEvidenceSchema.js.map +1 -1
- package/data/sqlite/principalHistoryNodeRetentionSchema.js +30 -0
- package/data/sqlite/principalHistoryNodeRetentionSchema.js.map +1 -0
- package/data/sqlite/principalHistoryRetentionSchema.js +40 -0
- package/data/sqlite/principalHistoryRetentionSchema.js.map +1 -0
- package/data/sqlite/principalHistoryStageSchema.js +2 -0
- package/data/sqlite/principalHistoryStageSchema.js.map +1 -1
- package/data/sqlite/schema.d.ts +453 -0
- package/data/sqlite/schema.js +6 -0
- package/data/sqlite/schema.js.map +1 -1
- package/data/sqlite/sqliteCommitGuards.js +49 -0
- package/data/sqlite/sqliteCommitGuards.js.map +1 -0
- package/data/sqlite/sqlitePersistenceRuntime.js +14 -2
- package/data/sqlite/sqlitePersistenceRuntime.js.map +1 -1
- package/index.d.ts +3 -1
- package/index.js.map +1 -1
- package/internal/api-client/ApiClient.d.ts +60 -137
- package/internal/api-client/ApiClient.js +7 -5
- package/internal/api-client/ApiClient.js.map +1 -1
- package/internal/api-client/apiRequestRuntime.js +7 -7
- package/internal/api-client/apiRequestRuntime.js.map +1 -1
- package/internal/api-client/documentPurge.js +25 -0
- package/internal/api-client/documentPurge.js.map +1 -0
- package/internal/api-client/organizationPolicyHistory.d.ts +53 -0
- package/internal/api-client/organizationPolicyHistory.js +15 -0
- package/internal/api-client/organizationPolicyHistory.js.map +1 -0
- package/internal/api-client/requestInternals.js +4 -4
- package/internal/api-client/requestInternals.js.map +1 -1
- package/internal/api-client/types.d.ts +2 -2
- package/internal/api-client/types.js.map +1 -1
- package/internal/crypto/keying/principalPolicyHistory.js +4 -0
- package/internal/crypto/keying/principalPolicyHistory.js.map +1 -1
- package/internal/crypto/keying/principalPolicyHistoryTypes.d.ts +2 -0
- package/internal/crypto/keying/principalPolicyHistoryTypes.js.map +1 -1
- package/internal/validators/operation/containerMutations.d.ts +448 -6528
- package/internal/validators/operation/documentMutations.d.ts +323 -579
- package/internal/validators/operation/documentMutations.js +22 -4
- package/internal/validators/operation/documentMutations.js.map +1 -1
- package/internal/validators/operation/documentSync.d.ts +26 -406
- package/internal/validators/operation/organizationPolicyHistory.d.ts +188 -310
- package/internal/validators/operation/organizationPolicyHistory.js +17 -3
- package/internal/validators/operation/organizationPolicyHistory.js.map +1 -1
- package/internal/validators/response/containerMutationError.d.ts +28 -408
- package/internal/validators/response/documentMutation.d.ts +232 -544
- package/internal/validators/response/documentMutation.js +1 -2
- package/internal/validators/response/documentMutation.js.map +1 -1
- package/internal/validators/response/documentSyncError.d.ts +26 -406
- package/internal/validators/response/documentSyncError.js +4 -3
- package/internal/validators/response/documentSyncError.js.map +1 -1
- package/internal/validators/response/index.d.ts +1 -1
- package/internal/validators/response/index.js +1 -1
- package/internal/validators/response/index.js.map +1 -1
- package/internal/validators/response/organizationPolicyHistory.d.ts +127 -309
- package/internal/validators/response/organizationPolicyHistory.js +10 -4
- package/internal/validators/response/organizationPolicyHistory.js.map +1 -1
- package/internal/validators/response/principal.d.ts +28 -408
- package/internal/validators/response/principal.js +3 -1
- package/internal/validators/response/principal.js.map +1 -1
- package/internal/validators/util/index.d.ts +1 -1
- package/internal/validators/util/index.js +1 -1
- package/internal/validators/util/index.js.map +1 -1
- package/internal/validators/util/principalHistoryWire.d.ts +2 -0
- package/internal/validators/util/principalHistoryWire.js +2 -0
- package/internal/validators/util/principalHistoryWire.js.map +1 -1
- package/package.json +1 -1
- package/workflows/container-contents/container-state/createWithMetadata.js +19 -49
- package/workflows/container-contents/container-state/createWithMetadata.js.map +1 -1
- package/workflows/container-contents/container-state/groupGrantVerification.js +29 -0
- package/workflows/container-contents/container-state/groupGrantVerification.js.map +1 -1
- package/workflows/container-contents/container-state/groupShareEpoch.js +33 -0
- package/workflows/container-contents/container-state/groupShareEpoch.js.map +1 -0
- package/workflows/container-contents/container-state/remote.js +4 -1
- package/workflows/container-contents/container-state/remote.js.map +1 -1
- package/workflows/container-contents/container-state/share.js +2 -24
- package/workflows/container-contents/container-state/share.js.map +1 -1
- package/workflows/container-contents/documentTombstoneEvidence.js +23 -12
- package/workflows/container-contents/documentTombstoneEvidence.js.map +1 -1
- package/workflows/containers/child/create.js +1 -3
- package/workflows/containers/child/create.js.map +1 -1
- package/workflows/containers/child/createSubmission.d.ts +1 -3
- package/workflows/containers/child/createSubmission.js +13 -15
- package/workflows/containers/child/createSubmission.js.map +1 -1
- package/workflows/containers/child/currentShareGrantMutation.js +82 -0
- package/workflows/containers/child/currentShareGrantMutation.js.map +1 -0
- package/workflows/containers/child/currentSharePrincipalPolicy.js +83 -0
- package/workflows/containers/child/currentSharePrincipalPolicy.js.map +1 -0
- package/workflows/containers/child/mutationSubmit.js +1 -1
- package/workflows/containers/child/mutationSubmit.js.map +1 -1
- package/workflows/containers/child/runtimeCurrentGroupShare.js +82 -0
- package/workflows/containers/child/runtimeCurrentGroupShare.js.map +1 -0
- package/workflows/containers/child/share.js +1 -14
- package/workflows/containers/child/share.js.map +1 -1
- package/workflows/containers/child/shareProjection.js +15 -0
- package/workflows/containers/child/shareProjection.js.map +1 -0
- package/workflows/documents/purge.d.ts +3 -0
- package/workflows/documents/purge.js +13 -0
- package/workflows/documents/purge.js.map +1 -1
- package/workflows/documents/sync.js +2 -0
- package/workflows/documents/sync.js.map +1 -1
- package/workflows/documents/syncFailures.js +2 -2
- package/workflows/documents/syncFailures.js.map +1 -1
- package/workflows/documents/syncPolicyRepair.js +15 -13
- package/workflows/documents/syncPolicyRepair.js.map +1 -1
- package/workflows/organizations/initialPrincipalCurrentPublication.js +2 -0
- package/workflows/organizations/initialPrincipalCurrentPublication.js.map +1 -1
- package/workflows/organizations/loadPolicyHistoryDetails.js +31 -32
- package/workflows/organizations/loadPolicyHistoryDetails.js.map +1 -1
- package/workflows/organizations/organizationPolicyHistoryDetails.js +37 -15
- package/workflows/organizations/organizationPolicyHistoryDetails.js.map +1 -1
- package/workflows/organizations/organizationPolicyHistoryVerification.js +56 -69
- package/workflows/organizations/organizationPolicyHistoryVerification.js.map +1 -1
- package/workflows/organizations/policyHistoryCache.js +5 -2
- package/workflows/organizations/policyHistoryCache.js.map +1 -1
- package/workflows/organizations/policyHistoryTypes.d.ts +1 -0
- package/workflows/organizations/policyHistoryTypes.js.map +1 -1
- package/workflows/organizations/retainAcknowledgedPrincipalCurrents.js +4 -1
- package/workflows/organizations/retainAcknowledgedPrincipalCurrents.js.map +1 -1
- package/workflows/principals/loadRecoveredPrincipalHistoryPage.d.ts +0 -1
- package/workflows/principals/loadRecoveredPrincipalHistoryPage.js +1 -2
- package/workflows/principals/loadRecoveredPrincipalHistoryPage.js.map +1 -1
- package/workflows/principals/policyRepair.js +38 -0
- package/workflows/principals/policyRepair.js.map +1 -0
- package/workflows/principals/policyRepairBudget.d.ts +2 -2
- package/workflows/principals/policyRepairBudget.js +3 -3
- package/workflows/principals/policyRepairBudget.js.map +1 -1
- package/workflows/principals/principalHistoryRecoveryPrefix.js +4 -0
- package/workflows/principals/principalHistoryRecoveryPrefix.js.map +1 -1
- package/workflows/principals/principalRecoveryLocalPreference.js +3 -2
- package/workflows/principals/principalRecoveryLocalPreference.js.map +1 -1
- package/workflows/principals/publicPrincipalHistoryPage.js +1 -0
- package/workflows/principals/publicPrincipalHistoryPage.js.map +1 -1
- package/workflows/principals/publicPrincipalHistoryProgress.js +1 -0
- package/workflows/principals/publicPrincipalHistoryProgress.js.map +1 -1
- package/workflows/principals/publicPrincipalHistoryTypes.d.ts +2 -0
- package/workflows/principals/publicPrincipalHistoryTypes.js.map +1 -1
- package/workflows/principals/publicProjectionPrincipal.js +1 -0
- package/workflows/principals/publicProjectionPrincipal.js.map +1 -1
- package/workflows/principals/recoverPrincipalHistoryPage.js +2 -1
- package/workflows/principals/recoverPrincipalHistoryPage.js.map +1 -1
- package/workflows/principals/recoverPrincipalPolicyHistory.js +1 -0
- package/workflows/principals/recoverPrincipalPolicyHistory.js.map +1 -1
- package/workflows/principals/recoverPublicPrincipalHistory.js +2 -0
- package/workflows/principals/recoverPublicPrincipalHistory.js.map +1 -1
- package/workflows/principals/runtimePolicyRecovery.js +2 -1
- package/workflows/principals/runtimePolicyRecovery.js.map +1 -1
- package/workflows/principals/selectPublicPrincipalHistory.js +21 -15
- package/workflows/principals/selectPublicPrincipalHistory.js.map +1 -1
- package/workflows/sync/remoteResetPrincipalScope.js +6 -0
- package/workflows/sync/remoteResetPrincipalScope.js.map +1 -1
- package/data/keyingProjectionVerification/principalPolicySnapshotVerification.js +0 -208
- package/data/keyingProjectionVerification/principalPolicySnapshotVerification.js.map +0 -1
- package/data/keyingProjectionVerification/snapshotVerificationCache.js +0 -39
- package/data/keyingProjectionVerification/snapshotVerificationCache.js.map +0 -1
- package/workflows/containers/child/policyRepair.js +0 -23
- package/workflows/containers/child/policyRepair.js.map +0 -1
|
@@ -6,6 +6,8 @@ export type ProjectionUserKey = TrustedUserIdentity;
|
|
|
6
6
|
export type ProjectionUserKeyResolver = (userId: string) => Promise<ProjectionUserKey | null>;
|
|
7
7
|
export type PrincipalPolicyCache = Map<string, PrincipalPolicyCurrentEvidence>;
|
|
8
8
|
export interface PrincipalPolicyResolveRequest {
|
|
9
|
+
/** Reuse authenticated local ancestry before an authorized online recovery. */
|
|
10
|
+
readonly preferLocalHistory?: boolean | undefined;
|
|
9
11
|
/** Identity of one projection collection, never caller-supplied evidence. */
|
|
10
12
|
readonly recoveryBatch?: object | undefined;
|
|
11
13
|
readonly organizationId: string;
|
|
@@ -29,6 +31,8 @@ export interface PrincipalPolicyBundleCacheRequest {
|
|
|
29
31
|
readonly stillCurrent?: (() => boolean) | undefined;
|
|
30
32
|
}
|
|
31
33
|
export interface ProjectionPolicyHistoryResolveRequest {
|
|
34
|
+
/** Authenticate terminal evidence first; its consumer must validate durable currency. */
|
|
35
|
+
readonly historicalProof?: boolean | undefined;
|
|
32
36
|
readonly organizationId: string;
|
|
33
37
|
readonly evidence: ProjectionPolicyEvidenceResponse;
|
|
34
38
|
readonly references: readonly ReferencedPrincipalHead[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"AAmFA,MAAM,OAAO,oCAAqC,SAAQ,KAAK;IAC7D;QACE,KAAK,CAAC,4CAA4C,CAAC,CAAC;QACpD,IAAI,CAAC,IAAI,GAAG,sCAAsC,CAAC;IACrD,CAAC;CACF;AAED,MAAM,kCAAkC,GACtC,IAAI,oCAAoC,EAAE,CAAC;AAE7C,MAAM,UAAU,sCAAsC,CACpD,KAAc;IAEd,OAAO,KAAK,KAAK,kCAAkC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,sCAAsC,CAAC,KAAc;IACnE,IAAI,sCAAsC,CAAC,KAAK,CAAC;QAAE,MAAM,KAAK,CAAC;AACjE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wCAAwC,CAC5D,SAA2B;IAE3B,IAAI,CAAC;QACH,OAAO,MAAM,SAAS,EAAE,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,sCAAsC,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAC/D,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,mCAAmC,CACjD,YAAyC;IAEzC,IAAI,YAAY,EAAE,EAAE,KAAK,KAAK,EAAE,CAAC;QAC/B,MAAM,kCAAkC,CAAC;IAC3C,CAAC;AACH,CAAC;AAED,MAAM,UAAU,sCAAsC,CACpD,MAAmD,EACnD,YAAyC;IAEzC,IAAI,CAAC,MAAM,IAAI,CAAC,YAAY;QAAE,OAAO,MAAM,CAAC;IAC5C,MAAM,KAAK,GACT,CAAC,SAA0C,EAAE,EAAE,CAC/C,KAAK,EAAE,KAA2C,EAAE,EAAE;QACpD,mCAAmC,CAAC,YAAY,CAAC,CAAC;QAClD,MAAM,SAAS,CAAC,EAAE,GAAG,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;QAC5C,mCAAmC,CAAC,YAAY,CAAC,CAAC;IACpD,CAAC,CAAC;IACJ,MAAM,qBAAqB,GAAG,CAC5B,SAA0C,EACT,EAAE;QACnC,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC;QACjC,MAAM,OAAO,GAAG,SAAS,CAAC,gBAAgB,CAAC;QAC3C,MAAM,wBAAwB,GAAG,SAAS,CAAC,wBAAwB,CAAC;QACpE,OAAO,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE;YAC5B,GAAG,CAAC,wBAAwB;gBAC1B,CAAC,CAAC;oBACE,wBAAwB,EAAE,KAAK,EAC7B,KAA4C,EAC5C,EAAE;wBACF,MAAM,OAAO,GAAG,GAAG,EAAE,CACnB,YAAY,EAAE,IAAI,KAAK,CAAC,YAAY,EAAE,EAAE,KAAK,KAAK,CAAC;wBACrD,mCAAmC,CAAC,OAAO,CAAC,CAAC;wBAC7C,MAAM,MAAM,GAAG,MAAM,wBAAwB,CAAC;4BAC5C,GAAG,KAAK;4BACR,YAAY,EAAE,OAAO;yBACtB,CAAC,CAAC;wBACH,mCAAmC,CAAC,OAAO,CAAC,CAAC;wBAC7C,OAAO,MAAM,CAAC;oBAChB,CAAC;iBACF;gBACH,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,OAAO;gBACT,CAAC,CAAC;oBACE,gBAAgB,EAAE,KAAK,EAAE,KAAoC,EAAE,EAAE;wBAC/D,MAAM,OAAO,GAAG,GAAG,EAAE,CACnB,YAAY,EAAE,IAAI,KAAK,CAAC,YAAY,EAAE,EAAE,KAAK,KAAK,CAAC;wBACrD,mCAAmC,CAAC,OAAO,CAAC,CAAC;wBAC7C,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,EAAE,GAAG,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,CAAC;wBAClE,mCAAmC,CAAC,OAAO,CAAC,CAAC;wBAC7C,OAAO,MAAM,CAAC;oBAChB,CAAC;iBACF;gBACH,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,SAAS,CAAC,YAAY;gBACxB,CAAC,CAAC;oBACE,YAAY,EAAE,KAAK,EAAE,KAAwC,EAAE,EAAE;wBAC/D,mCAAmC,CAAC,YAAY,CAAC,CAAC;wBAClD,MAAM,SAAS,CAAC,YAAY,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;wBAC3D,mCAAmC,CAAC,YAAY,CAAC,CAAC;oBACpD,CAAC;iBACF;gBACH,CAAC,CAAC,EAAE,CAAC;SACR,CAAC,CAAC;IACL,CAAC,CAAC;IACF,OAAO,qBAAqB,CAAC,MAAM,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,iCAAiC,CAO/C,KAAY;IACZ,OAAO;QACL,GAAG,KAAK;QACR,+BAA+B,EAAE,sCAAsC,CACrE,KAAK,CAAC,+BAA+B,EACrC,KAAK,CAAC,YAAY,CACnB;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type {\n ReferencedPrincipalHead,\n VerifiedPrincipalPolicyCurrent,\n VerifiedPrincipalPolicySelection,\n} from \"@tearleads/crypto\";\nimport type {\n PrincipalPolicyBundleResponse,\n ProjectionPolicyEvidenceResponse,\n} from \"@tearleads/validators/response\";\nimport type { PrincipalPolicyCurrentEvidence } from \"../principals/principalPolicyEvidence\";\nimport type { TrustedUserIdentity } from \"../trustedUserIdentity\";\n\nexport type ProjectionUserKey = TrustedUserIdentity;\n\nexport type ProjectionUserKeyResolver = (\n userId: string,\n) => Promise<ProjectionUserKey | null>;\n\nexport type PrincipalPolicyCache = Map<string, PrincipalPolicyCurrentEvidence>;\n\nexport interface PrincipalPolicyResolveRequest {\n /** Reuse authenticated local ancestry before an authorized online recovery. */\n readonly preferLocalHistory?: boolean | undefined;\n /** Identity of one projection collection, never caller-supplied evidence. */\n readonly recoveryBatch?: object | undefined;\n readonly organizationId: string;\n readonly reference: ReferencedPrincipalHead;\n readonly stillCurrent?: (() => boolean) | undefined;\n}\n\nexport interface ResolvedPrincipalPolicyEvidence {\n readonly stillCurrent: () => boolean;\n readonly organizationId: string;\n readonly policy: VerifiedPrincipalPolicyCurrent;\n readonly dependencies: readonly VerifiedPrincipalPolicyCurrent[];\n}\n\nexport interface ReferencedPrincipalPolicyWarmRequest {\n readonly organizationId: string;\n readonly references: readonly ReferencedPrincipalHead[];\n readonly stillCurrent?: (() => boolean) | undefined;\n}\n\nexport interface PrincipalPolicyBundleCacheRequest {\n readonly bundles: readonly PrincipalPolicyBundleResponse[];\n readonly organizationId: string;\n readonly stillCurrent?: (() => boolean) | undefined;\n}\n\nexport interface ProjectionPolicyHistoryResolveRequest {\n /** Authenticate terminal evidence first; its consumer must validate durable currency. */\n readonly historicalProof?: boolean | undefined;\n readonly organizationId: string;\n readonly evidence: ProjectionPolicyEvidenceResponse;\n readonly references: readonly ReferencedPrincipalHead[];\n readonly stillCurrent?: (() => boolean) | undefined;\n}\n\nexport interface ResolvedProjectionPolicyHistory {\n readonly policies: readonly VerifiedPrincipalPolicySelection[];\n readonly stillCurrent: () => boolean;\n}\n\nexport type PrincipalPolicyBundleCacher = (\n input: PrincipalPolicyBundleCacheRequest,\n) => Promise<void>;\n\nexport type ReferencedPrincipalPolicyWarmer = ((\n input: ReferencedPrincipalPolicyWarmRequest,\n) => Promise<void>) & {\n readonly cacheBundles?: PrincipalPolicyBundleCacher | undefined;\n readonly resolveProjectionHistory?:\n | ((\n input: ProjectionPolicyHistoryResolveRequest,\n ) => Promise<ResolvedProjectionPolicyHistory>)\n | undefined;\n readonly resolveReference?:\n | ((\n input: PrincipalPolicyResolveRequest,\n ) => Promise<ResolvedPrincipalPolicyEvidence>)\n | undefined;\n};\n\nexport class ProjectionVerificationCancelledError extends Error {\n constructor() {\n super(\"Projection verification generation expired\");\n this.name = \"ProjectionVerificationCancelledError\";\n }\n}\n\nconst projectionVerificationCancellation =\n new ProjectionVerificationCancelledError();\n\nexport function isProjectionVerificationCancelledError(\n error: unknown,\n): error is ProjectionVerificationCancelledError {\n return error === projectionVerificationCancellation;\n}\n\nexport function rethrowProjectionVerificationCancelled(error: unknown): void {\n if (isProjectionVerificationCancelledError(error)) throw error;\n}\n\nexport async function nullOnProjectionVerificationCancellation<T>(\n operation: () => Promise<T>,\n): Promise<T | null> {\n try {\n return await operation();\n } catch (error) {\n if (isProjectionVerificationCancelledError(error)) return null;\n throw error;\n }\n}\n\nexport function assertProjectionVerificationCurrent(\n stillCurrent: (() => boolean) | undefined,\n): void {\n if (stillCurrent?.() === false) {\n throw projectionVerificationCancellation;\n }\n}\n\nexport function generationGuardedPrincipalPolicyWarmer(\n warmer: ReferencedPrincipalPolicyWarmer | undefined,\n stillCurrent: (() => boolean) | undefined,\n): ReferencedPrincipalPolicyWarmer | undefined {\n if (!warmer || !stillCurrent) return warmer;\n const guard =\n (operation: ReferencedPrincipalPolicyWarmer) =>\n async (input: ReferencedPrincipalPolicyWarmRequest) => {\n assertProjectionVerificationCurrent(stillCurrent);\n await operation({ ...input, stillCurrent });\n assertProjectionVerificationCurrent(stillCurrent);\n };\n const guardWithCapabilities = (\n operation: ReferencedPrincipalPolicyWarmer,\n ): ReferencedPrincipalPolicyWarmer => {\n const guarded = guard(operation);\n const resolve = operation.resolveReference;\n const resolveProjectionHistory = operation.resolveProjectionHistory;\n return Object.assign(guarded, {\n ...(resolveProjectionHistory\n ? {\n resolveProjectionHistory: async (\n input: ProjectionPolicyHistoryResolveRequest,\n ) => {\n const current = () =>\n stillCurrent() && input.stillCurrent?.() !== false;\n assertProjectionVerificationCurrent(current);\n const result = await resolveProjectionHistory({\n ...input,\n stillCurrent: current,\n });\n assertProjectionVerificationCurrent(current);\n return result;\n },\n }\n : {}),\n ...(resolve\n ? {\n resolveReference: async (input: PrincipalPolicyResolveRequest) => {\n const current = () =>\n stillCurrent() && input.stillCurrent?.() !== false;\n assertProjectionVerificationCurrent(current);\n const result = await resolve({ ...input, stillCurrent: current });\n assertProjectionVerificationCurrent(current);\n return result;\n },\n }\n : {}),\n ...(operation.cacheBundles\n ? {\n cacheBundles: async (input: PrincipalPolicyBundleCacheRequest) => {\n assertProjectionVerificationCurrent(stillCurrent);\n await operation.cacheBundles?.({ ...input, stillCurrent });\n assertProjectionVerificationCurrent(stillCurrent);\n },\n }\n : {}),\n });\n };\n return guardWithCapabilities(warmer);\n}\n\nexport function withGenerationGuardedPolicyWarmer<\n Input extends {\n readonly stillCurrent?: (() => boolean) | undefined;\n readonly warmReferencedPrincipalPolicies?:\n | ReferencedPrincipalPolicyWarmer\n | undefined;\n },\n>(input: Input): Input {\n return {\n ...input,\n warmReferencedPrincipalPolicies: generationGuardedPrincipalPolicyWarmer(\n input.warmReferencedPrincipalPolicies,\n input.stillCurrent,\n ),\n };\n}\n"]}
|
|
@@ -5,7 +5,7 @@ export class PrincipalAuthorizationCheckpointUnavailableError extends KeyingVeri
|
|
|
5
5
|
super("missing_dependency", "Public authorization cannot connect to the latest durable checkpoint");
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
|
-
/**
|
|
8
|
+
/** Validate public evidence or a verified private ancestry bridge without admitting pins. */
|
|
9
9
|
export async function validatePrincipalAuthorizationCheckpoints(tx, policies) {
|
|
10
10
|
for (const policy of policies) {
|
|
11
11
|
const checkpoint = await loadStoredPrincipalPolicyCheckpoint(tx, policy);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"principalAuthorizationCheckpoints.js","sourceRoot":"","sources":["principalAuthorizationCheckpoints.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,
|
|
1
|
+
{"version":3,"file":"principalAuthorizationCheckpoints.js","sourceRoot":"","sources":["principalAuthorizationCheckpoints.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EAGvB,+BAA+B,GAChC,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,mCAAmC,EAAE,MAAM,+BAA+B,CAAC;AAEpF,MAAM,OAAO,gDAAiD,SAAQ,uBAAuB;IAC3F;QACE,KAAK,CACH,oBAAoB,EACpB,sEAAsE,CACvE,CAAC;IACJ,CAAC;CACF;AAED,6FAA6F;AAC7F,MAAM,CAAC,KAAK,UAAU,yCAAyC,CAC7D,EAAgC,EAChC,QAGG;IAEH,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;QAC9B,MAAM,UAAU,GAAG,MAAM,mCAAmC,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACzE,IAAI,CAAC,UAAU;YAAE,SAAS;QAC1B,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAClC,CAAC,KAAK,EAAE,EAAE,CACR,KAAK,CAAC,aAAa,KAAK,MAAM,CAAC,aAAa;YAC5C,KAAK,CAAC,WAAW,KAAK,MAAM,CAAC,WAAW;YACxC,KAAK,CAAC,SAAS,KAAK,MAAM,CAAC,SAAS,CACvC,CAAC;QACF,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QACrE,IACE,UAAU,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO;YACnC,CAAC,UAAU,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO;gBAClC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,UAAU,CAAC,OAAO,CAAC,CAAC;YAEnE,MAAM,IAAI,gDAAgD,EAAE,CAAC;QAC/D,+BAA+B,CAAC;YAC9B,KAAK;YACL,YAAY,EAAE,MAAM,CAAC,KAAK;YAC1B,eAAe,EAAE,UAAU;SAC5B,CAAC,CAAC;IACL,CAAC;AACH,CAAC","sourcesContent":["import {\n KeyingVerificationError,\n type VerifiedPrincipalPolicyCurrent,\n type VerifiedPrincipalPolicySelection,\n verifyPrincipalPolicyCheckpoint,\n} from \"@tearleads/crypto\";\nimport type { ClientSQLiteTransactionScope } from \"../sqlite/sqlitePersistenceRuntime\";\nimport { loadStoredPrincipalPolicyCheckpoint } from \"./keyingCheckpointPersistence\";\n\nexport class PrincipalAuthorizationCheckpointUnavailableError extends KeyingVerificationError {\n constructor() {\n super(\n \"missing_dependency\",\n \"Public authorization cannot connect to the latest durable checkpoint\",\n );\n }\n}\n\n/** Validate public evidence or a verified private ancestry bridge without admitting pins. */\nexport async function validatePrincipalAuthorizationCheckpoints(\n tx: ClientSQLiteTransactionScope,\n policies: readonly (\n | VerifiedPrincipalPolicySelection\n | VerifiedPrincipalPolicyCurrent\n )[],\n): Promise<void> {\n for (const policy of policies) {\n const checkpoint = await loadStoredPrincipalPolicyCheckpoint(tx, policy);\n if (!checkpoint) continue;\n const matchingHead = policies.filter(\n (other) =>\n other.principalType === policy.principalType &&\n other.principalId === policy.principalId &&\n other.stateHash === policy.stateHash,\n );\n const chain = matchingHead.flatMap((other) => other.retainedHistory);\n if (\n checkpoint.version > policy.version ||\n (checkpoint.version < policy.version &&\n !chain.some(({ state }) => state.version === checkpoint.version))\n )\n throw new PrincipalAuthorizationCheckpointUnavailableError();\n verifyPrincipalPolicyCheckpoint({\n chain,\n currentState: policy.state,\n localCheckpoint: checkpoint,\n });\n }\n}\n"]}
|
|
@@ -10,6 +10,7 @@ export interface AcknowledgedPrincipalCurrentPublication {
|
|
|
10
10
|
readonly previousPrefixProgress: string | null;
|
|
11
11
|
readonly stage: PrincipalHistoryStage;
|
|
12
12
|
readonly predecessorStage: PrincipalHistoryStage | null;
|
|
13
|
+
readonly predecessorIndexRootHash: string | null;
|
|
13
14
|
readonly evidence: PrincipalHistoryEvidencePage;
|
|
14
15
|
}
|
|
15
16
|
export interface AcknowledgedPrincipalCurrentRetirement {
|
|
@@ -9,6 +9,10 @@ import { ensureSqlTables } from "../sqlite/sqlSchema.js";
|
|
|
9
9
|
import { loadStoredPrincipalPolicyCheckpoint, principalPolicyKey, upsertPrincipalPolicyCheckpointInTransaction, } from "./keyingCheckpointPersistence.js";
|
|
10
10
|
import { storePrincipalGrantRetirements } from "./principalGrantRetirementPersistence.js";
|
|
11
11
|
import { writePrincipalHistoryEvidencePage, } from "./principalHistoryEvidencePersistence.js";
|
|
12
|
+
import { reclaimPrincipalHistoryNodes } from "./principalHistoryNodeRetention.js";
|
|
13
|
+
import { retainPrincipalHistoryRoot } from "./principalHistoryRootOwnership.js";
|
|
14
|
+
import { reclaimCompletedPrincipalHistoryStages, recordPrincipalHistoryStageScope, } from "./principalHistoryStageRetention.js";
|
|
15
|
+
import { archivePrincipalHistoryKeyEnvelopes } from "./principalKeyEnvelopeArchivePersistence.js";
|
|
12
16
|
function assertPublicationScope(entries, organizationId) {
|
|
13
17
|
const seen = new Set();
|
|
14
18
|
for (const entry of entries) {
|
|
@@ -17,6 +21,8 @@ function assertPublicationScope(entries, organizationId) {
|
|
|
17
21
|
throw new KeyingVerificationError("duplicate_entry", "Acknowledged current batch repeats a principal");
|
|
18
22
|
seen.add(key);
|
|
19
23
|
if (entry.prefix.organizationId !== organizationId ||
|
|
24
|
+
(entry.predecessorStage === null) !==
|
|
25
|
+
(entry.predecessorIndexRootHash === null) ||
|
|
20
26
|
entry.stage.organizationId !== organizationId ||
|
|
21
27
|
(entry.predecessorStage
|
|
22
28
|
? entry.predecessorStage.organizationId !== organizationId ||
|
|
@@ -53,6 +59,44 @@ async function validatePublication(tx, entry) {
|
|
|
53
59
|
if ((prefix?.progress ?? null) !== entry.previousPrefixProgress)
|
|
54
60
|
throw new KeyingVerificationError("stale_predecessor", "Authenticated principal prefix changed before acknowledgement");
|
|
55
61
|
}
|
|
62
|
+
async function archiveAcknowledgedStageKeys(tx, entry) {
|
|
63
|
+
for (const stage of [entry.predecessorStage, entry.stage]) {
|
|
64
|
+
if (!stage)
|
|
65
|
+
continue;
|
|
66
|
+
await recordPrincipalHistoryStageScope(tx, {
|
|
67
|
+
id: stage.id,
|
|
68
|
+
afterVersion: stage.afterVersion,
|
|
69
|
+
complete: stage.complete,
|
|
70
|
+
organizationId: entry.prefix.organizationId,
|
|
71
|
+
scopeId: entry.prefix.scopeId,
|
|
72
|
+
});
|
|
73
|
+
await archivePrincipalHistoryKeyEnvelopes(tx, {
|
|
74
|
+
...stage,
|
|
75
|
+
version: stage.afterVersion + 1,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function retainPredecessorStage(tx, entry) {
|
|
80
|
+
// Preserve a prefix-only predecessor without replacing an existing completion.
|
|
81
|
+
if (!entry.predecessorStage || !entry.predecessorIndexRootHash)
|
|
82
|
+
return;
|
|
83
|
+
const inserted = await tx
|
|
84
|
+
.insert(principalHistoryStages)
|
|
85
|
+
.values(entry.predecessorStage)
|
|
86
|
+
.onConflictDoUpdate({
|
|
87
|
+
target: principalHistoryStages.id,
|
|
88
|
+
set: entry.predecessorStage,
|
|
89
|
+
setWhere: eq(principalHistoryStages.complete, false),
|
|
90
|
+
})
|
|
91
|
+
.returning({ id: principalHistoryStages.id });
|
|
92
|
+
if (inserted.length > 0)
|
|
93
|
+
await retainPrincipalHistoryRoot(tx, {
|
|
94
|
+
id: `stage:${entry.predecessorStage.id}`,
|
|
95
|
+
scopeId: entry.prefix.scopeId,
|
|
96
|
+
organizationId: entry.prefix.organizationId,
|
|
97
|
+
rootHash: entry.predecessorIndexRootHash,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
56
100
|
/** Store current artifacts and resumable progress in the same transaction as their pins. */
|
|
57
101
|
export async function persistAcknowledgedPrincipalCurrents(input) {
|
|
58
102
|
const entries = structuredClone(input.entries);
|
|
@@ -78,18 +122,7 @@ export async function persistAcknowledgedPrincipalCurrents(input) {
|
|
|
78
122
|
}
|
|
79
123
|
for (const entry of entries) {
|
|
80
124
|
await writePrincipalHistoryEvidencePage(tx, entry.evidence);
|
|
81
|
-
|
|
82
|
-
// before its reusable slot is replaced by the acknowledged successor.
|
|
83
|
-
if (entry.predecessorStage)
|
|
84
|
-
await tx
|
|
85
|
-
.insert(principalHistoryStages)
|
|
86
|
-
.values(entry.predecessorStage)
|
|
87
|
-
.onConflictDoUpdate({
|
|
88
|
-
target: principalHistoryStages.id,
|
|
89
|
-
set: entry.predecessorStage,
|
|
90
|
-
setWhere: eq(principalHistoryStages.complete, false),
|
|
91
|
-
})
|
|
92
|
-
.run();
|
|
125
|
+
await retainPredecessorStage(tx, entry);
|
|
93
126
|
// A fully authenticated publication supersedes an in-flight stage for
|
|
94
127
|
// this exact head. Its other writer fails its progress CAS and resumes.
|
|
95
128
|
await tx
|
|
@@ -100,6 +133,7 @@ export async function persistAcknowledgedPrincipalCurrents(input) {
|
|
|
100
133
|
set: entry.stage,
|
|
101
134
|
})
|
|
102
135
|
.run();
|
|
136
|
+
await archiveAcknowledgedStageKeys(tx, entry);
|
|
103
137
|
await tx
|
|
104
138
|
.insert(principalHistoryPrefixes)
|
|
105
139
|
.values(entry.prefix)
|
|
@@ -108,6 +142,18 @@ export async function persistAcknowledgedPrincipalCurrents(input) {
|
|
|
108
142
|
set: entry.prefix,
|
|
109
143
|
})
|
|
110
144
|
.run();
|
|
145
|
+
await reclaimCompletedPrincipalHistoryStages(tx, entry.prefix);
|
|
146
|
+
for (const id of [
|
|
147
|
+
`stage:${entry.stage.id}`,
|
|
148
|
+
`prefix:${entry.prefix.scopeId}`,
|
|
149
|
+
])
|
|
150
|
+
await retainPrincipalHistoryRoot(tx, {
|
|
151
|
+
id,
|
|
152
|
+
scopeId: entry.prefix.scopeId,
|
|
153
|
+
organizationId,
|
|
154
|
+
rootHash: entry.evidence.indexRootHash,
|
|
155
|
+
});
|
|
156
|
+
await reclaimPrincipalHistoryNodes(tx, entry.prefix);
|
|
111
157
|
await upsertPrincipalPolicyCheckpointInTransaction(tx, entry.policy.checkpoint, new Date().toISOString(), organizationId);
|
|
112
158
|
}
|
|
113
159
|
}, stillCurrent, { behavior: "immediate" });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"principalCurrentAcknowledgementPersistence.js","sourceRoot":"","sources":["principalCurrentAcknowledgementPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n type VerifiedPrincipalPolicyCurrent,\n verifyPrincipalPolicyCheckpoint,\n} from \"@tearleads/crypto\";\nimport { eq } from \"drizzle-orm\";\nimport { assertProjectionVerificationCurrent } from \"../keyingProjectionVerification/types\";\nimport { principalHistoryPrefixes } from \"../sqlite/principalHistoryEvidenceSchema\";\nimport { principalHistoryStages } from \"../sqlite/principalHistoryStageSchema\";\nimport {\n keyingCheckpointTables,\n principalPolicyTables,\n} from \"../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../sqlite/sqlSchema\";\nimport {\n loadStoredPrincipalPolicyCheckpoint,\n principalPolicyKey,\n upsertPrincipalPolicyCheckpointInTransaction,\n} from \"./keyingCheckpointPersistence\";\nimport { storePrincipalGrantRetirements } from \"./principalGrantRetirementPersistence\";\nimport {\n type PrincipalHistoryEvidencePage,\n writePrincipalHistoryEvidencePage,\n} from \"./principalHistoryEvidencePersistence\";\nimport type { PrincipalHistoryPrefix } from \"./principalHistoryPrefixPersistence\";\nimport type { PrincipalHistoryStage } from \"./principalHistoryStagePersistence\";\n\n/** Owned output of authenticated prefix extension and exact receipt verification. */\nexport interface AcknowledgedPrincipalCurrentPublication {\n readonly policy: VerifiedPrincipalPolicyCurrent;\n readonly prefix: PrincipalHistoryPrefix;\n readonly previousPrefixProgress: string | null;\n readonly stage: PrincipalHistoryStage;\n readonly predecessorStage: PrincipalHistoryStage | null;\n readonly evidence: PrincipalHistoryEvidencePage;\n}\n\nexport interface AcknowledgedPrincipalCurrentRetirement {\n readonly principalId: string;\n readonly principalType: VerifiedPrincipalPolicyCurrent[\"principalType\"];\n readonly containerIds: readonly string[];\n}\n\nfunction assertPublicationScope(\n entries: readonly AcknowledgedPrincipalCurrentPublication[],\n organizationId: string,\n) {\n const seen = new Set<string>();\n for (const entry of entries) {\n const key = principalPolicyKey(entry.policy);\n if (seen.has(key))\n throw new KeyingVerificationError(\n \"duplicate_entry\",\n \"Acknowledged current batch repeats a principal\",\n );\n seen.add(key);\n if (\n entry.prefix.organizationId !== organizationId ||\n entry.stage.organizationId !== organizationId ||\n (entry.predecessorStage\n ? entry.predecessorStage.organizationId !== organizationId ||\n !entry.predecessorStage.complete ||\n entry.predecessorStage.afterVersion !== entry.policy.version - 2\n : entry.policy.principalType !== \"group\" ||\n entry.policy.version !== 1 ||\n entry.policy.state.prevStateHash !== null ||\n entry.previousPrefixProgress !== null) ||\n entry.evidence.organizationId !== organizationId ||\n entry.prefix.scopeId !== entry.evidence.scopeId ||\n entry.prefix.version !== entry.policy.version ||\n entry.stage.afterVersion !== entry.policy.version - 1 ||\n !entry.stage.complete ||\n entry.stage.currentJson !== entry.prefix.currentJson\n )\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"Acknowledged current publication is outside its scope\",\n );\n }\n}\n\nasync function validatePublication(\n tx: ClientSQLiteTransactionScope,\n entry: AcknowledgedPrincipalCurrentPublication,\n) {\n const checkpoint = await loadStoredPrincipalPolicyCheckpoint(\n tx,\n entry.policy,\n );\n verifyPrincipalPolicyCheckpoint({\n chain: entry.policy.retainedHistory,\n currentState: entry.policy.state,\n localCheckpoint: checkpoint,\n });\n if (\n (!checkpoint && entry.policy.version !== 1) ||\n (checkpoint && entry.policy.version > checkpoint.version + 1)\n )\n throw new KeyingVerificationError(\n \"stale_predecessor\",\n \"Acknowledged current policy must directly extend its durable checkpoint\",\n );\n const [prefix] = await tx\n .select()\n .from(principalHistoryPrefixes)\n .where(eq(principalHistoryPrefixes.scopeId, entry.prefix.scopeId))\n .limit(1);\n if ((prefix?.progress ?? null) !== entry.previousPrefixProgress)\n throw new KeyingVerificationError(\n \"stale_predecessor\",\n \"Authenticated principal prefix changed before acknowledgement\",\n );\n}\n\n/** Store current artifacts and resumable progress in the same transaction as their pins. */\nexport async function persistAcknowledgedPrincipalCurrents(input: {\n readonly execSql: ExecSql;\n readonly organizationId: string;\n readonly entries: readonly AcknowledgedPrincipalCurrentPublication[];\n readonly retirements?:\n | readonly AcknowledgedPrincipalCurrentRetirement[]\n | undefined;\n readonly stillCurrent: () => boolean;\n}): Promise<void> {\n const entries = structuredClone(input.entries);\n const retirements = structuredClone(input.retirements ?? []);\n const organizationId = input.organizationId;\n const stillCurrent = input.stillCurrent;\n assertPublicationScope(entries, organizationId);\n assertProjectionVerificationCurrent(stillCurrent);\n await ensureSqlTables(input.execSql, [\n ...principalPolicyTables,\n ...keyingCheckpointTables,\n ]);\n const runtime = getClientSQLitePersistenceRuntime(input.execSql);\n const stored = await runtime.guardedTransaction(\n async (tx) => {\n for (const entry of entries) await validatePublication(tx, entry);\n for (const retirement of retirements) {\n const policy = entries.find(\n (entry) =>\n entry.policy.principalId === retirement.principalId &&\n entry.policy.principalType === retirement.principalType,\n )?.policy;\n if (!policy)\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"Retirement must accompany its acknowledged principal\",\n );\n await storePrincipalGrantRetirements(\n tx,\n { ...retirement, policy, organizationId },\n entries.map((entry) => entry.policy),\n organizationId,\n );\n }\n for (const entry of entries) {\n await writePrincipalHistoryEvidencePage(tx, entry.evidence);\n // A prefix-only predecessor must keep its encrypted key envelopes\n // before its reusable slot is replaced by the acknowledged successor.\n if (entry.predecessorStage)\n await tx\n .insert(principalHistoryStages)\n .values(entry.predecessorStage)\n .onConflictDoUpdate({\n target: principalHistoryStages.id,\n set: entry.predecessorStage,\n setWhere: eq(principalHistoryStages.complete, false),\n })\n .run();\n // A fully authenticated publication supersedes an in-flight stage for\n // this exact head. Its other writer fails its progress CAS and resumes.\n await tx\n .insert(principalHistoryStages)\n .values(entry.stage)\n .onConflictDoUpdate({\n target: principalHistoryStages.id,\n set: entry.stage,\n })\n .run();\n await tx\n .insert(principalHistoryPrefixes)\n .values(entry.prefix)\n .onConflictDoUpdate({\n target: principalHistoryPrefixes.scopeId,\n set: entry.prefix,\n })\n .run();\n await upsertPrincipalPolicyCheckpointInTransaction(\n tx,\n entry.policy.checkpoint,\n new Date().toISOString(),\n organizationId,\n );\n }\n },\n stillCurrent,\n { behavior: \"immediate\" },\n );\n assertProjectionVerificationCurrent(() => stored.committed);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"principalCurrentAcknowledgementPersistence.js","sourceRoot":"","sources":["principalCurrentAcknowledgementPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n type VerifiedPrincipalPolicyCurrent,\n verifyPrincipalPolicyCheckpoint,\n} from \"@tearleads/crypto\";\nimport { eq } from \"drizzle-orm\";\nimport { assertProjectionVerificationCurrent } from \"../keyingProjectionVerification/types\";\nimport { principalHistoryPrefixes } from \"../sqlite/principalHistoryEvidenceSchema\";\nimport { principalHistoryStages } from \"../sqlite/principalHistoryStageSchema\";\nimport {\n keyingCheckpointTables,\n principalPolicyTables,\n} from \"../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../sqlite/sqlSchema\";\nimport {\n loadStoredPrincipalPolicyCheckpoint,\n principalPolicyKey,\n upsertPrincipalPolicyCheckpointInTransaction,\n} from \"./keyingCheckpointPersistence\";\nimport { storePrincipalGrantRetirements } from \"./principalGrantRetirementPersistence\";\nimport {\n type PrincipalHistoryEvidencePage,\n writePrincipalHistoryEvidencePage,\n} from \"./principalHistoryEvidencePersistence\";\nimport { reclaimPrincipalHistoryNodes } from \"./principalHistoryNodeRetention\";\nimport type { PrincipalHistoryPrefix } from \"./principalHistoryPrefixPersistence\";\nimport { retainPrincipalHistoryRoot } from \"./principalHistoryRootOwnership\";\nimport type { PrincipalHistoryStage } from \"./principalHistoryStagePersistence\";\nimport {\n reclaimCompletedPrincipalHistoryStages,\n recordPrincipalHistoryStageScope,\n} from \"./principalHistoryStageRetention\";\nimport { archivePrincipalHistoryKeyEnvelopes } from \"./principalKeyEnvelopeArchivePersistence\";\n\n/** Owned output of authenticated prefix extension and exact receipt verification. */\nexport interface AcknowledgedPrincipalCurrentPublication {\n readonly policy: VerifiedPrincipalPolicyCurrent;\n readonly prefix: PrincipalHistoryPrefix;\n readonly previousPrefixProgress: string | null;\n readonly stage: PrincipalHistoryStage;\n readonly predecessorStage: PrincipalHistoryStage | null;\n readonly predecessorIndexRootHash: string | null;\n readonly evidence: PrincipalHistoryEvidencePage;\n}\n\nexport interface AcknowledgedPrincipalCurrentRetirement {\n readonly principalId: string;\n readonly principalType: VerifiedPrincipalPolicyCurrent[\"principalType\"];\n readonly containerIds: readonly string[];\n}\n\nfunction assertPublicationScope(\n entries: readonly AcknowledgedPrincipalCurrentPublication[],\n organizationId: string,\n) {\n const seen = new Set<string>();\n for (const entry of entries) {\n const key = principalPolicyKey(entry.policy);\n if (seen.has(key))\n throw new KeyingVerificationError(\n \"duplicate_entry\",\n \"Acknowledged current batch repeats a principal\",\n );\n seen.add(key);\n if (\n entry.prefix.organizationId !== organizationId ||\n (entry.predecessorStage === null) !==\n (entry.predecessorIndexRootHash === null) ||\n entry.stage.organizationId !== organizationId ||\n (entry.predecessorStage\n ? entry.predecessorStage.organizationId !== organizationId ||\n !entry.predecessorStage.complete ||\n entry.predecessorStage.afterVersion !== entry.policy.version - 2\n : entry.policy.principalType !== \"group\" ||\n entry.policy.version !== 1 ||\n entry.policy.state.prevStateHash !== null ||\n entry.previousPrefixProgress !== null) ||\n entry.evidence.organizationId !== organizationId ||\n entry.prefix.scopeId !== entry.evidence.scopeId ||\n entry.prefix.version !== entry.policy.version ||\n entry.stage.afterVersion !== entry.policy.version - 1 ||\n !entry.stage.complete ||\n entry.stage.currentJson !== entry.prefix.currentJson\n )\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"Acknowledged current publication is outside its scope\",\n );\n }\n}\n\nasync function validatePublication(\n tx: ClientSQLiteTransactionScope,\n entry: AcknowledgedPrincipalCurrentPublication,\n) {\n const checkpoint = await loadStoredPrincipalPolicyCheckpoint(\n tx,\n entry.policy,\n );\n verifyPrincipalPolicyCheckpoint({\n chain: entry.policy.retainedHistory,\n currentState: entry.policy.state,\n localCheckpoint: checkpoint,\n });\n if (\n (!checkpoint && entry.policy.version !== 1) ||\n (checkpoint && entry.policy.version > checkpoint.version + 1)\n )\n throw new KeyingVerificationError(\n \"stale_predecessor\",\n \"Acknowledged current policy must directly extend its durable checkpoint\",\n );\n const [prefix] = await tx\n .select()\n .from(principalHistoryPrefixes)\n .where(eq(principalHistoryPrefixes.scopeId, entry.prefix.scopeId))\n .limit(1);\n if ((prefix?.progress ?? null) !== entry.previousPrefixProgress)\n throw new KeyingVerificationError(\n \"stale_predecessor\",\n \"Authenticated principal prefix changed before acknowledgement\",\n );\n}\n\nasync function archiveAcknowledgedStageKeys(\n tx: ClientSQLiteTransactionScope,\n entry: AcknowledgedPrincipalCurrentPublication,\n) {\n for (const stage of [entry.predecessorStage, entry.stage]) {\n if (!stage) continue;\n await recordPrincipalHistoryStageScope(tx, {\n id: stage.id,\n afterVersion: stage.afterVersion,\n complete: stage.complete,\n organizationId: entry.prefix.organizationId,\n scopeId: entry.prefix.scopeId,\n });\n await archivePrincipalHistoryKeyEnvelopes(tx, {\n ...stage,\n version: stage.afterVersion + 1,\n });\n }\n}\n\nasync function retainPredecessorStage(\n tx: ClientSQLiteTransactionScope,\n entry: AcknowledgedPrincipalCurrentPublication,\n) {\n // Preserve a prefix-only predecessor without replacing an existing completion.\n if (!entry.predecessorStage || !entry.predecessorIndexRootHash) return;\n const inserted = await tx\n .insert(principalHistoryStages)\n .values(entry.predecessorStage)\n .onConflictDoUpdate({\n target: principalHistoryStages.id,\n set: entry.predecessorStage,\n setWhere: eq(principalHistoryStages.complete, false),\n })\n .returning({ id: principalHistoryStages.id });\n if (inserted.length > 0)\n await retainPrincipalHistoryRoot(tx, {\n id: `stage:${entry.predecessorStage.id}`,\n scopeId: entry.prefix.scopeId,\n organizationId: entry.prefix.organizationId,\n rootHash: entry.predecessorIndexRootHash,\n });\n}\n\n/** Store current artifacts and resumable progress in the same transaction as their pins. */\nexport async function persistAcknowledgedPrincipalCurrents(input: {\n readonly execSql: ExecSql;\n readonly organizationId: string;\n readonly entries: readonly AcknowledgedPrincipalCurrentPublication[];\n readonly retirements?:\n | readonly AcknowledgedPrincipalCurrentRetirement[]\n | undefined;\n readonly stillCurrent: () => boolean;\n}): Promise<void> {\n const entries = structuredClone(input.entries);\n const retirements = structuredClone(input.retirements ?? []);\n const organizationId = input.organizationId;\n const stillCurrent = input.stillCurrent;\n assertPublicationScope(entries, organizationId);\n assertProjectionVerificationCurrent(stillCurrent);\n await ensureSqlTables(input.execSql, [\n ...principalPolicyTables,\n ...keyingCheckpointTables,\n ]);\n const runtime = getClientSQLitePersistenceRuntime(input.execSql);\n const stored = await runtime.guardedTransaction(\n async (tx) => {\n for (const entry of entries) await validatePublication(tx, entry);\n for (const retirement of retirements) {\n const policy = entries.find(\n (entry) =>\n entry.policy.principalId === retirement.principalId &&\n entry.policy.principalType === retirement.principalType,\n )?.policy;\n if (!policy)\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"Retirement must accompany its acknowledged principal\",\n );\n await storePrincipalGrantRetirements(\n tx,\n { ...retirement, policy, organizationId },\n entries.map((entry) => entry.policy),\n organizationId,\n );\n }\n for (const entry of entries) {\n await writePrincipalHistoryEvidencePage(tx, entry.evidence);\n await retainPredecessorStage(tx, entry);\n // A fully authenticated publication supersedes an in-flight stage for\n // this exact head. Its other writer fails its progress CAS and resumes.\n await tx\n .insert(principalHistoryStages)\n .values(entry.stage)\n .onConflictDoUpdate({\n target: principalHistoryStages.id,\n set: entry.stage,\n })\n .run();\n await archiveAcknowledgedStageKeys(tx, entry);\n await tx\n .insert(principalHistoryPrefixes)\n .values(entry.prefix)\n .onConflictDoUpdate({\n target: principalHistoryPrefixes.scopeId,\n set: entry.prefix,\n })\n .run();\n await reclaimCompletedPrincipalHistoryStages(tx, entry.prefix);\n for (const id of [\n `stage:${entry.stage.id}`,\n `prefix:${entry.prefix.scopeId}`,\n ])\n await retainPrincipalHistoryRoot(tx, {\n id,\n scopeId: entry.prefix.scopeId,\n organizationId,\n rootHash: entry.evidence.indexRootHash,\n });\n await reclaimPrincipalHistoryNodes(tx, entry.prefix);\n await upsertPrincipalPolicyCheckpointInTransaction(\n tx,\n entry.policy.checkpoint,\n new Date().toISOString(),\n organizationId,\n );\n }\n },\n stillCurrent,\n { behavior: \"immediate\" },\n );\n assertProjectionVerificationCurrent(() => stored.committed);\n}\n"]}
|
|
@@ -3,6 +3,7 @@ import { type PrincipalPolicyStateChainEntryResponse } from "../../internal/vali
|
|
|
3
3
|
import { type ClientSQLiteTransactionScope } from "../sqlite/sqlitePersistenceRuntime.js";
|
|
4
4
|
import { type ExecSql } from "../sqlite/sqlSchema.js";
|
|
5
5
|
export interface PrincipalHistoryEvidencePage {
|
|
6
|
+
readonly indexRootHash: string;
|
|
6
7
|
readonly scopeId: string;
|
|
7
8
|
readonly organizationId: string;
|
|
8
9
|
readonly entries: readonly {
|
|
@@ -13,6 +14,7 @@ export interface PrincipalHistoryEvidencePage {
|
|
|
13
14
|
}
|
|
14
15
|
/** Prepare only a page accepted by the signature/authorization verifier. */
|
|
15
16
|
export declare function preparePrincipalHistoryEvidencePage(input: {
|
|
17
|
+
readonly indexRootHash: string;
|
|
16
18
|
readonly scopeId: string;
|
|
17
19
|
readonly organizationId: string;
|
|
18
20
|
readonly entries: readonly PrincipalPolicyStateChainEntryResponse[];
|
|
@@ -4,6 +4,7 @@ import { and, eq } from "drizzle-orm";
|
|
|
4
4
|
import { principalHistoryEntries, principalHistoryEvidenceTables, principalHistoryNodes, } from "../sqlite/principalHistoryEvidenceSchema.js";
|
|
5
5
|
import { getClientSQLitePersistenceRuntime, } from "../sqlite/sqlitePersistenceRuntime.js";
|
|
6
6
|
import { ensureSqlTables } from "../sqlite/sqlSchema.js";
|
|
7
|
+
import { recordPrincipalHistoryNodeEdges } from "./principalHistoryNodeRetention.js";
|
|
7
8
|
/** Prepare only a page accepted by the signature/authorization verifier. */
|
|
8
9
|
export async function preparePrincipalHistoryEvidencePage(input) {
|
|
9
10
|
if (input.entries.length > PRINCIPAL_HISTORY_PAGE_ENTRY_LIMIT ||
|
|
@@ -36,6 +37,7 @@ export async function writePrincipalHistoryEvidencePage(tx, page) {
|
|
|
36
37
|
.run();
|
|
37
38
|
}
|
|
38
39
|
for (const node of [...page.nodes].sort((a, b) => compareCanonicalStrings(a.hash, b.hash))) {
|
|
40
|
+
await recordPrincipalHistoryNodeEdges(tx, scope, node);
|
|
39
41
|
const row = { ...scope, ...node };
|
|
40
42
|
await tx
|
|
41
43
|
.insert(principalHistoryNodes)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"principalHistoryEvidencePersistence.js","sourceRoot":"","sources":["principalHistoryEvidencePersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,kCAAkC,EAGlC,yBAAyB,EAEzB,iCAAiC,GAElC,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,wCAAwC,GAEzC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EACL,uBAAuB,EACvB,8BAA8B,EAC9B,qBAAqB,GACtB,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAEL,iCAAiC,GAClC,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAgB,eAAe,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"principalHistoryEvidencePersistence.js","sourceRoot":"","sources":["principalHistoryEvidencePersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n compareCanonicalStrings,\n KeyingVerificationError,\n PRINCIPAL_HISTORY_PAGE_ENTRY_LIMIT,\n type PrincipalHistoryIndexNode,\n type PrincipalPolicyHistoryReferenceProof,\n principalHistoryIndexLeaf,\n type ReferencedPrincipalHead,\n resolvePrincipalHistoryIndexProof,\n type VerifiedPrincipalPolicyHistory,\n} from \"@tearleads/crypto\";\nimport {\n isPrincipalPolicyStateChainEntryResponse,\n type PrincipalPolicyStateChainEntryResponse,\n} from \"@tearleads/validators/response\";\nimport { and, eq } from \"drizzle-orm\";\nimport {\n principalHistoryEntries,\n principalHistoryEvidenceTables,\n principalHistoryNodes,\n} from \"../sqlite/principalHistoryEvidenceSchema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../sqlite/sqlSchema\";\nimport { recordPrincipalHistoryNodeEdges } from \"./principalHistoryNodeRetention\";\n\nexport interface PrincipalHistoryEvidencePage {\n readonly indexRootHash: string;\n readonly scopeId: string;\n readonly organizationId: string;\n readonly entries: readonly {\n readonly leafHash: string;\n readonly entryJson: string;\n }[];\n readonly nodes: readonly PrincipalHistoryIndexNode[];\n}\n\n/** Prepare only a page accepted by the signature/authorization verifier. */\nexport async function preparePrincipalHistoryEvidencePage(input: {\n readonly indexRootHash: string;\n readonly scopeId: string;\n readonly organizationId: string;\n readonly entries: readonly PrincipalPolicyStateChainEntryResponse[];\n readonly nodes: readonly PrincipalHistoryIndexNode[];\n}): Promise<PrincipalHistoryEvidencePage> {\n if (\n input.entries.length > PRINCIPAL_HISTORY_PAGE_ENTRY_LIMIT ||\n input.nodes.length > 2 * PRINCIPAL_HISTORY_PAGE_ENTRY_LIMIT\n )\n throw new KeyingVerificationError(\n \"invalid_shape\",\n \"Principal evidence page exceeds its batch budget\",\n );\n const owned = structuredClone(input);\n return {\n ...owned,\n entries: await Promise.all(\n owned.entries.map(async (entry) => ({\n leafHash: await principalHistoryIndexLeaf(entry.state),\n entryJson: JSON.stringify(entry),\n })),\n ),\n };\n}\n\n/** Publish in the same guarded transaction as the accepted page's progress. */\nexport async function writePrincipalHistoryEvidencePage(\n tx: ClientSQLiteTransactionScope,\n page: PrincipalHistoryEvidencePage,\n): Promise<void> {\n const scope = { scopeId: page.scopeId, organizationId: page.organizationId };\n for (const entry of [...page.entries].sort((a, b) =>\n compareCanonicalStrings(a.leafHash, b.leafHash),\n )) {\n const row = { ...scope, ...entry };\n await tx\n .insert(principalHistoryEntries)\n .values(row)\n .onConflictDoUpdate({\n target: [\n principalHistoryEntries.scopeId,\n principalHistoryEntries.leafHash,\n ],\n set: row,\n })\n .run();\n }\n for (const node of [...page.nodes].sort((a, b) =>\n compareCanonicalStrings(a.hash, b.hash),\n )) {\n await recordPrincipalHistoryNodeEdges(tx, scope, node);\n const row = { ...scope, ...node };\n await tx\n .insert(principalHistoryNodes)\n .values(row)\n .onConflictDoUpdate({\n target: [principalHistoryNodes.scopeId, principalHistoryNodes.hash],\n set: row,\n })\n .run();\n }\n}\n\n/** Returns untrusted material. The caller must verify it against the issued root. */\nexport async function loadPrincipalHistoryReference(input: {\n readonly execSql: ExecSql;\n readonly scopeId: string;\n readonly history: VerifiedPrincipalPolicyHistory;\n readonly version: number;\n readonly expectedReference?: ReferencedPrincipalHead | undefined;\n}): Promise<PrincipalPolicyHistoryReferenceProof> {\n await ensureSqlTables(input.execSql, principalHistoryEvidenceTables);\n const { db } = getClientSQLitePersistenceRuntime(input.execSql);\n const resolved = await resolvePrincipalHistoryIndexProof({\n rootHash: input.history.indexRootHash,\n treeSize: input.history.currentEntry.state.version,\n version: input.version,\n readNode: async (hash) => {\n const [row] = await db\n .select()\n .from(principalHistoryNodes)\n .where(\n and(\n eq(principalHistoryNodes.scopeId, input.scopeId),\n eq(principalHistoryNodes.hash, hash),\n ),\n )\n .limit(1);\n return row ?? null;\n },\n });\n const [row] = await db\n .select()\n .from(principalHistoryEntries)\n .where(\n and(\n eq(principalHistoryEntries.scopeId, input.scopeId),\n eq(principalHistoryEntries.leafHash, resolved.leafHash),\n ),\n )\n .limit(1);\n let entry: unknown;\n try {\n entry = row ? JSON.parse(row.entryJson) : null;\n } catch {\n entry = null;\n }\n if (!isPrincipalPolicyStateChainEntryResponse(entry))\n throw new KeyingVerificationError(\n \"missing_dependency\",\n \"Principal history entry is missing or corrupt\",\n );\n const reference = input.expectedReference ?? {\n principalType: entry.state.principalType,\n principalId: entry.state.principalId,\n version: entry.state.version,\n keyEpoch: entry.state.keyEpoch,\n stateHash: entry.state.stateHash,\n keyFingerprint: entry.state.keyFingerprint,\n };\n return { reference, entry, proof: resolved.proof };\n}\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { and, asc, desc, eq, inArray, ne } from "drizzle-orm";
|
|
2
|
+
import { principalHistoryStageScopes } from "../sqlite/principalHistoryRetentionSchema.js";
|
|
3
|
+
import { principalHistoryStages } from "../sqlite/principalHistoryStageSchema.js";
|
|
4
|
+
import { releasePrincipalHistoryRoot } from "./principalHistoryRootOwnership.js";
|
|
5
|
+
const INCOMPLETE_STAGE_LIMIT = 8;
|
|
6
|
+
const RECLAIM_BATCH_LIMIT = 16;
|
|
7
|
+
/** Disposable work only: completed artifacts and trust pins are unaffected. */
|
|
8
|
+
export async function reclaimIncompletePrincipalHistoryStages(tx, current) {
|
|
9
|
+
const obsolete = await tx
|
|
10
|
+
.select({ id: principalHistoryStages.id })
|
|
11
|
+
.from(principalHistoryStageScopes)
|
|
12
|
+
.innerJoin(principalHistoryStages, eq(principalHistoryStageScopes.id, principalHistoryStages.id))
|
|
13
|
+
.where(and(eq(principalHistoryStageScopes.scopeId, current.scopeId), eq(principalHistoryStageScopes.organizationId, current.organizationId), eq(principalHistoryStageScopes.complete, false), eq(principalHistoryStages.organizationId, current.organizationId), eq(principalHistoryStages.complete, false), ne(principalHistoryStageScopes.id, current.id)))
|
|
14
|
+
.orderBy(desc(principalHistoryStageScopes.touchedAt), asc(principalHistoryStageScopes.id))
|
|
15
|
+
// The writer always keeps its own stage, even when recency hints tie.
|
|
16
|
+
.offset(INCOMPLETE_STAGE_LIMIT - (current.complete ? 0 : 1))
|
|
17
|
+
.limit(RECLAIM_BATCH_LIMIT);
|
|
18
|
+
const ids = obsolete.map(({ id }) => id);
|
|
19
|
+
for (const id of ids)
|
|
20
|
+
await releasePrincipalHistoryRoot(tx, `stage:${id}`);
|
|
21
|
+
if (ids.length === 0)
|
|
22
|
+
return;
|
|
23
|
+
await tx
|
|
24
|
+
.delete(principalHistoryStages)
|
|
25
|
+
.where(inArray(principalHistoryStages.id, ids))
|
|
26
|
+
.run();
|
|
27
|
+
await tx
|
|
28
|
+
.delete(principalHistoryStageScopes)
|
|
29
|
+
.where(inArray(principalHistoryStageScopes.id, ids))
|
|
30
|
+
.run();
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=principalHistoryIncompleteRetention.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"principalHistoryIncompleteRetention.js","sourceRoot":"","sources":["principalHistoryIncompleteRetention.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,2BAA2B,EAAE,MAAM,2CAA2C,CAAC;AACxF,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAE/E,OAAO,EAAE,2BAA2B,EAAE,MAAM,iCAAiC,CAAC;AAE9E,MAAM,sBAAsB,GAAG,CAAC,CAAC;AACjC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,uCAAuC,CAC3D,EAAgC,EAChC,OAKC;IAED,MAAM,QAAQ,GAAG,MAAM,EAAE;SACtB,MAAM,CAAC,EAAE,EAAE,EAAE,sBAAsB,CAAC,EAAE,EAAE,CAAC;SACzC,IAAI,CAAC,2BAA2B,CAAC;SACjC,SAAS,CACR,sBAAsB,EACtB,EAAE,CAAC,2BAA2B,CAAC,EAAE,EAAE,sBAAsB,CAAC,EAAE,CAAC,CAC9D;SACA,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,2BAA2B,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,EACxD,EAAE,CAAC,2BAA2B,CAAC,cAAc,EAAE,OAAO,CAAC,cAAc,CAAC,EACtE,EAAE,CAAC,2BAA2B,CAAC,QAAQ,EAAE,KAAK,CAAC,EAC/C,EAAE,CAAC,sBAAsB,CAAC,cAAc,EAAE,OAAO,CAAC,cAAc,CAAC,EACjE,EAAE,CAAC,sBAAsB,CAAC,QAAQ,EAAE,KAAK,CAAC,EAC1C,EAAE,CAAC,2BAA2B,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC,CAC/C,CACF;SACA,OAAO,CACN,IAAI,CAAC,2BAA2B,CAAC,SAAS,CAAC,EAC3C,GAAG,CAAC,2BAA2B,CAAC,EAAE,CAAC,CACpC;QACD,sEAAsE;SACrE,MAAM,CAAC,sBAAsB,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SAC3D,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAC9B,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;IACzC,KAAK,MAAM,EAAE,IAAI,GAAG;QAAE,MAAM,2BAA2B,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;IAC3E,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAC7B,MAAM,EAAE;SACL,MAAM,CAAC,sBAAsB,CAAC;SAC9B,KAAK,CAAC,OAAO,CAAC,sBAAsB,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;SAC9C,GAAG,EAAE,CAAC;IACT,MAAM,EAAE;SACL,MAAM,CAAC,2BAA2B,CAAC;SACnC,KAAK,CAAC,OAAO,CAAC,2BAA2B,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;SACnD,GAAG,EAAE,CAAC;AACX,CAAC","sourcesContent":["import { and, asc, desc, eq, inArray, ne } from \"drizzle-orm\";\nimport { principalHistoryStageScopes } from \"../sqlite/principalHistoryRetentionSchema\";\nimport { principalHistoryStages } from \"../sqlite/principalHistoryStageSchema\";\nimport type { ClientSQLiteTransactionScope } from \"../sqlite/sqlitePersistenceRuntime\";\nimport { releasePrincipalHistoryRoot } from \"./principalHistoryRootOwnership\";\n\nconst INCOMPLETE_STAGE_LIMIT = 8;\nconst RECLAIM_BATCH_LIMIT = 16;\n\n/** Disposable work only: completed artifacts and trust pins are unaffected. */\nexport async function reclaimIncompletePrincipalHistoryStages(\n tx: ClientSQLiteTransactionScope,\n current: {\n readonly id: string;\n readonly scopeId: string;\n readonly organizationId: string;\n readonly complete: boolean;\n },\n) {\n const obsolete = await tx\n .select({ id: principalHistoryStages.id })\n .from(principalHistoryStageScopes)\n .innerJoin(\n principalHistoryStages,\n eq(principalHistoryStageScopes.id, principalHistoryStages.id),\n )\n .where(\n and(\n eq(principalHistoryStageScopes.scopeId, current.scopeId),\n eq(principalHistoryStageScopes.organizationId, current.organizationId),\n eq(principalHistoryStageScopes.complete, false),\n eq(principalHistoryStages.organizationId, current.organizationId),\n eq(principalHistoryStages.complete, false),\n ne(principalHistoryStageScopes.id, current.id),\n ),\n )\n .orderBy(\n desc(principalHistoryStageScopes.touchedAt),\n asc(principalHistoryStageScopes.id),\n )\n // The writer always keeps its own stage, even when recency hints tie.\n .offset(INCOMPLETE_STAGE_LIMIT - (current.complete ? 0 : 1))\n .limit(RECLAIM_BATCH_LIMIT);\n const ids = obsolete.map(({ id }) => id);\n for (const id of ids) await releasePrincipalHistoryRoot(tx, `stage:${id}`);\n if (ids.length === 0) return;\n await tx\n .delete(principalHistoryStages)\n .where(inArray(principalHistoryStages.id, ids))\n .run();\n await tx\n .delete(principalHistoryStageScopes)\n .where(inArray(principalHistoryStageScopes.id, ids))\n .run();\n}\n"]}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { and, asc, eq } from "drizzle-orm";
|
|
2
|
+
import { principalHistoryNodes } from "../sqlite/principalHistoryEvidenceSchema.js";
|
|
3
|
+
import { principalHistoryNodeReferences } from "../sqlite/principalHistoryNodeRetentionSchema.js";
|
|
4
|
+
import { decrementPrincipalHistoryNodeReference, incrementPrincipalHistoryNodeReference, } from "./principalHistoryRootOwnership.js";
|
|
5
|
+
/** Register edges before inserting their proof node, preserving pre-index evidence. */
|
|
6
|
+
export async function recordPrincipalHistoryNodeEdges(tx, scope, node) {
|
|
7
|
+
const key = and(eq(principalHistoryNodeReferences.scopeId, scope.scopeId), eq(principalHistoryNodeReferences.hash, node.hash));
|
|
8
|
+
const [previous] = await tx
|
|
9
|
+
.select()
|
|
10
|
+
.from(principalHistoryNodeReferences)
|
|
11
|
+
.where(key)
|
|
12
|
+
.limit(1);
|
|
13
|
+
if (previous?.leftHash === node.leftHash &&
|
|
14
|
+
previous.rightHash === node.rightHash)
|
|
15
|
+
return;
|
|
16
|
+
const [existing] = await tx
|
|
17
|
+
.select({ hash: principalHistoryNodes.hash })
|
|
18
|
+
.from(principalHistoryNodes)
|
|
19
|
+
.where(and(eq(principalHistoryNodes.scopeId, scope.scopeId), eq(principalHistoryNodes.hash, node.hash)))
|
|
20
|
+
.limit(1);
|
|
21
|
+
for (const hash of [node.leftHash, node.rightHash])
|
|
22
|
+
await incrementPrincipalHistoryNodeReference(tx, scope, hash);
|
|
23
|
+
await tx
|
|
24
|
+
.insert(principalHistoryNodeReferences)
|
|
25
|
+
.values({
|
|
26
|
+
...scope,
|
|
27
|
+
...node,
|
|
28
|
+
referenceCount: previous?.referenceCount ?? 0,
|
|
29
|
+
managed: previous?.managed || !existing,
|
|
30
|
+
})
|
|
31
|
+
.onConflictDoUpdate({
|
|
32
|
+
target: [
|
|
33
|
+
principalHistoryNodeReferences.scopeId,
|
|
34
|
+
principalHistoryNodeReferences.hash,
|
|
35
|
+
],
|
|
36
|
+
set: {
|
|
37
|
+
leftHash: node.leftHash,
|
|
38
|
+
rightHash: node.rightHash,
|
|
39
|
+
managed: previous?.managed || !existing,
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
.run();
|
|
43
|
+
for (const hash of [previous?.leftHash, previous?.rightHash])
|
|
44
|
+
if (hash)
|
|
45
|
+
await decrementPrincipalHistoryNodeReference(tx, scope, hash);
|
|
46
|
+
}
|
|
47
|
+
/** A bounded cascade releases only managed proof nodes with no live incoming edge. */
|
|
48
|
+
export async function reclaimPrincipalHistoryNodes(tx, scope) {
|
|
49
|
+
let reclaimed = 0;
|
|
50
|
+
while (reclaimed < 64) {
|
|
51
|
+
const obsolete = await tx
|
|
52
|
+
.select()
|
|
53
|
+
.from(principalHistoryNodeReferences)
|
|
54
|
+
.where(and(eq(principalHistoryNodeReferences.scopeId, scope.scopeId), eq(principalHistoryNodeReferences.organizationId, scope.organizationId), eq(principalHistoryNodeReferences.managed, true), eq(principalHistoryNodeReferences.referenceCount, 0)))
|
|
55
|
+
.orderBy(asc(principalHistoryNodeReferences.hash))
|
|
56
|
+
.limit(64 - reclaimed);
|
|
57
|
+
if (obsolete.length === 0)
|
|
58
|
+
break;
|
|
59
|
+
for (const node of obsolete) {
|
|
60
|
+
await tx
|
|
61
|
+
.delete(principalHistoryNodes)
|
|
62
|
+
.where(and(eq(principalHistoryNodes.scopeId, scope.scopeId), eq(principalHistoryNodes.organizationId, scope.organizationId), eq(principalHistoryNodes.hash, node.hash)))
|
|
63
|
+
.run();
|
|
64
|
+
await tx
|
|
65
|
+
.delete(principalHistoryNodeReferences)
|
|
66
|
+
.where(and(eq(principalHistoryNodeReferences.scopeId, scope.scopeId), eq(principalHistoryNodeReferences.hash, node.hash)))
|
|
67
|
+
.run();
|
|
68
|
+
for (const hash of [node.leftHash, node.rightHash])
|
|
69
|
+
if (hash)
|
|
70
|
+
await decrementPrincipalHistoryNodeReference(tx, scope, hash);
|
|
71
|
+
reclaimed += 1;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return reclaimed;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=principalHistoryNodeRetention.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"principalHistoryNodeRetention.js","sourceRoot":"","sources":["principalHistoryNodeRetention.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PrincipalHistoryIndexNode } from \"@tearleads/crypto\";\nimport { and, asc, eq } from \"drizzle-orm\";\nimport { principalHistoryNodes } from \"../sqlite/principalHistoryEvidenceSchema\";\nimport { principalHistoryNodeReferences } from \"../sqlite/principalHistoryNodeRetentionSchema\";\nimport type { ClientSQLiteTransactionScope } from \"../sqlite/sqlitePersistenceRuntime\";\nimport {\n decrementPrincipalHistoryNodeReference,\n incrementPrincipalHistoryNodeReference,\n type PrincipalHistoryNodeScope,\n} from \"./principalHistoryRootOwnership\";\n\n/** Register edges before inserting their proof node, preserving pre-index evidence. */\nexport async function recordPrincipalHistoryNodeEdges(\n tx: ClientSQLiteTransactionScope,\n scope: PrincipalHistoryNodeScope,\n node: PrincipalHistoryIndexNode,\n) {\n const key = and(\n eq(principalHistoryNodeReferences.scopeId, scope.scopeId),\n eq(principalHistoryNodeReferences.hash, node.hash),\n );\n const [previous] = await tx\n .select()\n .from(principalHistoryNodeReferences)\n .where(key)\n .limit(1);\n if (\n previous?.leftHash === node.leftHash &&\n previous.rightHash === node.rightHash\n )\n return;\n const [existing] = await tx\n .select({ hash: principalHistoryNodes.hash })\n .from(principalHistoryNodes)\n .where(\n and(\n eq(principalHistoryNodes.scopeId, scope.scopeId),\n eq(principalHistoryNodes.hash, node.hash),\n ),\n )\n .limit(1);\n for (const hash of [node.leftHash, node.rightHash])\n await incrementPrincipalHistoryNodeReference(tx, scope, hash);\n await tx\n .insert(principalHistoryNodeReferences)\n .values({\n ...scope,\n ...node,\n referenceCount: previous?.referenceCount ?? 0,\n managed: previous?.managed || !existing,\n })\n .onConflictDoUpdate({\n target: [\n principalHistoryNodeReferences.scopeId,\n principalHistoryNodeReferences.hash,\n ],\n set: {\n leftHash: node.leftHash,\n rightHash: node.rightHash,\n managed: previous?.managed || !existing,\n },\n })\n .run();\n for (const hash of [previous?.leftHash, previous?.rightHash])\n if (hash) await decrementPrincipalHistoryNodeReference(tx, scope, hash);\n}\n\n/** A bounded cascade releases only managed proof nodes with no live incoming edge. */\nexport async function reclaimPrincipalHistoryNodes(\n tx: ClientSQLiteTransactionScope,\n scope: PrincipalHistoryNodeScope,\n): Promise<number> {\n let reclaimed = 0;\n while (reclaimed < 64) {\n const obsolete = await tx\n .select()\n .from(principalHistoryNodeReferences)\n .where(\n and(\n eq(principalHistoryNodeReferences.scopeId, scope.scopeId),\n eq(\n principalHistoryNodeReferences.organizationId,\n scope.organizationId,\n ),\n eq(principalHistoryNodeReferences.managed, true),\n eq(principalHistoryNodeReferences.referenceCount, 0),\n ),\n )\n .orderBy(asc(principalHistoryNodeReferences.hash))\n .limit(64 - reclaimed);\n if (obsolete.length === 0) break;\n for (const node of obsolete) {\n await tx\n .delete(principalHistoryNodes)\n .where(\n and(\n eq(principalHistoryNodes.scopeId, scope.scopeId),\n eq(principalHistoryNodes.organizationId, scope.organizationId),\n eq(principalHistoryNodes.hash, node.hash),\n ),\n )\n .run();\n await tx\n .delete(principalHistoryNodeReferences)\n .where(\n and(\n eq(principalHistoryNodeReferences.scopeId, scope.scopeId),\n eq(principalHistoryNodeReferences.hash, node.hash),\n ),\n )\n .run();\n for (const hash of [node.leftHash, node.rightHash])\n if (hash) await decrementPrincipalHistoryNodeReference(tx, scope, hash);\n reclaimed += 1;\n }\n }\n return reclaimed;\n}\n"]}
|
|
@@ -13,6 +13,7 @@ export declare function loadPrincipalHistoryPrefix(execSql: ExecSql, scopeId: st
|
|
|
13
13
|
export declare function savePrincipalHistoryPrefix(input: {
|
|
14
14
|
readonly execSql: ExecSql;
|
|
15
15
|
readonly prefix: PrincipalHistoryPrefix;
|
|
16
|
+
readonly indexRootHash: string;
|
|
16
17
|
readonly stillCurrent: () => boolean;
|
|
17
18
|
readonly rejectedPrefix?: PrincipalHistoryPrefix | null;
|
|
18
19
|
}): Promise<void>;
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { and, eq } from "drizzle-orm";
|
|
2
2
|
import { assertProjectionVerificationCurrent } from "../keyingProjectionVerification/types.js";
|
|
3
3
|
import { principalHistoryEvidenceTables, principalHistoryPrefixes, } from "../sqlite/principalHistoryEvidenceSchema.js";
|
|
4
|
+
import { principalHistoryStageTables } from "../sqlite/principalHistoryStageSchema.js";
|
|
4
5
|
import { getClientSQLitePersistenceRuntime } from "../sqlite/sqlitePersistenceRuntime.js";
|
|
5
6
|
import { ensureSqlTables } from "../sqlite/sqlSchema.js";
|
|
7
|
+
import { reclaimPrincipalHistoryNodes } from "./principalHistoryNodeRetention.js";
|
|
8
|
+
import { releasePrincipalHistoryRoot, retainPrincipalHistoryRoot, } from "./principalHistoryRootOwnership.js";
|
|
9
|
+
import { reclaimCompletedPrincipalHistoryStages } from "./principalHistoryStageRetention.js";
|
|
10
|
+
import { archivePrincipalHistoryKeyEnvelopes } from "./principalKeyEnvelopeArchivePersistence.js";
|
|
6
11
|
export async function loadPrincipalHistoryPrefix(execSql, scopeId) {
|
|
7
12
|
await ensureSqlTables(execSql, principalHistoryEvidenceTables);
|
|
8
13
|
const { db } = getClientSQLitePersistenceRuntime(execSql);
|
|
@@ -16,8 +21,12 @@ export async function loadPrincipalHistoryPrefix(execSql, scopeId) {
|
|
|
16
21
|
/** Keep the newest prefix unless replay replaces the exact rejected candidate. */
|
|
17
22
|
export async function savePrincipalHistoryPrefix(input) {
|
|
18
23
|
const prefix = { ...input.prefix };
|
|
24
|
+
const indexRootHash = input.indexRootHash;
|
|
19
25
|
const rejected = input.rejectedPrefix ? { ...input.rejectedPrefix } : null;
|
|
20
|
-
await ensureSqlTables(input.execSql,
|
|
26
|
+
await ensureSqlTables(input.execSql, [
|
|
27
|
+
...principalHistoryEvidenceTables,
|
|
28
|
+
...principalHistoryStageTables,
|
|
29
|
+
]);
|
|
21
30
|
const runtime = getClientSQLitePersistenceRuntime(input.execSql);
|
|
22
31
|
const result = await runtime.guardedTransaction(async (tx) => {
|
|
23
32
|
const [previous] = await tx
|
|
@@ -30,6 +39,9 @@ export async function savePrincipalHistoryPrefix(input) {
|
|
|
30
39
|
!(rejected?.scopeId === previous.scopeId &&
|
|
31
40
|
rejected.progress === previous.progress))
|
|
32
41
|
return;
|
|
42
|
+
if (previous)
|
|
43
|
+
await archivePrincipalHistoryKeyEnvelopes(tx, previous);
|
|
44
|
+
await archivePrincipalHistoryKeyEnvelopes(tx, prefix);
|
|
33
45
|
await tx
|
|
34
46
|
.insert(principalHistoryPrefixes)
|
|
35
47
|
.values(prefix)
|
|
@@ -38,14 +50,31 @@ export async function savePrincipalHistoryPrefix(input) {
|
|
|
38
50
|
set: prefix,
|
|
39
51
|
})
|
|
40
52
|
.run();
|
|
53
|
+
await reclaimCompletedPrincipalHistoryStages(tx, prefix);
|
|
54
|
+
await retainPrincipalHistoryRoot(tx, {
|
|
55
|
+
id: `prefix:${prefix.scopeId}`,
|
|
56
|
+
scopeId: prefix.scopeId,
|
|
57
|
+
organizationId: prefix.organizationId,
|
|
58
|
+
rootHash: indexRootHash,
|
|
59
|
+
});
|
|
60
|
+
await reclaimPrincipalHistoryNodes(tx, prefix);
|
|
41
61
|
}, input.stillCurrent, { behavior: "immediate" });
|
|
42
62
|
assertProjectionVerificationCurrent(() => result.committed);
|
|
43
63
|
}
|
|
44
64
|
/** Invalid hints are disposable, but a losing reader must preserve a newer hint. */
|
|
45
65
|
export async function discardPrincipalHistoryPrefix(input) {
|
|
46
66
|
const prefix = { ...input.prefix };
|
|
67
|
+
await ensureSqlTables(input.execSql, principalHistoryEvidenceTables);
|
|
47
68
|
const runtime = getClientSQLitePersistenceRuntime(input.execSql);
|
|
48
69
|
const result = await runtime.guardedTransaction(async (tx) => {
|
|
70
|
+
const [current] = await tx
|
|
71
|
+
.select({ progress: principalHistoryPrefixes.progress })
|
|
72
|
+
.from(principalHistoryPrefixes)
|
|
73
|
+
.where(eq(principalHistoryPrefixes.scopeId, prefix.scopeId))
|
|
74
|
+
.limit(1);
|
|
75
|
+
if (current?.progress !== prefix.progress)
|
|
76
|
+
return;
|
|
77
|
+
await releasePrincipalHistoryRoot(tx, `prefix:${prefix.scopeId}`);
|
|
49
78
|
await tx
|
|
50
79
|
.delete(principalHistoryPrefixes)
|
|
51
80
|
.where(and(eq(principalHistoryPrefixes.scopeId, prefix.scopeId), eq(principalHistoryPrefixes.progress, prefix.progress)))
|